aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorredmatrix <git@macgirvin.com>2016-05-10 17:26:44 -0700
committerredmatrix <git@macgirvin.com>2016-05-10 17:26:44 -0700
commit0b02a6d123b2014705998c94ddf3d460948d3eac (patch)
tree78ff2cab9944a4f5ab3f80ec93cbe1120de90bb2
parent40b5b6e9d2da7ab65c8b4d38cdceac83a4d78deb (diff)
downloadvolse-hubzilla-0b02a6d123b2014705998c94ddf3d460948d3eac.tar.gz
volse-hubzilla-0b02a6d123b2014705998c94ddf3d460948d3eac.tar.bz2
volse-hubzilla-0b02a6d123b2014705998c94ddf3d460948d3eac.zip
initial sabre upgrade (needs lots of work - to wit: authentication, redo the browser interface, and rework event export/import)
-rw-r--r--Zotlabs/Module/Dav.php16
-rw-r--r--Zotlabs/Storage/Browser.php10
-rw-r--r--include/event.php20
-rw-r--r--vendor/autoload.php2
l---------vendor/bin/generate_vcards1
l---------vendor/bin/naturalselection1
l---------vendor/bin/sabredav1
l---------vendor/bin/vobject1
-rw-r--r--vendor/composer/ClassLoader.php44
-rw-r--r--vendor/composer/LICENSE21
-rw-r--r--vendor/composer/autoload_files.php16
-rw-r--r--vendor/composer/autoload_namespaces.php6
-rw-r--r--vendor/composer/autoload_psr4.php9
-rw-r--r--vendor/composer/autoload_real.php52
-rw-r--r--vendor/composer/autoload_static.php81
-rw-r--r--vendor/composer/installed.json361
-rw-r--r--vendor/sabre/dav/.gitignore47
-rw-r--r--vendor/sabre/dav/.travis.yml29
-rw-r--r--vendor/sabre/dav/CHANGELOG.md2242
-rw-r--r--vendor/sabre/dav/CONTRIBUTING.md87
-rw-r--r--vendor/sabre/dav/ChangeLog1164
-rw-r--r--vendor/sabre/dav/LICENSE2
-rw-r--r--vendor/sabre/dav/README.md59
-rwxr-xr-x[-rw-r--r--]vendor/sabre/dav/bin/build.php60
-rwxr-xr-xvendor/sabre/dav/bin/migrateto17.php54
-rwxr-xr-xvendor/sabre/dav/bin/migrateto20.php453
-rwxr-xr-xvendor/sabre/dav/bin/migrateto21.php180
-rwxr-xr-xvendor/sabre/dav/bin/migrateto30.php171
-rwxr-xr-xvendor/sabre/dav/bin/naturalselection (renamed from vendor/sabre/dav/bin/naturalselection.py)30
-rwxr-xr-xvendor/sabre/dav/bin/sabredav.php10
-rw-r--r--vendor/sabre/dav/examples/addressbookserver.php13
-rw-r--r--vendor/sabre/dav/examples/basicauth.php26
-rw-r--r--vendor/sabre/dav/examples/digestauth.php25
-rw-r--r--vendor/sabre/dav/examples/simplefsserver.php123
-rw-r--r--vendor/sabre/dav/examples/sql/mysql.addressbook.sql26
-rw-r--r--vendor/sabre/dav/examples/sql/mysql.calendars.sql60
-rw-r--r--vendor/sabre/dav/examples/sql/mysql.locks.sql9
-rw-r--r--vendor/sabre/dav/examples/sql/mysql.principals.sql10
-rw-r--r--vendor/sabre/dav/examples/sql/mysql.users.sql6
-rw-r--r--vendor/sabre/dav/examples/sql/pgsql.addressbook.sql23
-rw-r--r--vendor/sabre/dav/examples/sql/pgsql.calendars.sql59
-rw-r--r--vendor/sabre/dav/examples/sql/pgsql.locks.sql12
-rw-r--r--vendor/sabre/dav/examples/sql/pgsql.principals.sql12
-rw-r--r--vendor/sabre/dav/examples/sql/pgsql.users.sql3
-rw-r--r--vendor/sabre/dav/examples/sql/sqlite.addressbooks.sql25
-rw-r--r--vendor/sabre/dav/examples/sql/sqlite.calendars.sql64
-rw-r--r--vendor/sabre/dav/examples/sql/sqlite.locks.sql2
-rw-r--r--vendor/sabre/dav/examples/sql/sqlite.principals.sql11
-rw-r--r--vendor/sabre/dav/examples/sql/sqlite.users.sql6
-rw-r--r--vendor/sabre/dav/examples/webserver/apache2_vhost.conf4
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php226
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Backend/BackendInterface.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Backend/BackendInterface.php)137
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Backend/NotificationSupport.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Backend/NotificationSupport.php)15
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Backend/PDO.php1210
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Backend/SchedulingSupport.php65
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Backend/SharingSupport.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Backend/SharingSupport.php)8
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Backend/SubscriptionSupport.php89
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Backend/SyncSupport.php81
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Calendar.php527
-rw-r--r--vendor/sabre/dav/lib/CalDAV/CalendarHome.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/UserCalendars.php)196
-rw-r--r--vendor/sabre/dav/lib/CalDAV/CalendarObject.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/CalendarObject.php)85
-rw-r--r--vendor/sabre/dav/lib/CalDAV/CalendarQueryValidator.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/CalendarQueryValidator.php)63
-rw-r--r--vendor/sabre/dav/lib/CalDAV/CalendarRoot.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/CalendarRootNode.php)15
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Exception/InvalidComponentType.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Exception/InvalidComponentType.php)6
-rw-r--r--vendor/sabre/dav/lib/CalDAV/ICSExportPlugin.php366
-rw-r--r--vendor/sabre/dav/lib/CalDAV/ICalendar.php18
-rw-r--r--vendor/sabre/dav/lib/CalDAV/ICalendarObject.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/ICalendarObject.php)4
-rw-r--r--vendor/sabre/dav/lib/CalDAV/ICalendarObjectContainer.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/ICalendar.php)15
-rw-r--r--vendor/sabre/dav/lib/CalDAV/IShareableCalendar.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/IShareableCalendar.php)2
-rw-r--r--vendor/sabre/dav/lib/CalDAV/ISharedCalendar.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/ISharedCalendar.php)2
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Notifications/Collection.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/Collection.php)34
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Notifications/ICollection.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/ICollection.php)3
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Notifications/INode.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/INode.php)2
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Notifications/Node.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/Node.php)39
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Notifications/Plugin.php180
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Plugin.php1025
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Principal/Collection.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Principal/Collection.php)7
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Principal/IProxyRead.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Principal/IProxyRead.php)2
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Principal/IProxyWrite.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Principal/IProxyWrite.php)2
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Principal/ProxyRead.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Principal/ProxyRead.php)27
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Principal/ProxyWrite.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Principal/ProxyWrite.php)27
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Principal/User.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Principal/User.php)27
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Schedule/IInbox.php15
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Schedule/IMipPlugin.php190
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Schedule/IOutbox.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Schedule/IOutbox.php)3
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Schedule/ISchedulingObject.php13
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Schedule/Inbox.php261
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Schedule/Outbox.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Schedule/Outbox.php)65
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Schedule/Plugin.php994
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Schedule/SchedulingObject.php165
-rw-r--r--vendor/sabre/dav/lib/CalDAV/ShareableCalendar.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/ShareableCalendar.php)8
-rw-r--r--vendor/sabre/dav/lib/CalDAV/SharedCalendar.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/SharedCalendar.php)62
-rw-r--r--vendor/sabre/dav/lib/CalDAV/SharingPlugin.php426
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Subscriptions/ISubscription.php40
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Subscriptions/Plugin.php120
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Subscriptions/Subscription.php274
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Filter/CalendarData.php84
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Filter/CompFilter.php97
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Filter/ParamFilter.php82
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Filter/PropFilter.php98
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Notification/Invite.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/Notification/Invite.php)165
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Notification/InviteReply.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/Notification/InviteReply.php)102
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Notification/NotificationInterface.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/INotificationType.php)15
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Notification/SystemStatus.php (renamed from vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/Notification/SystemStatus.php)68
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Property/AllowedSharingModes.php87
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Property/EmailAddressSet.php80
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Property/Invite.php252
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Property/ScheduleCalendarTransp.php140
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarComponentSet.php129
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarData.php60
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Property/SupportedCollationSet.php57
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php124
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php139
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Request/FreeBusyQueryReport.php91
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Request/InviteReply.php149
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php79
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Xml/Request/Share.php116
-rw-r--r--vendor/sabre/dav/lib/CardDAV/AddressBook.php433
-rw-r--r--vendor/sabre/dav/lib/CardDAV/AddressBookHome.php (renamed from vendor/sabre/dav/lib/Sabre/CardDAV/UserAddressBooks.php)77
-rw-r--r--vendor/sabre/dav/lib/CardDAV/AddressBookRoot.php (renamed from vendor/sabre/dav/lib/Sabre/CardDAV/AddressBookRoot.php)10
-rw-r--r--vendor/sabre/dav/lib/CardDAV/Backend/AbstractBackend.php38
-rw-r--r--vendor/sabre/dav/lib/CardDAV/Backend/BackendInterface.php (renamed from vendor/sabre/dav/lib/Sabre/CardDAV/Backend/BackendInterface.php)59
-rw-r--r--vendor/sabre/dav/lib/CardDAV/Backend/PDO.php545
-rw-r--r--vendor/sabre/dav/lib/CardDAV/Backend/SyncSupport.php81
-rw-r--r--vendor/sabre/dav/lib/CardDAV/Card.php (renamed from vendor/sabre/dav/lib/Sabre/CardDAV/Card.php)57
-rw-r--r--vendor/sabre/dav/lib/CardDAV/IAddressBook.php (renamed from vendor/sabre/dav/lib/Sabre/CardDAV/IAddressBook.php)4
-rw-r--r--vendor/sabre/dav/lib/CardDAV/ICard.php (renamed from vendor/sabre/dav/lib/Sabre/CardDAV/ICard.php)3
-rw-r--r--vendor/sabre/dav/lib/CardDAV/IDirectory.php (renamed from vendor/sabre/dav/lib/Sabre/CardDAV/IDirectory.php)3
-rw-r--r--vendor/sabre/dav/lib/CardDAV/Plugin.php871
-rw-r--r--vendor/sabre/dav/lib/CardDAV/VCFExportPlugin.php152
-rw-r--r--vendor/sabre/dav/lib/CardDAV/Xml/Filter/AddressData.php59
-rw-r--r--vendor/sabre/dav/lib/CardDAV/Xml/Filter/ParamFilter.php89
-rw-r--r--vendor/sabre/dav/lib/CardDAV/Xml/Filter/PropFilter.php98
-rw-r--r--vendor/sabre/dav/lib/CardDAV/Xml/Property/SupportedAddressData.php83
-rw-r--r--vendor/sabre/dav/lib/CardDAV/Xml/Property/SupportedCollationSet.php47
-rw-r--r--vendor/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php113
-rw-r--r--vendor/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php192
-rw-r--r--vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractBasic.php144
-rw-r--r--vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractBearer.php138
-rw-r--r--vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php162
-rw-r--r--vendor/sabre/dav/lib/DAV/Auth/Backend/Apache.php96
-rw-r--r--vendor/sabre/dav/lib/DAV/Auth/Backend/BackendInterface.php70
-rw-r--r--vendor/sabre/dav/lib/DAV/Auth/Backend/BasicCallBack.php58
-rw-r--r--vendor/sabre/dav/lib/DAV/Auth/Backend/File.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/File.php)16
-rw-r--r--vendor/sabre/dav/lib/DAV/Auth/Backend/PDO.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/PDO.php)24
-rw-r--r--vendor/sabre/dav/lib/DAV/Auth/Plugin.php213
-rw-r--r--vendor/sabre/dav/lib/DAV/Browser/GuessContentType.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Browser/GuessContentType.php)44
-rw-r--r--vendor/sabre/dav/lib/DAV/Browser/HtmlOutput.php34
-rw-r--r--vendor/sabre/dav/lib/DAV/Browser/HtmlOutputHelper.php117
-rw-r--r--vendor/sabre/dav/lib/DAV/Browser/MapGetToPropFind.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Browser/MapGetToPropFind.php)23
-rw-r--r--vendor/sabre/dav/lib/DAV/Browser/Plugin.php797
-rw-r--r--vendor/sabre/dav/lib/DAV/Browser/PropFindAll.php132
-rw-r--r--vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/ICON-LICENSE21
-rw-r--r--vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.css510
-rw-r--r--vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.eotbin0 -> 23144 bytes
-rw-r--r--vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.otfbin0 -> 21048 bytes
-rw-r--r--vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.svg543
-rw-r--r--vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.ttfbin0 -> 25568 bytes
-rw-r--r--vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.woffbin0 -> 12404 bytes
-rw-r--r--vendor/sabre/dav/lib/DAV/Browser/assets/sabredav.css228
-rw-r--r--vendor/sabre/dav/lib/DAV/Browser/assets/sabredav.pngbin0 -> 2825 bytes
-rw-r--r--vendor/sabre/dav/lib/DAV/Client.php448
-rw-r--r--vendor/sabre/dav/lib/DAV/Collection.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Collection.php)17
-rw-r--r--vendor/sabre/dav/lib/DAV/CorePlugin.php927
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception.php)23
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/BadRequest.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/BadRequest.php)8
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/Conflict.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/Conflict.php)8
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/ConflictingLock.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/ConflictingLock.php)9
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/Forbidden.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/Forbidden.php)8
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/InsufficientStorage.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/InsufficientStorage.php)8
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/InvalidResourceType.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/InvalidResourceType.php)6
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/InvalidSyncToken.php38
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/LengthRequired.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/LengthRequired.php)4
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/LockTokenMatchesRequestUri.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php)8
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/Locked.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/Locked.php)13
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/MethodNotAllowed.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/MethodNotAllowed.php)16
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/NotAuthenticated.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/NotAuthenticated.php)4
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/NotFound.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/NotFound.php)9
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/NotImplemented.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/NotImplemented.php)8
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/PaymentRequired.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/PaymentRequired.php)4
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/PreconditionFailed.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/PreconditionFailed.php)8
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/ReportNotSupported.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/ReportNotSupported.php)8
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/RequestedRangeNotSatisfiable.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php)5
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/ServiceUnavailable.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/ServiceUnavailable.php)18
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/TooManyMatches.php38
-rw-r--r--vendor/sabre/dav/lib/DAV/Exception/UnsupportedMediaType.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Exception/UnsupportedMediaType.php)8
-rw-r--r--vendor/sabre/dav/lib/DAV/FS/Directory.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/FS/Directory.php)45
-rw-r--r--vendor/sabre/dav/lib/DAV/FS/File.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/FS/File.php)28
-rw-r--r--vendor/sabre/dav/lib/DAV/FS/Node.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/FS/Node.php)22
-rw-r--r--vendor/sabre/dav/lib/DAV/FSExt/Directory.php219
-rw-r--r--vendor/sabre/dav/lib/DAV/FSExt/File.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/FSExt/File.php)48
-rw-r--r--vendor/sabre/dav/lib/DAV/File.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/File.php)13
-rw-r--r--vendor/sabre/dav/lib/DAV/ICollection.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/ICollection.php)3
-rw-r--r--vendor/sabre/dav/lib/DAV/IExtendedCollection.php43
-rw-r--r--vendor/sabre/dav/lib/DAV/IFile.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/IFile.php)14
-rw-r--r--vendor/sabre/dav/lib/DAV/IMoveTarget.php44
-rw-r--r--vendor/sabre/dav/lib/DAV/IMultiGet.php36
-rw-r--r--vendor/sabre/dav/lib/DAV/INode.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/INode.php)3
-rw-r--r--vendor/sabre/dav/lib/DAV/IProperties.php47
-rw-r--r--vendor/sabre/dav/lib/DAV/IQuota.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/IQuota.php)3
-rw-r--r--vendor/sabre/dav/lib/DAV/Locks/Backend/AbstractBackend.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/AbstractBackend.php)5
-rw-r--r--vendor/sabre/dav/lib/DAV/Locks/Backend/BackendInterface.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/BackendInterface.php)9
-rw-r--r--vendor/sabre/dav/lib/DAV/Locks/Backend/File.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/File.php)52
-rw-r--r--vendor/sabre/dav/lib/DAV/Locks/Backend/PDO.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/PDO.php)85
-rw-r--r--vendor/sabre/dav/lib/DAV/Locks/LockInfo.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Locks/LockInfo.php)3
-rw-r--r--vendor/sabre/dav/lib/DAV/Locks/Plugin.php589
-rw-r--r--vendor/sabre/dav/lib/DAV/MkCol.php71
-rw-r--r--vendor/sabre/dav/lib/DAV/Mount/Plugin.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Mount/Plugin.php)33
-rw-r--r--vendor/sabre/dav/lib/DAV/Node.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Node.php)15
-rw-r--r--vendor/sabre/dav/lib/DAV/PartialUpdate/IPatchSupport.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/PartialUpdate/IPatchSupport.php)3
-rw-r--r--vendor/sabre/dav/lib/DAV/PartialUpdate/Plugin.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/PartialUpdate/Plugin.php)123
-rw-r--r--vendor/sabre/dav/lib/DAV/PropFind.php347
-rw-r--r--vendor/sabre/dav/lib/DAV/PropPatch.php373
-rw-r--r--vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php80
-rw-r--r--vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php217
-rw-r--r--vendor/sabre/dav/lib/DAV/PropertyStorage/Plugin.php180
-rw-r--r--vendor/sabre/dav/lib/DAV/Server.php1627
-rw-r--r--vendor/sabre/dav/lib/DAV/ServerPlugin.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/ServerPlugin.php)42
-rw-r--r--vendor/sabre/dav/lib/DAV/SimpleCollection.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/SimpleCollection.php)23
-rw-r--r--vendor/sabre/dav/lib/DAV/SimpleFile.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/SimpleFile.php)18
-rw-r--r--vendor/sabre/dav/lib/DAV/StringUtil.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/StringUtil.php)22
-rw-r--r--vendor/sabre/dav/lib/DAV/Sync/ISyncCollection.php88
-rw-r--r--vendor/sabre/dav/lib/DAV/Sync/Plugin.php277
-rw-r--r--vendor/sabre/dav/lib/DAV/TemporaryFileFilterPlugin.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/TemporaryFileFilterPlugin.php)138
-rw-r--r--vendor/sabre/dav/lib/DAV/Tree.php340
-rw-r--r--vendor/sabre/dav/lib/DAV/UUIDUtil.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/UUIDUtil.php)16
-rw-r--r--vendor/sabre/dav/lib/DAV/Version.php (renamed from vendor/sabre/dav/lib/Sabre/DAV/Version.php)9
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Element/Prop.php116
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Element/Response.php253
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Property/Complex.php89
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Property/GetLastModified.php110
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Property/Href.php176
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Property/LockDiscovery.php106
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Property/ResourceType.php128
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Property/SupportedLock.php54
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Property/SupportedMethodSet.php126
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Property/SupportedReportSet.php154
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Request/Lock.php81
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Request/MkCol.php82
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Request/PropFind.php83
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Request/PropPatch.php118
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php122
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php142
-rw-r--r--vendor/sabre/dav/lib/DAV/Xml/Service.php47
-rw-r--r--vendor/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php (renamed from vendor/sabre/dav/lib/Sabre/DAVACL/AbstractPrincipalCollection.php)68
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Exception/AceConflict.php (renamed from vendor/sabre/dav/lib/Sabre/DAVACL/Exception/AceConflict.php)6
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php (renamed from vendor/sabre/dav/lib/Sabre/DAVACL/Exception/NeedPrivileges.php)21
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Exception/NoAbstract.php (renamed from vendor/sabre/dav/lib/Sabre/DAVACL/Exception/NoAbstract.php)6
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php (renamed from vendor/sabre/dav/lib/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php)6
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php (renamed from vendor/sabre/dav/lib/Sabre/DAVACL/Exception/NotSupportedPrivilege.php)6
-rw-r--r--vendor/sabre/dav/lib/DAVACL/FS/Collection.php153
-rw-r--r--vendor/sabre/dav/lib/DAVACL/FS/File.php123
-rw-r--r--vendor/sabre/dav/lib/DAVACL/FS/HomeCollection.php188
-rw-r--r--vendor/sabre/dav/lib/DAVACL/IACL.php (renamed from vendor/sabre/dav/lib/Sabre/DAVACL/IACL.php)3
-rw-r--r--vendor/sabre/dav/lib/DAVACL/IPrincipal.php (renamed from vendor/sabre/dav/lib/Sabre/DAVACL/IPrincipal.php)2
-rw-r--r--vendor/sabre/dav/lib/DAVACL/IPrincipalCollection.php (renamed from vendor/sabre/dav/lib/Sabre/DAVACL/IPrincipalCollection.php)30
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Plugin.php (renamed from vendor/sabre/dav/lib/Sabre/DAVACL/Plugin.php)935
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Principal.php (renamed from vendor/sabre/dav/lib/Sabre/DAVACL/Principal.php)73
-rw-r--r--vendor/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php53
-rw-r--r--vendor/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php (renamed from vendor/sabre/dav/lib/Sabre/DAVACL/PrincipalBackend/BackendInterface.php)82
-rw-r--r--vendor/sabre/dav/lib/DAVACL/PrincipalBackend/CreatePrincipalSupport.php30
-rw-r--r--vendor/sabre/dav/lib/DAVACL/PrincipalBackend/PDO.php431
-rw-r--r--vendor/sabre/dav/lib/DAVACL/PrincipalCollection.php151
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Property/Acl.php277
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php45
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php159
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Property/Principal.php196
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php159
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php103
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php127
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php58
-rw-r--r--vendor/sabre/dav/lib/Sabre/CalDAV/Backend/AbstractBackend.php155
-rw-r--r--vendor/sabre/dav/lib/Sabre/CalDAV/Backend/PDO.php691
-rw-r--r--vendor/sabre/dav/lib/Sabre/CalDAV/Calendar.php376
-rw-r--r--vendor/sabre/dav/lib/Sabre/CalDAV/CalendarQueryParser.php298
-rw-r--r--vendor/sabre/dav/lib/Sabre/CalDAV/ICSExportPlugin.php142
-rw-r--r--vendor/sabre/dav/lib/Sabre/CalDAV/Plugin.php1338
-rw-r--r--vendor/sabre/dav/lib/Sabre/CalDAV/Property/AllowedSharingModes.php74
-rw-r--r--vendor/sabre/dav/lib/Sabre/CalDAV/Property/Invite.php227
-rw-r--r--vendor/sabre/dav/lib/Sabre/CalDAV/Property/ScheduleCalendarTransp.php102
-rw-r--r--vendor/sabre/dav/lib/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php88
-rw-r--r--vendor/sabre/dav/lib/Sabre/CalDAV/Property/SupportedCalendarData.php40
-rw-r--r--vendor/sabre/dav/lib/Sabre/CalDAV/Property/SupportedCollationSet.php45
-rw-r--r--vendor/sabre/dav/lib/Sabre/CalDAV/Schedule/IMip.php111
-rw-r--r--vendor/sabre/dav/lib/Sabre/CalDAV/SharingPlugin.php526
-rw-r--r--vendor/sabre/dav/lib/Sabre/CalDAV/Version.php24
-rw-r--r--vendor/sabre/dav/lib/Sabre/CardDAV/AddressBook.php315
-rw-r--r--vendor/sabre/dav/lib/Sabre/CardDAV/AddressBookQueryParser.php221
-rw-r--r--vendor/sabre/dav/lib/Sabre/CardDAV/Backend/AbstractBackend.php18
-rw-r--r--vendor/sabre/dav/lib/Sabre/CardDAV/Backend/PDO.php333
-rw-r--r--vendor/sabre/dav/lib/Sabre/CardDAV/Plugin.php706
-rw-r--r--vendor/sabre/dav/lib/Sabre/CardDAV/Property/SupportedAddressData.php72
-rw-r--r--vendor/sabre/dav/lib/Sabre/CardDAV/VCFExportPlugin.php108
-rw-r--r--vendor/sabre/dav/lib/Sabre/CardDAV/Version.php26
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/AbstractBasic.php87
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/AbstractDigest.php101
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/Apache.php63
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/BackendInterface.php36
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Auth/Plugin.php112
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Browser/Plugin.php491
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/addressbook.pngbin7232 -> 0 bytes
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/calendar.pngbin4388 -> 0 bytes
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/card.pngbin5695 -> 0 bytes
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/collection.pngbin3474 -> 0 bytes
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/file.pngbin2837 -> 0 bytes
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/parent.pngbin3474 -> 0 bytes
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/principal.pngbin5480 -> 0 bytes
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Client.php575
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Exception/FileNotFound.php19
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/FSExt/Directory.php159
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/FSExt/Node.php214
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/IExtendedCollection.php28
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/IProperties.php71
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/FS.php193
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Locks/Plugin.php649
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/ObjectTree.php159
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/PartialUpdate/IFile.php39
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Property.php31
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Property/GetLastModified.php78
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Property/Href.php99
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Property/HrefList.php105
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Property/IHref.php25
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Property/LockDiscovery.php104
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Property/ResourceType.php127
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Property/Response.php157
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Property/ResponseList.php59
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Property/SupportedLock.php78
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Property/SupportedReportSet.php111
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/PropertyInterface.php21
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Server.php2178
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Tree.php193
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/Tree/Filesystem.php133
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/URLUtil.php124
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAV/XMLUtil.php191
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAVACL/PrincipalBackend/AbstractBackend.php18
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAVACL/PrincipalBackend/PDO.php428
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAVACL/PrincipalCollection.php33
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAVACL/Property/Acl.php211
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAVACL/Property/AclRestrictions.php34
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php124
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAVACL/Property/Principal.php161
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAVACL/Property/SupportedPrivilegeSet.php94
-rw-r--r--vendor/sabre/dav/lib/Sabre/DAVACL/Version.php24
-rw-r--r--vendor/sabre/dav/lib/Sabre/HTTP/AbstractAuth.php111
-rw-r--r--vendor/sabre/dav/lib/Sabre/HTTP/BasicAuth.php67
-rw-r--r--vendor/sabre/dav/lib/Sabre/HTTP/Request.php284
-rw-r--r--vendor/sabre/dav/lib/Sabre/HTTP/Response.php175
-rw-r--r--vendor/sabre/dav/lib/Sabre/HTTP/Util.php82
-rw-r--r--vendor/sabre/dav/lib/Sabre/HTTP/Version.php24
-rw-r--r--vendor/sabre/dav/lib/Sabre/autoload.php25
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractPDOTest.php733
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractTest.php184
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Backend/Mock.php267
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Backend/PDOMySQLTest.php6
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Backend/PDOSqliteTest.php16
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/CalendarObjectTest.php50
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/CalendarQueryParserTest.php540
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/CalendarQueryValidatorTest.php70
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/CalendarTest.php74
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDTest.php55
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDbyDayTest.php49
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDoubleEventsTest.php6
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/FreeBusyReportTest.php71
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/FreeBusyRequestTest.php282
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/GetEventsByTimerangeTest.php68
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/ICSExportPluginTest.php544
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Issue203Test.php60
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Issue205Test.php4
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Issue211Test.php4
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Issue220Test.php6
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Issue228Test.php4
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/CollectionTest.php4
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/NodeTest.php10
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/Notification/InviteReplyTest.php134
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/Notification/InviteTest.php230
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/Notification/SystemStatusTest.php61
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/OutboxPostTest.php545
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/PluginTest.php869
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Principal/UserTest.php2
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Property/AllowedSharingModesTest.php46
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Property/InviteTest.php196
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Property/ScheduleCalendarTranspTest.php99
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Property/SupportedCalendarComponentSetTest.php67
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Property/SupportedCalendarDataTest.php44
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Property/SupportedCollationSetTest.php46
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Schedule/IMip/Mock.php50
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/Schedule/OutboxTest.php21
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/ShareableCalendarTest.php10
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/SharedCalendarTest.php91
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/SharingPluginTest.php84
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/UserCalendarsSharedCalendarsTest.php93
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/UserCalendarsTest.php207
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/ValidateICalTest.php91
-rw-r--r--vendor/sabre/dav/tests/Sabre/CalDAV/VersionTest.php17
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/AbstractPluginTest.php2
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookQueryParserTest.php329
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookQueryTest.php134
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookRootTest.php2
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookTest.php56
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/Backend/AbstractPDOTest.php129
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/Backend/Mock.php30
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/Backend/PDOMySQLTest.php48
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/Backend/PDOSqliteTest.php57
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/CardTest.php33
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/MultiGetTest.php50
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/PluginTest.php96
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/Property/SupportedAddressDataTest.php44
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/SogoStripContentTypeTest.php17
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/TestUtil.php18
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/UserAddressBooksTest.php162
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/VCFExportTest.php25
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/ValidateVCardTest.php47
-rw-r--r--vendor/sabre/dav/tests/Sabre/CardDAV/VersionTest.php17
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/AbstractServer.php3
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractBasicTest.php69
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractDigestTest.php124
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/ApacheTest.php53
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/FileTest.php2
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/Mock.php82
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/PDOMySQLTest.php18
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Auth/PluginTest.php94
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/BasicNodeTest.php1
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Browser/GuessContentTypeTest.php10
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Browser/MapGetToPropFindTest.php14
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Browser/PluginTest.php154
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/ClientMock.php18
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/ClientTest.php1071
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/FSExt/FileTest.php73
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/FSExt/NodeTest.php178
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/FSExt/ServerTest.php236
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/HTTPPreferParsingTest.php146
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/HttpDeleteTest.php90
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/HttpPutTest.php273
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Issue33Test.php11
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Locks/Backend/FSTest.php31
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Locks/GetIfConditionsTest.php375
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Locks/MSWordTest.php31
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Locks/PluginTest.php501
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Mock/Collection.php61
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Mock/File.php46
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Mount/PluginTest.php10
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/ObjectTreeTest.php2
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/PartialUpdate/FileMock.php54
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/PartialUpdate/PluginTest.php119
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/PartialUpdate/SpecificationTest.php38
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Property/GetLastModifiedTest.php75
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Property/HrefListTest.php91
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Property/HrefTest.php119
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Property/ResourceTypeTest.php111
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Property/ResponseListTest.php19
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Property/ResponseTest.php230
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Property/SupportedReportSetTest.php128
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/ServerCopyMoveTest.php268
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/ServerEventsTest.php74
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/ServerMKCOLTest.php159
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/ServerPluginTest.php37
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/ServerPreconditionTest.php227
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/ServerPropsTest.php342
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/ServerRangeTest.php350
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/ServerSimpleTest.php454
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/ServerUpdatePropertiesTest.php75
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/SimpleFileTest.php4
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/TemporaryFileFilterTest.php159
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/TestPlugin.php14
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/Tree/FilesystemTest.php88
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/TreeTest.php104
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/URLUtilTest.php131
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAV/XMLUtilTest.php284
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/ACLMethodTest.php55
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/AllowAccessTest.php92
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/BlockAccessTest.php100
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/ExpandPropertiesTest.php102
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/PluginAdminTest.php49
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/PluginPropertiesTest.php335
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/PluginUpdatePropertiesTest.php64
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/AbstractPDOTest.php50
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/Mock.php144
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOMySQLTest.php33
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOSqliteTest.php23
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalCollectionTest.php9
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalPropertySearchTest.php192
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalSearchPropertySetTest.php32
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalTest.php10
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/Property/ACLRestrictionsTest.php35
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/Property/ACLTest.php335
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/Property/CurrentUserPrivilegeSetTest.php68
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/Property/PrincipalTest.php181
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/Property/SupportedPrivilegeSetTest.php106
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/SimplePluginTest.php51
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVACL/VersionTest.php17
-rw-r--r--vendor/sabre/dav/tests/Sabre/DAVServerTest.php120
-rw-r--r--vendor/sabre/dav/tests/Sabre/HTTP/AWSAuthTest.php242
-rw-r--r--vendor/sabre/dav/tests/Sabre/HTTP/BasicAuthTest.php132
-rw-r--r--vendor/sabre/dav/tests/Sabre/HTTP/DigestAuthTest.php228
-rw-r--r--vendor/sabre/dav/tests/Sabre/HTTP/RequestTest.php150
-rw-r--r--vendor/sabre/dav/tests/Sabre/HTTP/ResponseMock.php35
-rw-r--r--vendor/sabre/dav/tests/Sabre/HTTP/ResponseTest.php70
-rw-r--r--vendor/sabre/dav/tests/Sabre/HTTP/UtilTest.php78
-rw-r--r--vendor/sabre/dav/tests/Sabre/HTTP/VersionTest.php17
-rw-r--r--vendor/sabre/dav/tests/Sabre/TestUtil.php7
-rw-r--r--vendor/sabre/dav/tests/bootstrap.php37
-rw-r--r--vendor/sabre/dav/tests/phpunit.xml34
-rw-r--r--vendor/sabre/event/.gitignore14
-rw-r--r--vendor/sabre/event/.travis.yml26
-rw-r--r--vendor/sabre/event/CHANGELOG.md78
-rw-r--r--vendor/sabre/event/LICENSE27
-rw-r--r--vendor/sabre/event/README.md50
-rw-r--r--vendor/sabre/event/bin/.empty0
-rw-r--r--vendor/sabre/event/lib/EventEmitter.php18
-rw-r--r--vendor/sabre/event/lib/EventEmitterInterface.php100
-rw-r--r--vendor/sabre/event/lib/EventEmitterTrait.php211
-rw-r--r--vendor/sabre/event/lib/Loop/Loop.php386
-rw-r--r--vendor/sabre/event/lib/Loop/functions.php183
-rw-r--r--vendor/sabre/event/lib/Promise.php320
-rw-r--r--vendor/sabre/event/lib/Promise/functions.php135
-rw-r--r--vendor/sabre/event/lib/PromiseAlreadyResolvedException.php15
-rw-r--r--vendor/sabre/event/lib/Version.php19
-rw-r--r--vendor/sabre/event/lib/coroutine.php120
-rw-r--r--vendor/sabre/event/phpunit.xml.dist18
-rw-r--r--vendor/sabre/http/.gitignore15
-rw-r--r--vendor/sabre/http/.travis.yml24
-rw-r--r--vendor/sabre/http/CHANGELOG.md250
-rw-r--r--vendor/sabre/http/LICENSE27
-rw-r--r--vendor/sabre/http/README.md746
-rw-r--r--vendor/sabre/http/bin/.empty0
-rw-r--r--vendor/sabre/http/lib/Auth/AWS.php (renamed from vendor/sabre/dav/lib/Sabre/HTTP/AWSAuth.php)79
-rw-r--r--vendor/sabre/http/lib/Auth/AbstractAuth.php73
-rw-r--r--vendor/sabre/http/lib/Auth/Basic.php63
-rw-r--r--vendor/sabre/http/lib/Auth/Bearer.php56
-rw-r--r--vendor/sabre/http/lib/Auth/Digest.php (renamed from vendor/sabre/dav/lib/Sabre/HTTP/DigestAuth.php)83
-rw-r--r--vendor/sabre/http/lib/Client.php601
-rw-r--r--vendor/sabre/http/lib/ClientException.php15
-rw-r--r--vendor/sabre/http/lib/ClientHttpException.php58
-rw-r--r--vendor/sabre/http/lib/HttpException.php30
-rw-r--r--vendor/sabre/http/lib/Message.php314
-rw-r--r--vendor/sabre/http/lib/MessageDecoratorTrait.php250
-rw-r--r--vendor/sabre/http/lib/MessageInterface.php177
-rw-r--r--vendor/sabre/http/lib/Request.php316
-rw-r--r--vendor/sabre/http/lib/RequestDecorator.php231
-rw-r--r--vendor/sabre/http/lib/RequestInterface.php147
-rw-r--r--vendor/sabre/http/lib/Response.php194
-rw-r--r--vendor/sabre/http/lib/ResponseDecorator.php84
-rw-r--r--vendor/sabre/http/lib/ResponseInterface.php45
-rw-r--r--vendor/sabre/http/lib/Sapi.php194
-rw-r--r--vendor/sabre/http/lib/URLUtil.php103
-rw-r--r--vendor/sabre/http/lib/Util.php74
-rw-r--r--vendor/sabre/http/lib/Version.php19
-rw-r--r--vendor/sabre/http/lib/functions.php445
-rw-r--r--vendor/sabre/uri/.gitignore13
-rw-r--r--vendor/sabre/uri/.travis.yml14
-rw-r--r--vendor/sabre/uri/CHANGELOG.md34
-rw-r--r--vendor/sabre/uri/LICENSE27
-rw-r--r--vendor/sabre/uri/README.md55
-rw-r--r--vendor/sabre/uri/lib/Version.php19
-rw-r--r--vendor/sabre/uri/lib/functions.php282
-rw-r--r--vendor/sabre/vobject/.gitignore17
-rw-r--r--vendor/sabre/vobject/.travis.yml23
-rw-r--r--vendor/sabre/vobject/CHANGELOG.md739
-rw-r--r--vendor/sabre/vobject/ChangeLog88
-rw-r--r--vendor/sabre/vobject/LICENSE2
-rw-r--r--vendor/sabre/vobject/README.md375
-rwxr-xr-xvendor/sabre/vobject/bin/bench.php4
-rw-r--r--vendor/sabre/vobject/bin/bench_freebusygenerator.php62
-rw-r--r--vendor/sabre/vobject/bin/bench_manipulatevcard.php69
-rwxr-xr-xvendor/sabre/vobject/bin/fetch_windows_zones.php51
-rwxr-xr-xvendor/sabre/vobject/bin/generate_vcards241
-rwxr-xr-xvendor/sabre/vobject/bin/generateicalendardata.php41
-rwxr-xr-xvendor/sabre/vobject/bin/mergeduplicates.php184
-rw-r--r--vendor/sabre/vobject/bin/rrulebench.php32
-rwxr-xr-xvendor/sabre/vobject/bin/vobject27
-rwxr-xr-xvendor/sabre/vobject/bin/vobjectvalidate.php139
-rw-r--r--vendor/sabre/vobject/lib/BirthdayCalendarGenerator.php191
-rw-r--r--vendor/sabre/vobject/lib/Cli.php771
-rw-r--r--vendor/sabre/vobject/lib/Component.php700
-rw-r--r--vendor/sabre/vobject/lib/Component/Available.php126
-rw-r--r--vendor/sabre/vobject/lib/Component/VAlarm.php (renamed from vendor/sabre/vobject/lib/Sabre/VObject/Component/VAlarm.php)76
-rw-r--r--vendor/sabre/vobject/lib/Component/VAvailability.php156
-rw-r--r--vendor/sabre/vobject/lib/Component/VCalendar.php561
-rw-r--r--vendor/sabre/vobject/lib/Component/VCard.php553
-rw-r--r--vendor/sabre/vobject/lib/Component/VEvent.php153
-rw-r--r--vendor/sabre/vobject/lib/Component/VFreeBusy.php102
-rw-r--r--vendor/sabre/vobject/lib/Component/VJournal.php107
-rw-r--r--vendor/sabre/vobject/lib/Component/VTimeZone.php67
-rw-r--r--vendor/sabre/vobject/lib/Component/VTodo.php193
-rw-r--r--vendor/sabre/vobject/lib/DateTimeParser.php571
-rw-r--r--vendor/sabre/vobject/lib/Document.php270
-rw-r--r--vendor/sabre/vobject/lib/ElementList.php54
-rw-r--r--vendor/sabre/vobject/lib/EofException.php15
-rw-r--r--vendor/sabre/vobject/lib/FreeBusyData.php193
-rw-r--r--vendor/sabre/vobject/lib/FreeBusyGenerator.php604
-rw-r--r--vendor/sabre/vobject/lib/ITip/Broker.php989
-rw-r--r--vendor/sabre/vobject/lib/ITip/ITipException.php15
-rw-r--r--vendor/sabre/vobject/lib/ITip/Message.php141
-rw-r--r--vendor/sabre/vobject/lib/ITip/SameOrganizerForAllComponentsException.php18
-rw-r--r--vendor/sabre/vobject/lib/InvalidDataException.php14
-rw-r--r--vendor/sabre/vobject/lib/Node.php265
-rw-r--r--vendor/sabre/vobject/lib/PHPUnitAssertions.php82
-rw-r--r--vendor/sabre/vobject/lib/Parameter.php394
-rw-r--r--vendor/sabre/vobject/lib/ParseException.php13
-rw-r--r--vendor/sabre/vobject/lib/Parser/Json.php197
-rw-r--r--vendor/sabre/vobject/lib/Parser/MimeDir.php696
-rw-r--r--vendor/sabre/vobject/lib/Parser/Parser.php80
-rw-r--r--vendor/sabre/vobject/lib/Parser/XML.php428
-rw-r--r--vendor/sabre/vobject/lib/Parser/XML/Element/KeyValue.php70
-rw-r--r--vendor/sabre/vobject/lib/Property.php662
-rw-r--r--vendor/sabre/vobject/lib/Property/Binary.php128
-rw-r--r--vendor/sabre/vobject/lib/Property/Boolean.php84
-rw-r--r--vendor/sabre/vobject/lib/Property/FlatText.php50
-rw-r--r--vendor/sabre/vobject/lib/Property/FloatValue.php142
-rw-r--r--vendor/sabre/vobject/lib/Property/ICalendar/CalAddress.php61
-rw-r--r--vendor/sabre/vobject/lib/Property/ICalendar/Date.php18
-rw-r--r--vendor/sabre/vobject/lib/Property/ICalendar/DateTime.php404
-rw-r--r--vendor/sabre/vobject/lib/Property/ICalendar/Duration.php85
-rw-r--r--vendor/sabre/vobject/lib/Property/ICalendar/Period.php155
-rw-r--r--vendor/sabre/vobject/lib/Property/ICalendar/Recur.php293
-rw-r--r--vendor/sabre/vobject/lib/Property/IntegerValue.php88
-rw-r--r--vendor/sabre/vobject/lib/Property/Text.php413
-rw-r--r--vendor/sabre/vobject/lib/Property/Time.php144
-rw-r--r--vendor/sabre/vobject/lib/Property/Unknown.php44
-rw-r--r--vendor/sabre/vobject/lib/Property/Uri.php122
-rw-r--r--vendor/sabre/vobject/lib/Property/UtcOffset.php77
-rw-r--r--vendor/sabre/vobject/lib/Property/VCard/Date.php43
-rw-r--r--vendor/sabre/vobject/lib/Property/VCard/DateAndOrTime.php405
-rw-r--r--vendor/sabre/vobject/lib/Property/VCard/DateTime.php30
-rw-r--r--vendor/sabre/vobject/lib/Property/VCard/LanguageTag.php60
-rw-r--r--vendor/sabre/vobject/lib/Property/VCard/TimeStamp.php86
-rw-r--r--vendor/sabre/vobject/lib/Reader.php98
-rw-r--r--vendor/sabre/vobject/lib/Recur/EventIterator.php507
-rw-r--r--vendor/sabre/vobject/lib/Recur/MaxInstancesExceededException.php16
-rw-r--r--vendor/sabre/vobject/lib/Recur/NoInstancesException.php18
-rw-r--r--vendor/sabre/vobject/lib/Recur/RDateIterator.php182
-rw-r--r--vendor/sabre/vobject/lib/Recur/RRuleIterator.php921
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Component.php405
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Component/VCalendar.php244
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Component/VCard.php107
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Component/VEvent.php70
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Component/VFreeBusy.php68
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Component/VJournal.php46
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Component/VTodo.php68
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/DateTimeParser.php181
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Document.php109
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/ElementList.php172
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/FreeBusyGenerator.php322
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Node.php187
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Parameter.php104
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/ParseException.php12
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Property.php444
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Property/Compound.php125
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Property/DateTime.php245
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Property/MultiDateTime.php180
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Reader.php223
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/RecurrenceIterator.php1144
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/StringUtil.php61
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/TimeZoneUtil.php527
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/Version.php24
-rw-r--r--vendor/sabre/vobject/lib/Sabre/VObject/includes.php41
-rw-r--r--vendor/sabre/vobject/lib/Settings.php56
-rw-r--r--vendor/sabre/vobject/lib/Splitter/ICalendar.php (renamed from vendor/sabre/vobject/lib/Sabre/VObject/Splitter/ICalendar.php)50
-rw-r--r--vendor/sabre/vobject/lib/Splitter/SplitterInterface.php (renamed from vendor/sabre/vobject/lib/Sabre/VObject/Splitter/SplitterInterface.php)10
-rw-r--r--vendor/sabre/vobject/lib/Splitter/VCard.php (renamed from vendor/sabre/vobject/lib/Sabre/VObject/Splitter/VCard.php)46
-rw-r--r--vendor/sabre/vobject/lib/StringUtil.php66
-rw-r--r--vendor/sabre/vobject/lib/TimeZoneUtil.php266
-rw-r--r--vendor/sabre/vobject/lib/UUIDUtil.php69
-rw-r--r--vendor/sabre/vobject/lib/VCardConverter.php467
-rw-r--r--vendor/sabre/vobject/lib/Version.php19
-rw-r--r--vendor/sabre/vobject/lib/Writer.php81
-rw-r--r--vendor/sabre/vobject/lib/timezonedata/exchangezones.php93
-rw-r--r--vendor/sabre/vobject/lib/timezonedata/lotuszones.php101
-rw-r--r--vendor/sabre/vobject/lib/timezonedata/php-bc.php154
-rw-r--r--vendor/sabre/vobject/lib/timezonedata/php-workaround.php46
-rw-r--r--vendor/sabre/vobject/lib/timezonedata/windowszones.php119
-rw-r--r--vendor/sabre/vobject/resources/schema/xcal.rng1192
-rw-r--r--vendor/sabre/vobject/resources/schema/xcard.rng388
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Component/VAlarmTest.php175
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Component/VCalendarTest.php244
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Component/VCardTest.php100
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Component/VEventTest.php74
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Component/VFreeBusyTest.php39
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Component/VJournalTest.php41
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Component/VTodoTest.php67
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/ComponentTest.php413
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/DateTimeParserTest.php153
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/DocumentTest.php26
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/ElementListTest.php32
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/EmClientTest.php55
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/FreeBusyGeneratorTest.php246
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Issue153Test.php14
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Issue154Test.php29
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Issue48Test.php47
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Issue50Test.php128
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/ParameterTest.php44
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Property/CompoundTest.php59
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Property/DateTimeTest.php240
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Property/MultiDateTimeTest.php208
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/PropertyTest.php324
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/ReaderTest.php367
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorFifthTuesdayProblemTest.php44
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorIncorrectExpandTest.php62
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorInfiniteLoopProblemTest.php91
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorMinusOneProblemTest.php30
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorMissingOverriddenTest.php63
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorTest.php1425
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/SlashRTest.php19
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Splitter/ICalendarTest.php283
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/Splitter/VCardTest.php138
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/StringUtilTest.php59
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/TimeZoneUtilTest.php306
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/VersionTest.php17
-rw-r--r--vendor/sabre/vobject/tests/Sabre/VObject/issue153.vcf352
-rw-r--r--vendor/sabre/vobject/tests/bootstrap.php18
-rw-r--r--vendor/sabre/vobject/tests/phpunit.xml14
-rw-r--r--vendor/sabre/xml/.gitignore9
-rw-r--r--vendor/sabre/xml/.travis.yml20
-rw-r--r--vendor/sabre/xml/CHANGELOG.md208
-rw-r--r--vendor/sabre/xml/LICENSE27
-rw-r--r--vendor/sabre/xml/README.md25
-rw-r--r--vendor/sabre/xml/bin/.empty0
-rw-r--r--vendor/sabre/xml/lib/ContextStackTrait.php123
-rw-r--r--vendor/sabre/xml/lib/Deserializer/functions.php255
-rw-r--r--vendor/sabre/xml/lib/Element.php20
-rw-r--r--vendor/sabre/xml/lib/Element/Base.php91
-rw-r--r--vendor/sabre/xml/lib/Element/Cdata.php64
-rw-r--r--vendor/sabre/xml/lib/Element/Elements.php108
-rw-r--r--vendor/sabre/xml/lib/Element/KeyValue.php108
-rw-r--r--vendor/sabre/xml/lib/Element/Uri.php104
-rw-r--r--vendor/sabre/xml/lib/Element/XmlFragment.php147
-rw-r--r--vendor/sabre/xml/lib/LibXMLException.php53
-rw-r--r--vendor/sabre/xml/lib/ParseException.php17
-rw-r--r--vendor/sabre/xml/lib/Reader.php308
-rw-r--r--vendor/sabre/xml/lib/Serializer/functions.php249
-rw-r--r--vendor/sabre/xml/lib/Service.php291
-rw-r--r--vendor/sabre/xml/lib/Version.php19
-rw-r--r--vendor/sabre/xml/lib/Writer.php266
-rw-r--r--vendor/sabre/xml/lib/XmlDeserializable.php38
-rw-r--r--vendor/sabre/xml/lib/XmlSerializable.php36
735 files changed, 64674 insertions, 42343 deletions
diff --git a/Zotlabs/Module/Dav.php b/Zotlabs/Module/Dav.php
index 9af059974..cc6ea4905 100644
--- a/Zotlabs/Module/Dav.php
+++ b/Zotlabs/Module/Dav.php
@@ -60,8 +60,16 @@ class Dav extends \Zotlabs\Web\Controller {
if ($which)
profile_load($a, $which, $profile);
+
+
$auth = new \Zotlabs\Storage\BasicAuth();
-
+
+ $authBackend = new \Sabre\DAV\Auth\Backend\BasicCallBack(function($userName,$password) {
+ if(account_verify_password($userName,$password))
+ return true;
+ return false;
+ });
+
$ob_hash = get_observer_hash();
if ($ob_hash) {
@@ -92,6 +100,12 @@ class Dav extends \Zotlabs\Web\Controller {
// A SabreDAV server-object
$server = new SDAV\Server($rootDirectory);
+
+
+ $authPlugin = new \Sabre\DAV\Auth\Plugin($authBackend);
+ $server->addPlugin($authPlugin);
+
+
// prevent overwriting changes each other with a lock backend
$lockBackend = new SDAV\Locks\Backend\File('store/[data]/locks');
$lockPlugin = new SDAV\Locks\Plugin($lockBackend);
diff --git a/Zotlabs/Storage/Browser.php b/Zotlabs/Storage/Browser.php
index 294baaa28..ca262b739 100644
--- a/Zotlabs/Storage/Browser.php
+++ b/Zotlabs/Storage/Browser.php
@@ -101,8 +101,8 @@ class Browser extends DAV\Browser\Plugin {
$parentpath = array();
// only show parent if not leaving /cloud/; TODO how to improve this?
if ($path && $path != "cloud") {
- list($parentUri) = DAV\URLUtil::splitPath($path);
- $fullPath = DAV\URLUtil::encodePath($this->server->getBaseUri() . $parentUri);
+ list($parentUri) = \Sabre\HTTP\URLUtil::splitPath($path);
+ $fullPath = \Sabre\HTTP\URLUtil::encodePath($this->server->getBaseUri() . $parentUri);
$parentpath['icon'] = $this->enableAssets ? '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl('icons/parent' . $this->iconExtension) . '" width="24" alt="' . t('parent') . '"></a>' : '';
$parentpath['path'] = $fullPath;
@@ -116,7 +116,7 @@ class Browser extends DAV\Browser\Plugin {
// This is the current directory, we can skip it
if (rtrim($file['href'],'/') == $path) continue;
- list(, $name) = DAV\URLUtil::splitPath($file['href']);
+ list(, $name) = \Sabre\HTTP\URLUtil::splitPath($file['href']);
if (isset($file[200]['{DAV:}resourcetype'])) {
$type = $file[200]['{DAV:}resourcetype']->getValue();
@@ -166,7 +166,7 @@ class Browser extends DAV\Browser\Plugin {
$size = isset($file[200]['{DAV:}getcontentlength']) ? (int)$file[200]['{DAV:}getcontentlength'] : '';
$lastmodified = ((isset($file[200]['{DAV:}getlastmodified'])) ? $file[200]['{DAV:}getlastmodified']->getTime()->format('Y-m-d H:i:s') : '');
- $fullPath = DAV\URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/'));
+ $fullPath = \Sabre\HTTP\URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/'));
$displayName = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $name;
@@ -219,7 +219,7 @@ class Browser extends DAV\Browser\Plugin {
$output = '';
if ($this->enablePost) {
- $this->server->broadcastEvent('onHTMLActionsPanel', array($parent, &$output));
+ $this->server->emit('onHTMLActionsPanel', array($parent, &$output));
}
$html .= replace_macros(get_markup_template('cloud.tpl'), array(
diff --git a/include/event.php b/include/event.php
index e54a172c2..5e50d6cae 100644
--- a/include/event.php
+++ b/include/event.php
@@ -539,16 +539,16 @@ function event_import_ical($ical, $uid) {
// logger('dtstart: ' . var_export($dtstart,true));
-
- switch($dtstart->timezone_type) {
- case VObject\Property\DateTime::UTC :
- $ev['adjust'] = 0;
- break;
- case VObject\Property\DateTime::LOCALTZ :
- default:
- $ev['adjust'] = 1;
- break;
- }
+// @FIXME - convert/upgrade to vobject [3|4]
+// switch($dtstart->timezone_type) {
+// case VObject\Property\DateTime::UTC :
+// $ev['adjust'] = 0;
+// break;
+// case VObject\Property\DateTime::LOCALTZ :
+// default:
+// $ev['adjust'] = 1;
+// break;
+// }
$ev['start'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC',
$dtstart->format(\DateTime::W3C));
diff --git a/vendor/autoload.php b/vendor/autoload.php
index 655527c54..568834318 100644
--- a/vendor/autoload.php
+++ b/vendor/autoload.php
@@ -4,4 +4,4 @@
require_once __DIR__ . '/composer' . '/autoload_real.php';
-return ComposerAutoloaderInita478c0bdc9041edcc4f485e8fb39b90d::getLoader();
+return ComposerAutoloaderInit85a1cefa95be2f464cf7f947cbc4c785::getLoader();
diff --git a/vendor/bin/generate_vcards b/vendor/bin/generate_vcards
new file mode 120000
index 000000000..cb76da13a
--- /dev/null
+++ b/vendor/bin/generate_vcards
@@ -0,0 +1 @@
+../sabre/vobject/bin/generate_vcards \ No newline at end of file
diff --git a/vendor/bin/naturalselection b/vendor/bin/naturalselection
new file mode 120000
index 000000000..e6f1b3a2a
--- /dev/null
+++ b/vendor/bin/naturalselection
@@ -0,0 +1 @@
+../sabre/dav/bin/naturalselection \ No newline at end of file
diff --git a/vendor/bin/sabredav b/vendor/bin/sabredav
new file mode 120000
index 000000000..3b5e4511d
--- /dev/null
+++ b/vendor/bin/sabredav
@@ -0,0 +1 @@
+../sabre/dav/bin/sabredav \ No newline at end of file
diff --git a/vendor/bin/vobject b/vendor/bin/vobject
new file mode 120000
index 000000000..f5b111eac
--- /dev/null
+++ b/vendor/bin/vobject
@@ -0,0 +1 @@
+../sabre/vobject/bin/vobject \ No newline at end of file
diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php
index 88684c526..ff6ecfb82 100644
--- a/vendor/composer/ClassLoader.php
+++ b/vendor/composer/ClassLoader.php
@@ -13,9 +13,7 @@
namespace Composer\Autoload;
/**
- * ClassLoader implements a PSR-0 class loader
- *
- * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
@@ -39,6 +37,8 @@ namespace Composer\Autoload;
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
+ * @see http://www.php-fig.org/psr/psr-0/
+ * @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
@@ -54,9 +54,15 @@ class ClassLoader
private $useIncludePath = false;
private $classMap = array();
+ private $classMapAuthoritative = false;
+
public function getPrefixes()
{
- return call_user_func_array('array_merge', $this->prefixesPsr0);
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
+ }
+
+ return array();
}
public function getPrefixesPsr4()
@@ -141,8 +147,10 @@ class ClassLoader
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
- * @param array|string $paths The PSR-0 base directories
+ * @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
@@ -204,6 +212,8 @@ class ClassLoader
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
@@ -241,6 +251,27 @@ class ClassLoader
}
/**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
@@ -291,6 +322,9 @@ class ClassLoader
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
+ if ($this->classMapAuthoritative) {
+ return false;
+ }
$file = $this->findFileWithExtension($class, '.php');
diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE
new file mode 100644
index 000000000..1a2812488
--- /dev/null
+++ b/vendor/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) 2016 Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php
new file mode 100644
index 000000000..a78cbe6fb
--- /dev/null
+++ b/vendor/composer/autoload_files.php
@@ -0,0 +1,16 @@
+<?php
+
+// autoload_files.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+ '383eaff206634a77a1be54e64e6459c7' => $vendorDir . '/sabre/uri/lib/functions.php',
+ '2b9d0f43f9552984cfa82fee95491826' => $vendorDir . '/sabre/event/lib/coroutine.php',
+ 'd81bab31d3feb45bfe2f283ea3c8fdf7' => $vendorDir . '/sabre/event/lib/Loop/functions.php',
+ 'a1cce3d26cc15c00fcd0b3354bd72c88' => $vendorDir . '/sabre/event/lib/Promise/functions.php',
+ '3569eecfeed3bcf0bad3c998a494ecb8' => $vendorDir . '/sabre/xml/lib/Deserializer/functions.php',
+ '93aa591bc4ca510c520999e34229ee79' => $vendorDir . '/sabre/xml/lib/Serializer/functions.php',
+ 'ebdb698ed4152ae445614b69b5e4bb6a' => $vendorDir . '/sabre/http/lib/functions.php',
+);
diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php
index e64e0a875..b7fc0125d 100644
--- a/vendor/composer/autoload_namespaces.php
+++ b/vendor/composer/autoload_namespaces.php
@@ -6,10 +6,4 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
- 'Sabre\\VObject' => array($vendorDir . '/sabre/vobject/lib'),
- 'Sabre\\HTTP' => array($vendorDir . '/sabre/dav/lib'),
- 'Sabre\\DAVACL' => array($vendorDir . '/sabre/dav/lib'),
- 'Sabre\\DAV' => array($vendorDir . '/sabre/dav/lib'),
- 'Sabre\\CardDAV' => array($vendorDir . '/sabre/dav/lib'),
- 'Sabre\\CalDAV' => array($vendorDir . '/sabre/dav/lib'),
);
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
index b265c64a2..8e90b0195 100644
--- a/vendor/composer/autoload_psr4.php
+++ b/vendor/composer/autoload_psr4.php
@@ -6,4 +6,13 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
+ 'Sabre\\Xml\\' => array($vendorDir . '/sabre/xml/lib'),
+ 'Sabre\\VObject\\' => array($vendorDir . '/sabre/vobject/lib'),
+ 'Sabre\\Uri\\' => array($vendorDir . '/sabre/uri/lib'),
+ 'Sabre\\HTTP\\' => array($vendorDir . '/sabre/http/lib'),
+ 'Sabre\\Event\\' => array($vendorDir . '/sabre/event/lib'),
+ 'Sabre\\DAV\\' => array($vendorDir . '/sabre/dav/lib/DAV'),
+ 'Sabre\\DAVACL\\' => array($vendorDir . '/sabre/dav/lib/DAVACL'),
+ 'Sabre\\CardDAV\\' => array($vendorDir . '/sabre/dav/lib/CardDAV'),
+ 'Sabre\\CalDAV\\' => array($vendorDir . '/sabre/dav/lib/CalDAV'),
);
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
index 09fa3b8c0..f2df43375 100644
--- a/vendor/composer/autoload_real.php
+++ b/vendor/composer/autoload_real.php
@@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
-class ComposerAutoloaderInita478c0bdc9041edcc4f485e8fb39b90d
+class ComposerAutoloaderInit85a1cefa95be2f464cf7f947cbc4c785
{
private static $loader;
@@ -19,32 +19,52 @@ class ComposerAutoloaderInita478c0bdc9041edcc4f485e8fb39b90d
return self::$loader;
}
- spl_autoload_register(array('ComposerAutoloaderInita478c0bdc9041edcc4f485e8fb39b90d', 'loadClassLoader'), true, true);
+ spl_autoload_register(array('ComposerAutoloaderInit85a1cefa95be2f464cf7f947cbc4c785', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
- spl_autoload_unregister(array('ComposerAutoloaderInita478c0bdc9041edcc4f485e8fb39b90d', 'loadClassLoader'));
+ spl_autoload_unregister(array('ComposerAutoloaderInit85a1cefa95be2f464cf7f947cbc4c785', 'loadClassLoader'));
- $map = require __DIR__ . '/autoload_namespaces.php';
- foreach ($map as $namespace => $path) {
- $loader->set($namespace, $path);
- }
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
+ if ($useStaticLoader) {
+ require_once __DIR__ . '/autoload_static.php';
- $map = require __DIR__ . '/autoload_psr4.php';
- foreach ($map as $namespace => $path) {
- $loader->setPsr4($namespace, $path);
- }
+ call_user_func(\Composer\Autoload\ComposerStaticInit85a1cefa95be2f464cf7f947cbc4c785::getInitializer($loader));
+ } else {
+ $map = require __DIR__ . '/autoload_namespaces.php';
+ foreach ($map as $namespace => $path) {
+ $loader->set($namespace, $path);
+ }
- $classMap = require __DIR__ . '/autoload_classmap.php';
- if ($classMap) {
- $loader->addClassMap($classMap);
+ $map = require __DIR__ . '/autoload_psr4.php';
+ foreach ($map as $namespace => $path) {
+ $loader->setPsr4($namespace, $path);
+ }
+
+ $classMap = require __DIR__ . '/autoload_classmap.php';
+ if ($classMap) {
+ $loader->addClassMap($classMap);
+ }
}
$loader->register(true);
+ if ($useStaticLoader) {
+ $includeFiles = Composer\Autoload\ComposerStaticInit85a1cefa95be2f464cf7f947cbc4c785::$files;
+ } else {
+ $includeFiles = require __DIR__ . '/autoload_files.php';
+ }
+ foreach ($includeFiles as $fileIdentifier => $file) {
+ composerRequire85a1cefa95be2f464cf7f947cbc4c785($fileIdentifier, $file);
+ }
+
return $loader;
}
}
-function composerRequirea478c0bdc9041edcc4f485e8fb39b90d($file)
+function composerRequire85a1cefa95be2f464cf7f947cbc4c785($fileIdentifier, $file)
{
- require $file;
+ if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+ require $file;
+
+ $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+ }
}
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
new file mode 100644
index 000000000..04dd83351
--- /dev/null
+++ b/vendor/composer/autoload_static.php
@@ -0,0 +1,81 @@
+<?php
+
+// autoload_static.php @generated by Composer
+
+namespace Composer\Autoload;
+
+class ComposerStaticInit85a1cefa95be2f464cf7f947cbc4c785
+{
+ public static $files = array (
+ '383eaff206634a77a1be54e64e6459c7' => __DIR__ . '/..' . '/sabre/uri/lib/functions.php',
+ '2b9d0f43f9552984cfa82fee95491826' => __DIR__ . '/..' . '/sabre/event/lib/coroutine.php',
+ 'd81bab31d3feb45bfe2f283ea3c8fdf7' => __DIR__ . '/..' . '/sabre/event/lib/Loop/functions.php',
+ 'a1cce3d26cc15c00fcd0b3354bd72c88' => __DIR__ . '/..' . '/sabre/event/lib/Promise/functions.php',
+ '3569eecfeed3bcf0bad3c998a494ecb8' => __DIR__ . '/..' . '/sabre/xml/lib/Deserializer/functions.php',
+ '93aa591bc4ca510c520999e34229ee79' => __DIR__ . '/..' . '/sabre/xml/lib/Serializer/functions.php',
+ 'ebdb698ed4152ae445614b69b5e4bb6a' => __DIR__ . '/..' . '/sabre/http/lib/functions.php',
+ );
+
+ public static $prefixLengthsPsr4 = array (
+ 'S' =>
+ array (
+ 'Sabre\\Xml\\' => 10,
+ 'Sabre\\VObject\\' => 14,
+ 'Sabre\\Uri\\' => 10,
+ 'Sabre\\HTTP\\' => 11,
+ 'Sabre\\Event\\' => 12,
+ 'Sabre\\DAV\\' => 10,
+ 'Sabre\\DAVACL\\' => 13,
+ 'Sabre\\CardDAV\\' => 14,
+ 'Sabre\\CalDAV\\' => 13,
+ ),
+ );
+
+ public static $prefixDirsPsr4 = array (
+ 'Sabre\\Xml\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/sabre/xml/lib',
+ ),
+ 'Sabre\\VObject\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/sabre/vobject/lib',
+ ),
+ 'Sabre\\Uri\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/sabre/uri/lib',
+ ),
+ 'Sabre\\HTTP\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/sabre/http/lib',
+ ),
+ 'Sabre\\Event\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/sabre/event/lib',
+ ),
+ 'Sabre\\DAV\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/sabre/dav/lib/DAV',
+ ),
+ 'Sabre\\DAVACL\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL',
+ ),
+ 'Sabre\\CardDAV\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV',
+ ),
+ 'Sabre\\CalDAV\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV',
+ ),
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->prefixLengthsPsr4 = ComposerStaticInit85a1cefa95be2f464cf7f947cbc4c785::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInit85a1cefa95be2f464cf7f947cbc4c785::$prefixDirsPsr4;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 42f46fb2c..5714e1c19 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -1,32 +1,280 @@
[
{
+ "name": "sabre/uri",
+ "version": "1.1.0",
+ "version_normalized": "1.1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/fruux/sabre-uri.git",
+ "reference": "9012116434d84ef6e5e37a89dfdbfbe2204a8704"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/fruux/sabre-uri/zipball/9012116434d84ef6e5e37a89dfdbfbe2204a8704",
+ "reference": "9012116434d84ef6e5e37a89dfdbfbe2204a8704",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.7"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "*",
+ "sabre/cs": "~0.0.1"
+ },
+ "time": "2016-03-08 02:29:27",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "lib/functions.php"
+ ],
+ "psr-4": {
+ "Sabre\\Uri\\": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Evert Pot",
+ "email": "me@evertpot.com",
+ "homepage": "http://evertpot.com/",
+ "role": "Developer"
+ }
+ ],
+ "description": "Functions for making sense out of URIs.",
+ "homepage": "http://sabre.io/uri/",
+ "keywords": [
+ "rfc3986",
+ "uri",
+ "url"
+ ]
+ },
+ {
+ "name": "sabre/event",
+ "version": "3.0.0",
+ "version_normalized": "3.0.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/fruux/sabre-event.git",
+ "reference": "831d586f5a442dceacdcf5e9c4c36a4db99a3534"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/fruux/sabre-event/zipball/831d586f5a442dceacdcf5e9c4c36a4db99a3534",
+ "reference": "831d586f5a442dceacdcf5e9c4c36a4db99a3534",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "*",
+ "sabre/cs": "~0.0.4"
+ },
+ "time": "2015-11-05 20:14:39",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Sabre\\Event\\": "lib/"
+ },
+ "files": [
+ "lib/coroutine.php",
+ "lib/Loop/functions.php",
+ "lib/Promise/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Evert Pot",
+ "email": "me@evertpot.com",
+ "homepage": "http://evertpot.com/",
+ "role": "Developer"
+ }
+ ],
+ "description": "sabre/event is a library for lightweight event-based programming",
+ "homepage": "http://sabre.io/event/",
+ "keywords": [
+ "EventEmitter",
+ "async",
+ "events",
+ "hooks",
+ "plugin",
+ "promise",
+ "signal"
+ ]
+ },
+ {
+ "name": "sabre/http",
+ "version": "4.2.1",
+ "version_normalized": "4.2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/fruux/sabre-http.git",
+ "reference": "2e93bc8321524c67be4ca5b8415daebd4c8bf85e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/fruux/sabre-http/zipball/2e93bc8321524c67be4ca5b8415daebd4c8bf85e",
+ "reference": "2e93bc8321524c67be4ca5b8415daebd4c8bf85e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=5.4",
+ "sabre/event": ">=1.0.0,<4.0.0",
+ "sabre/uri": "~1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.3",
+ "sabre/cs": "~0.0.1"
+ },
+ "suggest": {
+ "ext-curl": " to make http requests with the Client class"
+ },
+ "time": "2016-01-06 23:00:08",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "lib/functions.php"
+ ],
+ "psr-4": {
+ "Sabre\\HTTP\\": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Evert Pot",
+ "email": "me@evertpot.com",
+ "homepage": "http://evertpot.com/",
+ "role": "Developer"
+ }
+ ],
+ "description": "The sabre/http library provides utilities for dealing with http requests and responses. ",
+ "homepage": "https://github.com/fruux/sabre-http",
+ "keywords": [
+ "http"
+ ]
+ },
+ {
+ "name": "sabre/xml",
+ "version": "1.4.1",
+ "version_normalized": "1.4.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/fruux/sabre-xml.git",
+ "reference": "59998046db252634259a878baf1af18159f508f3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/fruux/sabre-xml/zipball/59998046db252634259a878baf1af18159f508f3",
+ "reference": "59998046db252634259a878baf1af18159f508f3",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-xmlreader": "*",
+ "ext-xmlwriter": "*",
+ "lib-libxml": ">=2.6.20",
+ "php": ">=5.4.1",
+ "sabre/uri": "~1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "*",
+ "sabre/cs": "~0.0.2"
+ },
+ "time": "2016-03-12 22:23:16",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Sabre\\Xml\\": "lib/"
+ },
+ "files": [
+ "lib/Deserializer/functions.php",
+ "lib/Serializer/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Evert Pot",
+ "email": "me@evertpot.com",
+ "homepage": "http://evertpot.com/",
+ "role": "Developer"
+ },
+ {
+ "name": "Markus Staab",
+ "email": "markus.staab@redaxo.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "sabre/xml is an XML library that you may not hate.",
+ "homepage": "https://sabre.io/xml/",
+ "keywords": [
+ "XMLReader",
+ "XMLWriter",
+ "dom",
+ "xml"
+ ]
+ },
+ {
"name": "sabre/vobject",
- "version": "2.1.4",
- "version_normalized": "2.1.4.0",
+ "version": "4.1.0",
+ "version_normalized": "4.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/fruux/sabre-vobject.git",
- "reference": "199b6ec87104b05e3013dfd5b90eafbbe4cf97dc"
+ "reference": "8899c0e856b3178b17f4e9a4e85010209f32a2fa"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/fruux/sabre-vobject/zipball/199b6ec87104b05e3013dfd5b90eafbbe4cf97dc",
- "reference": "199b6ec87104b05e3013dfd5b90eafbbe4cf97dc",
+ "url": "https://api.github.com/repos/fruux/sabre-vobject/zipball/8899c0e856b3178b17f4e9a4e85010209f32a2fa",
+ "reference": "8899c0e856b3178b17f4e9a4e85010209f32a2fa",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
- "php": ">=5.3.1"
+ "php": ">=5.5",
+ "sabre/xml": "~1.1"
},
- "time": "2014-03-30 23:01:06",
+ "require-dev": {
+ "phpunit/phpunit": "*",
+ "sabre/cs": "~0.0.3"
+ },
+ "suggest": {
+ "hoa/bench": "If you would like to run the benchmark scripts"
+ },
+ "time": "2016-04-07 00:48:27",
"bin": [
- "bin/vobjectvalidate.php"
+ "bin/vobject",
+ "bin/generate_vcards"
],
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0.x-dev"
+ }
+ },
"installation-source": "dist",
"autoload": {
- "psr-0": {
- "Sabre\\VObject": "lib/"
+ "psr-4": {
+ "Sabre\\VObject\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -36,32 +284,65 @@
"authors": [
{
"name": "Evert Pot",
- "email": "evert@rooftopsolutions.nl",
+ "email": "me@evertpot.com",
"homepage": "http://evertpot.com/",
"role": "Developer"
+ },
+ {
+ "name": "Dominik Tobschall",
+ "email": "dominik@fruux.com",
+ "homepage": "http://tobschall.de/",
+ "role": "Developer"
+ },
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net",
+ "homepage": "http://mnt.io/",
+ "role": "Developer"
}
],
"description": "The VObject library for PHP allows you to easily parse and manipulate iCalendar and vCard objects",
- "homepage": "https://github.com/fruux/sabre-vobject",
+ "homepage": "http://sabre.io/vobject/",
"keywords": [
- "VObject",
+ "availability",
+ "freebusy",
"iCalendar",
- "vCard"
+ "ics",
+ "jCal",
+ "jCard",
+ "recurrence",
+ "rfc2425",
+ "rfc2426",
+ "rfc2739",
+ "rfc4770",
+ "rfc5545",
+ "rfc5546",
+ "rfc6321",
+ "rfc6350",
+ "rfc6351",
+ "rfc6474",
+ "rfc6638",
+ "rfc6715",
+ "rfc6868",
+ "vCard",
+ "vcf",
+ "xCal",
+ "xCard"
]
},
{
"name": "sabre/dav",
- "version": "1.8.10",
- "version_normalized": "1.8.10.0",
+ "version": "3.1.3",
+ "version_normalized": "3.1.3.0",
"source": {
"type": "git",
"url": "https://github.com/fruux/sabre-dav.git",
- "reference": "0d064536ed3c7974e486b6ebb5b17ad7a974fe18"
+ "reference": "8a266c7b5e140da79529414b9cde2a2d058b536b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/fruux/sabre-dav/zipball/0d064536ed3c7974e486b6ebb5b17ad7a974fe18",
- "reference": "0d064536ed3c7974e486b6ebb5b17ad7a974fe18",
+ "url": "https://api.github.com/repos/fruux/sabre-dav/zipball/8a266c7b5e140da79529414b9cde2a2d058b536b",
+ "reference": "8a266c7b5e140da79529414b9cde2a2d058b536b",
"shasum": ""
},
"require": {
@@ -69,39 +350,45 @@
"ext-date": "*",
"ext-dom": "*",
"ext-iconv": "*",
- "ext-libxml": "*",
"ext-mbstring": "*",
"ext-pcre": "*",
"ext-simplexml": "*",
"ext-spl": "*",
- "php": ">=5.3.1",
- "sabre/vobject": "~2.1.0"
- },
- "provide": {
- "evert/sabredav": "1.7.*"
+ "lib-libxml": ">=2.7.0",
+ "php": ">=5.5.0",
+ "sabre/event": ">=2.0.0, <4.0.0",
+ "sabre/http": "^4.2.1",
+ "sabre/uri": "~1.0",
+ "sabre/vobject": "~4.0",
+ "sabre/xml": "~1.0"
},
"require-dev": {
- "evert/phpdoc-md": "~0.0.7",
- "phpunit/phpunit": "~4.0.0"
+ "evert/phpdoc-md": "~0.1.0",
+ "phpunit/phpunit": "> 4.8, <=6.0.0",
+ "sabre/cs": "~0.0.5"
},
"suggest": {
- "ext-apc": "*",
"ext-curl": "*",
"ext-pdo": "*"
},
- "time": "2014-05-16 00:14:02",
+ "time": "2016-04-07 01:02:57",
"bin": [
- "bin/sabredav"
+ "bin/sabredav",
+ "bin/naturalselection"
],
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.1.0-dev"
+ }
+ },
"installation-source": "dist",
"autoload": {
- "psr-0": {
- "Sabre\\DAV": "lib/",
- "Sabre\\HTTP": "lib/",
- "Sabre\\DAVACL": "lib/",
- "Sabre\\CalDAV": "lib/",
- "Sabre\\CardDAV": "lib/"
+ "psr-4": {
+ "Sabre\\DAV\\": "lib/DAV/",
+ "Sabre\\DAVACL\\": "lib/DAVACL/",
+ "Sabre\\CalDAV\\": "lib/CalDAV/",
+ "Sabre\\CardDAV\\": "lib/CardDAV/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -117,7 +404,7 @@
}
],
"description": "WebDAV Framework for PHP",
- "homepage": "http://code.google.com/p/sabredav/",
+ "homepage": "http://sabre.io/",
"keywords": [
"CalDAV",
"CardDAV",
diff --git a/vendor/sabre/dav/.gitignore b/vendor/sabre/dav/.gitignore
index 9a17f7748..6cf245883 100644
--- a/vendor/sabre/dav/.gitignore
+++ b/vendor/sabre/dav/.gitignore
@@ -1,22 +1,43 @@
-docs/api
-docs/wikidocs
-build.properties
-build
-public
-data
-fileserver.php
-fileserver2.php
-calendarserver.php
-groupwareserver.php
-package.xml
-tmpdata
+# Unit tests
tests/temp
tests/.sabredav
+tests/cov
+
+# Custom settings for tests
+tests/config.user.php
+
+# ViM
*.swp
+
+# Composer
composer.lock
vendor
+
+# Composer binaries
bin/phing
bin/phpunit
-bin/vobjectvalidate.php
+bin/vobject
+bin/generate_vcards
bin/phpdocmd
bin/phpunit
+bin/php-cs-fixer
+bin/sabre-cs-fixer
+
+# Assuming every .php file in the root is for testing
+/*.php
+
+# Other testing stuff
+/tmpdata
+/data
+/public
+
+# Build
+build
+build.properties
+
+# Docs
+docs/api
+docs/wikidocs
+
+# Mac
+.DS_Store
diff --git a/vendor/sabre/dav/.travis.yml b/vendor/sabre/dav/.travis.yml
index 9c98702f0..48c88b169 100644
--- a/vendor/sabre/dav/.travis.yml
+++ b/vendor/sabre/dav/.travis.yml
@@ -1,28 +1,33 @@
language: php
php:
- - 5.3.3
- - 5.3
- - 5.4
- 5.5
- 5.6
+ - 7
- hhvm
matrix:
+ fast_finish: true
allow_failures:
- - php: 5.6
- - php: hhvm
+ - php: hhvm
+
+env:
+ matrix:
+ - LOWEST_DEPS="" TEST_DEPS=""
+ - LOWEST_DEPS="--prefer-lowest" TEST_DEPS="tests/Sabre/"
services:
- mysql
+sudo: false
+
+cache: vendor
+
before_script:
- mysql -e 'create database sabredav'
- - composer self-update
- - composer install --prefer-source
-# - echo "zend.enable_gc=0" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`
+ # - composer self-update
+ - composer update --prefer-source $LOWEST_DEPS
script:
- - phpunit --configuration tests/phpunit.xml
- - cp tests/composer.vobject3.json composer.json
- - composer update --no-dev
- - phpunit --configuration tests/phpunit.xml
+ - ./bin/phpunit --configuration tests/phpunit.xml $TEST_DEPS
+ - ./bin/sabre-cs-fixer fix lib/ --dry-run --diff
+
diff --git a/vendor/sabre/dav/CHANGELOG.md b/vendor/sabre/dav/CHANGELOG.md
new file mode 100644
index 000000000..f719c8e1a
--- /dev/null
+++ b/vendor/sabre/dav/CHANGELOG.md
@@ -0,0 +1,2242 @@
+ChangeLog
+=========
+
+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.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-??-??)
+-------------------
+
+* #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.
+
+
+2.1.10 (2016-03-10)
+-------------------
+
+* #784: Sync logs for address books were not correctly cleaned up after
+ deleting them.
+
+
+2.1.9 (2016-01-25)
+------------------
+
+* #674: PHP7 support (@DeepDiver1975).
+* The zip release ships with [sabre/vobject 3.5.0][vobj],
+ [sabre/http 3.0.5][http], and [sabre/event 2.0.2][evnt].
+
+
+2.1.8 (2016-01-04)
+------------------
+
+* #729: Fixed a caching problem in the Tree object.
+* #740: Bugs in `migrate20.php` script.
+* The zip release ships with [sabre/vobject 3.4.8][vobj],
+ [sabre/http 3.0.5][http], and [sabre/event 2.0.2][evnt].
+
+
+2.1.7 (2015-09-05)
+------------------
+
+* #705: A `MOVE` request that gets prevented from deleting the source resource
+ will still remove the target resource. Now all events are triggered before
+ any destructive operations.
+* The zip release ships with [sabre/vobject 3.4.7][vobj],
+ [sabre/http 3.0.5][http], and [sabre/event 2.0.2][evnt].
+
+
+2.1.6 (2015-07-21)
+------------------
+
+* #657: Migration script would break when coming a cross an iCalendar object
+ with no UID.
+* #691: Workaround for broken Windows Phone client.
+* The zip release ships with [sabre/vobject 3.4.5][vobj],
+ [sabre/http 3.0.5][http], and [sabre/event 2.0.2][evnt].
+
+
+2.1.5 (2015-07-11)
+------------------
+
+* #677: Resources with the name '0' would not get retrieved when using
+ `Depth: infinity` in a `PROPFIND` request.
+* The zip release ships with [sabre/vobject 3.4.5][vobj],
+ [sabre/http 3.0.5][http], and [sabre/event 2.0.2][evnt].
+
+
+2.1.4 (2015-05-25)
+------------------
+
+* #651: Double-encoded path in the browser plugin. Should fix a few broken
+ links in some setups.
+* #650: Correctly cleaning up change info after deleting calendars (@ErrOrnAmE).
+* #658: Updating `schedule-calendar-default-URL` does not work well, so we're
+ disabling it until there's a better fix.
+* The zip release ships with [sabre/vobject 3.4.3][vobj],
+ [sabre/http 3.0.5][http], and [sabre/event 2.0.2][evnt].
+
+
+2.1.3 (2015-02-25)
+------------------
+
+* #586: `SCHEDULE-STATUS` should not contain a reason-phrase.
+* #539: Fixed a bug related to scheduling in shared calendars.
+* #595: Support for calendar-timezone in iCalendar exports.
+* #581: findByUri would send empty prefixes to the principal backend (@soydeedo)
+* #611: Escaping a bit more HTML output in the browser plugin. (@LukasReschke)
+* #610: Don't allow discovery of arbitrary files using `..` in the browser
+ plugin (@LukasReschke).
+* Browser plugin now shows quota properties.
+* #612: PropertyStorage didn't delete properties from nodes when a node's
+ parents get deleted.
+* #581: Fixed problems related to finding attendee information during
+ scheduling.
+* The zip release ships with [sabre/vobject 3.4.2][vobj],
+ [sabre/http 3.0.4][http], and [sabre/event 2.0.1][evnt].
+
+
+2.1.2 (2014-12-10)
+------------------
+
+* #566: Another issue related to the migration script, which would cause
+ scheduling to not work well for events that were already added before the
+ migration.
+* #567: Doing freebusy requests on accounts that had 0 calendars would throw
+ a `E_NOTICE`.
+* #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 recieve both vCard 3.0 and 4.0.
+* Added: #444. Collections can now opt-in to support high-speed `MOVE`.
+* Changed: PropertyStorage backends now have a `move` method.
+* Added: `beforeMove`, and `afterMove` events.
+* Changed: A few database changes for the CalDAV PDO backend. Make sure you
+ run `bin/migrate21.php` to upgrade your database schema.
+* Changed: CalDAV backends have a new method: `getCalendarObjectByUID`. This
+ method MUST be implemented by all backends, but the `AbstractBackend` has a
+ simple default implementation for this.
+* Changed: `Sabre\CalDAV\UserCalendars` has been renamed to
+ `Sabre\CalDAV\CalendarHome`.
+* Changed: `Sabre\CalDAV\CalendarRootNode` has been renamed to
+ `Sabre\CalDAV\CalendarRoot`.
+* Changed: The IMipHandler has been completely removed. With CalDAV scheduling
+ support, it is no longer needed. It's functionality has been replaced by
+ `Sabre\CalDAV\Schedule\IMipPlugin`, which can now send emails for clients
+ other than iCal.
+* Removed: `Sabre\DAV\ObjectTree` and `Sabre\DAV\Tree\FileSystem`. All this
+ functionality has been merged into `Sabre\DAV\Tree`.
+* Changed: PrincipalBackend now has a findByUri method.
+* Changed: `PrincipalBackend::searchPrincipals` has a new optional `test`
+ argument.
+* Added: Support for the `{http://calendarserver.org/ns/}email-address-set`
+ property.
+* #460: PropertyStorage must move properties during `MOVE` requests.
+* Changed: Restructured the zip distribution to be a little bit more lean
+ and consistent.
+* #524: Full support for the `test="anyof"` attribute in principal-search
+ `REPORT`.
+* #472: Always returning lock tokens in the lockdiscovery property.
+* Directory entries in the Browser plugin are sorted by type and name.
+ (@aklomp)
+* #486: It's now possible to return additional properties when an 'allprop'
+ PROPFIND request is being done. (@aklomp)
+* Changed: Now return HTTP errors when an addressbook-query REPORT is done
+ on a uri that's not a vcard. This should help with debugging this common
+ mistake.
+* Changed: `PUT` requests with a `Content-Range` header now emit a 400 status
+ instead of 501, as per RFC7231.
+* Added: Browser plugin can now display the contents of the
+ `{DAV:}supported-privilege-set` property.
+* Added: Now reporting `CALDAV:max-resource-size`, but we're not actively
+ restricting it yet.
+* Changed: CalDAV plugin is now responsible for reporting
+ `CALDAV:supported-collation-set` and `CALDAV:supported-calendar-data`
+ properties.
+* Added: Now reporting `CARDDAV:max-resource-size`, but we're not actively
+ restricting it yet.
+* Added: Support for `CARDDAV:supported-collation-set`.
+* Changed: CardDAV plugin is now responsible for reporting
+ `CARDDAV:supported-address-data`. This functionality has been removed from
+ the CardDAV PDO backend.
+* When a REPORT is not supported, we now emit HTTP error 415, instead of 403.
+* #348: `HEAD` requests now work wherever `GET` also works.
+* Changed: Lower priority for the iMip plugins `schedule` event listener.
+* Added: #523 Custom CalDAV backends can now mark any calendar as read-only.
+* The zip release ships with [sabre/vobject 3.3.3][vobj],
+ [sabre/http 3.0.0][http], and [sabre/event 2.0.0][evnt].
+
+
+2.0.9 (2015-09-04)
+------------------
+
+* #705: A `MOVE` request that gets prevented from deleting the source resource
+ will still remove the target resource. Now all events are triggered before
+ any destructive operations.
+* The zip release ships with [sabre/vobject 3.4.6][vobj],
+ [sabre/http 2.0.4][http], and [sabre/event 1.0.1][evnt].
+
+
+
+2.0.8 (2015-07-11)
+------------------
+
+* #677: Resources with the name '0' would not get retrieved when using
+ `Depth: infinity` in a `PROPFIND` request.
+* The zip release ships with [sabre/vobject 3.3.5][vobj],
+ [sabre/http 2.0.4][http], and [sabre/event 1.0.1][evnt].
+
+
+2.0.7 (2015-05-25)
+------------------
+
+* #650: Correctly cleaning up change info after deleting calendars (@ErrOrnAmE).
+* The zip release ships with [sabre/vobject 3.3.4][vobj],
+ [sabre/http 2.0.4][http], and [sabre/event 1.0.1][evnt].
+
+
+2.0.6 (2014-12-10)
+------------------
+
+* Added `Sabre\CalDAV\CalendarRoot` as an alias for
+ `Sabre\CalDAV\CalendarRootNode`. The latter is going to be deprecated in 2.1,
+ so this makes it slightly easier to write code that works in both branches.
+* #497: Making sure we're initializing the sync-token field with a value after
+ migration.
+* The zip release ships with [sabre/vobject 3.3.4][vobj],
+ [sabre/http 2.0.4][http], and [sabre/event 1.0.1][evnt].
+
+
+2.0.5 (2014-10-14)
+------------------
+
+* #514: CalDAV PDO backend didn't work when overriding the 'calendar changes'
+ database table name.
+* #515: 304 status code was not being sent when checking preconditions.
+* The zip release ships with [sabre/vobject 3.3.3][vobj],
+ [sabre/http 2.0.4][http], and [sabre/event 1.0.1][evnt].
+
+
+2.0.4 (2014-08-27)
+------------------
+
+* #483: typo in calendars creation for PostgreSQL.
+* #487: Locks are now automatically removed after a node has been deleted.
+* #496: Improve CalDAV and CardDAV sync when there is no webdav-sync support.
+* Added: Automatically mapping internal sync-tokens to getctag.
+* The zip release ships with [sabre/vobject 3.3.1][vobj],
+ [sabre/http 2.0.4][http], and [sabre/event 1.0.1][evnt].
+
+
+2.0.3 (2014-07-14)
+------------------
+
+* #474: Fixed PropertyStorage `pathFilter()`.
+* #476: CSP policy incorrect, causing stylesheets to not load in the browser
+ plugin.
+* #475: Href properties in the browser plugin sometimes included a backslash.
+* #478: `TooMuchMatches` exception never worked. This was fixed, and we also
+ took this opportunity to rename it to `TooManyMatches`.
+* The zip release ships with [sabre/vobject 3.2.4][vobj],
+ [sabre/http 2.0.4][http], and [sabre/event 1.0.1][evnt].
+
+
+2.0.2 (2014-06-12)
+------------------
+
+* #470: Fixed compatibility with PHP < 5.4.14.
+* #467: Fixed a problem in `examples/calendarserver.php`.
+* #466: All the postgresql sample files have been updated.
+* Fixed: An error would be thrown if a client did a propfind on a node the
+ user didn't have access to.
+* Removed: Old and broken example code from the `examples/` directory.
+* The zip release ships with [sabre/vobject 3.2.3][vobj],
+ [sabre/http 2.0.3][http], and [sabre/event 1.0.1][evnt].
+
+
+2.0.1 (2014-05-28)
+------------------
+
+* #459: PROPFIND requests on Files with no Depth header would return a fatal
+ error.
+* #464: A PROPFIND allprops request should not return properties with status
+ 404.
+* The zip release ships with [sabre/vobject 3.2.2][vobj],
+ [sabre/http 2.0.3][http], and [sabre/event 1.0.0][evnt].
+
+
+2.0.0 (2014-05-22)
+------------------
+
+* The zip release ships with [sabre/vobject 3.2.2][vobj],
+ [sabre/http 2.0.3][http], and [sabre/event 1.0.0][evnt].
+* Fixed: #456: Issue in sqlite migration script.
+* Updated: MySQL database schema optimized by using more efficient column types.
+* Cleaned up browser design.
+
+
+2.0.0-beta1 (2014-05-15)
+-------------------------
+
+* The zip release ships with [sabre/vobject 3.2.2][vobj],
+ [sabre/http 2.0.3][http], and [sabre/event 1.0.0][evnt].
+* BC Break: Property updating and fetching got refactored. Read the [migration
+ document][mi20] for more information. This allows for creation of a generic
+ property storage, and other property-related functionality that was not
+ possible before.
+* BC Break: Removed `propertyUpdate`, `beforeGetProperties` and
+ `afterGetProperties` events.
+* Fixed: #413: Memory optimizations for the CardDAV PDO backend.
+* Updated: Brand new browser plugin with more debugging features and a design
+ that is slightly less painful.
+* Added: Support for the `{DAV:}supported-method-set` property server-wide.
+* Making it easier for implementors to override how the CardDAV addressbook
+ home is located.
+* Fixed: Issue #422 Preconditions were not being set on PUT on non-existant
+ files. Not really a chance for data-loss, but incorrect nevertheless.
+* Fixed: Issue #428: Etag check with `If:` fails if the target is a collection.
+* Fixed: Issues #430, #431, #433: Locks plugin didn't not properly release
+ filesystem based locks.
+* Fixed: #443. Support for creating new calendar subscriptions for OS X 10.9.2
+ and up.
+* Removed: `Sabre\DAV\Server::NODE_*` constants.
+* Moved all precondition checking into a central place, instead of having to
+ think about it on a per-method basis.
+* jCal transformation for calendar-query REPORT now works again.
+* Switched to PSR-4
+* Fixed: #175. Returning ETag header upon a failed `If-Match` or
+ `If-None-Match` check.
+* Removed: `lib/Sabre/autoload.php`. Use `vendor/autoload.php` instead.
+* Removed: all the rfc documentation from the sabre/dav source. This made the
+ package needlessly larger.
+* Updated: Issue #439. Lots of updates in PATCH support. The
+ Sabre_DAV_PartialUpdate_IFile interface is now deprecated and will be
+ removed in a future version.
+* Added: `Sabre\DAV\Exception\LengthRequired`.
+
+1.9.0-alpha2 (2014-01-14)
+-------------------------
+
+* The zip release ships with sabre/vobject 3.1.3, sabre/http 2.0.1, and
+ sabre/event 1.0.0.
+* Added: Browser can now inspect any node, if ?sabreaction=browser is appended.
+* Fixed: Issue #178. Support for multiple items in the Timeout header.
+* Fixed: Issue #382. Stricter checking if calendar-query is allowed to run.
+* Added: Depth: Infinity support for PROPFIND request. Thanks Thomas Müller and
+ Markus Goetz.
+
+
+1.9.0-alpha1 (2013-11-07)
+-------------------------
+
+* The zip release ships with sabre/vobject 3.1.3, sabre/http 2.0.0alpha5, and
+ sabre/event 1.0.0.
+* BC Break: The CardDAV and CalDAV BackendInterface each have a new method:
+ getMultipleCards and getMultipleCalendarObjects. The Abstract and PDO backends
+ have default implementations, but if you implement that interface directly,
+ this method is now required.
+* BC Break: XML property classes now receive an extra argument in their
+ unserialize method ($propertyMap). This allows for recursively parsing
+ properties, if needed.
+* BC Break: Now using sabre/event for event emitting/subscription. For plugin
+ authors this means Server::subscribeEvent is now Server::on, and
+ Server::broadcastEvent is now Server::emit.
+* BC Break: Almost all core functionality moved into a CorePlugin.
+* BC Break: Most events triggered by the server got an overhaul.
+* Changed: Sabre\HTTP now moved into a dedicated sabre/http package.
+* Added: Support for WebDAV-sync (rfc6578).
+* Added: Support for caldav-subscriptions, which is an easy way for caldav
+ clients to manage a list of subscriptions on the server.
+* Added: Support for emitting and receiving jCal instead of iCalendar for
+ CalDAV.
+* Added: BasicCallback authenticaton backend, for creating simple authentication
+ systems without having to define any classes.
+* Added: A $transactionType property on the server class. This can be used for
+ logging and performance measuring purposes.
+* Fixed: If event handlers modify the request body from a PUT request, an ETag
+ is no longer sent back.
+* Added: Sabre\DAV\IMultiGet to optimize requests that retrieve information
+ about lists of resources.
+* Added: MultiGet support to default CalDAV and CardDAV backends, speeding up
+ the multiget and sync reports quite a bit!
+* Added: ICSExportPlugin can now generate jCal, filter on time-ranges and expand
+ recurrences.
+* Fixed: Read-only access to calendars still allows the sharee to modify basic
+ calendar properties, such as the displayname and color.
+* Changed: The default supportedPrivilegeSet has changed. Most privileges are no
+ longer marked as abstract.
+* Changed: More elegant ACL management for CalendarObject and Card nodes.
+* Added: Browser plugin now marks a carddav directory as type Directory, and a
+ shared calendar as 'Shared'.
+* Added: When debugExceptions is turned on, all previous exceptions are also
+ traversed.
+* Removed: Got rid of the Version classes for CalDAV, CardDAV, HTTP, and DAVACL.
+ Now that there's no separate packages anymore, this makes a bit more sense.
+* Added: Generalized the multistatus response parser a bit more, for better
+ re-use.
+* Added: Sabre\DAV\Client now has support for complex properties for PROPPATCH.
+ (Issue #299).
+* Added: Sabre\DAV\Client has support for gzip and deflate encoding.
+* Added: Sabre\DAV\Client now has support for sending objects as streams.
+* Added: Deserializer for {DAV:}current-user-privilege-set.
+* Added: Addressbooks or backends can now specify custom acl rules when creating
+ cards.
+* Added: The ability for plugins to validate custom tokens in If: headers.
+* Changed: Completely refactored the Lock plugin to deal with the new If: header
+ system.
+* Added: Checking preconditions for MOVE, COPY, DELETE and PROPPATCH methods.
+* Added: has() method on DAV\Property\SupportedReportSet.
+* Added: If header now gets checked (with ETag) all the time. Before the dealing
+ with the If-header was a responsibility of the Locking plugin.
+* Fixed: Outbox access for delegates.
+* Added: Issue 333: It's now possible to override the calendar-home in the
+ CalDAV plugin.
+* Added: A negotiateContentType to HTTP\Request. A convenience method.
+* Fixed: Issue 349: Denying copying or moving a resource into it's own subtree.
+* Fixed: SabreDAV catches every exception again.
+* Added: Issue #358, adding a component=vevent parameter to the content-types
+ for calendar objects, if the caldav backend provides this info.
+
+
+1.8.12-stable (2015-01-21)
+--------------------------
+
+* The zip release ships with sabre/vobject 2.1.7.
+* #568: Support empty usernames and passwords in basic auth.
+
+
+1.8.11 (2014-12-10)
+-------------------
+
+* The zip release ships with sabre/vobject 2.1.6.
+* Updated: MySQL database schema optimized by using more efficient column types.
+* #516: The DAV client will now only redirect to HTTP and HTTPS urls.
+
+
+1.8.10 (2014-05-15)
+-------------------
+
+* The zip release ships with sabre/vobject 2.1.4.
+* includes changes from version 1.7.12.
+
+
+1.8.9 (2014-02-26)
+------------------
+
+* The zip release ships with sabre/vobject 2.1.3.
+* includes changes from version 1.7.11.
+
+
+1.8.8 (2014-02-09)
+------------------
+
+* includes changes from version 1.7.10.
+* The zip release ships with sabre/vobject 2.1.3.
+
+1.8.7 (2013-10-02)
+------------------
+
+* the zip release ships with sabre/vobject 2.1.3.
+* includes changes from version 1.7.9.
+
+
+1.8.6 (2013-06-18)
+------------------
+
+* The zip release ships with sabre/vobject 2.1.0.
+* Includes changes from version 1.7.8.
+
+
+1.8.5 (2013-04-11)
+------------------
+
+* The zip release ships with sabre/vobject 2.0.7.
+* Includes changes from version 1.7.7.
+
+
+1.8.4 (2013-04-08)
+------------------
+
+* The zip release ships with sabre/vobject 2.0.7.
+* Includes changes from version 1.7.6.
+
+
+1.8.3 (2013-03-01)
+------------------
+
+* The zip release ships with sabre/vobject 2.0.6.
+* Includes changes from version 1.7.5.
+* Fixed: organizer email-address for shared calendars is now prefixed with
+ mailto:, as it should.
+
+
+1.8.2 (2013-01-19)
+------------------
+
+* The zip release ships with sabre/vobject 2.0.5.
+* Includes changes from version 1.7.4.
+
+
+1.8.1 (2012-12-01)
+------------------
+
+* The zip release ships with sabre/vobject 2.0.5.
+* Includes changes from version 1.7.3.
+* Fixed: Typo in 1.7 migration script caused it to fail.
+
+
+1.8.0 (2012-11-08)
+------------------
+
+* The zip release ships with sabre/vobject 2.0.5.
+* BC Break: Moved the entire codebase to PHP namespaces.
+* BC Break: Every backend package (CalDAV, CardDAV, Auth, Locks, Principals) now
+ has consistent naming conventions. There's a BackendInterface, and an
+ AbstractBackend class.
+* BC Break: Changed a bunch of constructor signatures in the CalDAV package, to
+ reduce dependencies on the ACL package.
+* BC Break: Sabre_CalDAV_ISharedCalendar now also has a getShares method, so
+ sharees can figure out who is also on a shared calendar.
+* Added: Sabre_DAVACL_IPrincipalCollection interface, to advertise support for
+ principal-property-search on any node.
+* Added: Simple console script to fire up a fileserver in the current directory
+ using PHP 5.4's built-in webserver.
+* Added: Sharee's can now also read out the list of invites for a shared
+ calendar.
+* Added: The Proxy principal classes now both implement an interface, for
+ greater flexiblity.
+
+
+1.7.13 (2014-07-28)
+-------------------
+
+* The zip release ships with sabre/vobject 2.1.4.
+* Changed: Removed phing and went with a custom build script for now.
+
+
+1.7.12 (2014-05-15)
+-------------------
+
+* The zip release ships with sabre/vobject 2.1.4.
+* Updated: Issue #439. Lots of updates in PATCH support. The
+ Sabre_DAV_PartialUpdate_IFile interface is now deprecated and will be removed
+ in a future version.
+* Fixed: Restoring old setting after changing libxml_disable_entity_loader.
+* Fixed: Issue #422: Preconditions were not being set on PUT on non-existant
+ files. Not really a chance for data-loss, but incorrect nevertheless.
+* Fixed: Issue #427: Now checking preconditions on DELETE requests.
+* Fixed: Issue #428: Etag check with If: fails if the target is a collection.
+* Fixed: Issue #393: PATCH request with missing end-range was handled
+ incorrectly.
+* Added: Sabre_DAV_Exception_LengthRequired to omit 411 errors.
+
+
+1.7.11 (2014-02-26)
+-------------------
+
+* The zip release ships with sabre/vobject 2.1.3.
+* Fixed: Issue #407: large downloads failed.
+* Fixed: Issue #414: XXE security problem on older PHP versions.
+
+
+1.7.10 (2014-02-09)
+-------------------
+
+* Fixed: Issue #374: Don't urlescape colon (:) when it's not required.
+* Fixed: Potential security vulnerability in the http client.
+
+
+1.7.9 (2013-10-02)
+------------------
+
+* The zip release ships with sabre/vobject 2.1.3.
+* Fixed: Issue #365. Incorrect output when principal urls have spaces in them.
+* Added: Issue #367: Automatically adding a UID to vcards that don't have them.
+
+
+1.7.8 (2013-06-17)
+------------------
+
+* The zip release ships with sabre/vobject 2.1.0.
+* Changed: Sabre\DAV\Client::verifyPeer is now a protected property (instead of
+ private).
+* Fixed: Text was incorrectly escaped in the Href and HrefList properties,
+ disallowing urls with ampersands (&) in them.
+* Added: deserializer for Sabre\DAVACL\Property\CurrentUserPrivilegeSet.
+* Fixed: Issue 335: Client only deserializes properties with status 200.
+* Fixed: Issue 341: Escaping xml in 423 Locked error responses.
+* Added: Issue 339: beforeGetPropertiesForPath event.
+
+
+1.7.7 (2013-04-11)
+------------------
+
+* The zip release ships with sabre/vobject 2.0.7.
+* Fixed: Assets in the browser plugins were not being served on windows
+ machines.
+
+
+1.7.6 (2013-04-08)
+------------------
+
+* The zip release ships with sabre/vobject 2.0.7.
+* Fixed: vcardurl in database schema can now hold 255 characters instead of 80
+ (which is often way to small).
+* Fixed: The browser plugin potentially allowed people to open any arbitrary
+ file on windows servers (CVE-2013-1939).
+
+
+1.7.5 (2013-03-01)
+------------------
+
+* The zip release ships with sabre/vobject 2.0.6.
+* Change: No longer advertising support for 4.0 vcards. iOS and OS X address
+ book don't handle this well, and just advertising 3.0 support seems like the
+ most logical course of action.
+* Added: ->setVerifyPeers to Sabre_DAV_Client (greatly resisting against it,
+ don't use this..).
+
+
+1.7.4 (2013-01-19)
+------------------
+
+* The zip release ships with sabre/vobject 2.0.5.
+* Changed: To be compatibile with MS Office 2011 for Mac, a workaround was
+ removed that was added to support old versions of Windows XP (pre-SP3).
+ Indeed! We needed a crazy workaround to work with one MS product in the past,
+ and we can't keep that workaround to be compatible with another MS product.
+* Fixed: expand-properties REPORT had incorrect values for the href element.
+* Fixed: Range requests now work for non-seekable streams. (Thanks Alfred
+ Klomp).
+* Fixed: Changed serialization of {DAV:}getlastmodified and {DAV:}supportedlock
+ to improve compatiblity with MS Office 2011 for Mac.
+* Changed: reverted the automatic translation of 'DAV:' xml namespaces to
+ 'urn:DAV' when parsing files. Issues were reported with libxml 2.6.32, on a
+ relatively recent debian release, so we'll wait till 2015 to take this one out
+ again.
+* Added: Sabre_DAV_Exception_ServiceUnavailable, for emitting 503's.
+
+
+1.7.3 (2012-12-01)
+------------------
+
+* The zip release ships with sabre/vobject 2.0.5.
+* Fixed: Removing double slashes from getPropertiesForPath.
+* Change: Marked a few more properties in the CardDAV as protected, instead of
+ private.
+* Fixed: SharingPlugin now plays nicer with other plugins with similar
+ functionality.
+* Fixed: Issue 174. Sending back HTTP/1.0 for requests with this version.
+
+
+1.7.2 (2012-11-08)
+------------------
+
+* The zip release ships with sabre/vobject 2.0.5.
+* Added: ACL plugin advertises support for 'calendarserver-principal-
+ property-search'.
+* Fixed: [#153] Allowing for relative http principals in iMip requests.
+* Added: Support for cs:first-name and cs:last-name properties in sharing
+ invites.
+* Fixed: Made a bunch of properties protected, where they were private before.
+* Added: Some non-standard properties for sharing to improve compatibility.
+* Fixed: some bugfixes in postgres sql script.
+* Fixed: When requesting some properties using PROPFIND, they could show up as
+ both '200 Ok' and '403 Forbidden'.
+* Fixed: calendar-proxy principals were not checked for deeper principal
+ membership than 1 level.
+* Fixed: setGroupMemberSet argument now correctly receives relative principal
+ urls, instead of the absolute ones.
+* Fixed: Server class will filter out any bonus properties if any extra were
+ returned. This means the implementor of the IProperty class can be a bit
+ lazier when implementing. Note: bug numbers after this line refer to Google
+ Code tickets. We're using github now.
+
+
+1.7.1 (2012-10-07)
+------------------
+
+* Fixed: include path problem in the migration script.
+
+
+1.7.0 (2012-10-06)
+------------------
+
+* BC Break: The calendarobjects database table has a bunch of new fields, and a
+ migration script is required to ensure everything will keep working. Read the
+ wiki for more details.
+* BC Break: The ICalendar interface now has a new method: calendarQuery.
+* BC Break: In this version a number of classes have been deleted, that have
+ been previously deprecated. Namely: - Sabre_DAV_Directory (now:
+ Sabre_DAV_Collection) - Sabre_DAV_SimpleDirectory (now:
+ Sabre_DAV_SimpleCollection)
+* BC Break: Sabre_CalDAV_Schedule_IMip::sendMessage now has an extra argument.
+ If you extended this class, you should fix this method. It's only used for
+ informational purposes.
+* BC Break: The DAV: namespace is no longer converted to urn:DAV. This was a
+ workaround for a bug in older PHP versions (pre-5.3).
+* Removed: Sabre.includes.php was deprecated, and is now removed.
+* Removed: Sabre_CalDAV_Server was deprecated, and is now removed. Please use
+ Sabre_DAV_Server and check the examples in the examples/ directory.
+* Changed: The Sabre_VObject library now spawned into it's own project! The
+ VObject library is still included in the SabreDAV zip package.
+* Added: Experimental interfaces to allow implementation of caldav-sharing. Note
+ that no implementation is provided yet, just the api hooks.
+* Added: Free-busy reporting compliant with the caldav-scheduling standard. This
+ allows iCal and other clients to fetch other users' free-busy data.
+* Added: Experimental NotificationSupport interface to add caldav notifications.
+* Added: VCF Export plugin. If enabled, it can generate an export of an entire
+ addressbook.
+* Added: Support for PATCH using a SabreDAV format, to live-patch files.
+* Added: Support for Prefer: return-minimal and Brief: t headers for PROPFIND
+ and PROPPATCH requests.
+* Changed: Responsibility for dealing with the calendar-query is now moved from
+ the CalDAV plugin to the CalDAV backends. This allows for heavy optimizations.
+* Changed: The CalDAV PDO backend is now a lot faster for common calendar
+ queries.
+* Changed: We are now using the composer autoloader.
+* Changed: The CalDAV backend now all implement an interface.
+* Changed: Instead of Sabre_DAV_Property, Sabre_DAV_PropertyInterface is now the
+ basis of every property class.
+* Update: Caching results for principal lookups. This should cut down queries
+ and performance for a number of heavy requests.
+* Update: ObjectTree caches lookups much more aggresively, which will help
+ especially speeding up a bunch of REPORT queries.
+* Added: Support for the schedule-calendar-transp property.
+* Fixed: Marking both the text/calendar and text/x-vcard as UTF-8 encoded.
+* Fixed: Workaround for the SOGO connector, as it doesn't understand receiving
+ "text/x-vcard; charset=utf-8" for a contenttype.
+* Added: Sabre_DAV_Client now throws more specific exceptions in cases where we
+ already has an exception class.
+* Added: Sabre_DAV_PartialUpdate. This plugin allows you to use the PATCH method
+ to update parts of a file.
+* Added: Tons of timezone name mappings for Microsoft Exchange.
+* Added: Support for an 'exception' event in the server class.
+* Fixed: Uploaded VCards without a UID are now rejected. (thanks Dominik!)
+* Fixed: Rejecting calendar objects if they are not in the
+ supported-calendar-component list. (thanks Armin!)
+* Fixed: Issue 219: serialize() now reorders correctly.
+* Fixed: Sabre_DAV_XMLUtil no longer returns empty $dom->childNodes if there is
+ whitespace in $dom.
+* Fixed: Returning 409 Conflict instead of 500 when an attempt is made to create
+ a file as a child of something that's not a collection.
+* Fixed: Issue 237: xml-encoding values in SabreDAV error responses.
+* Fixed: Returning 403, instead of 501 when an unknown REPORT is requested.
+* Fixed: Postfixing slash on {DAV:}owner properties.
+* Fixed: Several embarrassing spelling mistakes in docblocks.
+
+
+1.6.10 (2013-06-17)
+-------------------
+
+* Fixed: Text was incorrectly escaped in the Href and HrefList properties,
+ disallowing urls with ampersands (&) in them.
+* Fixed: Issue 341: Escaping xml in 423 Locked error responses.
+
+
+1.6.9 (2013-04-11)
+------------------
+
+* Fixed: Assets in the browser plugins were not being served on windows
+ machines.
+
+
+1.6.8 (2013-04-08)
+------------------
+
+* Fixed: vcardurl in database schema can now hold 255 characters instead of 80
+ (which is often way to small).
+* Fixed: The browser plugin potentially allowed people to open any arbitrary
+ file on windows servers. (CVE-2013-1939).
+
+
+1.6.7 (2013-03-01)
+------------------
+
+* Change: No longer advertising support for 4.0 vcards. iOS and OS X address
+ book don't handle this well, and just advertising 3.0 support seems like the
+ most logical course of action.
+* Added: ->setVerifyPeers to Sabre_DAV_Client (greatly resisting against it,
+ don't use this..).
+
+
+1.6.6 (2013-01-19)
+------------------
+
+* Fixed: Backported a fix for broken XML serialization in error responses.
+ (Thanks @DeepDiver1975!)
+
+
+1.6.5 (2012-10-04)
+------------------
+
+* Fixed: Workaround for line-ending bug OS X 10.8 addressbook has.
+* Added: Ability to allow users to set SSL certificates for the Client class.
+ (Thanks schiesbn!).
+* Fixed: Directory indexes with lots of nodes should be a lot faster.
+* Fixed: Issue 235: E_NOTICE thrown when doing a propfind request with
+ Sabre_DAV_Client, and no valid properties are returned.
+* Fixed: Issue with filtering on alarms in tasks.
+
+
+1.6.4 (2012-08-02)
+------------------
+
+* Fixed: Issue 220: Calendar-query filters may fail when filtering on alarms, if
+ an overridden event has it's alarm removed.
+* Fixed: Compatibility for OS/X 10.8 iCal in the IMipHandler.
+* Fixed: Issue 222: beforeWriteContent shouldn't be called for lock requests.
+* Fixed: Problem with POST requests to the outbox if mailto: was not lower
+ cased.
+* Fixed: Yearly recurrence rule expansion on leap-days no behaves correctly.
+* Fixed: Correctly checking if recurring, all-day events with no dtstart fall in
+ a timerange if the start of the time-range exceeds the start of the instance
+ of an event, but not the end.
+* Fixed: All-day recurring events wouldn't match if an occurence ended exactly
+ on the start of a time-range.
+* Fixed: HTTP basic auth did not correctly deal with passwords containing colons
+ on some servers.
+* Fixed: Issue 228: DTEND is now non-inclusive for all-day events in the
+ calendar-query REPORT and free-busy calculations.
+
+
+1.6.3 (2012-06-12)
+------------------
+
+* Added: It's now possible to specify in Sabre_DAV_Client which type of
+ authentication is to be used.
+* Fixed: Issue 206: Sabre_DAV_Client PUT requests are fixed.
+* Fixed: Issue 205: Parsing an iCalendar 0-second date interval.
+* Fixed: Issue 112: Stronger validation of iCalendar objects. Now making sure
+ every iCalendar object only contains 1 component, and disallowing vcards,
+ forcing every component to have a UID.
+* Fixed: Basic validation for vcards in the CardDAV plugin.
+* Fixed: Issue 213: Workaround for an Evolution bug, that prevented it from
+ updating events.
+* Fixed: Issue 211: A time-limit query on a non-relative alarm trigger in a
+ recurring event could result in an endless loop.
+* Fixed: All uri fields are now a maximum of 200 characters. The Bynari outlook
+ plugin used much longer strings so this should improve compatibility.
+* Fixed: Added a workaround for a bug in KDE 4.8.2 contact syncing. See
+ https://bugs.kde.org/show_bug.cgi?id=300047
+* Fixed: Issue 217: Sabre_DAV_Tree_FileSystem was pretty broken.
+
+
+1.6.2 (2012-04-16)
+------------------
+
+* Fixed: Sabre_VObject_Node::$parent should have been public.
+* Fixed: Recurrence rules of events are now taken into consideration when doing
+ time-range queries on alarms.
+* Fixed: Added a workaround for the fact that php's DateInterval cannot parse
+ weeks and days at the same time.
+* Added: Sabre_DAV_Server::$exposeVersion, allowing you to hide SabreDAV's
+ version number from various outputs.
+* Fixed: DTSTART values would be incorrect when expanding events.
+* Fixed: DTSTART and DTEND would be incorrect for expansion of WEEKLY BYDAY
+ recurrences.
+* Fixed: Issue 203: A problem with overridden events hitting the exact date and
+ time of a subsequent event in the recurrence set.
+* Fixed: There was a problem with recurrence rules, for example the 5th tuesday
+ of the month, if this day did not exist.
+* Added: New HTTP status codes from draft-nottingham-http-new-status-04.
+
+
+1.6.1 (2012-03-05)
+------------------
+
+* Added: createFile and put() can now return an ETag.
+* Added: Sending back an ETag on for operations on CardDAV backends. This should
+ help with OS X 10.6 Addressbook compatibility.
+* Fixed: Fixed a bug where an infinite loop could occur in the recurrence
+ iterator if the recurrence was YEARLY, with a BYMONTH rule, and either BYDAY
+ or BYMONTHDAY match the first day of the month.
+* Fixed: Events that are excluded using EXDATE are still counted in the COUNT=
+ parameter in the RRULE property.
+* Added: Support for time-range filters on VALARM components.
+* Fixed: Correctly filtering all-day events.
+* Fixed: Sending back correct mimetypes from the browser plugin (thanks
+ Jürgen).
+* Fixed: Issue 195: Sabre_CardDAV pear package had an incorrect dependency.
+* Fixed: Calendardata would be destroyed when performing a MOVE request.
+
+
+1.6.0 (2012-02-22)
+------------------
+
+* BC Break: Now requires PHP 5.3
+* BC Break: Any node that implemented Sabre_DAVACL_IACL must now also implement
+ the getSupportedPrivilegeSet method. See website for details.
+* BC Break: Moved functions from Sabre_CalDAV_XMLUtil to
+ Sabre_VObject_DateTimeParser.
+* BC Break: The Sabre_DAVACL_IPrincipalCollection now has two new methods:
+ 'searchPrincipals' and 'updatePrincipal'.
+* BC Break: Sabre_DAV_ILockable is removed and all related per-node locking
+ functionality.
+* BC Break: Sabre_DAV_Exception_FileNotFound is now deprecated in favor of
+ Sabre_DAV_Exception_NotFound. The former will be removed in a later version.
+* BC Break: Removed Sabre_CalDAV_ICalendarUtil, use Sabre_VObject instead.
+* BC Break: Sabre_CalDAV_Server is now deprecated, check out the documentation
+ on how to setup a caldav server with just Sabre_DAV_Server.
+* BC Break: Default Principals PDO backend now needs a new field in the
+ 'principals' table. See the website for details.
+* Added: Ability to create new calendars and addressbooks from within the
+ browser plugin.
+* Added: Browser plugin: icons for various nodes.
+* Added: Support for FREEBUSY reports!
+* Added: Support for creating principals with admin-level privileges.
+* Added: Possibility to let server send out invitation emails on behalf of
+ CalDAV client, using Sabre_CalDAV_Schedule_IMip.
+* Changed: beforeCreateFile event now passes data argument by reference.
+* Changed: The 'propertyMap' property from Sabre_VObject_Reader, must now be
+ specified in Sabre_VObject_Property::$classMap.
+* Added: Ability for plugins to tell the ACL plugin which principal plugins are
+ searchable.
+* Added: [DAVACL] Per-node overriding of supported privileges. This allows for
+ custom privileges where needed.
+* Added: [DAVACL] Public 'principalSearch' method on the DAVACL plugin, which
+ allows for easy searching for principals, based on their properties.
+* Added: Sabre_VObject_Component::getComponents() to return a list of only
+ components and not properties.
+* Added: An includes.php file in every sub-package (CalDAV, CardDAV, DAV,
+ DAVACL, HTTP, VObject) as an alternative to the autoloader. This often works
+ much faster.
+* Added: Support for the 'Me card', which allows Addressbook.app users specify
+ which vcard is their own.
+* Added: Support for updating principal properties in the DAVACL principal
+ backends.
+* Changed: Major refactoring in the calendar-query REPORT code. Should make
+ things more flexible and correct.
+* Changed: The calendar-proxy-[read|write] principals will now only appear in
+ the tree, if they actually exist in the Principal backend. This should reduce
+ some problems people have been having with this.
+* Changed: Sabre_VObject_Element_* classes are now renamed to
+ Sabre_VObject_Property. Old classes are retained for backwards compatibility,
+ but this will be removed in the future.
+* Added: Sabre_VObject_FreeBusyGenerator to generate free-busy reports based on
+ lists of events.
+* Added: Sabre_VObject_RecurrenceIterator to find all the dates and times for
+ recurring events.
+* Fixed: Issue 97: Correctly handling RRULE for the calendar-query REPORT.
+* Fixed: Issue 154: Encoding of VObject parameters with no value was incorrect.
+* Added: Support for {DAV:}acl-restrictions property from RFC3744.
+* Added: The contentlength for calendar objects can now be supplied by a CalDAV
+ backend, allowing for more optimizations.
+* Fixed: Much faster implementation of Sabre_DAV_URLUtil::encodePath.
+* Fixed: {DAV:}getcontentlength may now be not specified.
+* Fixed: Issue 66: Using rawurldecode instead of urldecode to decode paths from
+ clients. This means that + will now be treated as a literal rather than a
+ space, and this should improve compatibility with the Windows built-in client.
+* Added: Sabre_DAV_Exception_PaymentRequired exception, to emit HTTP 402 status
+ codes.
+* Added: Some mysql unique constraints to example files.
+* Fixed: Correctly formatting HTTP dates.
+* Fixed: Issue 94: Sending back Last-Modified header for 304 responses.
+* Added: Sabre_VObject_Component_VEvent, Sabre_VObject_Component_VJournal,
+ Sabre_VObject_Component_VTodo and Sabre_VObject_Component_VCalendar.
+* Changed: Properties are now also automatically mapped to their appropriate
+ classes, if they are created using the add() or __set() methods.
+* Changed: Cloning VObject objects now clones the entire tree, rather than just
+ the default shallow copy.
+* Added: Support for recurrence expansion in the CALDAV:calendar-multiget and
+ CALDAV:calendar-query REPORTS.
+* Changed: CalDAV PDO backend now sorts calendars based on the internal
+ 'calendarorder' field.
+* Added: Issue 181: Carddav backends may no optionally not supply the carddata
+ in getCards, if etag and size are specified. This may speed up certain
+ requests.
+* Added: More arguments to beforeWriteContent and beforeCreateFile (see
+ WritingPlugins wiki document).
+* Added: Hook for iCalendar validation. This allows us to validate iCalendar
+ objects when they're uploaded. At the moment we're just validating syntax.
+* Added: VObject now support Windows Timezone names correctly (thanks mrpace2).
+* Added: If a timezonename could not be detected, we fall back on the default
+ PHP timezone.
+* Added: Now a Composer package (thanks willdurand).
+* Fixed: Support for \N as a newline character in the VObject reader.
+* Added: afterWriteContent, afterCreateFile and afterUnbind events.
+* Added: Postgresql example files. Not part of the unittests though, so use at
+ your own risk.
+* Fixed: Issue 182: Removed backticks from sql queries, so it will work with
+ Postgres.
+
+
+1.5.9 (2012-04-16)
+------------------
+
+* Fixed: Issue with parsing timezone identifiers that were surrounded by quotes.
+ (Fixes emClient compatibility).
+
+
+1.5.8 (2012-02-22)
+------------------
+
+* Fixed: Issue 95: Another timezone parsing issue, this time in calendar-query.
+
+
+1.5.7 (2012-02-19)
+------------------
+
+* Fixed: VObject properties are now always encoded before components.
+* Fixed: Sabre_DAVACL had issues with multiple levels of privilege aggregration.
+* Changed: Added 'GuessContentType' plugin to fileserver.php example.
+* Fixed: The Browser plugin will now trigger the correct events when creating
+ files.
+* Fixed: The ICSExportPlugin now considers ACL's.
+* Added: Made it optional to supply carddata from an Addressbook backend when
+ requesting getCards. This can make some operations much faster, and could
+ result in much lower memory use.
+* Fixed: Issue 187: Sabre_DAV_UUIDUtil was missing from includes file.
+* Fixed: Issue 191: beforeUnlock was triggered twice.
+
+
+1.5.6 (2012-01-07)
+------------------
+
+* Fixed: Issue 174: VObject could break UTF-8 characters.
+* Fixed: pear package installation issues.
+
+
+1.5.5 (2011-12-16)
+------------------
+
+* Fixed: CalDAV time-range filter workaround for recurring events.
+* Fixed: Bug in Sabre_DAV_Locks_Backend_File that didn't allow multiple files to
+ be locked at the same time.
+
+
+1.5.4 (2011-10-28)
+------------------
+
+* Fixed: GuessContentType plugin now supports mixed case file extensions.
+* Fixed: DATE-TIME encoding was wrong in VObject. (we used 'DATETIME').
+* Changed: Sending back HTTP 204 after a PUT request on an existing resource
+ instead of HTTP 200. This should fix Evolution CardDAV client compatibility.
+* Fixed: Issue 95: Parsing X-LIC-LOCATION if it's available.
+* Added: All VObject elements now have a reference to their parent node.
+
+
+1.5.3 (2011-09-28)
+------------------
+
+* Fixed: Sabre_DAV_Collection was missing from the includes file.
+* Fixed: Issue 152. iOS 1.4.2 apparantly requires HTTP/1.1 200 OK to be in
+ uppercase.
+* Fixed: Issue 153: Support for files with mixed newline styles in
+ Sabre_VObject.
+* Fixed: Issue 159: Automatically converting any vcard and icalendardata to
+ UTF-8.
+* Added: Sabre_DAV_SimpleFile class for easy static file creation.
+* Added: Issue 158: Support for the CARDDAV:supported-address-data property.
+
+
+1.5.2 (2011-09-21)
+------------------
+
+* Fixed: carddata and calendardata MySQL fields are now of type 'mediumblob'.
+ 'TEXT' was too small sometimes to hold all the data.
+* Fixed: {DAV:}supported-report-set is now correctly reporting the reports for
+ IAddressBook.
+* Added: Sabre_VObject_Property::add() to add duplicate parameters to
+ properties.
+* Added: Issue 151: Sabre_CalDAV_ICalendar and Sabre_CalDAV_ICalendarObject
+ interfaces.
+* Fixed: Issue 140: Not returning 201 Created if an event cancelled the creation
+ of a file.
+* Fixed: Issue 150: Faster URLUtil::encodePath() implementation.
+* Fixed: Issue 144: Browser plugin could interfere with
+ TemporaryFileFilterPlugin if it was loaded first.
+* Added: It's not possible to specify more 'alternate uris' in principal
+ backends.
+
+
+1.5.1 (2011-08-24)
+------------------
+
+* Fixed: Issue 137. Hiding action interface in HTML browser for non-collections.
+* Fixed: addressbook-query is now correctly returned from the
+ {DAV:}supported-report-set property.
+* Fixed: Issue 142: Bugs in groupwareserver.php example.
+* Fixed: Issue 139: Rejecting PUT requests with Content-Range.
+
+
+1.5.0 (2011-08-12)
+------------------
+
+* Added: CardDAV support.
+* Added: An experimental WebDAV client.
+* Added: MIME-Directory grouping support in the VObject library. This is very
+ useful for people attempting to parse vcards.
+* BC Break: Adding parameters with the VObject libraries now overwrites the
+ previous parameter, rather than just add it. This makes more sense for 99% of
+ the cases.
+* BC Break: lib/Sabre.autoload.php is now removed in favor of
+ lib/Sabre/autoload.php.
+* Deprecated: Sabre_DAV_Directory is now deprecated and will be removed in a
+ future version. Use Sabre_DAV_Collection instead.
+* Deprecated: Sabre_DAV_SimpleDirectory is now deprecated and will be removed in
+ a future version. Use Sabre_DAV_SimpleCollection instead.
+* Fixed: Problem with overriding tablenames for the CalDAV backend.
+* Added: Clark-notation parser to XML utility.
+* Added: unset() support to VObject components.
+* Fixed: Refactored CalDAV property fetching to be faster and simpler.
+* Added: Central string-matcher for CalDAV and CardDAV plugins.
+* Added: i;unicode-casemap support
+* Fixed: VObject bug: wouldn't parse parameters if they weren't specified in
+ uppercase.
+* Fixed: VObject bug: Parameters now behave more like Properties.
+* Fixed: VObject bug: Parameters with no value are now correctly parsed.
+* Changed: If calendars don't specify which components they allow, 'all'
+ components are assumed (e.g.: VEVENT, VTODO, VJOURNAL).
+* Changed: Browser plugin now uses POST variable 'sabreAction' instead of
+ 'action' to reduce the chance of collisions.
+
+
+1.4.4 (2011-07-07)
+------------------
+
+* Fixed: Issue 131: Custom CalDAV backends could break in certain cases.
+* Added: The option to override the default tablename all PDO backends use.
+ (Issue 60).
+* Fixed: Issue 124: 'File' authentication backend now takes realm into
+ consideration.
+* Fixed: Sabre_DAV_Property_HrefList now properly deserializes. This allows
+ users to update the {DAV:}group-member-set property.
+* Added: Helper functions for DateTime-values in Sabre_VObject package.
+* Added: VObject library can now automatically map iCalendar properties to
+ custom classes.
+
+
+1.4.3 (2011-04-25)
+------------------
+
+* Fixed: Issue 123: Added workaround for Windows 7 UNLOCK bug.
+* Fixed: datatype of lastmodified field in mysql.calendars.sql. Please change
+ the DATETIME field to an INT to ensure this field will work correctly.
+* Change: Sabre_DAV_Property_Principal is now renamed to
+ Sabre_DAVACL_Property_Principal.
+* Added: API level support for ACL HTTP method.
+* Fixed: Bug in serializing {DAV:}acl property.
+* Added: deserializer for {DAV:}resourcetype property.
+* Added: deserializer for {DAV:}acl property.
+* Added: deserializer for {DAV:}principal property.
+
+
+1.4.2-beta (2011-04-01)
+-----------------------
+
+* Added: It's not possible to disable listing of nodes that are denied read
+ access by ACL.
+* Fixed: Changed a few properties in CalDAV classes from private to protected.
+* Fixed: Issue 119: Terrible things could happen when relying on guessBaseUri,
+ the server was running on the root of the domain and a user tried to access a
+ file ending in .php. This is a slight BC break.
+* Fixed: Issue 118: Lock tokens in If headers without a uri should be treated as
+ the request uri, not 'all relevant uri's.
+* Fixed: Issue 120: PDO backend was incorrectly fetching too much locks in cases
+ where there were similar named locked files in a directory.
+
+
+1.4.1-beta (2011-02-26)
+-----------------------
+
+* Fixed: Sabre_DAV_Locks_Backend_PDO returned too many locks.
+* Fixed: Sabre_HTTP_Request::getHeader didn't return Content-Type when running
+ on apache, so a few workarounds were added.
+* Change: Slightly changed CalDAV Backend API's, to allow for heavy
+ optimizations. This is non-bc breaking.
+
+
+1.4.0-beta (2011-02-12)
+-----------------------
+
+* Added: Partly RFC3744 ACL support.
+* Added: Calendar-delegation (caldav-proxy) support.
+* BC break: In order to fix Issue 99, a new argument had to be added to
+ Sabre_DAV_Locks_Backend_*::getLocks classes. Consult the classes for details.
+* Deprecated: Sabre_DAV_Locks_Backend_FS is now deprecated and will be removed
+ in a later version. Use PDO or the new File class instead.
+* Deprecated: The Sabre_CalDAV_ICalendarUtil class is now marked deprecated, and
+ will be removed in a future version. Please use Sabre_VObject instead.
+* Removed: All principal-related functionality has been removed from the
+ Sabre_DAV_Auth_Plugin, and moved to the Sabre_DAVACL_Plugin.
+* Added: VObject library, for easy vcard/icalendar parsing using a natural
+ interface.
+* Added: Ability to automatically generate full .ics feeds off calendars. To
+ use: Add the Sabre_CalDAV_ICSExportPlugin, and add ?export to your calendar
+ url.
+* Added: Plugins can now specify a pluginname, for easy access using
+ Sabre_DAV_Server::getPlugin().
+* Added: beforeGetProperties event.
+* Added: updateProperties event.
+* Added: Principal listings and calendar-access can now be done privately,
+ disallowing users from accessing or modifying other users' data.
+* Added: You can now pass arrays to the Sabre_DAV_Server constructor. If it's an
+ array with node-objects, a Root collection will automatically be created, and
+ the nodes are used as top-level children.
+* Added: The principal base uri is now customizable. It used to be hardcoded to
+ 'principals/[user]'.
+* Added: getSupportedReportSet method in ServerPlugin class. This allows you to
+ easily specify which reports you're implementing.
+* Added: A '..' link to the HTML browser.
+* Fixed: Issue 99: Locks on child elements were ignored when their parent nodes
+ were deleted.
+* Fixed: Issue 90: lockdiscovery property and LOCK response now include a
+ {DAV}lockroot element.
+* Fixed: Issue 96: support for 'default' collation in CalDAV text-match filters.
+* Fixed: Issue 102: Ensuring that copy and move with identical source and
+ destination uri's fails.
+* Fixed: Issue 105: Supporting MKCALENDAR with no body.
+* Fixed: Issue 109: Small fixes in Sabre_HTTP_Util.
+* Fixed: Issue 111: Properly catching the ownername in a lock (if it's a string)
+* Fixed: Sabre_DAV_ObjectTree::nodeExist always returned false for the root
+ node.
+* Added: Global way to easily supply new resourcetypes for certain node classes.
+* Fixed: Issue 59: Allowing the user to override the authentication realm in
+ Sabre_CalDAV_Server.
+* Update: Issue 97: Looser time-range checking if there's a recurrence rule in
+ an event. This fixes 'missing recurring events'.
+
+
+1.3.0 (2010-10-14)
+------------------
+
+* Added: childExists method to Sabre_DAV_ICollection. This is an api break, so
+ if you implement Sabre_DAV_ICollection directly, add the method.
+* Changed: Almost all HTTP method implementations now take a uri argument,
+ including events. This allows for internal rerouting of certain calls. If you
+ have custom plugins, make sure they use this argument. If they don't, they
+ will likely still work, but it might get in the way of future changes.
+* Changed: All getETag methods MUST now surround the etag with double-quotes.
+ This was a mistake made in all previous SabreDAV versions. If you don't do
+ this, any If-Match, If-None-Match and If: headers using Etags will work
+ incorrectly. (Issue 85).
+* Added: Sabre_DAV_Auth_Backend_AbstractBasic class, which can be used to easily
+ implement basic authentication.
+* Removed: Sabre_DAV_PermissionDenied class. Use Sabre_DAV_Forbidden instead.
+* Removed: Sabre_DAV_IDirectory interface, use Sabre_DAV_ICollection instead.
+* Added: Browser plugin now uses {DAV:}displayname if this property is
+ available.
+* Added: Cache layer in the ObjectTree.
+* Added: Tree classes now have a delete and getChildren method.
+* Fixed: If-Modified-Since and If-Unmodified-Since would be incorrect if the
+ date is an exact match.
+* Fixed: Support for multiple ETags in If-Match and If-None-Match headers.
+* Fixed: Improved baseUrl handling.
+* Fixed: Issue 67: Non-seekable stream support in ::put()/::get().
+* Fixed: Issue 65: Invalid dates are now ignored.
+* Updated: Refactoring in Sabre_CalDAV to make everything a bit more ledgable.
+* Fixed: Issue 88, Issue 89: Fixed compatibility for running SabreDAV on
+ Windows.
+* Fixed: Issue 86: Fixed Content-Range top-boundary from 'file size' to 'file
+ size'-1.
+
+
+1.2.5 (2010-08-18)
+------------------
+
+* Fixed: Issue 73: guessBaseUrl fails for some servers.
+* Fixed: Issue 67: SabreDAV works better with non-seekable streams.
+* Fixed: If-Modified-Since and If-Unmodified-Since would be incorrect if
+ the date is an exact match.
+
+
+1.2.4 (2010-07-13)
+------------------
+
+* Fixed: Issue 62: Guessing baseUrl fails when url contains a query-string.
+* Added: Apache configuration sample for CGI/FastCGI setups.
+* Fixed: Issue 64: Only returning calendar-data when it was actually requested.
+
+
+1.2.3 (2010-06-26)
+------------------
+
+* Fixed: Issue 57: Supporting quotes around etags in If-Match and If-None-Match
+
+
+1.2.2 (2010-06-21)
+------------------
+
+* Updated: SabreDAV now attempts to guess the BaseURI if it's not set.
+* Updated: Better compatibility with BitKinex
+* Fixed: Issue 56: Incorrect behaviour for If-None-Match headers and GET
+ requests.
+* Fixed: Issue with certain encoded paths in Browser Plugin.
+
+
+1.2.1 (2010-06-07)
+------------------
+
+* Fixed: Issue 50, patch by Mattijs Hoitink.
+* Fixed: Issue 51, Adding windows 7 lockfiles to TemporaryFileFilter.
+* Fixed: Issue 38, Allowing custom filters to be added to TemporaryFileFilter.
+* Fixed: Issue 53, ETags in the If: header were always failing. This behaviour
+ is now corrected.
+* Added: Apache Authentication backend, in case authentication through .htaccess
+ is desired.
+* Updated: Small improvements to example files.
+
+
+1.2.0 (2010-05-24)
+------------------
+
+* Fixed: Browser plugin now displays international characters.
+* Changed: More properties in CalDAV classes are now protected instead of
+ private.
+
+
+1.2.0beta3 (2010-05-14)
+-----------------------
+
+* Fixed: Custom properties were not properly sent back for allprops requests.
+* Fixed: Issue 49, incorrect parsing of PROPPATCH, affecting Office 2007.
+* Changed: Removed CalDAV items from includes.php, and added a few missing ones.
+
+
+1.2.0beta2 (2010-05-04)
+-----------------------
+
+* Fixed: Issue 46: Fatal error for some non-existent nodes.
+* Updated: some example sql to include email address.
+* Added: 208 and 508 statuscodes from RFC5842.
+* Added: Apache2 configuration examples
+
+
+1.2.0beta1 (2010-04-28)
+-----------------------
+
+* Fixed: redundant namespace declaration in resourcetypes.
+* Fixed: 2 locking bugs triggered by litmus when no Sabre_DAV_ILockable
+ interface is used.
+* Changed: using http://sabredav.org/ns for all custom xml properties.
+* Added: email address property to principals.
+* Updated: CalendarObject validation.
+
+
+1.2.0alpha4 (2010-04-24)
+------------------------
+
+* Added: Support for If-Range, If-Match, If-None-Match, If-Modified-Since,
+ If-Unmodified-Since.
+* Changed: Brand new build system. Functionality is split up between Sabre,
+ Sabre_HTTP, Sabre_DAV and Sabre_CalDAV packages. In addition to that a new
+ non-pear package will be created with all this functionality combined.
+* Changed: Autoloader moved to Sabre/autoload.php.
+* Changed: The Allow: header is now more accurate, with appropriate HTTP methods
+ per uri.
+* Changed: Now throwing back Sabre_DAV_Exception_MethodNotAllowed on a few
+ places where Sabre_DAV_Exception_NotImplemented was used.
+
+
+1.2.0alpha3 (2010-04-20)
+------------------------
+
+* Update: Complete rewrite of property updating. Now easier to use and atomic.
+* Fixed: Issue 16, automatically adding trailing / to baseUri.
+* Added: text/plain is used for .txt files in GuessContentType plugin.
+* Added: support for principal-property-search and principal-search-property-set
+ reports.
+* Added: Issue 31: Hiding exception information by default. Can be turned on
+ with the Sabre_DAV_Server::$debugExceptions property.
+
+
+1.2.0alpha2 (2010-04-08)
+------------------------
+
+* Added: Calendars are now private and can only be read by the owner.
+* Fixed: double namespace declaration in multistatus responses.
+* Added: MySQL database dumps. MySQL is now also supported next to SQLite.
+* Added: expand-properties REPORT from RFC 3253.
+* Added: Sabre_DAV_Property_IHref interface for properties exposing urls.
+* Added: Issue 25: Throwing error on broken Finder behaviour.
+* Changed: Authentication backend is now aware of current user.
+
+
+1.2.0alpha1 (2010-03-31)
+------------------------
+
+* Fixed: Issue 26: Workaround for broken GVFS behaviour with encoded special
+ characters.
+* Fixed: Issue 34: Incorrect Lock-Token response header for LOCK. Fixes Office
+ 2010 compatibility.
+* Added: Issue 35: SabreDAV version to header to OPTIONS response to ease
+ debugging.
+* Fixed: Issue 36: Incorrect variable name, throwing error in some requests.
+* Fixed: Issue 37: Incorrect smultron regex in temporary filefilter.
+* Fixed: Issue 33: Converting ISO-8859-1 characters to UTF-8.
+* Fixed: Issue 39 & Issue 40: Basename fails on non-utf-8 locales.
+* Added: More unittests.
+* Added: SabreDAV version to all error responses.
+* Added: URLUtil class for decoding urls.
+* Changed: Now using pear.sabredav.org pear channel.
+* Changed: Sabre_DAV_Server::getCopyAndMoveInfo is now a public method.
+
+
+1.1.2-alpha (2010-03-18)
+------------------------
+
+* Added: RFC5397 - current-user-principal support.
+* Fixed: Issue 27: encoding entities in property responses.
+* Added: naturalselection script now allows the user to specify a 'minimum
+ number of bytes' for deletion. This should reduce load due to less crawling
+* Added: Full support for the calendar-query report.
+* Added: More unittests.
+* Added: Support for complex property deserialization through the static
+ ::unserialize() method.
+* Added: Support for modifying calendar-component-set
+* Fixed: Issue 29: Added TIMEOUT_INFINITE constant
+
+
+1.1.1-alpha (2010-03-11)
+------------------------
+
+* Added: RFC5689 - Extended MKCOL support.
+* Fixed: Evolution support for CalDAV.
+* Fixed: PDO-locks backend was pretty much completely broken. This is 100%
+ unittested now.
+* Added: support for ctags.
+* Fixed: Comma's between HTTP methods in 'Allow' method.
+* Changed: default argument for Sabre_DAV_Locks_Backend_FS. This means a
+ datadirectory must always be specified from now on.
+* Changed: Moved Sabre_DAV_Server::parseProps to
+ Sabre_DAV_XMLUtil::parseProperties.
+* Changed: Sabre_DAV_IDirectory is now Sabre_DAV_ICollection.
+* Changed: Sabre_DAV_Exception_PermissionDenied is now
+ Sabre_DAV_Exception_Forbidden.
+* Changed: Sabre_CalDAV_ICalendarCollection is removed.
+* Added: Sabre_DAV_IExtendedCollection.
+* Added: Many more unittests.
+* Added: support for calendar-timezone property.
+
+
+1.1.0-alpha (2010-03-01)
+------------------------
+
+* Note: This version is forked from version 1.0.5, so release dates may be out
+ of order.
+* Added: CalDAV - RFC 4791
+* Removed: Sabre_PHP_Exception. PHP has a built-in ErrorException for this.
+* Added: PDO authentication backend.
+* Added: Example sql for auth, caldav, locks for sqlite.
+* Added: Sabre_DAV_Browser_GuessContentType plugin
+* Changed: Authentication plugin refactored, making it possible to implement
+ non-digest authentication.
+* Fixed: Better error display in browser plugin.
+* Added: Support for {DAV:}supported-report-set
+* Added: XML utility class with helper functions for the WebDAV protocol.
+* Added: Tons of unittests
+* Added: PrincipalCollection and Principal classes
+* Added: Sabre_DAV_Server::getProperties for easy property retrieval
+* Changed: {DAV:}resourceType defaults to 0
+* Changed: Any non-null resourceType now gets a / appended to the href value.
+ Before this was just for {DAV:}collection's, but this is now also the case for
+ for example {DAV:}principal.
+* Changed: The Href property class can now optionally create non-relative uri's.
+* Changed: Sabre_HTTP_Response now returns false if headers are already sent and
+ header-methods are called.
+* Fixed: Issue 19: HEAD requests on Collections
+* Fixed: Issue 21: Typo in Sabre_DAV_Property_Response
+* Fixed: Issue 18: Doesn't work with Evolution Contacts
+
+
+1.0.15 (2010-05-28)
+-------------------
+
+* Added: Issue 31: Hiding exception information by default. Can be turned on
+ with the Sabre_DAV_Server::$debugExceptions property.
+* Added: Moved autoload from lib/ to lib/Sabre/autoload.php. This is also the
+ case in the upcoming 1.2.0, so it will improve future compatibility.
+
+
+1.0.14 (2010-04-15)
+-------------------
+
+* Fixed: double namespace declaration in multistatus responses.
+
+
+1.0.13 (2010-03-30)
+-------------------
+
+* Fixed: Issue 40: Last references to basename/dirname
+
+
+1.0.12 (2010-03-30)
+-------------------
+
+* Fixed: Issue 37: Incorrect smultron regex in temporary filefilter.
+* Fixed: Issue 26: Workaround for broken GVFS behaviour with encoded special
+ characters.
+* Fixed: Issue 33: Converting ISO-8859-1 characters to UTF-8.
+* Fixed: Issue 39: Basename fails on non-utf-8 locales.
+* Added: More unittests.
+* Added: SabreDAV version to all error responses.
+* Added: URLUtil class for decoding urls.
+* Updated: Now using pear.sabredav.org pear channel.
+
+
+1.0.11 (2010-03-23)
+-------------------
+
+* Non-public release. This release is identical to 1.0.10, but it is used to
+ test releasing packages to pear.sabredav.org.
+
+
+1.0.10 (2010-03-22)
+-------------------
+
+* Fixed: Issue 34: Invalid Lock-Token header response.
+* Added: Issue 35: Addign SabreDAV version to HTTP OPTIONS responses.
+
+
+1.0.9 (2010-03-19)
+------------------
+
+* Fixed: Issue 27: Entities not being encoded in PROPFIND responses.
+* Fixed: Issue 29: Added missing TIMEOUT_INFINITE constant.
+
+
+1.0.8 (2010-03-03)
+------------------
+
+* Fixed: Issue 21: typos causing errors
+* Fixed: Issue 23: Comma's between methods in Allow header.
+* Added: Sabre_DAV_ICollection interface, to aid in future compatibility.
+* Added: Sabre_DAV_Exception_Forbidden exception. This will replace
+ Sabre_DAV_Exception_PermissionDenied in the future, and can already be used to
+ ensure future compatibility.
+
+
+1.0.7 (2010-02-24)
+------------------
+
+* Fixed: Issue 19 regression for MS Office
+
+
+1.0.6 (2010-02-23)
+------------------
+
+* Fixed: Issue 19: HEAD requests on Collections
+
+
+1.0.5 (2010-01-22)
+------------------
+
+* Fixed: Fatal error when a malformed url was used for unlocking, in conjuction
+ with Sabre.autoload.php due to a incorrect filename.
+* Fixed: Improved unittests and build system
+
+
+1.0.4 (2010-01-11)
+------------------
+
+* Fixed: needed 2 different releases. One for googlecode and one for pearfarm.
+ This is to retain the old method to install SabreDAV until pearfarm becomes
+ the standard installation method.
+
+
+1.0.3 (2010-01-11)
+------------------
+
+* Added: RFC4709 support (davmount)
+* Added: 6 unittests
+* Added: naturalselection. A tool to keep cache directories below a specified
+ theshold.
+* Changed: Now using pearfarm.org channel server.
+
+
+1.0.1 (2009-12-22)
+------------------
+
+* Fixed: Issue 15: typos in examples
+* Fixed: Minor pear installation issues
+
+
+1.0.0 (2009-11-02)
+------------------
+
+* Added: SimpleDirectory class. This class allows creating static directory
+ structures with ease.
+* Changed: Custom complex properties and exceptions now get an instance of
+ Sabre_DAV_Server as their first argument in serialize()
+* Changed: Href complex property now prepends server's baseUri
+* Changed: delete before an overwriting copy/move is now handles by server class
+ instead of tree classes
+* Changed: events must now explicitly return false to stop execution. Before,
+ execution would be stopped by anything loosely evaluating to false.
+* Changed: the getPropertiesForPath method now takes a different set of
+ arguments, and returns a different response. This allows plugin developers to
+ return statuses for properties other than 200 and 404. The hrefs are now also
+ always calculated relative to the baseUri, and not the uri of the request.
+* Changed: generatePropFindResponse is renamed to generateMultiStatus, and now
+ takes a list of properties similar to the response of getPropertiesForPath.
+ This was also needed to improve flexibility for plugin development.
+* Changed: Auth plugins are no longer included. They were not yet stable
+ quality, so they will probably be reintroduced in a later version.
+* Changed: PROPPATCH also used generateMultiStatus now.
+* Removed: unknownProperties event. This is replaced by the afterGetProperties
+ event, which should provide more flexibility.
+* Fixed: Only calling getSize() on IFile instances in httpHead()
+* Added: beforeBind event. This is invoked upon file or directory creation
+* Added: beforeWriteContent event, this is invoked by PUT and LOCK on an
+ existing resource.
+* Added: beforeUnbind event. This is invoked right before deletion of any
+ resource.
+* Added: afterGetProperties event. This event can be used to make modifications
+ to property responses.
+* Added: beforeLock and beforeUnlock events.
+* Added: afterBind event.
+* Fixed: Copy and Move could fail in the root directory. This is now fixed.
+* Added: Plugins can now be retrieved by their classname. This is useful for
+ inter-plugin communication.
+* Added: The Auth backend can now return usernames and user-id's.
+* Added: The Auth backend got a getUsers method
+* Added: Sabre_DAV_FSExt_Directory now returns quota info
+
+
+0.12.1-beta (2009-09-11)
+------------------------
+
+* Fixed: UNLOCK bug. Unlock didn't work at all
+
+
+0.12-beta (2009-09-10)
+----------------------
+
+* Updated: Browser plugin now shows multiple {DAV:}resourcetype values if
+ available.
+* Added: Experimental PDO backend for Locks Manager
+* Fixed: Sending Content-Length: 0 for every empty response. This improves NGinx
+ compatibility.
+* Fixed: Last modification time is reported in UTC timezone. This improves
+ Finder compatibility.
+
+
+0.11-beta (2009-08-11)
+----------------------
+
+* Updated: Now in Beta
+* Updated: Pear package no longer includes docs/ directory. These just contained
+ rfc's, which are 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_Aggregrate, which can 'host' multiple Tree objects into
+ one.
+* Added: simple basis for HTTP REPORT method. This method is not used yet, but
+ can be used by plugins to add reports.
+* Changed: ->getSize is only called for files, no longer for collections. r303
+* Changed: Sabre_DAV_FilterTree is now Sabre_DAV_Tree_Filter
+* Changed: Sabre_DAV_TemporaryFileFilter is now called
+ Sabre_DAV_Tree_TemporaryFileFilter.
+* Changed: removed functions (get(/set)HTTPRequest(/Response)) from Server
+ class, and using a public property instead.
+* Fixed: bug related to parsing proppatch and propfind requests. Didn't show up
+ in most clients, but it needed fixing regardless. (r255)
+* Fixed: auth-int is now properly supported within HTTP Digest.
+* Fixed: Using application/xml for a mimetype vs. text/xml as per RFC4918 sec
+ 8.2.
+* Fixed: TemporaryFileFilter now lets through GET's if they actually exist on
+ the backend. (r274)
+* FIxed: Some methods didn't get passed through in the FilterTree (r283).
+* Fixed: LockManager is now slightly more complex, Tree classes slightly less.
+ (r287)
+
+
+0.6-alpha (2009-02-16)
+----------------------
+
+* Added: Now uses streams for files, instead of strings. This means it won't
+ require to hold entire files in memory, which can be an issue if you're
+ dealing with big files. Note that this breaks compatibility for put() and
+ createFile methods.
+* Added: HTTP Digest Authentication helper class.
+* Added: Support for HTTP Range header
+* Added: Support for ETags within If: headers
+* Added: The API can now return ETags and override the default Content-Type
+* Added: starting with basic framework for unittesting, using PHPUnit.
+* Added: 49 unittests.
+* Added: Abstraction for the HTTP request.
+* Updated: Using Clark Notation for tags in properties. This means tags are
+ serialized as {namespace}tagName instead of namespace#tagName
+* Fixed: HTTP_BasicAuth class now works as expected.
+* Fixed: DAV_Server uses / for a default baseUrl.
+* Fixed: Last modification date is no longer ignored in PROPFIND.
+* Fixed: PROPFIND now sends back information about the requestUri even when
+ "Depth: 1" is specified.
+
+
+0.5-alpha (2009-01-14)
+----------------------
+
+* Added: Added a very simple example for implementing a mapping to PHP file
+ streams. This should allow easy implementation of for example a WebDAV to FTP
+ proxy.
+* Added: HTTP Basic Authentication helper class.
+* Added: Sabre_HTTP_Response class. This centralizes HTTP operations and will be
+ a start towards the creating of a testing framework.
+* Updated: Backwards compatibility break: all require_once() statements are
+ removed from all the files. It is now recommended to use autoloading of
+ classes, or just including lib/Sabre.includes.php. This fix was made to allow
+ easier integration into applications not using this standard inclusion model.
+* Updated: Better in-file documentation.
+* Updated: Sabre_DAV_Tree can now work with Sabre_DAV_LockManager.
+* Updated: Fixes a shared-lock bug.
+* Updated: Removed ?> from the bottom of each php file.
+* Updated: Split up some operations from Sabre_DAV_Server to
+ Sabre_HTTP_Response.
+* Fixed: examples are now actually included in the pear package.
+
+
+0.4-alpha (2008-11-05)
+----------------------
+
+* Passes all litmus tests!
+* Added: more examples
+* Added: Custom property support
+* Added: Shared lock support
+* Added: Depth support to locks
+* Added: Locking on unmapped urls (non-existent nodes)
+* Fixed: Advertising as WebDAV class 3 support
+
+
+0.3-alpha (2008-06-29)
+----------------------
+
+* Fully working in MS Windows clients.
+* Added: temporary file filter: support for smultron files.
+* Added: Phing build scripts
+* Added: PEAR package
+* Fixed: MOVE bug identified using finder.
+* Fixed: Using gzuncompress instead of gzdecode in the temporary file filter.
+ This seems more common.
+
+
+0.2-alpha (2008-05-27)
+----------------------
+
+* Somewhat working in Windows clients
+* Added: Working PROPPATCH method (doesn't support custom properties yet)
+* Added: Temporary filename handling system
+* Added: Sabre_DAV_IQuota to return quota information
+* Added: PROPFIND now reads the request body and only supplies the requested
+ properties
+
+
+0.1-alpha (2008-04-04)
+----------------------
+
+* First release!
+* Passes litmus: basic, http and copymove test.
+* Fully working in Finder and 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/vendor/sabre/dav/CONTRIBUTING.md b/vendor/sabre/dav/CONTRIBUTING.md
new file mode 100644
index 000000000..425ee19ba
--- /dev/null
+++ b/vendor/sabre/dav/CONTRIBUTING.md
@@ -0,0 +1,87 @@
+Contributing to sabre projects
+==============================
+
+Want to contribute to sabre/dav? Here are some guidelines to ensure your patch
+gets accepted.
+
+
+Building a new feature? Contact us first
+----------------------------------------
+
+We may not want to accept every feature that comes our way. Sometimes
+features are out of scope for our projects.
+
+We don't want to waste your time, so by having a quick chat with us first,
+you may find out quickly if the feature makes sense to us, and we can give
+some tips on how to best build the feature.
+
+If we don't accept the feature, it could be for a number of reasons. For
+instance, we've rejected features in the past because we felt uncomfortable
+assuming responsibility for maintaining the feature.
+
+In those cases, it's often possible to keep the feature separate from the
+sabre projects. sabre/dav for instance has a plugin system, and there's no
+reason the feature can't live in a project you own.
+
+In that case, definitely let us know about your plugin as well, so we can
+feature it on [sabre.io][4].
+
+We are often on [IRC][5], in the #sabredav channel on freenode. If there's
+no one there, post a message on the [mailing list][6].
+
+
+Coding standards
+----------------
+
+sabre projects follow:
+
+1. [PSR-1][1]
+2. [PSR-4][2]
+
+sabre projects don't follow [PSR-2][3].
+
+In addition to that, here's a list of basic rules:
+
+1. PHP 5.4 array syntax must be used every where. This means you use `[` and
+ `]` instead of `array(` and `)`.
+2. Use PHP namespaces everywhere.
+3. Use 4 spaces for indentation.
+4. Try to keep your lines under 80 characters. This is not a hard rule, as
+ there are many places in the source where it felt more sensibile to not
+ do so. In particular, function declarations are never split over multiple
+ lines.
+5. Opening braces (`{`) are _always_ on the same line as the `class`, `if`,
+ `function`, etc. they belong to.
+6. `public` must be omitted from method declarations. It must also be omitted
+ for static properties.
+7. All files should use unix-line endings (`\n`).
+8. Files must omit the closing php tag (`?>`).
+9. `true`, `false` and `null` are always lower-case.
+10. Constants are always upper-case.
+11. Any of the rules stated before may be broken where this is the pragmatic
+ thing to do.
+
+
+Unit test requirements
+----------------------
+
+Any new feature or change requires unittests. We use [PHPUnit][7] for all our
+tests.
+
+Adding unittests will greatly increase the likelyhood of us quickly accepting
+your pull request. If unittests are not included though for whatever reason,
+we'd still _love_ your pull request.
+
+We may have to write the tests ourselves, which can increase the time it takes
+to accept the patch, but we'd still really like your contribution!
+
+To run the testsuite jump into the directory `cd tests` and trigger `phpunit`.
+Make sure you did a `composer install` beforehand.
+
+[1]: http://www.php-fig.org/psr/psr-1/
+[2]: http://www.php-fig.org/psr/psr-4/
+[3]: http://www.php-fig.org/psr/psr-2/
+[4]: http://sabre.io/
+[5]: irc://freenode.net/#sabredav
+[6]: http://groups.google.com/group/sabredav-discuss
+[7]: http://phpunit.de/
diff --git a/vendor/sabre/dav/ChangeLog b/vendor/sabre/dav/ChangeLog
deleted file mode 100644
index 92fe1a231..000000000
--- a/vendor/sabre/dav/ChangeLog
+++ /dev/null
@@ -1,1164 +0,0 @@
-1.8.10-stable (2014-05-15)
- * The zip release ships with sabre/vobject 2.1.4.
- * includes changes from version 1.7.12.
-
-1.8.9-stable (2014-02-26)
- * The zip release ships with sabre/vobject 2.1.3.
- * includes changes from version 1.7.11.
-
-1.8.8-stable (2014-02-09)
- * The zip release ships with sabre/vobject 2.1.3.
- * includes changes from version 1.7.10.
-
-1.8.7-stable (2013-10-02)
- * the zip release ships with sabre/vobject 2.1.3.
- * includes changes from version 1.7.9.
-
-1.8.6-stable (2013-06-18)
- * The zip release ships with sabre/vobject 2.1.0.
- * Includes changes from version 1.7.8.
-
-1.8.5-stable (2013-04-11)
- * The zip release ships with sabre/vobject 2.0.7.
- * Includes changes from version 1.7.7.
-
-1.8.4-stable (2013-04-08)
- * The zip release ships with sabre/vobject 2.0.7.
- * Includes changes from version 1.7.6.
-
-1.8.3-stable (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-stable (2013-01-19)
- * The zip release ships with sabre/vobject 2.0.5.
- * Includes changes from version 1.7.4.
-
-1.8.1-stable (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-stable (2012-11-08)
- * The zip release ships with sabre/vobject 2.0.5.
- * BC Break: Moved the entire codebase to PHP namespaces.
- * BC Break: Every backend package (CalDAV, CardDAV, Auth, Locks,
- Principals) now has consistent naming conventions. There's a
- BackendInterface, and an AbstractBackend class.
- * BC Break: Changed a bunch of constructor signatures in the CalDAV
- package, to reduce dependencies on the ACL package.
- * BC Break: Sabre_CalDAV_ISharedCalendar now also has a getShares method,
- so sharees can figure out who is also on a shared calendar.
-
- * Added: Sabre_DAVACL_IPrincipalCollection interface, to advertise support
- for principal-property-search on any node.
- * Added: Simple console script to fire up a fileserver in the current
- directory using PHP 5.4's built-in webserver.
- * Added: Sharee's can now also read out the list of invites for a shared
- calendar.
- * Added: The Proxy principal classes now both implement an interface, for
- greater flexiblity.
-
-1.7.13-stable (????-??-??)
- * Changed: Removed phing and went with a custom build script for now.
-
-1.7.12-stable (2014-05-15)
- * The zip release ships with sabre/vobject 2.1.4.
- * Updated: Issue #439. Lots of updates in PATCH support. The
- Sabre_DAV_PartialUpdate_IFile interface is now deprecated and will be
- removed in a future version.
- * Fixed: Restoring old setting after changing
- libxml_disable_entity_loader.
- * Fixed: Issue #422: Preconditions were not being set on PUT on non-
- existant files. Not really a chance for data-loss, but incorrect
- nevertheless.
- * Fixed: Issue #427: Now checking preconditions on DELETE requests.
- * Fixed: Issue #428: Etag check with If: fails if the target is a
- collection.
- * Fixed: Issue #393: PATCH request with missing end-range was handled
- incorrectly.
- * Added: Sabre_DAV_Exception_LengthRequired to omit 411 errors.
-
-1.7.11-stable (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-stable (2014-02-09)
- * The zip release ships with sabre/vobject 2.1.3.
- * Fixed: Potential security vulnerability in the http client.
- * Fixed: Issue #374: Don't urlescape colon (:) when it's not required.
-
-1.7.9-stable (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-stable (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-stable (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-stable (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-stable (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-stable (2013-01-19)
- * The zip release ships with sabre/vobject 2.0.5.
- * Changed: To be compatibile with MS Office 2011 for Mac, a workaround was
- removed that was added to support old versions of Windows XP (pre-SP3).
- Indeed! We needed a crazy workaround to work with one MS product in the
- past, and we can't keep that workaround to be compatible with another MS
- product.
- * Fixed: expand-properties REPORT had incorrect values for the href
- element.
- * Fixed: Range requests now work for non-seekable streams. (Thanks Alfred
- Klomp).
- * Fixed: Changed serialization of {DAV:}getlastmodified and
- {DAV:}supportedlock to improve compatiblity with MS Office 2011 for Mac.
- * Changed: reverted the automatic translation of 'DAV:' xml namespaces to
- 'urn:DAV' when parsing files. Issues were reported with libxml 2.6.32,
- on a relatively recent debian release, so we'll wait till 2015 to take
- this one out again.
- * Added: Sabre_DAV_Exception_ServiceUnavailable, for emitting 503's.
-
-1.7.3-stable (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-stable (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-stable (2012-10-07)
- * Fixed: include path problem in the migration script.
-
-1.7.0-stable (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-stable (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-stable (2013-04-11)
- * Fixed: Assets in the browser plugins were not being served on windows
- machines.
-
-1.6.8-stable (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-stable (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-stable (2013-01-19)
- * Fixed: Backported a fix for broken XML serialization in error responses.
- (Thanks @DeepDiver1975!)
-
-1.6.5-stable (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-stable (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-stable (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-stable (2012-04-16)
- * Fixed: Sabre_VObject_Node::$parent should have been public.
- * Fixed: Recurrence rules of events are now taken into consideration when
- doing time-range queries on alarms.
- * Fixed: Added a workaround for the fact that php's DateInterval cannot
- parse weeks and days at the same time.
- * Added: Sabre_DAV_Server::$exposeVersion, allowing you to hide SabreDAV's
- version number from various outputs.
- * Fixed: DTSTART values would be incorrect when expanding events.
- * Fixed: DTSTART and DTEND would be incorrect for expansion of WEEKLY
- BYDAY recurrences.
- * Fixed: Issue 203: A problem with overridden events hitting the exact
- date and time of a subsequent event in the recurrence set.
- * Fixed: There was a problem with recurrence rules, for example the 5th
- tuesday of the month, if this day did not exist.
- * Added: New HTTP status codes from draft-nottingham-http-new-status-04.
-
-1.6.1-stable (2012-03-05)
- * Added: createFile and put() can now return an ETag.
- * Added: Sending back an ETag on for operations on CardDAV backends. This
- should help with OS X 10.6 Addressbook compatibility.
- * Fixed: Fixed a bug where an infinite loop could occur in the recurrence
- iterator if the recurrence was YEARLY, with a BYMONTH rule, and either
- BYDAY or BYMONTHDAY match the first day of the month.
- * Fixed: Events that are excluded using EXDATE are still counted in the
- COUNT= parameter in the RRULE property.
- * Added: Support for time-range filters on VALARM components.
- * Fixed: Correctly filtering all-day events.
- * Fixed: Sending back correct mimetypes from the browser plugin (thanks
- Jürgen).
- * Fixed: Issue 195: Sabre_CardDAV pear package had an incorrect dependency.
- * Fixed: Calendardata would be destroyed when performing a MOVE request.
-
-1.6.0-stable (2012-02-22)
- * BC Break: Now requires PHP 5.3
- * BC Break: Any node that implemented Sabre_DAVACL_IACL must now also
- implement the getSupportedPrivilegeSet method. See website for details.
- * BC Break: Moved functions from Sabre_CalDAV_XMLUtil to
- Sabre_VObject_DateTimeParser.
- * BC Break: The Sabre_DAVACL_IPrincipalCollection now has two new methods:
- 'searchPrincipals' and 'updatePrincipal'.
- * BC Break: Sabre_DAV_ILockable is removed and all related per-node
- locking functionality.
- * BC Break: Sabre_DAV_Exception_FileNotFound is now deprecated in favor of
- Sabre_DAV_Exception_NotFound. The former will be removed in a later
- version.
- * BC Break: Removed Sabre_CalDAV_ICalendarUtil, use Sabre_VObject instead.
- * BC Break: Sabre_CalDAV_Server is now deprecated, check out the
- documentation on how to setup a caldav server with just
- Sabre_DAV_Server.
- * BC Break: Default Principals PDO backend now needs a new field in the
- 'principals' table. See the website for details.
- * Added: Ability to create new calendars and addressbooks from within the
- browser plugin.
- * Added: Browser plugin: icons for various nodes.
- * Added: Support for FREEBUSY reports!
- * Added: Support for creating principals with admin-level privileges.
- * Added: Possibility to let server send out invitation emails on behalf of
- CalDAV client, using Sabre_CalDAV_Schedule_IMip.
- * Changed: beforeCreateFile event now passes data argument by reference.
- * Changed: The 'propertyMap' property from Sabre_VObject_Reader, must now
- be specified in Sabre_VObject_Property::$classMap.
- * Added: Ability for plugins to tell the ACL plugin which principal
- plugins are searchable.
- * Added: [DAVACL] Per-node overriding of supported privileges. This allows
- for custom privileges where needed.
- * Added: [DAVACL] Public 'principalSearch' method on the DAVACL plugin,
- which allows for easy searching for principals, based on their
- properties.
- * Added: Sabre_VObject_Component::getComponents() to return a list of only
- components and not properties.
- * Added: An includes.php file in every sub-package (CalDAV, CardDAV, DAV,
- DAVACL, HTTP, VObject) as an alternative to the autoloader. This often
- works much faster.
- * Added: Support for the 'Me card', which allows Addressbook.app users
- specify which vcard is their own.
- * Added: Support for updating principal properties in the DAVACL principal
- backends.
- * Changed: Major refactoring in the calendar-query REPORT code. Should
- make things more flexible and correct.
- * Changed: The calendar-proxy-[read|write] principals will now only appear
- in the tree, if they actually exist in the Principal backend. This should
- reduce some problems people have been having with this.
- * Changed: Sabre_VObject_Element_* classes are now renamed to
- Sabre_VObject_Property. Old classes are retained for backwards
- compatibility, but this will be removed in the future.
- * Added: Sabre_VObject_FreeBusyGenerator to generate free-busy reports
- based on lists of events.
- * Added: Sabre_VObject_RecurrenceIterator to find all the dates and times
- for recurring events.
- * Fixed: Issue 97: Correctly handling RRULE for the calendar-query REPORT.
- * Fixed: Issue 154: Encoding of VObject parameters with no value was
- incorrect.
- * Added: Support for {DAV:}acl-restrictions property from RFC3744.
- * Added: The contentlength for calendar objects can now be supplied by a
- CalDAV backend, allowing for more optimizations.
- * Fixed: Much faster implementation of Sabre_DAV_URLUtil::encodePath.
- * Fixed: {DAV:}getcontentlength may now be not specified.
- * Fixed: Issue 66: Using rawurldecode instead of urldecode to decode paths
- from clients. This means that + will now be treated as a literal rather
- than a space, and this should improve compatibility with the Windows
- built-in client.
- * Added: Sabre_DAV_Exception_PaymentRequired exception, to emit HTTP 402
- status codes.
- * Added: Some mysql unique constraints to example files.
- * Fixed: Correctly formatting HTTP dates.
- * Fixed: Issue 94: Sending back Last-Modified header for 304 responses.
- * Added: Sabre_VObject_Component_VEvent, Sabre_VObject_Component_VJournal,
- Sabre_VObject_Component_VTodo and Sabre_VObject_Component_VCalendar.
- * Changed: Properties are now also automatically mapped to their
- appropriate classes, if they are created using the add() or __set()
- methods.
- * Changed: Cloning VObject objects now clones the entire tree, rather than
- just the default shallow copy.
- * Added: Support for recurrence expansion in the CALDAV:calendar-multiget
- and CALDAV:calendar-query REPORTS.
- * Changed: CalDAV PDO backend now sorts calendars based on the internal
- 'calendarorder' field.
- * Added: Issue 181: Carddav backends may no optionally not supply the carddata in
- getCards, if etag and size are specified. This may speed up certain
- requests.
- * Added: More arguments to beforeWriteContent and beforeCreateFile (see
- WritingPlugins wiki document).
- * Added: Hook for iCalendar validation. This allows us to validate
- iCalendar objects when they're uploaded. At the moment we're just
- validating syntax.
- * Added: VObject now support Windows Timezone names correctly (thanks
- mrpace2).
- * Added: If a timezonename could not be detected, we fall back on the
- default PHP timezone.
- * Added: Now a Composer package (thanks willdurand).
- * Fixed: Support for \N as a newline character in the VObject reader.
- * Added: afterWriteContent, afterCreateFile and afterUnbind events.
- * Added: Postgresql example files. Not part of the unittests though, so
- use at your own risk.
- * Fixed: Issue 182: Removed backticks from sql queries, so it will work
- with Postgres.
-
-1.5.9-stable (2012-04-16)
- * Fixed: Issue with parsing timezone identifiers that were surrounded by
- quotes. (Fixes emClient compatibility).
-
-1.5.8-stable (2012-02-22)
- * Fixed: Issue 95: Another timezone parsing issue, this time in
- calendar-query.
-
-1.5.7-stable (2012-02-19)
- * Fixed: VObject properties are now always encoded before components.
- * Fixed: Sabre_DAVACL had issues with multiple levels of privilege
- aggregration.
- * Changed: Added 'GuessContentType' plugin to fileserver.php example.
- * Fixed: The Browser plugin will now trigger the correct events when
- creating files.
- * Fixed: The ICSExportPlugin now considers ACL's.
- * Added: Made it optional to supply carddata from an Addressbook backend
- when requesting getCards. This can make some operations much faster, and
- could result in much lower memory use.
- * Fixed: Issue 187: Sabre_DAV_UUIDUtil was missing from includes file.
- * Fixed: Issue 191: beforeUnlock was triggered twice.
-
-1.5.6-stable (2012-01-07)
- * Fixed: Issue 174: VObject could break UTF-8 characters.
- * Fixed: pear package installation issues.
-
-1.5.5-stable (2011-12-16)
- * Fixed: CalDAV time-range filter workaround for recurring events.
- * Fixed: Bug in Sabre_DAV_Locks_Backend_File that didn't allow multiple
- files to be locked at the same time.
-
-1.5.4-stable (2011-10-28)
- * Fixed: GuessContentType plugin now supports mixed case file extensions.
- * Fixed: DATE-TIME encoding was wrong in VObject. (we used 'DATETIME').
- * Changed: Sending back HTTP 204 after a PUT request on an existing resource
- instead of HTTP 200. This should fix Evolution CardDAV client
- compatibility.
- * Fixed: Issue 95: Parsing X-LIC-LOCATION if it's available.
- * Added: All VObject elements now have a reference to their parent node.
-
-1.5.3-stable (2011-09-28)
- * Fixed: Sabre_DAV_Collection was missing from the includes file.
- * Fixed: Issue 152. iOS 1.4.2 apparantly requires HTTP/1.1 200 OK to be in
- uppercase.
- * Fixed: Issue 153: Support for files with mixed newline styles in
- Sabre_VObject.
- * Fixed: Issue 159: Automatically converting any vcard and icalendardata
- to UTF-8.
- * Added: Sabre_DAV_SimpleFile class for easy static file creation.
- * Added: Issue 158: Support for the CARDDAV:supported-address-data
- property.
-
-1.5.2-stable (2011-09-21)
- * Fixed: carddata and calendardata MySQL fields are now of type
- 'mediumblob'. 'TEXT' was too small sometimes to hold all the data.
- * Fixed: {DAV:}supported-report-set is now correctly reporting the reports
- for IAddressBook.
- * Added: Sabre_VObject_Property::add() to add duplicate parameters to
- properties.
- * Added: Issue 151: Sabre_CalDAV_ICalendar and Sabre_CalDAV_ICalendarObject
- interfaces.
- * Fixed: Issue 140: Not returning 201 Created if an event cancelled the
- creation of a file.
- * Fixed: Issue 150: Faster URLUtil::encodePath() implementation.
- * Fixed: Issue 144: Browser plugin could interfere with
- TemporaryFileFilterPlugin if it was loaded first.
- * Added: It's not possible to specify more 'alternate uris' in principal
- backends.
-
-1.5.1-stable (2011-08-24)
- * Fixed: Issue 137. Hiding action interface in HTML browser for
- non-collections.
- * Fixed: addressbook-query is now correctly returned from the
- {DAV:}supported-report-set property.
- * Fixed: Issue 142: Bugs in groupwareserver.php example.
- * Fixed: Issue 139: Rejecting PUT requests with Content-Range.
-
-1.5.0-stable (2011-08-12)
- * Added: CardDAV support.
- * Added: An experimental WebDAV client.
- * Added: MIME-Directory grouping support in the VObject library. This is
- very useful for people attempting to parse vcards.
- * BC Break: Adding parameters with the VObject libraries now overwrites
- the previous parameter, rather than just add it. This makes more sense
- for 99% of the cases.
- * BC Break: lib/Sabre.autoload.php is now removed in favor of
- lib/Sabre/autoload.php.
- * Deprecated: Sabre_DAV_Directory is now deprecated and will be removed in
- a future version. Use Sabre_DAV_Collection instead.
- * Deprecated: Sabre_DAV_SimpleDirectory is now deprecated and will be
- removed in a future version. Use Sabre_DAV_SimpleCollection instead.
- * Fixed: Problem with overriding tablenames for the CalDAV backend.
- * Added: Clark-notation parser to XML utility.
- * Added: unset() support to VObject components.
- * Fixed: Refactored CalDAV property fetching to be faster and simpler.
- * Added: Central string-matcher for CalDAV and CardDAV plugins.
- * Added: i;unicode-casemap support
- * Fixed: VObject bug: wouldn't parse parameters if they weren't specified
- in uppercase.
- * Fixed: VObject bug: Parameters now behave more like Properties.
- * Fixed: VObject bug: Parameters with no value are now correctly parsed.
- * Changed: If calendars don't specify which components they allow, 'all'
- components are assumed (e.g.: VEVENT, VTODO, VJOURNAL).
- * Changed: Browser plugin now uses POST variable 'sabreAction' instead of
- 'action' to reduce the chance of collisions.
-
-1.4.4-stable (2011-07-07)
- * Fixed: Issue 131: Custom CalDAV backends could break in certain cases.
- * Added: The option to override the default tablename all PDO backends
- use. (Issue 60).
- * Fixed: Issue 124: 'File' authentication backend now takes realm into
- consideration.
- * Fixed: Sabre_DAV_Property_HrefList now properly deserializes. This
- allows users to update the {DAV:}group-member-set property.
- * Added: Helper functions for DateTime-values in Sabre_VObject package.
- * Added: VObject library can now automatically map iCalendar properties to
- custom classes.
-
-1.4.3-stable (2011-04-25)
- * Fixed: Issue 123: Added workaround for Windows 7 UNLOCK bug.
- * Fixed: datatype of lastmodified field in mysql.calendars.sql. Please
- change the DATETIME field to an INT to ensure this field will work
- correctly.
- * Change: Sabre_DAV_Property_Principal is now renamed to
- Sabre_DAVACL_Property_Principal.
- * Added: API level support for ACL HTTP method.
- * Fixed: Bug in serializing {DAV:}acl property.
- * Added: deserializer for {DAV:}resourcetype property.
- * Added: deserializer for {DAV:}acl property.
- * Added: deserializer for {DAV:}principal property.
-
-1.4.2-beta (2011-04-01)
- * Added: It's not possible to disable listing of nodes that are denied
- read access by ACL.
- * Fixed: Changed a few properties in CalDAV classes from private to
- protected.
- * Fixed: Issue 119: Terrible things could happen when relying on
- guessBaseUri, the server was running on the root of the domain and a user
- tried to access a file ending in .php. This is a slight BC break.
- * Fixed: Issue 118: Lock tokens in If headers without a uri should be
- treated as the request uri, not 'all relevant uri's.
- * Fixed: Issue 120: PDO backend was incorrectly fetching too much locks in
- cases where there were similar named locked files in a directory.
-
-1.4.1-beta (2011-02-26)
- * Fixed: Sabre_DAV_Locks_Backend_PDO returned too many locks.
- * Fixed: Sabre_HTTP_Request::getHeader didn't return Content-Type when
- running on apache, so a few workarounds were added.
- * Change: Slightly changed CalDAV Backend API's, to allow for heavy
- optimizations. This is non-bc breaking.
-
-1.4.0-beta (2011-02-12)
- * Added: Partly RFC3744 ACL support.
- * Added: Calendar-delegation (caldav-proxy) support.
- * BC break: In order to fix Issue 99, a new argument had to be added to
- Sabre_DAV_Locks_Backend_*::getLocks classes. Consult the classes for
- details.
- * Deprecated: Sabre_DAV_Locks_Backend_FS is now deprecated and will be
- removed in a later version. Use PDO or the new File class instead.
- * Deprecated: The Sabre_CalDAV_ICalendarUtil class is now marked
- deprecated, and will be removed in a future version. Please use
- Sabre_VObject instead.
- * Removed: All principal-related functionality has been removed from the
- Sabre_DAV_Auth_Plugin, and moved to the Sabre_DAVACL_Plugin.
- * Added: VObject library, for easy vcard/icalendar parsing using a natural
- interface.
- * Added: Ability to automatically generate full .ics feeds off calendars.
- To use: Add the Sabre_CalDAV_ICSExportPlugin, and add ?export to your
- calendar url.
- * Added: Plugins can now specify a pluginname, for easy access using
- Sabre_DAV_Server::getPlugin().
- * Added: beforeGetProperties event.
- * Added: updateProperties event.
- * Added: Principal listings and calendar-access can now be done privately,
- disallowing users from accessing or modifying other users' data.
- * Added: You can now pass arrays to the Sabre_DAV_Server constructor. If
- it's an array with node-objects, a Root collection will automatically be
- created, and the nodes are used as top-level children.
- * Added: The principal base uri is now customizable. It used to be
- hardcoded to 'principals/[user]'.
- * Added: getSupportedReportSet method in ServerPlugin class. This allows
- you to easily specify which reports you're implementing.
- * Added: A '..' link to the HTML browser.
- * Fixed: Issue 99: Locks on child elements were ignored when their parent
- nodes were deleted.
- * Fixed: Issue 90: lockdiscovery property and LOCK response now include a
- {DAV}lockroot element.
- * Fixed: Issue 96: support for 'default' collation in CalDAV text-match
- filters.
- * Fixed: Issue 102: Ensuring that copy and move with identical source and
- destination uri's fails.
- * Fixed: Issue 105: Supporting MKCALENDAR with no body.
- * Fixed: Issue 109: Small fixes in Sabre_HTTP_Util.
- * Fixed: Issue 111: Properly catching the ownername in a lock (if it's a
- string)
- * Fixed: Sabre_DAV_ObjectTree::nodeExist always returned false for the
- root node.
- * Added: Global way to easily supply new resourcetypes for certain node
- classes.
- * Fixed: Issue 59: Allowing the user to override the authentication realm
- in Sabre_CalDAV_Server.
- * Update: Issue 97: Looser time-range checking if there's a recurrence
- rule in an event. This fixes 'missing recurring events'.
-
-1.3.0 (2010-10-14)
- * Added: childExists method to Sabre_DAV_ICollection. This is an api
- break, so if you implement Sabre_DAV_ICollection directly, add the method.
- * Changed: Almost all HTTP method implementations now take a uri argument,
- including events. This allows for internal rerouting of certain calls.
- If you have custom plugins, make sure they use this argument. If they
- don't, they will likely still work, but it might get in the way of
- future changes.
- * Changed: All getETag methods MUST now surround the etag with
- double-quotes. This was a mistake made in all previous SabreDAV
- versions. If you don't do this, any If-Match, If-None-Match and If:
- headers using Etags will work incorrectly. (Issue 85).
- * Added: Sabre_DAV_Auth_Backend_AbstractBasic class, which can be used to
- easily implement basic authentication.
- * Removed: Sabre_DAV_PermissionDenied class. Use Sabre_DAV_Forbidden
- instead.
- * Removed: Sabre_DAV_IDirectory interface, use Sabre_DAV_ICollection
- instead.
- * Added: Browser plugin now uses {DAV:}displayname if this property is
- available.
- * Added: Cache layer in the ObjectTree.
- * Added: Tree classes now have a delete and getChildren method.
- * Fixed: If-Modified-Since and If-Unmodified-Since would be incorrect if
- the date is an exact match.
- * Fixed: Support for multiple ETags in If-Match and If-None-Match headers.
- * Fixed: Improved baseUrl handling.
- * Fixed: Issue 67: Non-seekable stream support in ::put()/::get().
- * Fixed: Issue 65: Invalid dates are now ignored.
- * Updated: Refactoring in Sabre_CalDAV to make everything a bit more
- ledgable.
- * Fixed: Issue 88, Issue 89: Fixed compatibility for running SabreDAV on
- Windows.
- * Fixed: Issue 86: Fixed Content-Range top-boundary from 'file size' to
- 'file size'-1.
-
-1.2.4 (2010-07-13)
- * Fixed: Issue 62: Guessing baseUrl fails when url contains a
- query-string.
- * Added: Apache configuration sample for CGI/FastCGI setups.
- * Fixed: Issue 64: Only returning calendar-data when it was actually
- requested.
-
-1.2.3 (2010-06-26)
- * Fixed: Issue 57: Supporting quotes around etags in If-Match and
- If-None-Match
-
-1.2.2 (2010-06-21)
- * Updated: SabreDAV now attempts to guess the BaseURI if it's not set.
- * Updated: Better compatibility with BitKinex
- * Fixed: Issue 56: Incorrect behaviour for If-None-Match headers and GET
- requests.
- * Fixed: Issue with certain encoded paths in Browser Plugin.
-
-1.2.1 (2010-06-07)
- * Fixed: Issue 50, patch by Mattijs Hoitink.
- * Fixed: Issue 51, Adding windows 7 lockfiles to TemporaryFileFilter.
- * Fixed: Issue 38, Allowing custom filters to be added to
- TemporaryFileFilter.
- * Fixed: Issue 53, ETags in the If: header were always failing. This
- behaviour is now corrected.
- * Added: Apache Authentication backend, in case authentication through
- .htaccess is desired.
- * Updated: Small improvements to example files.
-
-1.2.0 (2010-05-24)
- * Fixed: Browser plugin now displays international characters.
- * Changed: More properties in CalDAV classes are now protected instead of
- private.
-
-1.2.0beta3 (2010-05-14)
- * Fixed: Custom properties were not properly sent back for allprops
- requests.
- * Fixed: Issue 49, incorrect parsing of PROPPATCH, affecting Office 2007.
- * Changed: Removed CalDAV items from includes.php, and added a few missing
- ones.
-
-1.2.0beta2 (2010-05-04)
- * Fixed: Issue 46: Fatal error for some non-existent nodes.
- * Updated: some example sql to include email address.
- * Added: 208 and 508 statuscodes from RFC5842.
- * Added: Apache2 configuration examples
-
-1.2.0beta1 (2010-04-28)
- * Fixed: redundant namespace declaration in resourcetypes.
- * Fixed: 2 locking bugs triggered by litmus when no Sabre_DAV_ILockable
- interface is used.
- * Changed: using http://sabredav.org/ns for all custom xml properties.
- * Added: email address property to principals.
- * Updated: CalendarObject validation.
-
-1.2.0alpha4 (2010-04-24)
- * Added: Support for If-Range, If-Match, If-None-Match, If-Modified-Since,
- If-Unmodified-Since.
- * Changed: Brand new build system. Functionality is split up between
- Sabre, Sabre_HTTP, Sabre_DAV and Sabre_CalDAV packages. In addition to
- that a new non-pear package will be created with all this functionality
- combined.
- * Changed: Autoloader moved to Sabre/autoload.php.
- * Changed: The Allow: header is now more accurate, with appropriate HTTP
- methods per uri.
- * Changed: Now throwing back Sabre_DAV_Exception_MethodNotAllowed on a few
- places where Sabre_DAV_Exception_NotImplemented was used.
-
-1.2.0alpha3 (2010-04-20)
- * Update: Complete rewrite of property updating. Now easier to use and
- atomic.
- * Fixed: Issue 16, automatically adding trailing / to baseUri.
- * Added: text/plain is used for .txt files in GuessContentType plugin.
- * Added: support for principal-property-search and
- principal-search-property-set reports.
- * Added: Issue 31: Hiding exception information by default. Can be turned
- on with the Sabre_DAV_Server::$debugExceptions property.
-
-1.2.0alpha2 (2010-04-08)
- * Added: Calendars are now private and can only be read by the owner.
- * Fixed: double namespace declaration in multistatus responses.
- * Added: MySQL database dumps. MySQL is now also supported next to SQLite.
- * Added: expand-properties REPORT from RFC 3253.
- * Added: Sabre_DAV_Property_IHref interface for properties exposing urls.
- * Added: Issue 25: Throwing error on broken Finder behaviour.
- * Changed: Authentication backend is now aware of current user.
-
-1.2.0alpha1 (2010-03-31)
- * Fixed: Issue 26: Workaround for broken GVFS behaviour with encoded
- special characters.
- * Fixed: Issue 34: Incorrect Lock-Token response header for LOCK. Fixes
- Office 2010 compatibility.
- * Added: Issue 35: SabreDAV version to header to OPTIONS response to ease
- debugging.
- * Fixed: Issue 36: Incorrect variable name, throwing error in some
- requests.
- * Fixed: Issue 37: Incorrect smultron regex in temporary filefilter.
- * Fixed: Issue 33: Converting ISO-8859-1 characters to UTF-8.
- * Fixed: Issue 39 & Issue 40: Basename fails on non-utf-8 locales.
- * Added: More unittests.
- * Added: SabreDAV version to all error responses.
- * Added: URLUtil class for decoding urls.
- * Changed: Now using pear.sabredav.org pear channel.
- * Changed: Sabre_DAV_Server::getCopyAndMoveInfo is now a public method.
-
-1.1.2-alpha (2010-03-18)
- * Added: RFC5397 - current-user-principal support.
- * Fixed: Issue 27: encoding entities in property responses.
- * Added: naturalselection script now allows the user to specify a 'minimum
- number of bytes' for deletion. This should reduce load due to less
- crawling
- * Added: Full support for the calendar-query report.
- * Added: More unittests.
- * Added: Support for complex property deserialization through the static
- ::unserialize() method.
- * Added: Support for modifying calendar-component-set
- * Fixed: Issue 29: Added TIMEOUT_INFINITE constant
-
-1.1.1-alpha (2010-03-11)
- * Added: RFC5689 - Extended MKCOL support.
- * Fixed: Evolution support for CalDAV.
- * Fixed: PDO-locks backend was pretty much completely broken. This is
- 100% unittested now.
- * Added: support for ctags.
- * Fixed: Comma's between HTTP methods in 'Allow' method.
- * Changed: default argument for Sabre_DAV_Locks_Backend_FS. This means a
- datadirectory must always be specified from now on.
- * Changed: Moved Sabre_DAV_Server::parseProps to
- Sabre_DAV_XMLUtil::parseProperties.
- * Changed: Sabre_DAV_IDirectory is now Sabre_DAV_ICollection.
- * Changed: Sabre_DAV_Exception_PermissionDenied is now
- Sabre_DAV_Exception_Forbidden.
- * Changed: Sabre_CalDAV_ICalendarCollection is removed.
- * Added: Sabre_DAV_IExtendedCollection.
- * Added: Many more unittests.
- * Added: support for calendar-timezone property.
-
-1.1.0-alpha (2010-03-01)
- * Note: This version is forked from version 1.0.5, so release dates may be
- out of order.
- * Added: CalDAV - RFC 4791
- * Removed: Sabre_PHP_Exception. PHP has a built-in ErrorException for
- this.
- * Added: PDO authentication backend.
- * Added: Example sql for auth, caldav, locks for sqlite.
- * Added: Sabre_DAV_Browser_GuessContentType plugin
- * Changed: Authentication plugin refactored, making it possible to
- implement non-digest authentication.
- * Fixed: Better error display in browser plugin.
- * Added: Support for {DAV:}supported-report-set
- * Added: XML utility class with helper functions for the WebDAV protocol.
- * Added: Tons of unittests
- * Added: PrincipalCollection and Principal classes
- * Added: Sabre_DAV_Server::getProperties for easy property retrieval
- * Changed: {DAV:}resourceType defaults to 0
- * Changed: Any non-null resourceType now gets a / appended to the href
- value. Before this was just for {DAV:}collection's, but this is now also
- the case for for example {DAV:}principal.
- * Changed: The Href property class can now optionally create non-relative
- uri's.
- * Changed: Sabre_HTTP_Response now returns false if headers are already
- sent and header-methods are called.
- * Fixed: Issue 19: HEAD requests on Collections
- * Fixed: Issue 21: Typo in Sabre_DAV_Property_Response
- * Fixed: Issue 18: Doesn't work with Evolution Contacts
-
-1.0.15-stable (2010-05-28)
- * Added: Issue 31: Hiding exception information by default. Can be turned
- on with the Sabre_DAV_Server::$debugExceptions property.
- * Added: Moved autoload from lib/ to lib/Sabre/autoload.php. This is also
- the case in the upcoming 1.2.0, so it will improve future compatibility.
-
-1.0.14-stable (2010-04-15)
- * Fixed: double namespace declaration in multistatus responses.
-
-1.0.13-stable (2010-03-30)
- * Fixed: Issue 40: Last references to basename/dirname
-
-1.0.12-stable (2010-03-30)
- * Fixed: Issue 37: Incorrect smultron regex in temporary filefilter.
- * Fixed: Issue 26: Workaround for broken GVFS behaviour with encoded
- special characters.
- * Fixed: Issue 33: Converting ISO-8859-1 characters to UTF-8.
- * Fixed: Issue 39: Basename fails on non-utf-8 locales.
- * Added: More unittests.
- * Added: SabreDAV version to all error responses.
- * Added: URLUtil class for decoding urls.
- * Updated: Now using pear.sabredav.org pear channel.
-
-1.0.11-stable (2010-03-23)
- * Non-public release. This release is identical to 1.0.10, but it is used
- to test releasing packages to pear.sabredav.org.
-
-1.0.10-stable (2010-03-22)
- * Fixed: Issue 34: Invalid Lock-Token header response.
- * Added: Issue 35: Addign SabreDAV version to HTTP OPTIONS responses.
-
-1.0.9-stable (2010-03-19)
- * Fixed: Issue 27: Entities not being encoded in PROPFIND responses.
- * Fixed: Issue 29: Added missing TIMEOUT_INFINITE constant.
-
-1.0.8-stable (2010-03-03)
- * Fixed: Issue 21: typos causing errors
- * Fixed: Issue 23: Comma's between methods in Allow header.
- * Added: Sabre_DAV_ICollection interface, to aid in future compatibility.
- * Added: Sabre_DAV_Exception_Forbidden exception. This will replace
- Sabre_DAV_Exception_PermissionDenied in the future, and can already be
- used to ensure future compatibility.
-
-1.0.7-stable (2010-02-24)
- * Fixed: Issue 19 regression for MS Office
-
-1.0.6-stable (2010-02-23)
- * Fixed: Issue 19: HEAD requests on Collections
-
-1.0.5-stable (2010-01-22)
- * Fixed: Fatal error when a malformed url was used for unlocking, in
- conjuction with Sabre.autoload.php due to a incorrect filename.
- * Fixed: Improved unittests and build system
-
-1.0.4-stable (2010-01-11)
- * Fixed: needed 2 different releases. One for googlecode and one for
- pearfarm. This is to retain the old method to install SabreDAV until
- pearfarm becomes the standard installation method.
-
-1.0.3-stable (2010-01-11)
- * Added: RFC4709 support (davmount)
- * Added: 6 unittests
- * Added: naturalselection. A tool to keep cache directories below a
- specified theshold.
- * Changed: Now using pearfarm.org channel server.
-
-1.0.1-stable (2009-12-22)
- * Fixed: Issue 15: typos in examples
- * Fixed: Minor pear installation issues
-
-1.0.0-stable (2009-11-02)
- * Added: SimpleDirectory class. This class allows creating static
- directory structures with ease.
- * Changed: Custom complex properties and exceptions now get an instance of
- Sabre_DAV_Server as their first argument in serialize()
- * Changed: Href complex property now prepends server's baseUri
- * Changed: delete before an overwriting copy/move is now handles by server
- class instead of tree classes
- * Changed: events must now explicitly return false to stop execution.
- Before, execution would be stopped by anything loosely evaluating to
- false.
- * Changed: the getPropertiesForPath method now takes a different set of
- arguments, and returns a different response. This allows plugin
- developers to return statuses for properties other than 200 and 404. The
- hrefs are now also always calculated relative to the baseUri, and not
- the uri of the request.
- * Changed: generatePropFindResponse is renamed to generateMultiStatus, and
- now takes a list of properties similar to the response of
- getPropertiesForPath. This was also needed to improve flexibility for
- plugin development.
- * Changed: Auth plugins are no longer included. They were not yet stable
- quality, so they will probably be reintroduced in a later version.
- * Changed: PROPPATCH also used generateMultiStatus now.
- * Removed: unknownProperties event. This is replaced by the
- afterGetProperties event, which should provide more flexibility.
- * Fixed: Only calling getSize() on IFile instances in httpHead()
- * Added: beforeBind event. This is invoked upon file or directory creation
- * Added: beforeWriteContent event, this is invoked by PUT and LOCK on an
- existing resource.
- * Added: beforeUnbind event. This is invoked right before deletion of any
- resource.
- * Added: afterGetProperties event. This event can be used to make
- modifications to property responses.
- * Added: beforeLock and beforeUnlock events.
- * Added: afterBind event.
- * Fixed: Copy and Move could fail in the root directory. This is now
- fixed.
- * Added: Plugins can now be retrieved by their classname. This is useful
- for inter-plugin communication.
- * Added: The Auth backend can now return usernames and user-id's.
- * Added: The Auth backend got a getUsers method
- * Added: Sabre_DAV_FSExt_Directory now returns quota info
-
-0.12.1-beta (2009-09-11)
- * Fixed: UNLOCK bug. Unlock didn't work at all
-
-0.12-beta (2009-09-10)
- * Updated: Browser plugin now shows multiple {DAV:}resourcetype values
- if available.
- * Added: Experimental PDO backend for Locks Manager
- * Fixed: Sending Content-Length: 0 for every empty response. This
- improves NGinx compatibility.
- * Fixed: Last modification time is reported in UTC timezone. This improves
- Finder compatibility.
-
-0.11-beta (2009-08-11)
- * Updated: Now in Beta
- * Updated: Pear package no longer includes docs/ directory. These just
- contained rfc's, which are publically available. This reduces the
- package from ~800k to ~60k
- * Added: generatePropfindResponse now takes a baseUri argument
- * Added: ResourceType property can now contain multiple resourcetypes.
- * Fixed: Issue 13.
-
-0.10-alpha (2009-08-03)
- * Added: Plugin to automatically map GET requests to non-files to
- PROPFIND (Sabre_DAV_Browser_MapGetToPropFind). This should allow
- easier debugging of complicated WebDAV setups.
- * Added: Sabre_DAV_Property_Href class. For future use.
- * Added: Ability to choose to use auth-int, auth or both for HTTP Digest
- authentication. (Issue 11)
- * Changed: Made more methods in Sabre_DAV_Server public.
- * Fixed: TemporaryFileFilter plugin now intercepts HTTP LOCK requests
- to non-existent files. (Issue 12)
- * Added: Central list of defined xml namespace prefixes. This can reduce
- Bandwidth and legibility for xml bodies with user-defined namespaces.
- * Added: now a PEAR-compatible package again, thanks to Michael Gauthier
- * Changed: moved default copy and move logic from ObjectTree to Tree class
-
-0.9-alpha (2009-07-21)
- * Changed: Major refactoring, removed most of the logic from the Tree
- objects. The Server class now directly works with the INode, IFile
- and IDirectory objects. If you created your own Tree objects,
- this will most likely break in this release.
- * Changed: Moved all the Locking logic from the Tree and Server classes
- into a separate plugin.
- * Changed: TemporaryFileFilter is now a plugin.
- * Added: Comes with an autoloader script. This can be used instead of
- the includer script, and is preferred by some people.
- * Added: AWS Authentication class.
- * Added: simpleserversetup.py script. This will quickly get a fileserver
- up and running.
- * Added: When subscribing to events, it is now possible to supply a
- priority. This is for example needed to ensure that the Authentication
- Plugin is used before any other Plugin.
- * Added: 22 new tests.
- * Added: Users-manager plugin for .htdigest files. Experimental and
- subject to change.
- * Added: RFC 2324 HTTP 418 status code
- * Fixed: Exclusive locks could in some cases be picked up as shared locks
- * Fixed: Digest auth for non-apache servers had a bug (still not actually
- tested this well).
-
-0.8-alpha (2009-05-30)
- * Changed: Renamed all exceptions! This is a compatibility break. Every
- Exception now follows Sabre_DAV_Exception_FileNotFound convention
- instead of Sabre_DAV_FileNotFoundException.
- * Added: Browser plugin now allows uploading and creating directories
- straight from the browser.
- * Added: 12 more unittests
- * Fixed: Locking bug, which became prevalent on Windows Vista.
- * Fixed: Netdrive support
- * Fixed: TemporaryFileFilter filtered out too many files. Fixed some
- of the regexes.
- * Fixed: Added README and ChangeLog to package
-
-0.7-alpha (2009-03-29)
- * Added: System to return complex properties from PROPFIND.
- * Added: support for {DAV:}supportedlock.
- * Added: support for {DAV:}lockdiscovery.
- * Added: 6 new tests.
- * Added: New plugin system.
- * Added: Simple HTML directory plugin, for browser access.
- * Added: Server class now sends back standard pre-condition error xml
- bodies. This was new since RFC4918.
- * Added: Sabre_DAV_Tree_Aggregrate, which can 'host' multiple Tree objects
- into one.
- * Added: simple basis for HTTP REPORT method. This method is not used yet,
- but can be used by plugins to add reports.
- * Changed: ->getSize is only called for files, no longer for collections.
- r303
- * Changed: Sabre_DAV_FilterTree is now Sabre_DAV_Tree_Filter
- * Changed: Sabre_DAV_TemporaryFileFilter is now called
- Sabre_DAV_Tree_TemporaryFileFilter.
- * Changed: removed functions (get(/set)HTTPRequest(/Response)) from Server
- class, and using a public property instead.
- * Fixed: bug related to parsing proppatch and propfind requests. Didn't
- show up in most clients, but it needed fixing regardless. (r255)
- * Fixed: auth-int is now properly supported within HTTP Digest.
- * Fixed: Using application/xml for a mimetype vs. text/xml as per RFC4918
- sec 8.2.
- * Fixed: TemporaryFileFilter now lets through GET's if they actually
- exist on the backend. (r274)
- * FIxed: Some methods didn't get passed through in the FilterTree (r283).
- * Fixed: LockManager is now slightly more complex, Tree classes slightly
- less. (r287)
-
-0.6-alpha (2009-02-16)
- * Added: Now uses streams for files, instead of strings.
- This means it won't require to hold entire files in memory, which can be
- an issue if you're dealing with big files. Note that this breaks
- compatibility for put() and createFile methods.
- * Added: HTTP Digest Authentication helper class.
- * Added: Support for HTTP Range header
- * Added: Support for ETags within If: headers
- * Added: The API can now return ETags and override the default Content-Type
- * Added: starting with basic framework for unittesting, using PHPUnit.
- * Added: 49 unittests.
- * Added: Abstraction for the HTTP request.
- * Updated: Using Clark Notation for tags in properties. This means tags
- are serialized as {namespace}tagName instead of namespace#tagName
- * Fixed: HTTP_BasicAuth class now works as expected.
- * Fixed: DAV_Server uses / for a default baseUrl.
- * Fixed: Last modification date is no longer ignored in PROPFIND.
- * Fixed: PROPFIND now sends back information about the requestUri even
- when "Depth: 1" is specified.
-
-0.5-alpha (2009-01-14)
- * Added: Added a very simple example for implementing a mapping to PHP
- file streams. This should allow easy implementation of for example a
- WebDAV to FTP proxy.
- * Added: HTTP Basic Authentication helper class.
- * Added: Sabre_HTTP_Response class. This centralizes HTTP operations and
- will be a start towards the creating of a testing framework.
- * Updated: Backwards compatibility break: all require_once() statements
- are removed
- from all the files. It is now recommended to use autoloading of
- classes, or just including lib/Sabre.includes.php. This fix was made
- to allow easier integration into applications not using this standard
- inclusion model.
- * Updated: Better in-file documentation.
- * Updated: Sabre_DAV_Tree can now work with Sabre_DAV_LockManager.
- * Updated: Fixes a shared-lock bug.
- * Updated: Removed ?> from the bottom of each php file.
- * Updated: Split up some operations from Sabre_DAV_Server to
- Sabre_HTTP_Response.
- * Fixed: examples are now actually included in the pear package.
-
-0.4-alpha (2008-11-05)
- * Passes all litmus tests!
- * Added: more examples
- * Added: Custom property support
- * Added: Shared lock support
- * Added: Depth support to locks
- * Added: Locking on unmapped urls (non-existent nodes)
- * Fixed: Advertising as WebDAV class 3 support
-
-0.3-alpha (2008-06-29)
- * Fully working in MS Windows clients.
- * Added: temporary file filter: support for smultron files.
- * Added: Phing build scripts
- * Added: PEAR package
- * Fixed: MOVE bug identified using finder.
- * Fixed: Using gzuncompress instead of gzdecode in the temporary file
- filter. This seems more common.
-
-0.2-alpha (2008-05-27)
- * Somewhat working in Windows clients
- * Added: Working PROPPATCH method (doesn't support custom properties yet)
- * Added: Temporary filename handling system
- * Added: Sabre_DAV_IQuota to return quota information
- * Added: PROPFIND now reads the request body and only supplies the
- requested properties
-
-0.1-alpha (2008-04-04)
- * First release!
- * Passes litmus: basic, http and copymove test.
- * Fully working in Finder and DavFSv2
-
-Project started: 2007-12-13
diff --git a/vendor/sabre/dav/LICENSE b/vendor/sabre/dav/LICENSE
index 7435c19fd..fd3539e33 100644
--- a/vendor/sabre/dav/LICENSE
+++ b/vendor/sabre/dav/LICENSE
@@ -1,4 +1,4 @@
-Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+Copyright (C) 2007-2016 fruux GmbH (https://fruux.com/).
All rights reserved.
diff --git a/vendor/sabre/dav/README.md b/vendor/sabre/dav/README.md
index 8346c9a39..278187b55 100644
--- a/vendor/sabre/dav/README.md
+++ b/vendor/sabre/dav/README.md
@@ -1,30 +1,29 @@
-# What is SabreDAV
-
-SabreDAV allows you to easily add WebDAV support to a PHP application. SabreDAV is meant to cover the entire standard, and attempts to allow integration using an easy to understand API.
-
-### Feature list:
-
-* Fully WebDAV compliant
-* Supports Windows XP, Windows Vista, Mac OS/X, DavFSv2, Cadaver, Netdrive, Open Office, and probably more.
-* Passing all Litmus tests.
-* Supporting class 1, 2 and 3 Webdav servers.
-* Locking support.
-* Custom property support.
-* CalDAV (tested with [Evolution](http://code.google.com/p/sabredav/wiki/Evolution), [iCal](http://code.google.com/p/sabredav/wiki/ICal), [iPhone](http://code.google.com/p/sabredav/wiki/IPhone) and [Lightning](http://code.google.com/p/sabredav/wiki/Lightning)).
-* CardDAV (tested with [OS/X addressbook](http://code.google.com/p/sabredav/wiki/OSXAddressbook), the [iOS addressbook](http://code.google.com/p/sabredav/wiki/iOSCardDAV) and [Evolution](http://code.google.com/p/sabredav/wiki/Evolution)).
-* Over 97% unittest code coverage.
-
-### Supported RFC's:
-
-* [RFC2617](http://www.ietf.org/rfc/rfc2617.txt): Basic/Digest auth.
-* [RFC2518](http://www.ietf.org/rfc/rfc2518.txt): First WebDAV spec.
-* [RFC3744](http://www.ietf.org/rfc/rfc3744.txt): ACL (some features missing).
-* [RFC4709](http://www.ietf.org/rfc/rfc4709.txt): [DavMount](http://code.google.com/p/sabredav/wiki/DavMount).
-* [RFC4791](http://www.ietf.org/rfc/rfc4791.txt): CalDAV.
-* [RFC4918](http://www.ietf.org/rfc/rfc4918.txt): WebDAV revision.
-* [RFC5397](http://www.ietf.org/rfc/rfc5689.txt): current-user-principal.
-* [RFC5689](http://www.ietf.org/rfc/rfc5689.txt): Extended MKCOL.
-* [RFC5789](http://tools.ietf.org/html/rfc5789): PATCH method for HTTP.
-* [RFC6352](http://www.ietf.org/rfc/rfc6352.txt): CardDAV
-* [draft-daboo-carddav-directory-gateway](http://tools.ietf.org/html/draft-daboo-carddav-directory-gateway): CardDAV directory gateway
-* CalDAV ctag, CalDAV-proxy.
+![sabre's logo](http://sabre.io/img/logo.png) SabreDAV
+======================================================
+
+Introduction
+------------
+
+SabreDAV is the most popular WebDAV framework for PHP. Use it to create WebDAV, CalDAV and CardDAV servers.
+
+Full documentation can be found on the website:
+
+http://sabre.io/
+
+Build status
+------------
+
+| branch | status | minimum PHP version |
+| ------------ | ------ | ------------------- |
+| master | [![Build Status](https://travis-ci.org/fruux/sabre-dav.svg?branch=master)](https://travis-ci.org/fruux/sabre-dav) | PHP 5.5 |
+| 3.0 | [![Build Status](https://travis-ci.org/fruux/sabre-dav.svg?branch=3.0)](https://travis-ci.org/fruux/sabre-dav) | PHP 5.4 |
+| 2.1 | [![Build Status](https://travis-ci.org/fruux/sabre-dav.svg?branch=2.1)](https://travis-ci.org/fruux/sabre-dav) | PHP 5.4 |
+| 2.0 | [![Build Status](https://travis-ci.org/fruux/sabre-dav.svg?branch=2.0)](https://travis-ci.org/fruux/sabre-dav) | PHP 5.4 |
+| 1.8 | [![Build Status](https://travis-ci.org/fruux/sabre-dav.svg?branch=1.8)](https://travis-ci.org/fruux/sabre-dav) | PHP 5.3 |
+| 1.7 | [![Build Status](https://travis-ci.org/fruux/sabre-dav.svg?branch=1.7)](https://travis-ci.org/fruux/sabre-dav) | PHP 5.3 |
+| 1.6 | [![Build Status](https://travis-ci.org/fruux/sabre-dav.svg?branch=1.6)](https://travis-ci.org/fruux/sabre-dav) | PHP 5.3 |
+
+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/vendor/sabre/dav/bin/build.php b/vendor/sabre/dav/bin/build.php
index 11cca1e61..82b1e7530 100644..100755
--- a/vendor/sabre/dav/bin/build.php
+++ b/vendor/sabre/dav/bin/build.php
@@ -1,3 +1,4 @@
+#!/usr/bin/env php
<?php
$tasks = [
@@ -9,10 +10,10 @@ $tasks = [
'init', 'test', 'clean',
],
'clean' => [],
- 'test' => [
+ 'test' => [
'composerupdate',
],
- 'init' => [],
+ 'init' => [],
'composerupdate' => [],
];
@@ -35,9 +36,9 @@ if (!isset($tasks[$currentTask])) {
$newTaskList = [];
$oldTaskList = [$currentTask => true];
-while(count($oldTaskList)>0) {
+while (count($oldTaskList) > 0) {
- foreach($oldTaskList as $task=>$foo) {
+ foreach ($oldTaskList as $task => $foo) {
if (!isset($tasks[$task])) {
echo "Dependency not found: " . $task, "\n";
@@ -46,7 +47,7 @@ while(count($oldTaskList)>0) {
$dependencies = $tasks[$task];
$fullFilled = true;
- foreach($dependencies as $dependency) {
+ foreach ($dependencies as $dependency) {
if (isset($newTaskList[$dependency])) {
// Already in the fulfilled task list.
continue;
@@ -65,7 +66,7 @@ while(count($oldTaskList)>0) {
}
-foreach(array_keys($newTaskList) as $task) {
+foreach (array_keys($newTaskList) as $task) {
echo "task: " . $task, "\n";
call_user_func($task);
@@ -100,7 +101,7 @@ function composerupdate() {
global $baseDir;
echo " Updating composer packages to latest version\n\n";
- system('cd ' . $baseDir . '; composer update --dev');
+ system('cd ' . $baseDir . '; composer update');
}
function test() {
@@ -120,12 +121,51 @@ function test() {
function buildzip() {
global $baseDir, $version;
- echo " Asking composer to download sabre/dav $version\n\n";
- system("composer create-project --no-dev sabre/dav build/SabreDAV $version", $code);
- if ($code!==0) {
+ echo " Generating composer.json\n";
+
+ $input = json_decode(file_get_contents(__DIR__ . '/../composer.json'), true);
+ $newComposer = [
+ "require" => $input['require'],
+ "config" => [
+ "bin-dir" => "./bin",
+ ],
+ "prefer-stable" => true,
+ "minimum-stability" => "alpha",
+ ];
+ unset(
+ $newComposer['require']['sabre/vobject'],
+ $newComposer['require']['sabre/http'],
+ $newComposer['require']['sabre/uri'],
+ $newComposer['require']['sabre/event']
+ );
+ $newComposer['require']['sabre/dav'] = $version;
+ mkdir('build/SabreDAV');
+ 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) {
echo "Composer reported error code $code\n";
die(1);
}
+
+ echo " Removing pointless files\n";
+ unlink('build/SabreDAV/composer.json');
+ unlink('build/SabreDAV/composer.lock');
+
+ echo " Moving important files to the root of the project\n";
+
+ $fileNames = [
+ 'CHANGELOG.md',
+ 'LICENSE',
+ 'README.md',
+ 'examples',
+ ];
+ foreach ($fileNames as $fileName) {
+ echo " $fileName\n";
+ rename('build/SabreDAV/vendor/sabre/dav/' . $fileName, 'build/SabreDAV/' . $fileName);
+ }
+
// <zip destfile="build/SabreDAV-${sabredav.version}.zip" basedir="build/SabreDAV" prefix="SabreDAV/" />
echo "\n";
diff --git a/vendor/sabre/dav/bin/migrateto17.php b/vendor/sabre/dav/bin/migrateto17.php
index 66a9ee564..a1173c584 100755
--- a/vendor/sabre/dav/bin/migrateto17.php
+++ b/vendor/sabre/dav/bin/migrateto17.php
@@ -3,7 +3,7 @@
echo "SabreDAV migrate script for version 1.7\n";
-if ($argc<2) {
+if ($argc < 2) {
echo <<<HELLO
@@ -35,12 +35,12 @@ HELLO;
// There's a bunch of places where the autoloader could be, so we'll try all of
// them.
-$paths = array(
+$paths = [
__DIR__ . '/../vendor/autoload.php',
__DIR__ . '/../../../autoload.php',
-);
+];
-foreach($paths as $path) {
+foreach ($paths as $path) {
if (file_exists($path)) {
include $path;
break;
@@ -48,8 +48,8 @@ foreach($paths as $path) {
}
$dsn = $argv[1];
-$user = isset($argv[2])?$argv[2]:null;
-$pass = isset($argv[3])?$argv[3]:null;
+$user = isset($argv[2]) ? $argv[2] : null;
+$pass = isset($argv[3]) ? $argv[3] : null;
echo "Connecting to database: " . $dsn . "\n";
@@ -67,32 +67,32 @@ if (!$row) {
exit(-1);
}
-$requiredFields = array(
+$requiredFields = [
'id',
'calendardata',
'uri',
'calendarid',
'lastmodified',
-);
+];
-foreach($requiredFields as $requiredField) {
- if (!array_key_exists($requiredField,$row)) {
+foreach ($requiredFields as $requiredField) {
+ if (!array_key_exists($requiredField, $row)) {
echo "Error: The current 'calendarobjects' table was missing a field we expected to exist.\n";
echo "For safety reasons, this process is stopped.\n";
exit(-1);
}
}
-$fields17 = array(
+$fields17 = [
'etag',
'size',
'componenttype',
'firstoccurence',
'lastoccurence',
-);
+];
$found = 0;
-foreach($fields17 as $field) {
+foreach ($fields17 as $field) {
if (array_key_exists($field, $row)) {
$found++;
}
@@ -102,7 +102,7 @@ if ($found === 0) {
echo "The database had the 1.6 schema. Table will now be altered.\n";
echo "This may take some time for large tables\n";
- switch($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) {
+ switch ($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) {
case 'mysql' :
@@ -150,7 +150,7 @@ $stmt = $pdo->prepare('UPDATE calendarobjects SET etag = ?, size = ?, componentt
echo "Total records found: " . $result->rowCount() . "\n";
$done = 0;
$total = $result->rowCount();
-while($row = $result->fetch()) {
+while ($row = $result->fetch()) {
try {
$newData = getDenormalizedData($row['calendardata']);
@@ -161,14 +161,14 @@ while($row = $result->fetch()) {
echo "This record is ignored, you should inspect it to see if there's anything wrong.\n===\n";
continue;
}
- $stmt->execute(array(
+ $stmt->execute([
$newData['etag'],
$newData['size'],
$newData['componentType'],
$newData['firstOccurence'],
$newData['lastOccurence'],
$row['id'],
- ));
+ ]);
$done++;
if ($done % 500 === 0) {
@@ -188,7 +188,7 @@ if (array_key_exists('transparent', $row)) {
echo "Adding the 'transparent' field to the calendars table\n";
- switch($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) {
+ switch ($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) {
case 'mysql' :
$pdo->exec("ALTER TABLE calendars ADD transparent TINYINT(1) NOT NULL DEFAULT '0'");
@@ -229,8 +229,8 @@ function getDenormalizedData($calendarData) {
$component = null;
$firstOccurence = null;
$lastOccurence = null;
- foreach($vObject->getComponents() as $component) {
- if ($component->name!=='VTIMEZONE') {
+ foreach ($vObject->getComponents() as $component) {
+ if ($component->name !== 'VTIMEZONE') {
$componentType = $component->name;
break;
}
@@ -256,13 +256,13 @@ function getDenormalizedData($calendarData) {
$lastOccurence = $firstOccurence;
}
} else {
- $it = new \Sabre\VObject\RecurrenceIterator($vObject, (string)$component->UID);
+ $it = new \Sabre\VObject\Recur\EventIterator($vObject, (string)$component->UID);
$maxDate = new DateTime(\Sabre\CalDAV\Backend\PDO::MAX_DATE);
if ($it->isInfinite()) {
$lastOccurence = $maxDate->getTimeStamp();
} else {
$end = $it->getDtEnd();
- while($it->valid() && $end < $maxDate) {
+ while ($it->valid() && $end < $maxDate) {
$end = $it->getDtEnd();
$it->next();
@@ -273,12 +273,12 @@ function getDenormalizedData($calendarData) {
}
}
- return array(
- 'etag' => md5($calendarData),
- 'size' => strlen($calendarData),
- 'componentType' => $componentType,
+ return [
+ 'etag' => md5($calendarData),
+ 'size' => strlen($calendarData),
+ 'componentType' => $componentType,
'firstOccurence' => $firstOccurence,
'lastOccurence' => $lastOccurence,
- );
+ ];
}
diff --git a/vendor/sabre/dav/bin/migrateto20.php b/vendor/sabre/dav/bin/migrateto20.php
new file mode 100755
index 000000000..77236804f
--- /dev/null
+++ b/vendor/sabre/dav/bin/migrateto20.php
@@ -0,0 +1,453 @@
+#!/usr/bin/env php
+<?php
+
+echo "SabreDAV migrate script for version 2.0\n";
+
+if ($argc < 2) {
+
+ echo <<<HELLO
+
+This script help you migrate from a pre-2.0 database to 2.0 and later
+
+The 'calendars', 'addressbooks' and 'cards' tables will be upgraded, and new
+tables (calendarchanges, addressbookchanges, propertystorage) will be added.
+
+If you don't use the default PDO CalDAV or CardDAV backend, it's pointless to
+run this script.
+
+Keep in mind that ALTER TABLE commands will be executed. If you have a large
+dataset this may mean that this process takes a while.
+
+Lastly: Make a back-up first. This script has been tested, but the amount of
+potential variants are extremely high, so it's impossible to deal with every
+possible situation.
+
+In the worst case, you will lose all your data. This is not an overstatement.
+
+Usage:
+
+php {$argv[0]} [pdo-dsn] [username] [password]
+
+For example:
+
+php {$argv[0]} "mysql:host=localhost;dbname=sabredav" root password
+php {$argv[0]} sqlite:data/sabredav.db
+
+HELLO;
+
+ exit();
+
+}
+
+// There's a bunch of places where the autoloader could be, so we'll try all of
+// them.
+$paths = [
+ __DIR__ . '/../vendor/autoload.php',
+ __DIR__ . '/../../../autoload.php',
+];
+
+foreach ($paths as $path) {
+ if (file_exists($path)) {
+ include $path;
+ break;
+ }
+}
+
+$dsn = $argv[1];
+$user = isset($argv[2]) ? $argv[2] : null;
+$pass = isset($argv[3]) ? $argv[3] : null;
+
+echo "Connecting to database: " . $dsn . "\n";
+
+$pdo = new PDO($dsn, $user, $pass);
+$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
+
+$driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
+
+switch ($driver) {
+
+ case 'mysql' :
+ echo "Detected MySQL.\n";
+ break;
+ case 'sqlite' :
+ echo "Detected SQLite.\n";
+ break;
+ default :
+ echo "Error: unsupported driver: " . $driver . "\n";
+ die(-1);
+}
+
+foreach (['calendar', 'addressbook'] as $itemType) {
+
+ $tableName = $itemType . 's';
+ $tableNameOld = $tableName . '_old';
+ $changesTable = $itemType . 'changes';
+
+ echo "Upgrading '$tableName'\n";
+
+ // The only cross-db way to do this, is to just fetch a single record.
+ $row = $pdo->query("SELECT * FROM $tableName LIMIT 1")->fetch();
+
+ if (!$row) {
+
+ echo "No records were found in the '$tableName' table.\n";
+ echo "\n";
+ echo "We're going to rename the old table to $tableNameOld (just in case).\n";
+ echo "and re-create the new table.\n";
+
+ switch ($driver) {
+
+ case 'mysql' :
+ $pdo->exec("RENAME TABLE $tableName TO $tableNameOld");
+ switch ($itemType) {
+ case 'calendar' :
+ $pdo->exec("
+ CREATE TABLE calendars (
+ id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ principaluri VARCHAR(100),
+ displayname VARCHAR(100),
+ uri VARCHAR(200),
+ synctoken INT(11) UNSIGNED NOT NULL DEFAULT '1',
+ description TEXT,
+ calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ calendarcolor VARCHAR(10),
+ timezone TEXT,
+ components VARCHAR(20),
+ transparent TINYINT(1) NOT NULL DEFAULT '0',
+ UNIQUE(principaluri, uri)
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+ ");
+ break;
+ case 'addressbook' :
+ $pdo->exec("
+ CREATE TABLE addressbooks (
+ id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ principaluri VARCHAR(255),
+ displayname VARCHAR(255),
+ uri VARCHAR(200),
+ description TEXT,
+ synctoken INT(11) UNSIGNED NOT NULL DEFAULT '1',
+ UNIQUE(principaluri, uri)
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+ ");
+ break;
+ }
+ break;
+
+ case 'sqlite' :
+
+ $pdo->exec("ALTER TABLE $tableName RENAME TO $tableNameOld");
+
+ switch ($itemType) {
+ case 'calendar' :
+ $pdo->exec("
+ CREATE TABLE calendars (
+ id integer primary key asc,
+ principaluri text,
+ displayname text,
+ uri text,
+ synctoken integer,
+ description text,
+ calendarorder integer,
+ calendarcolor text,
+ timezone text,
+ components text,
+ transparent bool
+ );
+ ");
+ break;
+ case 'addressbook' :
+ $pdo->exec("
+ CREATE TABLE addressbooks (
+ id integer primary key asc,
+ principaluri text,
+ displayname text,
+ uri text,
+ description text,
+ synctoken integer
+ );
+ ");
+
+ break;
+ }
+ break;
+
+ }
+ echo "Creation of 2.0 $tableName table is complete\n";
+
+ } else {
+
+ // Checking if there's a synctoken field already.
+ if (array_key_exists('synctoken', $row)) {
+ echo "The 'synctoken' field already exists in the $tableName table.\n";
+ echo "It's likely you already upgraded, so we're simply leaving\n";
+ echo "the $tableName table alone\n";
+ } else {
+
+ echo "1.8 table schema detected\n";
+ switch ($driver) {
+
+ case 'mysql' :
+ $pdo->exec("ALTER TABLE $tableName ADD synctoken INT(11) UNSIGNED NOT NULL DEFAULT '1'");
+ $pdo->exec("ALTER TABLE $tableName DROP ctag");
+ $pdo->exec("UPDATE $tableName SET synctoken = '1'");
+ break;
+ case 'sqlite' :
+ $pdo->exec("ALTER TABLE $tableName ADD synctoken integer");
+ $pdo->exec("UPDATE $tableName SET synctoken = '1'");
+ echo "Note: there's no easy way to remove fields in sqlite.\n";
+ echo "The ctag field is no longer used, but it's kept in place\n";
+ break;
+
+ }
+
+ echo "Upgraded '$tableName' to 2.0 schema.\n";
+
+ }
+
+ }
+
+ try {
+ $pdo->query("SELECT * FROM $changesTable LIMIT 1");
+
+ echo "'$changesTable' already exists. Assuming that this part of the\n";
+ echo "upgrade was already completed.\n";
+
+ } catch (Exception $e) {
+ echo "Creating '$changesTable' table.\n";
+
+ switch ($driver) {
+
+ case 'mysql' :
+ $pdo->exec("
+ CREATE TABLE $changesTable (
+ id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ uri VARCHAR(200) NOT NULL,
+ synctoken INT(11) UNSIGNED NOT NULL,
+ {$itemType}id INT(11) UNSIGNED NOT NULL,
+ operation TINYINT(1) NOT NULL,
+ INDEX {$itemType}id_synctoken ({$itemType}id, synctoken)
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+ ");
+ break;
+ case 'sqlite' :
+ $pdo->exec("
+
+ CREATE TABLE $changesTable (
+ id integer primary key asc,
+ uri text,
+ synctoken integer,
+ {$itemType}id integer,
+ operation bool
+ );
+
+ ");
+ $pdo->exec("CREATE INDEX {$itemType}id_synctoken ON $changesTable ({$itemType}id, synctoken);");
+ break;
+
+ }
+
+ }
+
+}
+
+try {
+ $pdo->query("SELECT * FROM calendarsubscriptions LIMIT 1");
+
+ echo "'calendarsubscriptions' already exists. Assuming that this part of the\n";
+ echo "upgrade was already completed.\n";
+
+} catch (Exception $e) {
+ echo "Creating calendarsubscriptions table.\n";
+
+ switch ($driver) {
+
+ case 'mysql' :
+ $pdo->exec("
+CREATE TABLE calendarsubscriptions (
+ id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ uri VARCHAR(200) NOT NULL,
+ principaluri VARCHAR(100) NOT NULL,
+ source TEXT,
+ displayname VARCHAR(100),
+ refreshrate VARCHAR(10),
+ calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ calendarcolor VARCHAR(10),
+ striptodos TINYINT(1) NULL,
+ stripalarms TINYINT(1) NULL,
+ stripattachments TINYINT(1) NULL,
+ lastmodified INT(11) UNSIGNED,
+ UNIQUE(principaluri, uri)
+);
+ ");
+ break;
+ case 'sqlite' :
+ $pdo->exec("
+
+CREATE TABLE calendarsubscriptions (
+ id integer primary key asc,
+ uri text,
+ principaluri text,
+ source text,
+ displayname text,
+ refreshrate text,
+ calendarorder integer,
+ calendarcolor text,
+ striptodos bool,
+ stripalarms bool,
+ stripattachments bool,
+ lastmodified int
+);
+ ");
+
+ $pdo->exec("CREATE INDEX principaluri_uri ON calendarsubscriptions (principaluri, uri);");
+ break;
+
+ }
+
+}
+
+try {
+ $pdo->query("SELECT * FROM propertystorage LIMIT 1");
+
+ echo "'propertystorage' already exists. Assuming that this part of the\n";
+ echo "upgrade was already completed.\n";
+
+} catch (Exception $e) {
+ echo "Creating propertystorage table.\n";
+
+ switch ($driver) {
+
+ case 'mysql' :
+ $pdo->exec("
+CREATE TABLE propertystorage (
+ id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ path VARBINARY(1024) NOT NULL,
+ name VARBINARY(100) NOT NULL,
+ value MEDIUMBLOB
+);
+ ");
+ $pdo->exec("
+CREATE UNIQUE INDEX path_property ON propertystorage (path(600), name(100));
+ ");
+ break;
+ case 'sqlite' :
+ $pdo->exec("
+CREATE TABLE propertystorage (
+ id integer primary key asc,
+ path TEXT,
+ name TEXT,
+ value TEXT
+);
+ ");
+ $pdo->exec("
+CREATE UNIQUE INDEX path_property ON propertystorage (path, name);
+ ");
+
+ break;
+
+ }
+
+}
+
+echo "Upgrading cards table to 2.0 schema\n";
+
+try {
+
+ $create = false;
+ $row = $pdo->query("SELECT * FROM cards LIMIT 1")->fetch();
+ if (!$row) {
+ $random = mt_rand(1000, 9999);
+ echo "There was no data in the cards table, so we're re-creating it\n";
+ echo "The old table will be renamed to cards_old$random, just in case.\n";
+
+ $create = true;
+
+ switch ($driver) {
+ case 'mysql' :
+ $pdo->exec("RENAME TABLE cards TO cards_old$random");
+ break;
+ 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";
+ $create = true;
+
+}
+
+if ($create) {
+ switch ($driver) {
+ case 'mysql' :
+ $pdo->exec("
+CREATE TABLE cards (
+ id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ addressbookid INT(11) UNSIGNED NOT NULL,
+ carddata MEDIUMBLOB,
+ uri VARCHAR(200),
+ lastmodified INT(11) UNSIGNED,
+ etag VARBINARY(32),
+ size INT(11) UNSIGNED NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+ ");
+ break;
+
+ case 'sqlite' :
+
+ $pdo->exec("
+CREATE TABLE cards (
+ id integer primary key asc,
+ addressbookid integer,
+ carddata blob,
+ uri text,
+ lastmodified integer,
+ etag text,
+ size integer
+);
+ ");
+ break;
+
+ }
+} else {
+ switch ($driver) {
+ case 'mysql' :
+ $pdo->exec("
+ ALTER TABLE cards
+ ADD etag VARBINARY(32),
+ ADD size INT(11) UNSIGNED NOT NULL;
+ ");
+ break;
+
+ case 'sqlite' :
+
+ $pdo->exec("
+ ALTER TABLE cards ADD etag text;
+ ALTER TABLE cards ADD size integer;
+ ");
+ break;
+
+ }
+ echo "Reading all old vcards and populating etag and size fields.\n";
+ $result = $pdo->query('SELECT id, carddata FROM cards');
+ $stmt = $pdo->prepare('UPDATE cards SET etag = ?, size = ? WHERE id = ?');
+ while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
+ $stmt->execute([
+ md5($row['carddata']),
+ strlen($row['carddata']),
+ $row['id']
+ ]);
+ }
+
+
+}
+
+echo "Upgrade to 2.0 schema completed.\n";
diff --git a/vendor/sabre/dav/bin/migrateto21.php b/vendor/sabre/dav/bin/migrateto21.php
new file mode 100755
index 000000000..f42c4cf88
--- /dev/null
+++ b/vendor/sabre/dav/bin/migrateto21.php
@@ -0,0 +1,180 @@
+#!/usr/bin/env php
+<?php
+
+echo "SabreDAV migrate script for version 2.1\n";
+
+if ($argc < 2) {
+
+ echo <<<HELLO
+
+This script help you migrate from a pre-2.1 database to 2.1.
+
+Changes:
+ The 'calendarobjects' table will be upgraded.
+ 'schedulingobjects' will be created.
+
+If you don't use the default PDO CalDAV or CardDAV backend, it's pointless to
+run this script.
+
+Keep in mind that ALTER TABLE commands will be executed. If you have a large
+dataset this may mean that this process takes a while.
+
+Lastly: Make a back-up first. This script has been tested, but the amount of
+potential variants are extremely high, so it's impossible to deal with every
+possible situation.
+
+In the worst case, you will lose all your data. This is not an overstatement.
+
+Usage:
+
+php {$argv[0]} [pdo-dsn] [username] [password]
+
+For example:
+
+php {$argv[0]} "mysql:host=localhost;dbname=sabredav" root password
+php {$argv[0]} sqlite:data/sabredav.db
+
+HELLO;
+
+ exit();
+
+}
+
+// There's a bunch of places where the autoloader could be, so we'll try all of
+// them.
+$paths = [
+ __DIR__ . '/../vendor/autoload.php',
+ __DIR__ . '/../../../autoload.php',
+];
+
+foreach ($paths as $path) {
+ if (file_exists($path)) {
+ include $path;
+ break;
+ }
+}
+
+$dsn = $argv[1];
+$user = isset($argv[2]) ? $argv[2] : null;
+$pass = isset($argv[3]) ? $argv[3] : null;
+
+echo "Connecting to database: " . $dsn . "\n";
+
+$pdo = new PDO($dsn, $user, $pass);
+$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
+
+$driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
+
+switch ($driver) {
+
+ case 'mysql' :
+ echo "Detected MySQL.\n";
+ break;
+ case 'sqlite' :
+ echo "Detected SQLite.\n";
+ break;
+ default :
+ echo "Error: unsupported driver: " . $driver . "\n";
+ die(-1);
+}
+
+echo "Upgrading 'calendarobjects'\n";
+$addUid = false;
+try {
+ $result = $pdo->query('SELECT * FROM calendarobjects LIMIT 1');
+ $row = $result->fetch(\PDO::FETCH_ASSOC);
+
+ if (!$row) {
+ echo "No data in table. Going to try to add the uid field anyway.\n";
+ $addUid = true;
+ } elseif (array_key_exists('uid', $row)) {
+ echo "uid field exists. Assuming that this part of the migration has\n";
+ echo "Already been completed.\n";
+ } else {
+ echo "2.0 schema detected.\n";
+ $addUid = true;
+ }
+
+} catch (Exception $e) {
+ echo "Could not find a calendarobjects table. Skipping this part of the\n";
+ echo "upgrade.\n";
+}
+
+if ($addUid) {
+
+ switch ($driver) {
+ case 'mysql' :
+ $pdo->exec('ALTER TABLE calendarobjects ADD uid VARCHAR(200)');
+ break;
+ case 'sqlite' :
+ $pdo->exec('ALTER TABLE calendarobjects ADD uid TEXT');
+ break;
+ }
+
+ $result = $pdo->query('SELECT id, calendardata FROM calendarobjects');
+ $stmt = $pdo->prepare('UPDATE calendarobjects SET uid = ? WHERE id = ?');
+ $counter = 0;
+
+ while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
+
+ try {
+ $vobj = \Sabre\VObject\Reader::read($row['calendardata']);
+ } catch (\Exception $e) {
+ echo "Warning! Item with id $row[id] could not be parsed!\n";
+ continue;
+ }
+ $uid = null;
+ $item = $vobj->getBaseComponent();
+ if (!isset($item->UID)) {
+ echo "Warning! Item with id $item[id] does NOT have a UID property and this is required.\n";
+ continue;
+ }
+ $uid = (string)$item->UID;
+ $stmt->execute([$uid, $row['id']]);
+ $counter++;
+
+ }
+
+}
+
+echo "Creating 'schedulingobjects'\n";
+
+switch ($driver) {
+
+ case 'mysql' :
+ $pdo->exec('CREATE TABLE IF NOT EXISTS schedulingobjects
+(
+ id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ principaluri VARCHAR(255),
+ calendardata MEDIUMBLOB,
+ uri VARCHAR(200),
+ lastmodified INT(11) UNSIGNED,
+ etag VARCHAR(32),
+ size INT(11) UNSIGNED NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+ ');
+ break;
+
+
+ case 'sqlite' :
+ $pdo->exec('CREATE TABLE IF NOT EXISTS schedulingobjects (
+ id integer primary key asc,
+ principaluri text,
+ calendardata blob,
+ uri text,
+ lastmodified integer,
+ etag text,
+ size integer
+)
+');
+ break;
+ $pdo->exec('
+ CREATE INDEX principaluri_uri ON calendarsubscriptions (principaluri, uri);
+ ');
+ break;
+}
+
+echo "Done.\n";
+
+echo "Upgrade to 2.1 schema completed.\n";
diff --git a/vendor/sabre/dav/bin/migrateto30.php b/vendor/sabre/dav/bin/migrateto30.php
new file mode 100755
index 000000000..9ca77c13c
--- /dev/null
+++ b/vendor/sabre/dav/bin/migrateto30.php
@@ -0,0 +1,171 @@
+#!/usr/bin/env php
+<?php
+
+echo "SabreDAV migrate script for version 3.0\n";
+
+if ($argc < 2) {
+
+ echo <<<HELLO
+
+This script help you migrate from a pre-3.0 database to 3.0 and later
+
+Changes:
+ * The propertystorage table has changed to allow storage of complex
+ properties.
+ * the vcardurl field in the principals table is no more. This was moved to
+ the propertystorage table.
+
+Keep in mind that ALTER TABLE commands will be executed. If you have a large
+dataset this may mean that this process takes a while.
+
+Lastly: Make a back-up first. This script has been tested, but the amount of
+potential variants are extremely high, so it's impossible to deal with every
+possible situation.
+
+In the worst case, you will lose all your data. This is not an overstatement.
+
+Usage:
+
+php {$argv[0]} [pdo-dsn] [username] [password]
+
+For example:
+
+php {$argv[0]} "mysql:host=localhost;dbname=sabredav" root password
+php {$argv[0]} sqlite:data/sabredav.db
+
+HELLO;
+
+ exit();
+
+}
+
+// There's a bunch of places where the autoloader could be, so we'll try all of
+// them.
+$paths = [
+ __DIR__ . '/../vendor/autoload.php',
+ __DIR__ . '/../../../autoload.php',
+];
+
+foreach ($paths as $path) {
+ if (file_exists($path)) {
+ include $path;
+ break;
+ }
+}
+
+$dsn = $argv[1];
+$user = isset($argv[2]) ? $argv[2] : null;
+$pass = isset($argv[3]) ? $argv[3] : null;
+
+echo "Connecting to database: " . $dsn . "\n";
+
+$pdo = new PDO($dsn, $user, $pass);
+$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
+
+$driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
+
+switch ($driver) {
+
+ case 'mysql' :
+ echo "Detected MySQL.\n";
+ break;
+ case 'sqlite' :
+ echo "Detected SQLite.\n";
+ break;
+ default :
+ echo "Error: unsupported driver: " . $driver . "\n";
+ die(-1);
+}
+
+echo "Upgrading 'propertystorage'\n";
+$addValueType = false;
+try {
+ $result = $pdo->query('SELECT * FROM propertystorage LIMIT 1');
+ $row = $result->fetch(\PDO::FETCH_ASSOC);
+
+ if (!$row) {
+ echo "No data in table. Going to re-create the table.\n";
+ $random = mt_rand(1000, 9999);
+ echo "Renaming propertystorage -> propertystorage_old$random and creating new table.\n";
+
+ switch ($driver) {
+
+ 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,
+ path VARBINARY(1024) NOT NULL,
+ name VARBINARY(100) NOT NULL,
+ valuetype INT UNSIGNED,
+ value MEDIUMBLOB
+ );
+ ');
+ $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);
+ $pdo->exec('
+CREATE TABLE propertystorage (
+ id integer primary key asc,
+ path text,
+ name text,
+ valuetype integer,
+ value blob
+);');
+
+ $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";
+ echo "Already been completed.\n";
+ } else {
+ 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";
+ echo $e->getMessage(), "\n";
+}
+
+if ($addValueType) {
+
+ switch ($driver) {
+ case 'mysql' :
+ $pdo->exec('ALTER TABLE propertystorage ADD valuetype INT UNSIGNED');
+ break;
+ 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";
+
+$result = $pdo->query('SELECT id, uri, vcardurl FROM principals WHERE vcardurl IS NOT NULL');
+$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']),
+ '{http://calendarserver.org/ns/}me-card',
+ serialize(new Sabre\DAV\Xml\Property\Href($row['vcardurl']))
+ ]);
+
+ echo serialize(new Sabre\DAV\Xml\Property\Href($row['vcardurl']));
+
+}
+
+echo "Done.\n";
+echo "Upgrade to 3.0 schema completed.\n";
diff --git a/vendor/sabre/dav/bin/naturalselection.py b/vendor/sabre/dav/bin/naturalselection
index aa5554dd0..52720e31e 100755
--- a/vendor/sabre/dav/bin/naturalselection.py
+++ b/vendor/sabre/dav/bin/naturalselection
@@ -6,7 +6,7 @@
# http://www.rooftopsolutions.nl/
#
# This utility is distributed along with SabreDAV
-# license: http://code.google.com/p/sabredav/wiki/License Modified BSD License
+# license: http://sabre.io/license/ Modified BSD License
import os
from optparse import OptionParser
@@ -16,16 +16,16 @@ def getfreespace(path):
stat = os.statvfs(path)
return stat.f_frsize * stat.f_bavail
-def getbytesleft(path,treshold):
- return getfreespace(path)-treshold
+def getbytesleft(path,threshold):
+ return getfreespace(path)-threshold
-def run(cacheDir, treshold, sleep=5, simulate=False, min_erase = 0):
+def run(cacheDir, threshold, sleep=5, simulate=False, min_erase = 0):
- bytes = getbytesleft(cacheDir,treshold)
+ bytes = getbytesleft(cacheDir,threshold)
if (bytes>0):
- print "Bytes to go before we hit treshhold:", bytes
+ print "Bytes to go before we hit threshold:", bytes
else:
- print "Treshold exceeded with:", -bytes, "bytes"
+ print "Threshold exceeded with:", -bytes, "bytes"
dir = os.listdir(cacheDir)
dir2 = []
for file in dir:
@@ -46,7 +46,7 @@ def run(cacheDir, treshold, sleep=5, simulate=False, min_erase = 0):
left = min_erase
# If the min_erase setting is lower than the amount of bytes over
- # the treshold, we use that number instead.
+ # the threshold, we use that number instead.
if left < -bytes :
left = -bytes
@@ -72,8 +72,8 @@ def run(cacheDir, treshold, sleep=5, simulate=False, min_erase = 0):
def main():
parser = OptionParser(
- version="naturalselecton v0.3",
- description="Cache directory manager. Deletes cache entries based on accesstime and free space tresholds.\n" +
+ version="naturalselection v0.3",
+ description="Cache directory manager. Deletes cache entries based on accesstime and free space thresholds.\n" +
"This utility is distributed alongside SabreDAV.",
usage="usage: %prog [options] cacheDirectory",
)
@@ -98,15 +98,15 @@ def main():
default=5
)
parser.add_option(
- '-l','--treshold',
- help="Treshhold in bytes (default = 10737418240, which is 10GB)",
+ '-l','--threshold',
+ help="Threshold in bytes (default = 10737418240, which is 10GB)",
type="int",
- dest="treshold",
+ dest="threshold",
default=10737418240
)
parser.add_option(
'-m', '--min-erase',
- help="Minimum number of bytes to erase when the treshold is reached. " +
+ help="Minimum number of bytes to erase when the threshold is reached. " +
"Setting this option higher will reduce the amount of times the cache directory will need to be scanned. " +
"(the default is 1073741824, which is 1GB.)",
type="int",
@@ -130,7 +130,7 @@ def main():
cacheDir,
sleep=options.sleep,
simulate=options.simulate,
- treshold=options.treshold,
+ threshold=options.threshold,
min_erase=options.min_erase
)
if runs>0:
diff --git a/vendor/sabre/dav/bin/sabredav.php b/vendor/sabre/dav/bin/sabredav.php
index 34a674fd5..950075d1a 100755
--- a/vendor/sabre/dav/bin/sabredav.php
+++ b/vendor/sabre/dav/bin/sabredav.php
@@ -8,7 +8,7 @@ class CliLog {
function __construct() {
- $this->stream = fopen('php://stdout','w');
+ $this->stream = fopen('php://stdout', 'w');
}
@@ -20,19 +20,19 @@ class CliLog {
$log = new CliLog();
-if (php_sapi_name()!=='cli-server') {
+if (php_sapi_name() !== 'cli-server') {
die("This script is intended to run on the built-in php webserver");
}
// Finding composer
-$paths = array(
+$paths = [
__DIR__ . '/../vendor/autoload.php',
__DIR__ . '/../../../autoload.php',
-);
+];
-foreach($paths as $path) {
+foreach ($paths as $path) {
if (file_exists($path)) {
include $path;
break;
diff --git a/vendor/sabre/dav/examples/addressbookserver.php b/vendor/sabre/dav/examples/addressbookserver.php
index b8986bc41..6d1c9b26c 100644
--- a/vendor/sabre/dav/examples/addressbookserver.php
+++ b/vendor/sabre/dav/examples/addressbookserver.php
@@ -17,10 +17,10 @@ $baseUri = '/';
/* Database */
$pdo = new PDO('sqlite:data/db.sqlite');
-$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
+$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//Mapping PHP errors to exceptions
-function exception_error_handler($errno, $errstr, $errfile, $errline ) {
+function exception_error_handler($errno, $errstr, $errfile, $errline) {
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler("exception_error_handler");
@@ -35,22 +35,23 @@ $carddavBackend = new Sabre\CardDAV\Backend\PDO($pdo);
//$caldavBackend = new Sabre\CalDAV\Backend\PDO($pdo);
// Setting up the directory tree //
-$nodes = array(
+$nodes = [
new Sabre\DAVACL\PrincipalCollection($principalBackend),
-// new Sabre\CalDAV\CalendarRootNode($authBackend, $caldavBackend),
+// new Sabre\CalDAV\CalendarRoot($authBackend, $caldavBackend),
new Sabre\CardDAV\AddressBookRoot($principalBackend, $carddavBackend),
-);
+];
// The object tree needs in turn to be passed to the server class
$server = new Sabre\DAV\Server($nodes);
$server->setBaseUri($baseUri);
// Plugins
-$server->addPlugin(new Sabre\DAV\Auth\Plugin($authBackend,'SabreDAV'));
+$server->addPlugin(new Sabre\DAV\Auth\Plugin($authBackend));
$server->addPlugin(new Sabre\DAV\Browser\Plugin());
//$server->addPlugin(new Sabre\CalDAV\Plugin());
$server->addPlugin(new Sabre\CardDAV\Plugin());
$server->addPlugin(new Sabre\DAVACL\Plugin());
+$server->addPlugin(new Sabre\DAV\Sync\Plugin());
// And off we go!
$server->exec();
diff --git a/vendor/sabre/dav/examples/basicauth.php b/vendor/sabre/dav/examples/basicauth.php
deleted file mode 100644
index 743c07ce2..000000000
--- a/vendor/sabre/dav/examples/basicauth.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-// !!!! Make sure the Sabre directory is in the include_path !!!
-// example:
-// set_include_path('lib/' . PATH_SEPARATOR . get_include_path());
-
-// settings
-date_default_timezone_set('Canada/Eastern');
-
-// Files we need
-require_once 'vendor/autoload.php';
-
-$u = 'admin';
-$p = '1234';
-
-$auth = new \Sabre\HTTP\BasicAuth();
-
-$result = $auth->getUserPass();
-
-if (!$result || $result[0]!=$u || $result[1]!=$p) {
-
- $auth->requireLogin();
- echo "Authentication required\n";
- die();
-
-}
diff --git a/vendor/sabre/dav/examples/digestauth.php b/vendor/sabre/dav/examples/digestauth.php
deleted file mode 100644
index 1f4a74b44..000000000
--- a/vendor/sabre/dav/examples/digestauth.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-// !!!! Make sure the Sabre directory is in the include_path !!!
-// example:
-// set_include_path('lib/' . PATH_SEPARATOR . get_include_path());
-
-// settings
-date_default_timezone_set('Canada/Eastern');
-
-// Files we need
-require_once 'vendor/autoload.php';
-
-$u = 'admin';
-$p = '1234';
-
-$auth = new \Sabre\HTTP\DigestAuth();
-$auth->init();
-
-if ($auth->getUsername() != $u || !$auth->validatePassword($p)) {
-
- $auth->requireLogin();
- echo "Authentication required\n";
- die();
-
-}
diff --git a/vendor/sabre/dav/examples/simplefsserver.php b/vendor/sabre/dav/examples/simplefsserver.php
deleted file mode 100644
index f1b4a1100..000000000
--- a/vendor/sabre/dav/examples/simplefsserver.php
+++ /dev/null
@@ -1,123 +0,0 @@
-<?php
-
-// !!!! Make sure the Sabre directory is in the include_path !!!
-// example:
-// set_include_path('lib/' . PATH_SEPARATOR . get_include_path());
-
-/*
-
-This example demonstrates a simple way to create your own virtual filesystems.
-By extending the _File and Directory classes, you can easily create a tree
-based on various datasources.
-
-The most obvious example is the filesystem itself. A more complete and documented
-example can be found in:
-
-lib/Sabre/DAV/FS/Node.php
-lib/Sabre/DAV/FS/Directory.php
-lib/Sabre/DAV/FS/File.php
-
-*/
-
-// settings
-date_default_timezone_set('Canada/Eastern');
-$publicDir = 'public';
-
-// Files we need
-require_once 'vendor/autoload.php';
-
-class MyCollection extends Sabre\DAV\Collection {
-
- private $myPath;
-
- function __construct($myPath) {
-
- $this->myPath = $myPath;
-
- }
-
- function getChildren() {
-
- $children = array();
- // Loop through the directory, and create objects for each node
- foreach(scandir($this->myPath) as $node) {
-
- // Ignoring files staring with .
- if ($node[0]==='.') continue;
-
- $children[] = $this->getChild($node);
-
- }
-
- return $children;
-
- }
-
- function getChild($name) {
-
- $path = $this->myPath . '/' . $name;
-
- // We have to throw a NotFound exception if the file didn't exist
- if (!file\exists($this->myPath)) throw new \Sabre\DAV\Exception\NotFound('The file with name: ' . $name . ' could not be found');
- // Some added security
-
- if ($name[0]=='.') throw new \Sabre\DAV\Exception\Forbidden('Access denied');
-
- if (is_dir($path)) {
-
- return new \MyCollection($name);
-
- } else {
-
- return new \MyFile($path);
-
- }
-
- }
-
- function getName() {
-
- return basename($this->myPath);
-
- }
-
-}
-
-class MyFile extends \Sabre\DAV\File {
-
- private $myPath;
-
- function __construct($myPath) {
-
- $this->myPath = $myPath;
-
- }
-
- function getName() {
-
- return basename($this->myPath);
-
- }
-
- function get() {
-
- return fopen($this->myPath,'r');
-
- }
-
- function getSize() {
-
- return filesize($this->myPath);
-
- }
-
-}
-
-// Make sure there is a directory in your current directory named 'public'. We will be exposing that directory to WebDAV
-$rootNode = new \MyCollection($publicDir);
-
-// The rootNode needs to be passed to the server object.
-$server = new \Sabre\DAV\Server($rootNode);
-
-// And off we go!
-$server->exec();
diff --git a/vendor/sabre/dav/examples/sql/mysql.addressbook.sql b/vendor/sabre/dav/examples/sql/mysql.addressbook.sql
index f603ad4c5..9ec88babe 100644
--- a/vendor/sabre/dav/examples/sql/mysql.addressbook.sql
+++ b/vendor/sabre/dav/examples/sql/mysql.addressbook.sql
@@ -1,18 +1,28 @@
CREATE TABLE addressbooks (
id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
- principaluri VARCHAR(255),
+ principaluri VARBINARY(255),
displayname VARCHAR(255),
- uri VARCHAR(200),
+ uri VARBINARY(200),
description TEXT,
- ctag INT(11) UNSIGNED NOT NULL DEFAULT '1',
- UNIQUE(principaluri, uri)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+ synctoken INT(11) UNSIGNED NOT NULL DEFAULT '1',
+ UNIQUE(principaluri(100), uri(100))
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE cards (
id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
addressbookid INT(11) UNSIGNED NOT NULL,
carddata MEDIUMBLOB,
- uri VARCHAR(200),
- lastmodified INT(11) UNSIGNED
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+ uri VARBINARY(200),
+ lastmodified INT(11) UNSIGNED,
+ etag VARBINARY(32),
+ size INT(11) UNSIGNED NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+CREATE TABLE addressbookchanges (
+ id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ uri VARBINARY(200) NOT NULL,
+ synctoken INT(11) UNSIGNED NOT NULL,
+ addressbookid INT(11) UNSIGNED NOT NULL,
+ operation TINYINT(1) NOT NULL,
+ INDEX addressbookid_synctoken (addressbookid, synctoken)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
diff --git a/vendor/sabre/dav/examples/sql/mysql.calendars.sql b/vendor/sabre/dav/examples/sql/mysql.calendars.sql
index a8eb102d1..d41f11076 100644
--- a/vendor/sabre/dav/examples/sql/mysql.calendars.sql
+++ b/vendor/sabre/dav/examples/sql/mysql.calendars.sql
@@ -1,28 +1,64 @@
CREATE TABLE calendarobjects (
- id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
calendardata MEDIUMBLOB,
- uri VARCHAR(200),
+ uri VARBINARY(200),
calendarid INTEGER UNSIGNED NOT NULL,
lastmodified INT(11) UNSIGNED,
- etag VARCHAR(32),
+ etag VARBINARY(32),
size INT(11) UNSIGNED NOT NULL,
- componenttype VARCHAR(8),
+ componenttype VARBINARY(8),
firstoccurence INT(11) UNSIGNED,
lastoccurence INT(11) UNSIGNED,
+ uid VARBINARY(200),
UNIQUE(calendarid, uri)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE calendars (
id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
- principaluri VARCHAR(100),
+ principaluri VARBINARY(100),
displayname VARCHAR(100),
- uri VARCHAR(200),
- ctag INTEGER UNSIGNED NOT NULL DEFAULT '0',
+ uri VARBINARY(200),
+ synctoken INTEGER UNSIGNED NOT NULL DEFAULT '1',
description TEXT,
- calendarorder INTEGER UNSIGNED NOT NULL DEFAULT '0',
- calendarcolor VARCHAR(10),
+ calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ calendarcolor VARBINARY(10),
timezone TEXT,
- components VARCHAR(20),
+ components VARBINARY(21),
transparent TINYINT(1) NOT NULL DEFAULT '0',
UNIQUE(principaluri, uri)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE calendarchanges (
+ id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ uri VARBINARY(200) NOT NULL,
+ synctoken INT(11) UNSIGNED NOT NULL,
+ calendarid INT(11) UNSIGNED NOT NULL,
+ operation TINYINT(1) NOT NULL,
+ INDEX calendarid_synctoken (calendarid, synctoken)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE calendarsubscriptions (
+ id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ uri VARBINARY(200) NOT NULL,
+ principaluri VARBINARY(100) NOT NULL,
+ source TEXT,
+ displayname VARCHAR(100),
+ refreshrate VARCHAR(10),
+ calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ calendarcolor VARBINARY(10),
+ striptodos TINYINT(1) NULL,
+ stripalarms TINYINT(1) NULL,
+ stripattachments TINYINT(1) NULL,
+ lastmodified INT(11) UNSIGNED,
+ UNIQUE(principaluri, uri)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE schedulingobjects (
+ id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ principaluri VARBINARY(255),
+ calendardata MEDIUMBLOB,
+ uri VARBINARY(200),
+ lastmodified INT(11) UNSIGNED,
+ etag VARBINARY(32),
+ size INT(11) UNSIGNED NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
diff --git a/vendor/sabre/dav/examples/sql/mysql.locks.sql b/vendor/sabre/dav/examples/sql/mysql.locks.sql
index cf3caf4f7..96a3a88d9 100644
--- a/vendor/sabre/dav/examples/sql/mysql.locks.sql
+++ b/vendor/sabre/dav/examples/sql/mysql.locks.sql
@@ -3,11 +3,10 @@ CREATE TABLE locks (
owner VARCHAR(100),
timeout INTEGER UNSIGNED,
created INTEGER,
- token VARCHAR(100),
+ token VARBINARY(100),
scope TINYINT,
depth TINYINT,
- uri VARCHAR(1000),
+ uri VARBINARY(1000),
INDEX(token),
- INDEX(uri)
-);
-
+ INDEX(uri(100))
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
diff --git a/vendor/sabre/dav/examples/sql/mysql.principals.sql b/vendor/sabre/dav/examples/sql/mysql.principals.sql
index da9282818..ea0d16a27 100644
--- a/vendor/sabre/dav/examples/sql/mysql.principals.sql
+++ b/vendor/sabre/dav/examples/sql/mysql.principals.sql
@@ -1,19 +1,17 @@
CREATE TABLE principals (
id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
- uri VARCHAR(200) NOT NULL,
- email VARCHAR(80),
+ uri VARBINARY(200) NOT NULL,
+ email VARBINARY(80),
displayname VARCHAR(80),
- vcardurl VARCHAR(255),
UNIQUE(uri)
-);
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE groupmembers (
id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
principal_id INTEGER UNSIGNED NOT NULL,
member_id INTEGER UNSIGNED NOT NULL,
UNIQUE(principal_id, member_id)
-);
-
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO principals (uri,email,displayname) VALUES
('principals/admin', 'admin@example.org','Administrator'),
diff --git a/vendor/sabre/dav/examples/sql/mysql.users.sql b/vendor/sabre/dav/examples/sql/mysql.users.sql
index 1244f596f..22ac312d5 100644
--- a/vendor/sabre/dav/examples/sql/mysql.users.sql
+++ b/vendor/sabre/dav/examples/sql/mysql.users.sql
@@ -1,9 +1,9 @@
CREATE TABLE users (
id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
- username VARCHAR(50),
- digesta1 VARCHAR(32),
+ username VARBINARY(50),
+ digesta1 VARBINARY(32),
UNIQUE(username)
-);
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO users (username,digesta1) VALUES
('admin', '87fd274b7b6c01e48d7c2f965da8ddf7');
diff --git a/vendor/sabre/dav/examples/sql/pgsql.addressbook.sql b/vendor/sabre/dav/examples/sql/pgsql.addressbook.sql
index c3ca8b291..ef2cc5b9a 100644
--- a/vendor/sabre/dav/examples/sql/pgsql.addressbook.sql
+++ b/vendor/sabre/dav/examples/sql/pgsql.addressbook.sql
@@ -4,7 +4,7 @@ CREATE TABLE addressbooks (
displayname VARCHAR(255),
uri VARCHAR(200),
description TEXT,
- ctag INTEGER NOT NULL DEFAULT 1
+ synctoken INTEGER NOT NULL DEFAULT 1
);
ALTER TABLE ONLY addressbooks
@@ -18,7 +18,9 @@ CREATE TABLE cards (
addressbookid INTEGER NOT NULL,
carddata TEXT,
uri VARCHAR(200),
- lastmodified INTEGER
+ lastmodified INTEGER,
+ etag VARCHAR(32),
+ size INTEGER NOT NULL
);
ALTER TABLE ONLY cards
@@ -31,3 +33,20 @@ ALTER TABLE ONLY cards
ADD CONSTRAINT cards_addressbookid_fkey FOREIGN KEY (addressbookid) REFERENCES addressbooks(id)
ON DELETE CASCADE;
+CREATE TABLE addressbookchanges (
+ id SERIAL NOT NULL,
+ uri VARCHAR(200) NOT NULL,
+ synctoken INTEGER NOT NULL,
+ addressbookid INTEGER NOT NULL,
+ operation SMALLINT NOT NULL
+);
+
+ALTER TABLE ONLY addressbookchanges
+ ADD CONSTRAINT addressbookchanges_pkey PRIMARY KEY (id);
+
+CREATE INDEX addressbookchanges_addressbookid_synctoken_ix
+ ON addressbookchanges USING btree (addressbookid, synctoken);
+
+ALTER TABLE ONLY addressbookchanges
+ ADD CONSTRAINT addressbookchanges_addressbookid_fkey FOREIGN KEY (addressbookid) REFERENCES addressbooks(id)
+ ON DELETE CASCADE;
diff --git a/vendor/sabre/dav/examples/sql/pgsql.calendars.sql b/vendor/sabre/dav/examples/sql/pgsql.calendars.sql
index 23465ae93..d31084b86 100644
--- a/vendor/sabre/dav/examples/sql/pgsql.calendars.sql
+++ b/vendor/sabre/dav/examples/sql/pgsql.calendars.sql
@@ -3,12 +3,13 @@ CREATE TABLE calendars (
principaluri VARCHAR(100),
displayname VARCHAR(100),
uri VARCHAR(200),
- ctag INTEGER NOT NULL DEFAULT 0,
+ synctoken INTEGER NOT NULL DEFAULT 1,
description TEXT,
calendarorder INTEGER NOT NULL DEFAULT 0,
calendarcolor VARCHAR(10),
timezone TEXT,
components VARCHAR(20),
+ uid VARCHAR(200),
transparent SMALLINT NOT NULL DEFAULT '0'
);
@@ -20,15 +21,16 @@ CREATE UNIQUE INDEX calendars_ukey
CREATE TABLE calendarobjects (
id SERIAL NOT NULL,
- calendarid INTEGER NOT NULL,
calendardata TEXT,
uri VARCHAR(200),
+ calendarid INTEGER NOT NULL,
+ lastmodified INTEGER,
etag VARCHAR(32),
size INTEGER NOT NULL,
componenttype VARCHAR(8),
- lastmodified INTEGER,
firstoccurence INTEGER,
- lastoccurence INTEGER
+ lastoccurence INTEGER,
+ uid VARCHAR(200)
);
ALTER TABLE ONLY calendarobjects
@@ -40,3 +42,52 @@ CREATE UNIQUE INDEX calendarobjects_ukey
ALTER TABLE ONLY calendarobjects
ADD CONSTRAINT calendarobjects_calendarid_fkey FOREIGN KEY (calendarid) REFERENCES calendars(id)
ON DELETE CASCADE;
+
+CREATE TABLE calendarsubscriptions (
+ id SERIAL NOT NULL,
+ uri VARCHAR(200) NOT NULL,
+ principaluri VARCHAR(100) NOT NULL,
+ source TEXT,
+ displayname VARCHAR(100),
+ refreshrate VARCHAR(10),
+ calendarorder INTEGER NOT NULL DEFAULT 0,
+ calendarcolor VARCHAR(10),
+ striptodos SMALLINT NULL,
+ stripalarms SMALLINT NULL,
+ stripattachments SMALLINT NULL,
+ lastmodified INTEGER
+);
+
+ALTER TABLE ONLY calendarsubscriptions
+ ADD CONSTRAINT calendarsubscriptions_pkey PRIMARY KEY (id);
+
+CREATE UNIQUE INDEX calendarsubscriptions_ukey
+ ON calendarsubscriptions USING btree (principaluri, uri);
+
+CREATE TABLE calendarchanges (
+ id SERIAL NOT NULL,
+ uri VARCHAR(200) NOT NULL,
+ synctoken INTEGER NOT NULL,
+ calendarid INTEGER NOT NULL,
+ operation SMALLINT NOT NULL DEFAULT 0
+);
+
+ALTER TABLE ONLY calendarchanges
+ ADD CONSTRAINT calendarchanges_pkey PRIMARY KEY (id);
+
+CREATE INDEX calendarchanges_calendarid_synctoken_ix
+ ON calendarchanges USING btree (calendarid, synctoken);
+
+ALTER TABLE ONLY calendarchanges
+ ADD CONSTRAINT calendarchanges_calendar_fk FOREIGN KEY (calendarid) REFERENCES calendars(id)
+ ON DELETE CASCADE;
+
+CREATE TABLE schedulingobjects (
+ id SERIAL NOT NULL,
+ principaluri VARCHAR(255),
+ calendardata BYTEA,
+ uri VARCHAR(200),
+ lastmodified INTEGER,
+ etag VARCHAR(32),
+ size INTEGER NOT NULL
+);
diff --git a/vendor/sabre/dav/examples/sql/pgsql.locks.sql b/vendor/sabre/dav/examples/sql/pgsql.locks.sql
index ca6c82e96..0290528ce 100644
--- a/vendor/sabre/dav/examples/sql/pgsql.locks.sql
+++ b/vendor/sabre/dav/examples/sql/pgsql.locks.sql
@@ -4,10 +4,16 @@ CREATE TABLE locks (
timeout INTEGER,
created INTEGER,
token VARCHAR(100),
- scope smallint,
- depth smallint,
- uri text
+ scope SMALLINT,
+ depth SMALLINT,
+ uri TEXT
);
ALTER TABLE ONLY locks
ADD CONSTRAINT locks_pkey PRIMARY KEY (id);
+
+CREATE INDEX locks_token_ix
+ ON locks USING btree (token);
+
+CREATE INDEX locks_uri_ix
+ ON locks USING btree (uri);
diff --git a/vendor/sabre/dav/examples/sql/pgsql.principals.sql b/vendor/sabre/dav/examples/sql/pgsql.principals.sql
index 4afe51063..9157acde0 100644
--- a/vendor/sabre/dav/examples/sql/pgsql.principals.sql
+++ b/vendor/sabre/dav/examples/sql/pgsql.principals.sql
@@ -1,9 +1,8 @@
CREATE TABLE principals (
id SERIAL NOT NULL,
- uri VARCHAR(100) NOT NULL,
+ uri VARCHAR(200) NOT NULL,
email VARCHAR(80),
- displayname VARCHAR(80),
- vcardurl VARCHAR(255)
+ displayname VARCHAR(80)
);
ALTER TABLE ONLY principals
@@ -28,10 +27,9 @@ ALTER TABLE ONLY groupmembers
ADD CONSTRAINT groupmembers_principal_id_fkey FOREIGN KEY (principal_id) REFERENCES principals(id)
ON DELETE CASCADE;
--- Is this correct correct link ... or not?
--- ALTER TABLE ONLY groupmembers
--- ADD CONSTRAINT groupmembers_member_id_id_fkey FOREIGN KEY (member_id) REFERENCES users(id)
--- ON DELETE CASCADE;
+ALTER TABLE ONLY groupmembers
+ ADD CONSTRAINT groupmembers_member_id_id_fkey FOREIGN KEY (member_id) REFERENCES principals(id)
+ ON DELETE CASCADE;
INSERT INTO principals (uri,email,displayname) VALUES
('principals/admin', 'admin@example.org','Administrator'),
diff --git a/vendor/sabre/dav/examples/sql/pgsql.users.sql b/vendor/sabre/dav/examples/sql/pgsql.users.sql
index 939c931d8..9d6047b8c 100644
--- a/vendor/sabre/dav/examples/sql/pgsql.users.sql
+++ b/vendor/sabre/dav/examples/sql/pgsql.users.sql
@@ -1,8 +1,7 @@
CREATE TABLE users (
id SERIAL NOT NULL,
username VARCHAR(50),
- digesta1 VARCHAR(32),
- UNIQUE(username)
+ digesta1 VARCHAR(32)
);
ALTER TABLE ONLY users
diff --git a/vendor/sabre/dav/examples/sql/sqlite.addressbooks.sql b/vendor/sabre/dav/examples/sql/sqlite.addressbooks.sql
index aa7639c5f..0baed8bfb 100644
--- a/vendor/sabre/dav/examples/sql/sqlite.addressbooks.sql
+++ b/vendor/sabre/dav/examples/sql/sqlite.addressbooks.sql
@@ -1,17 +1,28 @@
CREATE TABLE addressbooks (
- id integer primary key asc,
- principaluri text,
+ id integer primary key asc NOT NULL,
+ principaluri text NOT NULL,
displayname text,
- uri text,
+ uri text NOT NULL,
description text,
- ctag integer
+ synctoken integer DEFAULT 1 NOT NULL
);
CREATE TABLE cards (
- id integer primary key asc,
- addressbookid integer,
+ id integer primary key asc NOT NULL,
+ addressbookid integer NOT NULL,
carddata blob,
+ uri text NOT NULL,
+ lastmodified integer,
+ etag text,
+ size integer
+);
+
+CREATE TABLE addressbookchanges (
+ id integer primary key asc NOT NULL,
uri text,
- lastmodified integer
+ synctoken integer NOT NULL,
+ addressbookid integer NOT NULL,
+ operation integer NOT NULL
);
+CREATE INDEX addressbookid_synctoken ON addressbookchanges (addressbookid, synctoken);
diff --git a/vendor/sabre/dav/examples/sql/sqlite.calendars.sql b/vendor/sabre/dav/examples/sql/sqlite.calendars.sql
index 537789906..a8654032d 100644
--- a/vendor/sabre/dav/examples/sql/sqlite.calendars.sql
+++ b/vendor/sabre/dav/examples/sql/sqlite.calendars.sql
@@ -1,26 +1,64 @@
CREATE TABLE calendarobjects (
- id integer primary key asc,
- calendardata blob,
- uri text,
- calendarid integer,
- lastmodified integer,
- etag text,
- size integer,
+ id integer primary key asc NOT NULL,
+ calendardata blob NOT NULL,
+ uri text NOT NULL,
+ calendarid integer NOT NULL,
+ lastmodified integer NOT NULL,
+ etag text NOT NULL,
+ size integer NOT NULL,
componenttype text,
firstoccurence integer,
- lastoccurence integer
+ lastoccurence integer,
+ uid text
);
CREATE TABLE calendars (
- id integer primary key asc,
- principaluri text,
+ id integer primary key asc NOT NULL,
+ principaluri text NOT NULL,
displayname text,
- uri text,
- ctag integer,
+ uri text NOT NULL,
+ synctoken integer DEFAULT 1 NOT NULL,
description text,
calendarorder integer,
calendarcolor text,
timezone text,
- components text,
+ components text NOT NULL,
transparent bool
);
+
+CREATE TABLE calendarchanges (
+ id integer primary key asc NOT NULL,
+ uri text,
+ synctoken integer NOT NULL,
+ calendarid integer NOT NULL,
+ operation integer NOT NULL
+);
+
+CREATE INDEX calendarid_synctoken ON calendarchanges (calendarid, synctoken);
+
+CREATE TABLE calendarsubscriptions (
+ id integer primary key asc NOT NULL,
+ uri text NOT NULL,
+ principaluri text NOT NULL,
+ source text NOT NULL,
+ displayname text,
+ refreshrate text,
+ calendarorder integer,
+ calendarcolor text,
+ striptodos bool,
+ stripalarms bool,
+ stripattachments bool,
+ lastmodified int
+);
+
+CREATE TABLE schedulingobjects (
+ id integer primary key asc NOT NULL,
+ principaluri text NOT NULL,
+ calendardata blob,
+ uri text NOT NULL,
+ lastmodified integer,
+ etag text NOT NULL,
+ size integer NOT NULL
+);
+
+CREATE INDEX principaluri_uri ON calendarsubscriptions (principaluri, uri);
diff --git a/vendor/sabre/dav/examples/sql/sqlite.locks.sql b/vendor/sabre/dav/examples/sql/sqlite.locks.sql
index fd89b41eb..622baea42 100644
--- a/vendor/sabre/dav/examples/sql/sqlite.locks.sql
+++ b/vendor/sabre/dav/examples/sql/sqlite.locks.sql
@@ -1,6 +1,6 @@
BEGIN TRANSACTION;
CREATE TABLE locks (
- id integer primary key asc,
+ id integer primary key asc NOT NULL,
owner text,
timeout integer,
created integer,
diff --git a/vendor/sabre/dav/examples/sql/sqlite.principals.sql b/vendor/sabre/dav/examples/sql/sqlite.principals.sql
index 09dbc4d24..4105156f8 100644
--- a/vendor/sabre/dav/examples/sql/sqlite.principals.sql
+++ b/vendor/sabre/dav/examples/sql/sqlite.principals.sql
@@ -1,16 +1,15 @@
CREATE TABLE principals (
- id INTEGER PRIMARY KEY ASC,
- uri TEXT,
+ id INTEGER PRIMARY KEY ASC NOT NULL,
+ uri TEXT NOT NULL,
email TEXT,
displayname TEXT,
- vcardurl TEXT,
UNIQUE(uri)
);
CREATE TABLE groupmembers (
- id INTEGER PRIMARY KEY ASC,
- principal_id INTEGER,
- member_id INTEGER,
+ id INTEGER PRIMARY KEY ASC NOT NULL,
+ principal_id INTEGER NOT NULL,
+ member_id INTEGER NOT NULL,
UNIQUE(principal_id, member_id)
);
diff --git a/vendor/sabre/dav/examples/sql/sqlite.users.sql b/vendor/sabre/dav/examples/sql/sqlite.users.sql
index f4b2c1674..5597b058a 100644
--- a/vendor/sabre/dav/examples/sql/sqlite.users.sql
+++ b/vendor/sabre/dav/examples/sql/sqlite.users.sql
@@ -1,7 +1,7 @@
CREATE TABLE users (
- id integer primary key asc,
- username TEXT,
- digesta1 TEXT,
+ id integer primary key asc NOT NULL,
+ username TEXT NOT NULL,
+ digesta1 TEXT NOT NULL,
UNIQUE(username)
);
diff --git a/vendor/sabre/dav/examples/webserver/apache2_vhost.conf b/vendor/sabre/dav/examples/webserver/apache2_vhost.conf
index bb374eb0f..74289641e 100644
--- a/vendor/sabre/dav/examples/webserver/apache2_vhost.conf
+++ b/vendor/sabre/dav/examples/webserver/apache2_vhost.conf
@@ -23,10 +23,6 @@
# This is also to prevent high memory usage
php_flag always_populate_raw_post_data off
- # This is almost a given, but magic quotes is *still* on on some
- # linux distributions
- php_flag magic_quotes_gpc off
-
# SabreDAV is not compatible with mbstring function overloading
php_flag mbstring.func_overload off
diff --git a/vendor/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php b/vendor/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php
new file mode 100644
index 000000000..d58b4a46e
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php
@@ -0,0 +1,226 @@
+<?php
+
+namespace Sabre\CalDAV\Backend;
+
+use Sabre\VObject;
+use Sabre\CalDAV;
+
+/**
+ * Abstract Calendaring backend. Extend this class to create your own backends.
+ *
+ * Checkout the BackendInterface for all the methods that must be implemented.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class AbstractBackend implements BackendInterface {
+
+ /**
+ * Updates properties for a calendar.
+ *
+ * The list of mutations is stored in a Sabre\DAV\PropPatch object.
+ * To do the actual updates, you must tell this object which properties
+ * you're going to process with the handle() method.
+ *
+ * Calling the handle method is like telling the PropPatch object "I
+ * promise I can handle updating this property".
+ *
+ * Read the PropPatch documenation for more info and examples.
+ *
+ * @param string $path
+ * @param \Sabre\DAV\PropPatch $propPatch
+ * @return void
+ */
+ function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) {
+
+ }
+
+ /**
+ * Returns a list of calendar objects.
+ *
+ * This method should work identical to getCalendarObject, but instead
+ * return all the calendar objects in the list as an array.
+ *
+ * If the backend supports this, it may allow for some speed-ups.
+ *
+ * @param mixed $calendarId
+ * @param array $uris
+ * @return array
+ */
+ function getMultipleCalendarObjects($calendarId, array $uris) {
+
+ return array_map(function($uri) use ($calendarId) {
+ return $this->getCalendarObject($calendarId, $uri);
+ }, $uris);
+
+ }
+
+ /**
+ * Performs a calendar-query on the contents of this calendar.
+ *
+ * The calendar-query is defined in RFC4791 : CalDAV. Using the
+ * calendar-query it is possible for a client to request a specific set of
+ * object, based on contents of iCalendar properties, date-ranges and
+ * iCalendar component types (VTODO, VEVENT).
+ *
+ * This method should just return a list of (relative) urls that match this
+ * query.
+ *
+ * The list of filters are specified as an array. The exact array is
+ * documented by \Sabre\CalDAV\CalendarQueryParser.
+ *
+ * Note that it is extremely likely that getCalendarObject for every path
+ * returned from this method will be called almost immediately after. You
+ * may want to anticipate this to speed up these requests.
+ *
+ * This method provides a default implementation, which parses *all* the
+ * iCalendar objects in the specified calendar.
+ *
+ * This default may well be good enough for personal use, and calendars
+ * that aren't very large. But if you anticipate high usage, big calendars
+ * or high loads, you are strongly adviced to optimize certain paths.
+ *
+ * The best way to do so is override this method and to optimize
+ * specifically for 'common filters'.
+ *
+ * Requests that are extremely common are:
+ * * requests for just VEVENTS
+ * * requests for just VTODO
+ * * requests with a time-range-filter on either VEVENT or VTODO.
+ *
+ * ..and combinations of these requests. It may not be worth it to try to
+ * handle every possible situation and just rely on the (relatively
+ * easy to use) CalendarQueryValidator to handle the rest.
+ *
+ * Note that especially time-range-filters may be difficult to parse. A
+ * time-range filter specified on a VEVENT must for instance also handle
+ * recurrence rules correctly.
+ * A good example of how to interprete all these filters can also simply
+ * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct
+ * as possible, so it gives you a good idea on what type of stuff you need
+ * to think of.
+ *
+ * @param mixed $calendarId
+ * @param array $filters
+ * @return array
+ */
+ function calendarQuery($calendarId, array $filters) {
+
+ $result = [];
+ $objects = $this->getCalendarObjects($calendarId);
+
+ foreach ($objects as $object) {
+
+ if ($this->validateFilterForObject($object, $filters)) {
+ $result[] = $object['uri'];
+ }
+
+ }
+
+ return $result;
+
+ }
+
+ /**
+ * This method validates if a filter (as passed to calendarQuery) matches
+ * the given object.
+ *
+ * @param array $object
+ * @param array $filters
+ * @return bool
+ */
+ protected function validateFilterForObject(array $object, array $filters) {
+
+ // Unfortunately, setting the 'calendardata' here is optional. If
+ // it was excluded, we actually need another call to get this as
+ // well.
+ if (!isset($object['calendardata'])) {
+ $object = $this->getCalendarObject($object['calendarid'], $object['uri']);
+ }
+
+ $vObject = VObject\Reader::read($object['calendardata']);
+
+ $validator = new CalDAV\CalendarQueryValidator();
+ $result = $validator->validate($vObject, $filters);
+
+ // Destroy circular references so PHP will GC the object.
+ $vObject->destroy();
+
+ return $result;
+
+ }
+
+ /**
+ * Searches through all of a users calendars and calendar objects to find
+ * an object with a specific UID.
+ *
+ * This method should return the path to this object, relative to the
+ * calendar home, so this path usually only contains two parts:
+ *
+ * calendarpath/objectpath.ics
+ *
+ * If the uid is not found, return null.
+ *
+ * This method should only consider * objects that the principal owns, so
+ * any calendars owned by other principals that also appear in this
+ * collection should be ignored.
+ *
+ * @param string $principalUri
+ * @param string $uid
+ * @return string|null
+ */
+ function getCalendarObjectByUID($principalUri, $uid) {
+
+ // Note: this is a super slow naive implementation of this method. You
+ // are highly recommended to optimize it, if your backend allows it.
+ foreach ($this->getCalendarsForUser($principalUri) as $calendar) {
+
+ // We must ignore calendars owned by other principals.
+ if ($calendar['principaluri'] !== $principalUri) {
+ continue;
+ }
+
+ // Ignore calendars that are shared.
+ if (isset($calendar['{http://sabredav.org/ns}owner-principal']) && $calendar['{http://sabredav.org/ns}owner-principal'] !== $principalUri) {
+ continue;
+ }
+
+ $results = $this->calendarQuery(
+ $calendar['id'],
+ [
+ 'name' => 'VCALENDAR',
+ 'prop-filters' => [],
+ 'comp-filters' => [
+ [
+ 'name' => 'VEVENT',
+ 'is-not-defined' => false,
+ 'time-range' => null,
+ 'comp-filters' => [],
+ 'prop-filters' => [
+ [
+ 'name' => 'UID',
+ 'is-not-defined' => false,
+ 'time-range' => null,
+ 'text-match' => [
+ 'value' => $uid,
+ 'negate-condition' => false,
+ 'collation' => 'i;octet',
+ ],
+ 'param-filters' => [],
+ ],
+ ]
+ ]
+ ],
+ ]
+ );
+ if ($results) {
+ // We have a match
+ return $calendar['uri'] . '/' . $results[0];
+ }
+
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Backend/BackendInterface.php b/vendor/sabre/dav/lib/CalDAV/Backend/BackendInterface.php
index 0dc31e5f4..7513fb60d 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Backend/BackendInterface.php
+++ b/vendor/sabre/dav/lib/CalDAV/Backend/BackendInterface.php
@@ -5,7 +5,7 @@ namespace Sabre\CalDAV\Backend;
/**
* Every CalDAV backend must at least implement this interface.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -17,7 +17,7 @@ interface BackendInterface {
* Every project is an array with the following keys:
* * id, a unique id that will be used by other functions to modify the
* calendar. This can be the same as the uri or a database key.
- * * uri, which the basename of the uri with which the calendar is
+ * * uri, which is the basename of the uri with which the calendar is
* accessed.
* * principaluri. The owner of the calendar. Almost always the same as
* principalUri passed to this method.
@@ -25,82 +25,74 @@ interface BackendInterface {
* Furthermore it can contain webdav properties in clark notation. A very
* common one is '{DAV:}displayname'.
*
+ * Many clients also require:
+ * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
+ * For this property, you can just return an instance of
+ * Sabre\CalDAV\Property\SupportedCalendarComponentSet.
+ *
+ * If you return {http://sabredav.org/ns}read-only and set the value to 1,
+ * ACL will automatically be put in read-only mode.
+ *
* @param string $principalUri
* @return array
*/
- public function getCalendarsForUser($principalUri);
+ function getCalendarsForUser($principalUri);
/**
* Creates a new calendar for a principal.
*
- * If the creation was a success, an id must be returned that can be used to reference
- * this calendar in other methods, such as updateCalendar.
+ * If the creation was a success, an id must be returned that can be used to
+ * reference this calendar in other methods, such as updateCalendar.
*
* @param string $principalUri
* @param string $calendarUri
* @param array $properties
* @return void
*/
- public function createCalendar($principalUri,$calendarUri,array $properties);
+ function createCalendar($principalUri, $calendarUri, array $properties);
/**
* Updates properties for a calendar.
*
- * The mutations array uses the propertyName in clark-notation as key,
- * and the array value for the property value. In the case a property
- * should be deleted, the property value will be null.
- *
- * This method must be atomic. If one property cannot be changed, the
- * entire operation must fail.
- *
- * If the operation was successful, true can be returned.
- * If the operation failed, false can be returned.
- *
- * Deletion of a non-existent property is always successful.
+ * The list of mutations is stored in a Sabre\DAV\PropPatch object.
+ * To do the actual updates, you must tell this object which properties
+ * you're going to process with the handle() method.
*
- * Lastly, it is optional to return detailed information about any
- * failures. In this case an array should be returned with the following
- * structure:
+ * Calling the handle method is like telling the PropPatch object "I
+ * promise I can handle updating this property".
*
- * array(
- * 403 => array(
- * '{DAV:}displayname' => null,
- * ),
- * 424 => array(
- * '{DAV:}owner' => null,
- * )
- * )
+ * Read the PropPatch documentation for more info and examples.
*
- * In this example it was forbidden to update {DAV:}displayname.
- * (403 Forbidden), which in turn also caused {DAV:}owner to fail
- * (424 Failed Dependency) because the request needs to be atomic.
- *
- * @param mixed $calendarId
- * @param array $mutations
- * @return bool|array
+ * @param string $path
+ * @param \Sabre\DAV\PropPatch $propPatch
+ * @return void
*/
- public function updateCalendar($calendarId, array $mutations);
+ function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch);
/**
- * Delete a calendar and all it's objects
+ * Delete a calendar and all its objects
*
* @param mixed $calendarId
* @return void
*/
- public function deleteCalendar($calendarId);
+ function deleteCalendar($calendarId);
/**
* Returns all calendar objects within a calendar.
*
* Every item contains an array with the following keys:
- * * id - unique identifier which will be used for subsequent updates
* * calendardata - The iCalendar-compatible calendar data
- * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
+ * * uri - a unique key which will be used to construct the uri. This can
+ * be any arbitrary string, but making sure it ends with '.ics' is a
+ * good idea. This is only the basename, or filename, not the full
+ * path.
* * lastmodified - a timestamp of the last modification time
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
- * ' "abcdef"')
- * * calendarid - The calendarid as it was passed to this function.
+ * '"abcdef"')
* * size - The size of the calendar objects, in bytes.
+ * * component - optional, a string containing the type of object, such
+ * as 'vevent' or 'vtodo'. If specified, this will be used to populate
+ * the Content-Type header.
*
* Note that the etag is optional, but it's highly encouraged to return for
* speed reasons.
@@ -116,12 +108,14 @@ interface BackendInterface {
* @param mixed $calendarId
* @return array
*/
- public function getCalendarObjects($calendarId);
+ function getCalendarObjects($calendarId);
/**
* Returns information from a single calendar object, based on it's object
* uri.
*
+ * The object uri is only the basename, or filename and not a full path.
+ *
* The returned array must have the same keys as getCalendarObjects. The
* 'calendardata' object is required here though, while it's not required
* for getCalendarObjects.
@@ -132,14 +126,30 @@ interface BackendInterface {
* @param string $objectUri
* @return array|null
*/
- public function getCalendarObject($calendarId,$objectUri);
+ function getCalendarObject($calendarId, $objectUri);
+
+ /**
+ * Returns a list of calendar objects.
+ *
+ * This method should work identical to getCalendarObject, but instead
+ * return all the calendar objects in the list as an array.
+ *
+ * If the backend supports this, it may allow for some speed-ups.
+ *
+ * @param mixed $calendarId
+ * @param array $uris
+ * @return array
+ */
+ function getMultipleCalendarObjects($calendarId, array $uris);
/**
* Creates a new calendar object.
*
- * It is possible return an etag from this function, which will be used in
- * the response to this PUT request. Note that the ETag must be surrounded
- * by double-quotes.
+ * The object uri is only the basename, or filename and not a full path.
+ *
+ * It is possible to return an etag from this function, which will be used
+ * in the response to this PUT request. Note that the ETag must be
+ * surrounded by double-quotes.
*
* However, you should only really return this ETag if you don't mangle the
* calendar-data. If the result of a subsequent GET to this object is not
@@ -150,11 +160,13 @@ interface BackendInterface {
* @param string $calendarData
* @return string|null
*/
- public function createCalendarObject($calendarId,$objectUri,$calendarData);
+ function createCalendarObject($calendarId, $objectUri, $calendarData);
/**
* Updates an existing calendarobject, based on it's uri.
*
+ * The object uri is only the basename, or filename and not a full path.
+ *
* It is possible return an etag from this function, which will be used in
* the response to this PUT request. Note that the ETag must be surrounded
* by double-quotes.
@@ -168,16 +180,18 @@ interface BackendInterface {
* @param string $calendarData
* @return string|null
*/
- public function updateCalendarObject($calendarId,$objectUri,$calendarData);
+ function updateCalendarObject($calendarId, $objectUri, $calendarData);
/**
* Deletes an existing calendar object.
*
+ * The object uri is only the basename, or filename and not a full path.
+ *
* @param mixed $calendarId
* @param string $objectUri
* @return void
*/
- public function deleteCalendarObject($calendarId,$objectUri);
+ function deleteCalendarObject($calendarId, $objectUri);
/**
* Performs a calendar-query on the contents of this calendar.
@@ -228,6 +242,27 @@ interface BackendInterface {
* @param array $filters
* @return array
*/
- public function calendarQuery($calendarId, array $filters);
+ function calendarQuery($calendarId, array $filters);
+
+ /**
+ * Searches through all of a users calendars and calendar objects to find
+ * an object with a specific UID.
+ *
+ * This method should return the path to this object, relative to the
+ * calendar home, so this path usually only contains two parts:
+ *
+ * calendarpath/objectpath.ics
+ *
+ * If the uid is not found, return null.
+ *
+ * This method should only consider * objects that the principal owns, so
+ * any calendars owned by other principals that also appear in this
+ * collection should be ignored.
+ *
+ * @param string $principalUri
+ * @param string $uid
+ * @return string|null
+ */
+ function getCalendarObjectByUID($principalUri, $uid);
}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Backend/NotificationSupport.php b/vendor/sabre/dav/lib/CalDAV/Backend/NotificationSupport.php
index 96533ad6a..19b9b22a7 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Backend/NotificationSupport.php
+++ b/vendor/sabre/dav/lib/CalDAV/Backend/NotificationSupport.php
@@ -2,6 +2,8 @@
namespace Sabre\CalDAV\Backend;
+use Sabre\CalDAV\Xml\Notification\NotificationInterface;
+
/**
* Adds caldav notification support to a backend.
*
@@ -16,7 +18,7 @@ namespace Sabre\CalDAV\Backend;
*
* The primary usecase is to allow for calendar-sharing.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -25,13 +27,10 @@ interface NotificationSupport extends BackendInterface {
/**
* Returns a list of notifications for a given principal url.
*
- * The returned array should only consist of implementations of
- * \Sabre\CalDAV\Notifications\INotificationType.
- *
* @param string $principalUri
- * @return array
+ * @return NotificationInterface[]
*/
- public function getNotificationsForPrincipal($principalUri);
+ function getNotificationsForPrincipal($principalUri);
/**
* This deletes a specific notifcation.
@@ -39,9 +38,9 @@ interface NotificationSupport extends BackendInterface {
* This may be called by a client once it deems a notification handled.
*
* @param string $principalUri
- * @param \Sabre\CalDAV\Notifications\INotificationType $notification
+ * @param NotificationInterface $notification
* @return void
*/
- public function deleteNotification($principalUri, \Sabre\CalDAV\Notifications\INotificationType $notification);
+ function deleteNotification($principalUri, NotificationInterface $notification);
}
diff --git a/vendor/sabre/dav/lib/CalDAV/Backend/PDO.php b/vendor/sabre/dav/lib/CalDAV/Backend/PDO.php
new file mode 100644
index 000000000..76b69dc6e
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Backend/PDO.php
@@ -0,0 +1,1210 @@
+<?php
+
+namespace Sabre\CalDAV\Backend;
+
+use Sabre\VObject;
+use Sabre\CalDAV;
+use Sabre\DAV;
+use Sabre\DAV\Exception\Forbidden;
+
+/**
+ * PDO CalDAV backend
+ *
+ * This backend is used to store calendar-data in a PDO database, such as
+ * sqlite or MySQL
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, SchedulingSupport {
+
+ /**
+ * We need to specify a max date, because we need to stop *somewhere*
+ *
+ * On 32 bit system the maximum for a signed integer is 2147483647, so
+ * MAX_DATE cannot be higher than date('Y-m-d', 2147483647) which results
+ * in 2038-01-19 to avoid problems when the date is converted
+ * to a unix timestamp.
+ */
+ const MAX_DATE = '2038-01-01';
+
+ /**
+ * pdo
+ *
+ * @var \PDO
+ */
+ protected $pdo;
+
+ /**
+ * The table name that will be used for calendars
+ *
+ * @var string
+ */
+ public $calendarTableName = 'calendars';
+
+ /**
+ * The table name that will be used for calendar objects
+ *
+ * @var string
+ */
+ public $calendarObjectTableName = 'calendarobjects';
+
+ /**
+ * The table name that will be used for tracking changes in calendars.
+ *
+ * @var string
+ */
+ public $calendarChangesTableName = 'calendarchanges';
+
+ /**
+ * The table name that will be used inbox items.
+ *
+ * @var string
+ */
+ public $schedulingObjectTableName = 'schedulingobjects';
+
+ /**
+ * The table name that will be used for calendar subscriptions.
+ *
+ * @var string
+ */
+ public $calendarSubscriptionsTableName = 'calendarsubscriptions';
+
+ /**
+ * List of CalDAV properties, and how they map to database fieldnames
+ * Add your own properties by simply adding on to this array.
+ *
+ * Note that only string-based properties are supported here.
+ *
+ * @var array
+ */
+ public $propertyMap = [
+ '{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',
+ ];
+
+ /**
+ * List of subscription properties, and how they map to database fieldnames.
+ *
+ * @var array
+ */
+ public $subscriptionPropertyMap = [
+ '{DAV:}displayname' => 'displayname',
+ '{http://apple.com/ns/ical/}refreshrate' => 'refreshrate',
+ '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder',
+ '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor',
+ '{http://calendarserver.org/ns/}subscribed-strip-todos' => 'striptodos',
+ '{http://calendarserver.org/ns/}subscribed-strip-alarms' => 'stripalarms',
+ '{http://calendarserver.org/ns/}subscribed-strip-attachments' => 'stripattachments',
+ ];
+
+ /**
+ * Creates the backend
+ *
+ * @param \PDO $pdo
+ */
+ function __construct(\PDO $pdo) {
+
+ $this->pdo = $pdo;
+
+ }
+
+ /**
+ * Returns a list of calendars for a principal.
+ *
+ * Every project is an array with the following keys:
+ * * id, a unique id that will be used by other functions to modify the
+ * calendar. This can be the same as the uri or a database key.
+ * * uri. This is just the 'base uri' or 'filename' of the calendar.
+ * * principaluri. The owner of the calendar. Almost always the same as
+ * principalUri passed to this method.
+ *
+ * Furthermore it can contain webdav properties in clark notation. A very
+ * common one is '{DAV:}displayname'.
+ *
+ * Many clients also require:
+ * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
+ * For this property, you can just return an instance of
+ * Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet.
+ *
+ * If you return {http://sabredav.org/ns}read-only and set the value to 1,
+ * ACL will automatically be put in read-only mode.
+ *
+ * @param string $principalUri
+ * @return array
+ */
+ function getCalendarsForUser($principalUri) {
+
+ $fields = array_values($this->propertyMap);
+ $fields[] = 'id';
+ $fields[] = 'uri';
+ $fields[] = 'synctoken';
+ $fields[] = 'components';
+ $fields[] = 'principaluri';
+ $fields[] = 'transparent';
+
+ // Making fields a comma-delimited list
+ $fields = implode(', ', $fields);
+ $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM " . $this->calendarTableName . " WHERE principaluri = ? ORDER BY calendarorder ASC");
+ $stmt->execute([$principalUri]);
+
+ $calendars = [];
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+
+ $components = [];
+ if ($row['components']) {
+ $components = explode(',', $row['components']);
+ }
+
+ $calendar = [
+ 'id' => $row['id'],
+ 'uri' => $row['uri'],
+ 'principaluri' => $row['principaluri'],
+ '{' . CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken'] ? $row['synctoken'] : '0'),
+ '{http://sabredav.org/ns}sync-token' => $row['synctoken'] ? $row['synctoken'] : '0',
+ '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet($components),
+ '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp($row['transparent'] ? 'transparent' : 'opaque'),
+ ];
+
+
+ foreach ($this->propertyMap as $xmlName => $dbName) {
+ $calendar[$xmlName] = $row[$dbName];
+ }
+
+ $calendars[] = $calendar;
+
+ }
+
+ return $calendars;
+
+ }
+
+ /**
+ * Creates a new calendar for a principal.
+ *
+ * If the creation was a success, an id must be returned that can be used
+ * to reference this calendar in other methods, such as updateCalendar.
+ *
+ * @param string $principalUri
+ * @param string $calendarUri
+ * @param array $properties
+ * @return string
+ */
+ function createCalendar($principalUri, $calendarUri, array $properties) {
+
+ $fieldNames = [
+ 'principaluri',
+ 'uri',
+ 'synctoken',
+ 'transparent',
+ ];
+ $values = [
+ ':principaluri' => $principalUri,
+ ':uri' => $calendarUri,
+ ':synctoken' => 1,
+ ':transparent' => 0,
+ ];
+
+ // Default value
+ $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
+ $fieldNames[] = 'components';
+ if (!isset($properties[$sccs])) {
+ $values[':components'] = 'VEVENT,VTODO';
+ } else {
+ if (!($properties[$sccs] instanceof CalDAV\Xml\Property\SupportedCalendarComponentSet)) {
+ throw new DAV\Exception('The ' . $sccs . ' property must be of type: \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet');
+ }
+ $values[':components'] = implode(',', $properties[$sccs]->getValue());
+ }
+ $transp = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp';
+ if (isset($properties[$transp])) {
+ $values[':transparent'] = $properties[$transp]->getValue() === 'transparent';
+ }
+
+ foreach ($this->propertyMap as $xmlName => $dbName) {
+ if (isset($properties[$xmlName])) {
+
+ $values[':' . $dbName] = $properties[$xmlName];
+ $fieldNames[] = $dbName;
+ }
+ }
+
+ $stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarTableName . " (" . implode(', ', $fieldNames) . ") VALUES (" . implode(', ', array_keys($values)) . ")");
+ $stmt->execute($values);
+
+ return $this->pdo->lastInsertId();
+
+ }
+
+ /**
+ * Updates properties for a calendar.
+ *
+ * The list of mutations is stored in a Sabre\DAV\PropPatch object.
+ * To do the actual updates, you must tell this object which properties
+ * you're going to process with the handle() method.
+ *
+ * Calling the handle method is like telling the PropPatch object "I
+ * promise I can handle updating this property".
+ *
+ * Read the PropPatch documenation for more info and examples.
+ *
+ * @param string $calendarId
+ * @param \Sabre\DAV\PropPatch $propPatch
+ * @return void
+ */
+ function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) {
+
+ $supportedProperties = array_keys($this->propertyMap);
+ $supportedProperties[] = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp';
+
+ $propPatch->handle($supportedProperties, function($mutations) use ($calendarId) {
+ $newValues = [];
+ foreach ($mutations as $propertyName => $propertyValue) {
+
+ switch ($propertyName) {
+ case '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' :
+ $fieldName = 'transparent';
+ $newValues[$fieldName] = $propertyValue->getValue() === 'transparent';
+ break;
+ default :
+ $fieldName = $this->propertyMap[$propertyName];
+ $newValues[$fieldName] = $propertyValue;
+ break;
+ }
+
+ }
+ $valuesSql = [];
+ foreach ($newValues as $fieldName => $value) {
+ $valuesSql[] = $fieldName . ' = ?';
+ }
+
+ $stmt = $this->pdo->prepare("UPDATE " . $this->calendarTableName . " SET " . implode(', ', $valuesSql) . " WHERE id = ?");
+ $newValues['id'] = $calendarId;
+ $stmt->execute(array_values($newValues));
+
+ $this->addChange($calendarId, "", 2);
+
+ return true;
+
+ });
+
+ }
+
+ /**
+ * Delete a calendar and all it's objects
+ *
+ * @param string $calendarId
+ * @return void
+ */
+ function deleteCalendar($calendarId) {
+
+ $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ?');
+ $stmt->execute([$calendarId]);
+
+ $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarTableName . ' WHERE id = ?');
+ $stmt->execute([$calendarId]);
+
+ $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarChangesTableName . ' WHERE calendarid = ?');
+ $stmt->execute([$calendarId]);
+
+ }
+
+ /**
+ * Returns all calendar objects within a calendar.
+ *
+ * Every item contains an array with the following keys:
+ * * calendardata - The iCalendar-compatible calendar data
+ * * uri - a unique key which will be used to construct the uri. This can
+ * be any arbitrary string, but making sure it ends with '.ics' is a
+ * good idea. This is only the basename, or filename, not the full
+ * path.
+ * * lastmodified - a timestamp of the last modification time
+ * * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
+ * ' "abcdef"')
+ * * size - The size of the calendar objects, in bytes.
+ * * component - optional, a string containing the type of object, such
+ * as 'vevent' or 'vtodo'. If specified, this will be used to populate
+ * the Content-Type header.
+ *
+ * Note that the etag is optional, but it's highly encouraged to return for
+ * speed reasons.
+ *
+ * The calendardata is also optional. If it's not returned
+ * 'getCalendarObject' will be called later, which *is* expected to return
+ * calendardata.
+ *
+ * If neither etag or size are specified, the calendardata will be
+ * used/fetched to determine these numbers. If both are specified the
+ * amount of times this is needed is reduced by a great degree.
+ *
+ * @param string $calendarId
+ * @return array
+ */
+ function getCalendarObjects($calendarId) {
+
+ $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ?');
+ $stmt->execute([$calendarId]);
+
+ $result = [];
+ foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
+ $result[] = [
+ 'id' => $row['id'],
+ 'uri' => $row['uri'],
+ 'lastmodified' => $row['lastmodified'],
+ 'etag' => '"' . $row['etag'] . '"',
+ 'calendarid' => $row['calendarid'],
+ 'size' => (int)$row['size'],
+ 'component' => strtolower($row['componenttype']),
+ ];
+ }
+
+ return $result;
+
+ }
+
+ /**
+ * Returns information from a single calendar object, based on it's object
+ * uri.
+ *
+ * The object uri is only the basename, or filename and not a full path.
+ *
+ * The returned array must have the same keys as getCalendarObjects. The
+ * 'calendardata' object is required here though, while it's not required
+ * for getCalendarObjects.
+ *
+ * This method must return null if the object did not exist.
+ *
+ * @param string $calendarId
+ * @param string $objectUri
+ * @return array|null
+ */
+ function getCalendarObject($calendarId, $objectUri) {
+
+ $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ? AND uri = ?');
+ $stmt->execute([$calendarId, $objectUri]);
+ $row = $stmt->fetch(\PDO::FETCH_ASSOC);
+
+ if (!$row) return null;
+
+ return [
+ 'id' => $row['id'],
+ 'uri' => $row['uri'],
+ 'lastmodified' => $row['lastmodified'],
+ 'etag' => '"' . $row['etag'] . '"',
+ 'calendarid' => $row['calendarid'],
+ 'size' => (int)$row['size'],
+ 'calendardata' => $row['calendardata'],
+ 'component' => strtolower($row['componenttype']),
+ ];
+
+ }
+
+ /**
+ * Returns a list of calendar objects.
+ *
+ * This method should work identical to getCalendarObject, but instead
+ * return all the calendar objects in the list as an array.
+ *
+ * If the backend supports this, it may allow for some speed-ups.
+ *
+ * @param mixed $calendarId
+ * @param array $uris
+ * @return array
+ */
+ function getMultipleCalendarObjects($calendarId, array $uris) {
+
+ $query = 'SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ? AND uri IN (';
+ // Inserting a whole bunch of question marks
+ $query .= implode(',', array_fill(0, count($uris), '?'));
+ $query .= ')';
+
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute(array_merge([$calendarId], $uris));
+
+ $result = [];
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+
+ $result[] = [
+ 'id' => $row['id'],
+ 'uri' => $row['uri'],
+ 'lastmodified' => $row['lastmodified'],
+ 'etag' => '"' . $row['etag'] . '"',
+ 'calendarid' => $row['calendarid'],
+ 'size' => (int)$row['size'],
+ 'calendardata' => $row['calendardata'],
+ 'component' => strtolower($row['componenttype']),
+ ];
+
+ }
+ return $result;
+
+ }
+
+
+ /**
+ * Creates a new calendar object.
+ *
+ * The object uri is only the basename, or filename and not a full path.
+ *
+ * It is possible return an etag from this function, which will be used in
+ * the response to this PUT request. Note that the ETag must be surrounded
+ * by double-quotes.
+ *
+ * However, you should only really return this ETag if you don't mangle the
+ * calendar-data. If the result of a subsequent GET to this object is not
+ * the exact same as this request body, you should omit the ETag.
+ *
+ * @param mixed $calendarId
+ * @param string $objectUri
+ * @param string $calendarData
+ * @return string|null
+ */
+ function createCalendarObject($calendarId, $objectUri, $calendarData) {
+
+ $extraData = $this->getDenormalizedData($calendarData);
+
+ $stmt = $this->pdo->prepare('INSERT INTO ' . $this->calendarObjectTableName . ' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)');
+ $stmt->execute([
+ $calendarId,
+ $objectUri,
+ $calendarData,
+ time(),
+ $extraData['etag'],
+ $extraData['size'],
+ $extraData['componentType'],
+ $extraData['firstOccurence'],
+ $extraData['lastOccurence'],
+ $extraData['uid'],
+ ]);
+ $this->addChange($calendarId, $objectUri, 1);
+
+ return '"' . $extraData['etag'] . '"';
+
+ }
+
+ /**
+ * Updates an existing calendarobject, based on it's uri.
+ *
+ * The object uri is only the basename, or filename and not a full path.
+ *
+ * It is possible return an etag from this function, which will be used in
+ * the response to this PUT request. Note that the ETag must be surrounded
+ * by double-quotes.
+ *
+ * However, you should only really return this ETag if you don't mangle the
+ * calendar-data. If the result of a subsequent GET to this object is not
+ * the exact same as this request body, you should omit the ETag.
+ *
+ * @param mixed $calendarId
+ * @param string $objectUri
+ * @param string $calendarData
+ * @return string|null
+ */
+ function updateCalendarObject($calendarId, $objectUri, $calendarData) {
+
+ $extraData = $this->getDenormalizedData($calendarData);
+
+ $stmt = $this->pdo->prepare('UPDATE ' . $this->calendarObjectTableName . ' SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?');
+ $stmt->execute([$calendarData, time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'], $extraData['uid'], $calendarId, $objectUri]);
+
+ $this->addChange($calendarId, $objectUri, 2);
+
+ return '"' . $extraData['etag'] . '"';
+
+ }
+
+ /**
+ * Parses some information from calendar objects, used for optimized
+ * calendar-queries.
+ *
+ * Returns an array with the following keys:
+ * * etag - An md5 checksum of the object without the quotes.
+ * * size - Size of the object in bytes
+ * * componentType - VEVENT, VTODO or VJOURNAL
+ * * firstOccurence
+ * * lastOccurence
+ * * uid - value of the UID property
+ *
+ * @param string $calendarData
+ * @return array
+ */
+ protected function getDenormalizedData($calendarData) {
+
+ $vObject = VObject\Reader::read($calendarData);
+ $componentType = null;
+ $component = null;
+ $firstOccurence = null;
+ $lastOccurence = null;
+ $uid = null;
+ foreach ($vObject->getComponents() as $component) {
+ if ($component->name !== 'VTIMEZONE') {
+ $componentType = $component->name;
+ $uid = (string)$component->UID;
+ break;
+ }
+ }
+ if (!$componentType) {
+ throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
+ }
+ if ($componentType === 'VEVENT') {
+ $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
+ // Finding the last occurence is a bit harder
+ if (!isset($component->RRULE)) {
+ if (isset($component->DTEND)) {
+ $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
+ } elseif (isset($component->DURATION)) {
+ $endDate = clone $component->DTSTART->getDateTime();
+ $endDate = $endDate->add(VObject\DateTimeParser::parse($component->DURATION->getValue()));
+ $lastOccurence = $endDate->getTimeStamp();
+ } elseif (!$component->DTSTART->hasTime()) {
+ $endDate = clone $component->DTSTART->getDateTime();
+ $endDate = $endDate->modify('+1 day');
+ $lastOccurence = $endDate->getTimeStamp();
+ } else {
+ $lastOccurence = $firstOccurence;
+ }
+ } else {
+ $it = new VObject\Recur\EventIterator($vObject, (string)$component->UID);
+ $maxDate = new \DateTime(self::MAX_DATE);
+ if ($it->isInfinite()) {
+ $lastOccurence = $maxDate->getTimeStamp();
+ } else {
+ $end = $it->getDtEnd();
+ while ($it->valid() && $end < $maxDate) {
+ $end = $it->getDtEnd();
+ $it->next();
+
+ }
+ $lastOccurence = $end->getTimeStamp();
+ }
+
+ }
+ }
+
+ // Destroy circular references to PHP will GC the object.
+ $vObject->destroy();
+
+ return [
+ 'etag' => md5($calendarData),
+ 'size' => strlen($calendarData),
+ 'componentType' => $componentType,
+ 'firstOccurence' => $firstOccurence,
+ 'lastOccurence' => $lastOccurence,
+ 'uid' => $uid,
+ ];
+
+ }
+
+ /**
+ * Deletes an existing calendar object.
+ *
+ * The object uri is only the basename, or filename and not a full path.
+ *
+ * @param string $calendarId
+ * @param string $objectUri
+ * @return void
+ */
+ function deleteCalendarObject($calendarId, $objectUri) {
+
+ $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ? AND uri = ?');
+ $stmt->execute([$calendarId, $objectUri]);
+
+ $this->addChange($calendarId, $objectUri, 3);
+
+ }
+
+ /**
+ * Performs a calendar-query on the contents of this calendar.
+ *
+ * The calendar-query is defined in RFC4791 : CalDAV. Using the
+ * calendar-query it is possible for a client to request a specific set of
+ * object, based on contents of iCalendar properties, date-ranges and
+ * iCalendar component types (VTODO, VEVENT).
+ *
+ * This method should just return a list of (relative) urls that match this
+ * query.
+ *
+ * The list of filters are specified as an array. The exact array is
+ * documented by \Sabre\CalDAV\CalendarQueryParser.
+ *
+ * Note that it is extremely likely that getCalendarObject for every path
+ * returned from this method will be called almost immediately after. You
+ * may want to anticipate this to speed up these requests.
+ *
+ * This method provides a default implementation, which parses *all* the
+ * iCalendar objects in the specified calendar.
+ *
+ * This default may well be good enough for personal use, and calendars
+ * that aren't very large. But if you anticipate high usage, big calendars
+ * or high loads, you are strongly adviced to optimize certain paths.
+ *
+ * The best way to do so is override this method and to optimize
+ * specifically for 'common filters'.
+ *
+ * Requests that are extremely common are:
+ * * requests for just VEVENTS
+ * * requests for just VTODO
+ * * requests with a time-range-filter on a VEVENT.
+ *
+ * ..and combinations of these requests. It may not be worth it to try to
+ * handle every possible situation and just rely on the (relatively
+ * easy to use) CalendarQueryValidator to handle the rest.
+ *
+ * Note that especially time-range-filters may be difficult to parse. A
+ * time-range filter specified on a VEVENT must for instance also handle
+ * recurrence rules correctly.
+ * A good example of how to interprete all these filters can also simply
+ * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct
+ * as possible, so it gives you a good idea on what type of stuff you need
+ * to think of.
+ *
+ * This specific implementation (for the PDO) backend optimizes filters on
+ * specific components, and VEVENT time-ranges.
+ *
+ * @param string $calendarId
+ * @param array $filters
+ * @return array
+ */
+ function calendarQuery($calendarId, array $filters) {
+
+ $componentType = null;
+ $requirePostFilter = true;
+ $timeRange = null;
+
+ // if no filters were specified, we don't need to filter after a query
+ if (!$filters['prop-filters'] && !$filters['comp-filters']) {
+ $requirePostFilter = false;
+ }
+
+ // Figuring out if there's a component filter
+ if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) {
+ $componentType = $filters['comp-filters'][0]['name'];
+
+ // Checking if we need post-filters
+ if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) {
+ $requirePostFilter = false;
+ }
+ // There was a time-range filter
+ if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) {
+ $timeRange = $filters['comp-filters'][0]['time-range'];
+
+ // If start time OR the end time is not specified, we can do a
+ // 100% accurate mysql query.
+ if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) {
+ $requirePostFilter = false;
+ }
+ }
+
+ }
+
+ if ($requirePostFilter) {
+ $query = "SELECT uri, calendardata FROM " . $this->calendarObjectTableName . " WHERE calendarid = :calendarid";
+ } else {
+ $query = "SELECT uri FROM " . $this->calendarObjectTableName . " WHERE calendarid = :calendarid";
+ }
+
+ $values = [
+ 'calendarid' => $calendarId,
+ ];
+
+ if ($componentType) {
+ $query .= " AND componenttype = :componenttype";
+ $values['componenttype'] = $componentType;
+ }
+
+ if ($timeRange && $timeRange['start']) {
+ $query .= " AND lastoccurence > :startdate";
+ $values['startdate'] = $timeRange['start']->getTimeStamp();
+ }
+ if ($timeRange && $timeRange['end']) {
+ $query .= " AND firstoccurence < :enddate";
+ $values['enddate'] = $timeRange['end']->getTimeStamp();
+ }
+
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute($values);
+
+ $result = [];
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ if ($requirePostFilter) {
+ if (!$this->validateFilterForObject($row, $filters)) {
+ continue;
+ }
+ }
+ $result[] = $row['uri'];
+
+ }
+
+ return $result;
+
+ }
+
+ /**
+ * Searches through all of a users calendars and calendar objects to find
+ * an object with a specific UID.
+ *
+ * This method should return the path to this object, relative to the
+ * calendar home, so this path usually only contains two parts:
+ *
+ * calendarpath/objectpath.ics
+ *
+ * If the uid is not found, return null.
+ *
+ * This method should only consider * objects that the principal owns, so
+ * any calendars owned by other principals that also appear in this
+ * collection should be ignored.
+ *
+ * @param string $principalUri
+ * @param string $uid
+ * @return string|null
+ */
+ function getCalendarObjectByUID($principalUri, $uid) {
+
+ $query = <<<SQL
+SELECT
+ calendars.uri AS calendaruri, calendarobjects.uri as objecturi
+FROM
+ $this->calendarObjectTableName AS calendarobjects
+LEFT JOIN
+ $this->calendarTableName AS calendars
+ ON calendarobjects.calendarid = calendars.id
+WHERE
+ calendars.principaluri = ?
+ AND
+ calendarobjects.uid = ?
+SQL;
+
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute([$principalUri, $uid]);
+
+ if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ return $row['calendaruri'] . '/' . $row['objecturi'];
+ }
+
+ }
+
+ /**
+ * The getChanges method returns all the changes that have happened, since
+ * the specified syncToken in the specified calendar.
+ *
+ * This function should return an array, such as the following:
+ *
+ * [
+ * 'syncToken' => 'The current synctoken',
+ * 'added' => [
+ * 'new.txt',
+ * ],
+ * 'modified' => [
+ * 'modified.txt',
+ * ],
+ * 'deleted' => [
+ * 'foo.php.bak',
+ * 'old.txt'
+ * ]
+ * ];
+ *
+ * The returned syncToken property should reflect the *current* syncToken
+ * of the calendar, as reported in the {http://sabredav.org/ns}sync-token
+ * property this is needed here too, to ensure the operation is atomic.
+ *
+ * If the $syncToken argument is specified as null, this is an initial
+ * sync, and all members should be reported.
+ *
+ * The modified property is an array of nodenames that have changed since
+ * the last token.
+ *
+ * The deleted property is an array with nodenames, that have been deleted
+ * from collection.
+ *
+ * The $syncLevel argument is basically the 'depth' of the report. If it's
+ * 1, you only have to report changes that happened only directly in
+ * immediate descendants. If it's 2, it should also include changes from
+ * the nodes below the child collections. (grandchildren)
+ *
+ * The $limit argument allows a client to specify how many results should
+ * be returned at most. If the limit is not specified, it should be treated
+ * as infinite.
+ *
+ * If the limit (infinite or not) is higher than you're willing to return,
+ * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
+ *
+ * If the syncToken is expired (due to data cleanup) or unknown, you must
+ * return null.
+ *
+ * The limit is 'suggestive'. You are free to ignore it.
+ *
+ * @param string $calendarId
+ * @param string $syncToken
+ * @param int $syncLevel
+ * @param int $limit
+ * @return array
+ */
+ function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null) {
+
+ // Current synctoken
+ $stmt = $this->pdo->prepare('SELECT synctoken FROM ' . $this->calendarTableName . ' WHERE id = ?');
+ $stmt->execute([ $calendarId ]);
+ $currentToken = $stmt->fetchColumn(0);
+
+ if (is_null($currentToken)) return null;
+
+ $result = [
+ 'syncToken' => $currentToken,
+ 'added' => [],
+ 'modified' => [],
+ 'deleted' => [],
+ ];
+
+ if ($syncToken) {
+
+ $query = "SELECT uri, operation FROM " . $this->calendarChangesTableName . " WHERE synctoken >= ? AND synctoken < ? AND calendarid = ? ORDER BY synctoken";
+ if ($limit > 0) $query .= " LIMIT " . (int)$limit;
+
+ // Fetching all changes
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute([$syncToken, $currentToken, $calendarId]);
+
+ $changes = [];
+
+ // This loop ensures that any duplicates are overwritten, only the
+ // last change on a node is relevant.
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+
+ $changes[$row['uri']] = $row['operation'];
+
+ }
+
+ foreach ($changes as $uri => $operation) {
+
+ switch ($operation) {
+ case 1 :
+ $result['added'][] = $uri;
+ break;
+ case 2 :
+ $result['modified'][] = $uri;
+ break;
+ case 3 :
+ $result['deleted'][] = $uri;
+ break;
+ }
+
+ }
+ } else {
+ // No synctoken supplied, this is the initial sync.
+ $query = "SELECT uri FROM " . $this->calendarObjectTableName . " WHERE calendarid = ?";
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute([$calendarId]);
+
+ $result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN);
+ }
+ return $result;
+
+ }
+
+ /**
+ * Adds a change record to the calendarchanges table.
+ *
+ * @param mixed $calendarId
+ * @param string $objectUri
+ * @param int $operation 1 = add, 2 = modify, 3 = delete.
+ * @return void
+ */
+ protected function addChange($calendarId, $objectUri, $operation) {
+
+ $stmt = $this->pdo->prepare('INSERT INTO ' . $this->calendarChangesTableName . ' (uri, synctoken, calendarid, operation) SELECT ?, synctoken, ?, ? FROM ' . $this->calendarTableName . ' WHERE id = ?');
+ $stmt->execute([
+ $objectUri,
+ $calendarId,
+ $operation,
+ $calendarId
+ ]);
+ $stmt = $this->pdo->prepare('UPDATE ' . $this->calendarTableName . ' SET synctoken = synctoken + 1 WHERE id = ?');
+ $stmt->execute([
+ $calendarId
+ ]);
+
+ }
+
+ /**
+ * Returns a list of subscriptions for a principal.
+ *
+ * Every subscription is an array with the following keys:
+ * * id, a unique id that will be used by other functions to modify the
+ * subscription. This can be the same as the uri or a database key.
+ * * uri. This is just the 'base uri' or 'filename' of the subscription.
+ * * principaluri. The owner of the subscription. Almost always the same as
+ * principalUri passed to this method.
+ * * source. Url to the actual feed
+ *
+ * Furthermore, all the subscription info must be returned too:
+ *
+ * 1. {DAV:}displayname
+ * 2. {http://apple.com/ns/ical/}refreshrate
+ * 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos
+ * should not be stripped).
+ * 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms
+ * should not be stripped).
+ * 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if
+ * attachments should not be stripped).
+ * 7. {http://apple.com/ns/ical/}calendar-color
+ * 8. {http://apple.com/ns/ical/}calendar-order
+ * 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
+ * (should just be an instance of
+ * Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of
+ * default components).
+ *
+ * @param string $principalUri
+ * @return array
+ */
+ function getSubscriptionsForUser($principalUri) {
+
+ $fields = array_values($this->subscriptionPropertyMap);
+ $fields[] = 'id';
+ $fields[] = 'uri';
+ $fields[] = 'source';
+ $fields[] = 'principaluri';
+ $fields[] = 'lastmodified';
+
+ // Making fields a comma-delimited list
+ $fields = implode(', ', $fields);
+ $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM " . $this->calendarSubscriptionsTableName . " WHERE principaluri = ? ORDER BY calendarorder ASC");
+ $stmt->execute([$principalUri]);
+
+ $subscriptions = [];
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+
+ $subscription = [
+ 'id' => $row['id'],
+ 'uri' => $row['uri'],
+ 'principaluri' => $row['principaluri'],
+ 'source' => $row['source'],
+ 'lastmodified' => $row['lastmodified'],
+
+ '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VTODO', 'VEVENT']),
+ ];
+
+ foreach ($this->subscriptionPropertyMap as $xmlName => $dbName) {
+ if (!is_null($row[$dbName])) {
+ $subscription[$xmlName] = $row[$dbName];
+ }
+ }
+
+ $subscriptions[] = $subscription;
+
+ }
+
+ return $subscriptions;
+
+ }
+
+ /**
+ * Creates a new subscription for a principal.
+ *
+ * If the creation was a success, an id must be returned that can be used to reference
+ * this subscription in other methods, such as updateSubscription.
+ *
+ * @param string $principalUri
+ * @param string $uri
+ * @param array $properties
+ * @return mixed
+ */
+ function createSubscription($principalUri, $uri, array $properties) {
+
+ $fieldNames = [
+ 'principaluri',
+ 'uri',
+ 'source',
+ 'lastmodified',
+ ];
+
+ if (!isset($properties['{http://calendarserver.org/ns/}source'])) {
+ throw new Forbidden('The {http://calendarserver.org/ns/}source property is required when creating subscriptions');
+ }
+
+ $values = [
+ ':principaluri' => $principalUri,
+ ':uri' => $uri,
+ ':source' => $properties['{http://calendarserver.org/ns/}source']->getHref(),
+ ':lastmodified' => time(),
+ ];
+
+ foreach ($this->subscriptionPropertyMap as $xmlName => $dbName) {
+ if (isset($properties[$xmlName])) {
+
+ $values[':' . $dbName] = $properties[$xmlName];
+ $fieldNames[] = $dbName;
+ }
+ }
+
+ $stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarSubscriptionsTableName . " (" . implode(', ', $fieldNames) . ") VALUES (" . implode(', ', array_keys($values)) . ")");
+ $stmt->execute($values);
+
+ return $this->pdo->lastInsertId();
+
+ }
+
+ /**
+ * Updates a subscription
+ *
+ * The list of mutations is stored in a Sabre\DAV\PropPatch object.
+ * To do the actual updates, you must tell this object which properties
+ * you're going to process with the handle() method.
+ *
+ * Calling the handle method is like telling the PropPatch object "I
+ * promise I can handle updating this property".
+ *
+ * Read the PropPatch documenation for more info and examples.
+ *
+ * @param mixed $subscriptionId
+ * @param \Sabre\DAV\PropPatch $propPatch
+ * @return void
+ */
+ function updateSubscription($subscriptionId, DAV\PropPatch $propPatch) {
+
+ $supportedProperties = array_keys($this->subscriptionPropertyMap);
+ $supportedProperties[] = '{http://calendarserver.org/ns/}source';
+
+ $propPatch->handle($supportedProperties, function($mutations) use ($subscriptionId) {
+
+ $newValues = [];
+
+ foreach ($mutations as $propertyName => $propertyValue) {
+
+ if ($propertyName === '{http://calendarserver.org/ns/}source') {
+ $newValues['source'] = $propertyValue->getHref();
+ } else {
+ $fieldName = $this->subscriptionPropertyMap[$propertyName];
+ $newValues[$fieldName] = $propertyValue;
+ }
+
+ }
+
+ // Now we're generating the sql query.
+ $valuesSql = [];
+ foreach ($newValues as $fieldName => $value) {
+ $valuesSql[] = $fieldName . ' = ?';
+ }
+
+ $stmt = $this->pdo->prepare("UPDATE " . $this->calendarSubscriptionsTableName . " SET " . implode(', ', $valuesSql) . ", lastmodified = ? WHERE id = ?");
+ $newValues['lastmodified'] = time();
+ $newValues['id'] = $subscriptionId;
+ $stmt->execute(array_values($newValues));
+
+ return true;
+
+ });
+
+ }
+
+ /**
+ * Deletes a subscription
+ *
+ * @param mixed $subscriptionId
+ * @return void
+ */
+ function deleteSubscription($subscriptionId) {
+
+ $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarSubscriptionsTableName . ' WHERE id = ?');
+ $stmt->execute([$subscriptionId]);
+
+ }
+
+ /**
+ * Returns a single scheduling object.
+ *
+ * The returned array should contain the following elements:
+ * * uri - A unique basename for the object. This will be used to
+ * construct a full uri.
+ * * calendardata - The iCalendar object
+ * * lastmodified - The last modification date. Can be an int for a unix
+ * timestamp, or a PHP DateTime object.
+ * * etag - A unique token that must change if the object changed.
+ * * size - The size of the object, in bytes.
+ *
+ * @param string $principalUri
+ * @param string $objectUri
+ * @return array
+ */
+ function getSchedulingObject($principalUri, $objectUri) {
+
+ $stmt = $this->pdo->prepare('SELECT uri, calendardata, lastmodified, etag, size FROM ' . $this->schedulingObjectTableName . ' WHERE principaluri = ? AND uri = ?');
+ $stmt->execute([$principalUri, $objectUri]);
+ $row = $stmt->fetch(\PDO::FETCH_ASSOC);
+
+ if (!$row) return null;
+
+ return [
+ 'uri' => $row['uri'],
+ 'calendardata' => $row['calendardata'],
+ 'lastmodified' => $row['lastmodified'],
+ 'etag' => '"' . $row['etag'] . '"',
+ 'size' => (int)$row['size'],
+ ];
+
+ }
+
+ /**
+ * Returns all scheduling objects for the inbox collection.
+ *
+ * These objects should be returned as an array. Every item in the array
+ * should follow the same structure as returned from getSchedulingObject.
+ *
+ * The main difference is that 'calendardata' is optional.
+ *
+ * @param string $principalUri
+ * @return array
+ */
+ function getSchedulingObjects($principalUri) {
+
+ $stmt = $this->pdo->prepare('SELECT id, calendardata, uri, lastmodified, etag, size FROM ' . $this->schedulingObjectTableName . ' WHERE principaluri = ?');
+ $stmt->execute([$principalUri]);
+
+ $result = [];
+ foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
+ $result[] = [
+ 'calendardata' => $row['calendardata'],
+ 'uri' => $row['uri'],
+ 'lastmodified' => $row['lastmodified'],
+ 'etag' => '"' . $row['etag'] . '"',
+ 'size' => (int)$row['size'],
+ ];
+ }
+
+ return $result;
+
+ }
+
+ /**
+ * Deletes a scheduling object
+ *
+ * @param string $principalUri
+ * @param string $objectUri
+ * @return void
+ */
+ function deleteSchedulingObject($principalUri, $objectUri) {
+
+ $stmt = $this->pdo->prepare('DELETE FROM ' . $this->schedulingObjectTableName . ' WHERE principaluri = ? AND uri = ?');
+ $stmt->execute([$principalUri, $objectUri]);
+
+ }
+
+ /**
+ * Creates a new scheduling object. This should land in a users' inbox.
+ *
+ * @param string $principalUri
+ * @param string $objectUri
+ * @param string $objectData
+ * @return void
+ */
+ function createSchedulingObject($principalUri, $objectUri, $objectData) {
+
+ $stmt = $this->pdo->prepare('INSERT INTO ' . $this->schedulingObjectTableName . ' (principaluri, calendardata, uri, lastmodified, etag, size) VALUES (?, ?, ?, ?, ?, ?)');
+ $stmt->execute([$principalUri, $objectData, $objectUri, time(), md5($objectData), strlen($objectData) ]);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Backend/SchedulingSupport.php b/vendor/sabre/dav/lib/CalDAV/Backend/SchedulingSupport.php
new file mode 100644
index 000000000..6ec0bf06b
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Backend/SchedulingSupport.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Sabre\CalDAV\Backend;
+
+/**
+ * Implementing this interface adds CalDAV Scheduling support to your caldav
+ * server, as defined in rfc6638.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface SchedulingSupport extends BackendInterface {
+
+ /**
+ * Returns a single scheduling object for the inbox collection.
+ *
+ * The returned array should contain the following elements:
+ * * uri - A unique basename for the object. This will be used to
+ * construct a full uri.
+ * * calendardata - The iCalendar object
+ * * lastmodified - The last modification date. Can be an int for a unix
+ * timestamp, or a PHP DateTime object.
+ * * etag - A unique token that must change if the object changed.
+ * * size - The size of the object, in bytes.
+ *
+ * @param string $principalUri
+ * @param string $objectUri
+ * @return array
+ */
+ function getSchedulingObject($principalUri, $objectUri);
+
+ /**
+ * Returns all scheduling objects for the inbox collection.
+ *
+ * These objects should be returned as an array. Every item in the array
+ * should follow the same structure as returned from getSchedulingObject.
+ *
+ * The main difference is that 'calendardata' is optional.
+ *
+ * @param string $principalUri
+ * @return array
+ */
+ function getSchedulingObjects($principalUri);
+
+ /**
+ * Deletes a scheduling object from the inbox collection.
+ *
+ * @param string $principalUri
+ * @param string $objectUri
+ * @return void
+ */
+ function deleteSchedulingObject($principalUri, $objectUri);
+
+ /**
+ * Creates a new scheduling object. This should land in a users' inbox.
+ *
+ * @param string $principalUri
+ * @param string $objectUri
+ * @param string $objectData
+ * @return void
+ */
+ function createSchedulingObject($principalUri, $objectUri, $objectData);
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Backend/SharingSupport.php b/vendor/sabre/dav/lib/CalDAV/Backend/SharingSupport.php
index 4538fa5b5..6a11b0ab1 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Backend/SharingSupport.php
+++ b/vendor/sabre/dav/lib/CalDAV/Backend/SharingSupport.php
@@ -70,7 +70,7 @@ namespace Sabre\CalDAV\Backend;
* In the case of an invite, the sharee may reply with an 'accept' or
* 'decline'. These are always represented by:
*
- * Sabre\CalDAV\Notifications\Notification\Invite
+ * Sabre\CalDAV\Notifications\Notification\InviteReply
*
*
* Calendar access by sharees
@@ -136,7 +136,7 @@ namespace Sabre\CalDAV\Backend;
* * unpublished
*
* If a calendar is published, the following property should be returned
- * for each calendar in getCalendarsForPrincipal.
+ * for each calendar in getCalendarsForUser.
*
* {http://calendarserver.org/ns/}publish-url
*
@@ -157,7 +157,7 @@ namespace Sabre\CalDAV\Backend;
* ==============================================
*
* If Sabre\CalDAV\Property\AllowedSharingModes is returned from
- * getCalendarsByUser, this allows the server to specify whether either sharing,
+ * getCalendarsForUser, this allows the server to specify whether either sharing,
* or publishing is supported.
*
* This allows a client to determine in advance which features are available,
@@ -165,7 +165,7 @@ namespace Sabre\CalDAV\Backend;
* the backend, the SharingPlugin automatically injects it and assumes both
* features are available.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
diff --git a/vendor/sabre/dav/lib/CalDAV/Backend/SubscriptionSupport.php b/vendor/sabre/dav/lib/CalDAV/Backend/SubscriptionSupport.php
new file mode 100644
index 000000000..a39289f5e
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Backend/SubscriptionSupport.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Sabre\CalDAV\Backend;
+
+use Sabre\DAV;
+
+/**
+ * Every CalDAV backend must at least implement this interface.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface SubscriptionSupport extends BackendInterface {
+
+ /**
+ * Returns a list of subscriptions for a principal.
+ *
+ * Every subscription is an array with the following keys:
+ * * id, a unique id that will be used by other functions to modify the
+ * subscription. This can be the same as the uri or a database key.
+ * * uri. This is just the 'base uri' or 'filename' of the subscription.
+ * * principaluri. The owner of the subscription. Almost always the same as
+ * principalUri passed to this method.
+ *
+ * Furthermore, all the subscription info must be returned too:
+ *
+ * 1. {DAV:}displayname
+ * 2. {http://apple.com/ns/ical/}refreshrate
+ * 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos
+ * should not be stripped).
+ * 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms
+ * should not be stripped).
+ * 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if
+ * attachments should not be stripped).
+ * 6. {http://calendarserver.org/ns/}source (Must be a
+ * Sabre\DAV\Property\Href).
+ * 7. {http://apple.com/ns/ical/}calendar-color
+ * 8. {http://apple.com/ns/ical/}calendar-order
+ * 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
+ * (should just be an instance of
+ * Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of
+ * default components).
+ *
+ * @param string $principalUri
+ * @return array
+ */
+ function getSubscriptionsForUser($principalUri);
+
+ /**
+ * Creates a new subscription for a principal.
+ *
+ * If the creation was a success, an id must be returned that can be used to reference
+ * this subscription in other methods, such as updateSubscription.
+ *
+ * @param string $principalUri
+ * @param string $uri
+ * @param array $properties
+ * @return mixed
+ */
+ function createSubscription($principalUri, $uri, array $properties);
+
+ /**
+ * Updates a subscription
+ *
+ * The list of mutations is stored in a Sabre\DAV\PropPatch object.
+ * To do the actual updates, you must tell this object which properties
+ * you're going to process with the handle() method.
+ *
+ * Calling the handle method is like telling the PropPatch object "I
+ * promise I can handle updating this property".
+ *
+ * Read the PropPatch documenation for more info and examples.
+ *
+ * @param mixed $subscriptionId
+ * @param \Sabre\DAV\PropPatch $propPatch
+ * @return void
+ */
+ function updateSubscription($subscriptionId, DAV\PropPatch $propPatch);
+
+ /**
+ * Deletes a subscription.
+ *
+ * @param mixed $subscriptionId
+ * @return void
+ */
+ function deleteSubscription($subscriptionId);
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Backend/SyncSupport.php b/vendor/sabre/dav/lib/CalDAV/Backend/SyncSupport.php
new file mode 100644
index 000000000..a934505f9
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Backend/SyncSupport.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Sabre\CalDAV\Backend;
+
+/**
+ * WebDAV-sync support for CalDAV backends.
+ *
+ * In order for backends to advertise support for WebDAV-sync, this interface
+ * must be implemented.
+ *
+ * Implementing this can result in a significant reduction of bandwidth and CPU
+ * time.
+ *
+ * For this to work, you _must_ return a {http://sabredav.org/ns}sync-token
+ * property from getCalendarsFromUser.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface SyncSupport extends BackendInterface {
+
+ /**
+ * The getChanges method returns all the changes that have happened, since
+ * the specified syncToken in the specified calendar.
+ *
+ * This function should return an array, such as the following:
+ *
+ * [
+ * 'syncToken' => 'The current synctoken',
+ * 'added' => [
+ * 'new.txt',
+ * ],
+ * 'modified' => [
+ * 'modified.txt',
+ * ],
+ * 'deleted' => [
+ * 'foo.php.bak',
+ * 'old.txt'
+ * ]
+ * );
+ *
+ * The returned syncToken property should reflect the *current* syncToken
+ * of the calendar, as reported in the {http://sabredav.org/ns}sync-token
+ * property This is * needed here too, to ensure the operation is atomic.
+ *
+ * If the $syncToken argument is specified as null, this is an initial
+ * sync, and all members should be reported.
+ *
+ * The modified property is an array of nodenames that have changed since
+ * the last token.
+ *
+ * The deleted property is an array with nodenames, that have been deleted
+ * from collection.
+ *
+ * The $syncLevel argument is basically the 'depth' of the report. If it's
+ * 1, you only have to report changes that happened only directly in
+ * immediate descendants. If it's 2, it should also include changes from
+ * the nodes below the child collections. (grandchildren)
+ *
+ * The $limit argument allows a client to specify how many results should
+ * be returned at most. If the limit is not specified, it should be treated
+ * as infinite.
+ *
+ * If the limit (infinite or not) is higher than you're willing to return,
+ * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
+ *
+ * If the syncToken is expired (due to data cleanup) or unknown, you must
+ * return null.
+ *
+ * The limit is 'suggestive'. You are free to ignore it.
+ *
+ * @param string $calendarId
+ * @param string $syncToken
+ * @param int $syncLevel
+ * @param int $limit
+ * @return array
+ */
+ function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null);
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Calendar.php b/vendor/sabre/dav/lib/CalDAV/Calendar.php
new file mode 100644
index 000000000..ff8e19b15
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Calendar.php
@@ -0,0 +1,527 @@
+<?php
+
+namespace Sabre\CalDAV;
+
+use Sabre\DAV;
+use Sabre\DAVACL;
+use Sabre\DAV\PropPatch;
+
+/**
+ * This object represents a CalDAV calendar.
+ *
+ * A calendar can contain multiple TODO and or Events. These are represented
+ * as \Sabre\CalDAV\CalendarObject objects.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, DAV\IMultiGet {
+
+ /**
+ * This is an array with calendar information
+ *
+ * @var array
+ */
+ protected $calendarInfo;
+
+ /**
+ * CalDAV backend
+ *
+ * @var Backend\BackendInterface
+ */
+ protected $caldavBackend;
+
+ /**
+ * Constructor
+ *
+ * @param Backend\BackendInterface $caldavBackend
+ * @param array $calendarInfo
+ */
+ function __construct(Backend\BackendInterface $caldavBackend, $calendarInfo) {
+
+ $this->caldavBackend = $caldavBackend;
+ $this->calendarInfo = $calendarInfo;
+
+ }
+
+ /**
+ * Returns the name of the calendar
+ *
+ * @return string
+ */
+ function getName() {
+
+ return $this->calendarInfo['uri'];
+
+ }
+
+ /**
+ * Updates properties on this node.
+ *
+ * This method received a PropPatch object, which contains all the
+ * information about the update.
+ *
+ * To update specific properties, call the 'handle' method on this object.
+ * Read the PropPatch documentation for more information.
+ *
+ * @param PropPatch $propPatch
+ * @return void
+ */
+ function propPatch(PropPatch $propPatch) {
+
+ return $this->caldavBackend->updateCalendar($this->calendarInfo['id'], $propPatch);
+
+ }
+
+ /**
+ * Returns the list of properties
+ *
+ * @param array $requestedProperties
+ * @return array
+ */
+ function getProperties($requestedProperties) {
+
+ $response = [];
+
+ foreach ($this->calendarInfo as $propName => $propValue) {
+
+ if ($propName[0] === '{')
+ $response[$propName] = $this->calendarInfo[$propName];
+
+ }
+ return $response;
+
+ }
+
+ /**
+ * Returns a calendar object
+ *
+ * The contained calendar objects are for example Events or Todo's.
+ *
+ * @param string $name
+ * @return \Sabre\CalDAV\ICalendarObject
+ */
+ function getChild($name) {
+
+ $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name);
+
+ if (!$obj) throw new DAV\Exception\NotFound('Calendar object not found');
+
+ $obj['acl'] = $this->getChildACL();
+
+ return new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj);
+
+ }
+
+ /**
+ * Returns the full list of calendar objects
+ *
+ * @return array
+ */
+ function getChildren() {
+
+ $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']);
+ $children = [];
+ foreach ($objs as $obj) {
+ $obj['acl'] = $this->getChildACL();
+ $children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj);
+ }
+ return $children;
+
+ }
+
+ /**
+ * This method receives a list of paths in it's first argument.
+ * It must return an array with Node objects.
+ *
+ * If any children are not found, you do not have to return them.
+ *
+ * @param string[] $paths
+ * @return array
+ */
+ function getMultipleChildren(array $paths) {
+
+ $objs = $this->caldavBackend->getMultipleCalendarObjects($this->calendarInfo['id'], $paths);
+ $children = [];
+ foreach ($objs as $obj) {
+ $obj['acl'] = $this->getChildACL();
+ $children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj);
+ }
+ return $children;
+
+ }
+
+ /**
+ * Checks if a child-node exists.
+ *
+ * @param string $name
+ * @return bool
+ */
+ function childExists($name) {
+
+ $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name);
+ if (!$obj)
+ return false;
+ else
+ return true;
+
+ }
+
+ /**
+ * Creates a new directory
+ *
+ * We actually block this, as subdirectories are not allowed in calendars.
+ *
+ * @param string $name
+ * @return void
+ */
+ function createDirectory($name) {
+
+ throw new DAV\Exception\MethodNotAllowed('Creating collections in calendar objects is not allowed');
+
+ }
+
+ /**
+ * Creates a new file
+ *
+ * The contents of the new file must be a valid ICalendar string.
+ *
+ * @param string $name
+ * @param resource $calendarData
+ * @return string|null
+ */
+ function createFile($name, $calendarData = null) {
+
+ if (is_resource($calendarData)) {
+ $calendarData = stream_get_contents($calendarData);
+ }
+ return $this->caldavBackend->createCalendarObject($this->calendarInfo['id'], $name, $calendarData);
+
+ }
+
+ /**
+ * Deletes the calendar.
+ *
+ * @return void
+ */
+ function delete() {
+
+ $this->caldavBackend->deleteCalendar($this->calendarInfo['id']);
+
+ }
+
+ /**
+ * Renames the calendar. Note that most calendars use the
+ * {DAV:}displayname to display a name to display a name.
+ *
+ * @param string $newName
+ * @return void
+ */
+ function setName($newName) {
+
+ throw new DAV\Exception\MethodNotAllowed('Renaming calendars is not yet supported');
+
+ }
+
+ /**
+ * Returns the last modification date as a unix timestamp.
+ *
+ * @return void
+ */
+ function getLastModified() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns the owner principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getOwner() {
+
+ return $this->calendarInfo['principaluri'];
+
+ }
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getGroup() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+
+ $acl = [
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->getOwner(),
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->getOwner() . '/calendar-proxy-write',
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->getOwner() . '/calendar-proxy-read',
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy',
+ 'principal' => '{DAV:}authenticated',
+ 'protected' => true,
+ ],
+
+ ];
+ if (empty($this->calendarInfo['{http://sabredav.org/ns}read-only'])) {
+ $acl[] = [
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->getOwner(),
+ 'protected' => true,
+ ];
+ $acl[] = [
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->getOwner() . '/calendar-proxy-write',
+ 'protected' => true,
+ ];
+ }
+
+ return $acl;
+
+ }
+
+ /**
+ * This method returns the ACL's for calendar objects in this calendar.
+ * The result of this method automatically gets passed to the
+ * calendar-object nodes in the calendar.
+ *
+ * @return array
+ */
+ function getChildACL() {
+
+ $acl = [
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->getOwner(),
+ 'protected' => true,
+ ],
+
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->getOwner() . '/calendar-proxy-write',
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->getOwner() . '/calendar-proxy-read',
+ 'protected' => true,
+ ],
+
+ ];
+ if (empty($this->calendarInfo['{http://sabredav.org/ns}read-only'])) {
+ $acl[] = [
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->getOwner(),
+ 'protected' => true,
+ ];
+ $acl[] = [
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->getOwner() . '/calendar-proxy-write',
+ 'protected' => true,
+ ];
+
+ }
+ return $acl;
+
+ }
+
+ /**
+ * Updates the ACL
+ *
+ * This method will receive a list of new ACE's.
+ *
+ * @param array $acl
+ * @return void
+ */
+ function setACL(array $acl) {
+
+ throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See \Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ function getSupportedPrivilegeSet() {
+
+ $default = DAVACL\Plugin::getDefaultSupportedPrivilegeSet();
+
+ // We need to inject 'read-free-busy' in the tree, aggregated under
+ // {DAV:}read.
+ foreach ($default['aggregates'] as &$agg) {
+
+ if ($agg['privilege'] !== '{DAV:}read') continue;
+
+ $agg['aggregates'][] = [
+ 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy',
+ ];
+
+ }
+ return $default;
+
+ }
+
+ /**
+ * Performs a calendar-query on the contents of this calendar.
+ *
+ * The calendar-query is defined in RFC4791 : CalDAV. Using the
+ * calendar-query it is possible for a client to request a specific set of
+ * object, based on contents of iCalendar properties, date-ranges and
+ * iCalendar component types (VTODO, VEVENT).
+ *
+ * This method should just return a list of (relative) urls that match this
+ * query.
+ *
+ * The list of filters are specified as an array. The exact array is
+ * documented by Sabre\CalDAV\CalendarQueryParser.
+ *
+ * @param array $filters
+ * @return array
+ */
+ function calendarQuery(array $filters) {
+
+ return $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters);
+
+ }
+
+ /**
+ * This method returns the current sync-token for this collection.
+ * This can be any string.
+ *
+ * If null is returned from this function, the plugin assumes there's no
+ * sync information available.
+ *
+ * @return string|null
+ */
+ function getSyncToken() {
+
+ if (
+ $this->caldavBackend instanceof Backend\SyncSupport &&
+ isset($this->calendarInfo['{DAV:}sync-token'])
+ ) {
+ return $this->calendarInfo['{DAV:}sync-token'];
+ }
+ if (
+ $this->caldavBackend instanceof Backend\SyncSupport &&
+ isset($this->calendarInfo['{http://sabredav.org/ns}sync-token'])
+ ) {
+ return $this->calendarInfo['{http://sabredav.org/ns}sync-token'];
+ }
+
+ }
+
+ /**
+ * The getChanges method returns all the changes that have happened, since
+ * the specified syncToken and the current collection.
+ *
+ * This function should return an array, such as the following:
+ *
+ * [
+ * 'syncToken' => 'The current synctoken',
+ * 'added' => [
+ * 'new.txt',
+ * ],
+ * 'modified' => [
+ * 'modified.txt',
+ * ],
+ * 'deleted' => [
+ * 'foo.php.bak',
+ * 'old.txt'
+ * ]
+ * ];
+ *
+ * The syncToken property should reflect the *current* syncToken of the
+ * collection, as reported getSyncToken(). This is needed here too, to
+ * ensure the operation is atomic.
+ *
+ * If the syncToken is specified as null, this is an initial sync, and all
+ * members should be reported.
+ *
+ * The modified property is an array of nodenames that have changed since
+ * the last token.
+ *
+ * The deleted property is an array with nodenames, that have been deleted
+ * from collection.
+ *
+ * The second argument is basically the 'depth' of the report. If it's 1,
+ * you only have to report changes that happened only directly in immediate
+ * descendants. If it's 2, it should also include changes from the nodes
+ * below the child collections. (grandchildren)
+ *
+ * The third (optional) argument allows a client to specify how many
+ * results should be returned at most. If the limit is not specified, it
+ * should be treated as infinite.
+ *
+ * If the limit (infinite or not) is higher than you're willing to return,
+ * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
+ *
+ * If the syncToken is expired (due to data cleanup) or unknown, you must
+ * return null.
+ *
+ * The limit is 'suggestive'. You are free to ignore it.
+ *
+ * @param string $syncToken
+ * @param int $syncLevel
+ * @param int $limit
+ * @return array
+ */
+ function getChanges($syncToken, $syncLevel, $limit = null) {
+
+ if (!$this->caldavBackend instanceof Backend\SyncSupport) {
+ return null;
+ }
+
+ return $this->caldavBackend->getChangesForCalendar(
+ $this->calendarInfo['id'],
+ $syncToken,
+ $syncLevel,
+ $limit
+ );
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/UserCalendars.php b/vendor/sabre/dav/lib/CalDAV/CalendarHome.php
index 6e700eb04..a53f829e2 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/UserCalendars.php
+++ b/vendor/sabre/dav/lib/CalDAV/CalendarHome.php
@@ -3,16 +3,24 @@
namespace Sabre\CalDAV;
use Sabre\DAV;
+use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\MkCol;
use Sabre\DAVACL;
+use Sabre\HTTP\URLUtil;
/**
- * The UserCalenders class contains all calendars associated to one user
+ * The CalendarHome represents a node that is usually in a users'
+ * calendar-homeset.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * It contains all the users' calendars, and can optionally contain a
+ * notifications collection, calendar subscriptions, a users' inbox, and a
+ * users' outbox.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
+class CalendarHome implements DAV\IExtendedCollection, DAVACL\IACL {
/**
* CalDAV backend
@@ -34,7 +42,7 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
* @param Backend\BackendInterface $caldavBackend
* @param mixed $userUri
*/
- public function __construct(Backend\BackendInterface $caldavBackend, $principalInfo) {
+ function __construct(Backend\BackendInterface $caldavBackend, $principalInfo) {
$this->caldavBackend = $caldavBackend;
$this->principalInfo = $principalInfo;
@@ -46,9 +54,9 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
*
* @return string
*/
- public function getName() {
+ function getName() {
- list(,$name) = DAV\URLUtil::splitPath($this->principalInfo['uri']);
+ list(, $name) = URLUtil::splitPath($this->principalInfo['uri']);
return $name;
}
@@ -59,7 +67,7 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
* @param string $name
* @return void
*/
- public function setName($name) {
+ function setName($name) {
throw new DAV\Exception\Forbidden();
@@ -70,7 +78,7 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
*
* @return void
*/
- public function delete() {
+ function delete() {
throw new DAV\Exception\Forbidden();
@@ -81,7 +89,7 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
*
* @return int
*/
- public function getLastModified() {
+ function getLastModified() {
return null;
@@ -96,7 +104,7 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
* @param resource $data
* @return void
*/
- public function createFile($filename, $data=null) {
+ function createFile($filename, $data = null) {
throw new DAV\Exception\MethodNotAllowed('Creating new files in this collection is not supported');
@@ -110,7 +118,7 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
* @param string $filename
* @return void
*/
- public function createDirectory($filename) {
+ function createDirectory($filename) {
throw new DAV\Exception\MethodNotAllowed('Creating new collections in this collection is not supported');
@@ -120,17 +128,46 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
* Returns a single calendar, by name
*
* @param string $name
- * @todo needs optimizing
* @return Calendar
*/
- public function getChild($name) {
+ function getChild($name) {
+
+ // Special nodes
+ if ($name === 'inbox' && $this->caldavBackend instanceof Backend\SchedulingSupport) {
+ return new Schedule\Inbox($this->caldavBackend, $this->principalInfo['uri']);
+ }
+ if ($name === 'outbox' && $this->caldavBackend instanceof Backend\SchedulingSupport) {
+ return new Schedule\Outbox($this->principalInfo['uri']);
+ }
+ if ($name === 'notifications' && $this->caldavBackend instanceof Backend\NotificationSupport) {
+ return new Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']);
+ }
+
+ // Calendars
+ foreach ($this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']) as $calendar) {
+ if ($calendar['uri'] === $name) {
+ if ($this->caldavBackend instanceof Backend\SharingSupport) {
+ if (isset($calendar['{http://calendarserver.org/ns/}shared-url'])) {
+ return new SharedCalendar($this->caldavBackend, $calendar);
+ } else {
+ return new ShareableCalendar($this->caldavBackend, $calendar);
+ }
+ } else {
+ return new Calendar($this->caldavBackend, $calendar);
+ }
+ }
+ }
- foreach($this->getChildren() as $child) {
- if ($name==$child->getName())
- return $child;
+ if ($this->caldavBackend instanceof Backend\SubscriptionSupport) {
+ foreach ($this->caldavBackend->getSubscriptionsForUser($this->principalInfo['uri']) as $subscription) {
+ if ($subscription['uri'] === $name) {
+ return new Subscriptions\Subscription($this->caldavBackend, $subscription);
+ }
+ }
}
- throw new DAV\Exception\NotFound('Calendar with name \'' . $name . '\' could not be found');
+
+ throw new NotFound('Node with name \'' . $name . '\' could not be found');
}
@@ -138,17 +175,15 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
* Checks if a calendar exists.
*
* @param string $name
- * @todo needs optimizing
* @return bool
*/
- public function childExists($name) {
-
- foreach($this->getChildren() as $child) {
- if ($name==$child->getName())
- return true;
+ function childExists($name) {
+ try {
+ return !!$this->getChild($name);
+ } catch (NotFound $e) {
+ return false;
}
- return false;
}
@@ -157,11 +192,11 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
*
* @return array
*/
- public function getChildren() {
+ function getChildren() {
$calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']);
- $objs = array();
- foreach($calendars as $calendar) {
+ $objs = [];
+ foreach ($calendars as $calendar) {
if ($this->caldavBackend instanceof Backend\SharingSupport) {
if (isset($calendar['{http://calendarserver.org/ns/}shared-url'])) {
$objs[] = new SharedCalendar($this->caldavBackend, $calendar);
@@ -172,28 +207,41 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
$objs[] = new Calendar($this->caldavBackend, $calendar);
}
}
- $objs[] = new Schedule\Outbox($this->principalInfo['uri']);
+
+ if ($this->caldavBackend instanceof Backend\SchedulingSupport) {
+ $objs[] = new Schedule\Inbox($this->caldavBackend, $this->principalInfo['uri']);
+ $objs[] = new Schedule\Outbox($this->principalInfo['uri']);
+ }
// We're adding a notifications node, if it's supported by the backend.
if ($this->caldavBackend instanceof Backend\NotificationSupport) {
$objs[] = new Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']);
}
+
+ // If the backend supports subscriptions, we'll add those as well,
+ if ($this->caldavBackend instanceof Backend\SubscriptionSupport) {
+ foreach ($this->caldavBackend->getSubscriptionsForUser($this->principalInfo['uri']) as $subscription) {
+ $objs[] = new Subscriptions\Subscription($this->caldavBackend, $subscription);
+ }
+ }
+
return $objs;
}
/**
- * Creates a new calendar
+ * Creates a new calendar or subscription.
*
* @param string $name
- * @param array $resourceType
- * @param array $properties
+ * @param MkCol $mkCol
+ * @throws DAV\Exception\InvalidResourceType
* @return void
*/
- public function createExtendedCollection($name, array $resourceType, array $properties) {
+ function createExtendedCollection($name, MkCol $mkCol) {
$isCalendar = false;
- foreach($resourceType as $rt) {
+ $isSubscription = false;
+ foreach ($mkCol->getResourceType() as $rt) {
switch ($rt) {
case '{DAV:}collection' :
case '{http://calendarserver.org/ns/}shared-owner' :
@@ -202,14 +250,30 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
case '{urn:ietf:params:xml:ns:caldav}calendar' :
$isCalendar = true;
break;
+ case '{http://calendarserver.org/ns/}subscribed' :
+ $isSubscription = true;
+ break;
default :
throw new DAV\Exception\InvalidResourceType('Unknown resourceType: ' . $rt);
}
}
- if (!$isCalendar) {
- throw new DAV\Exception\InvalidResourceType('You can only create calendars in this collection');
+
+ $properties = $mkCol->getRemainingValues();
+ $mkCol->setRemainingResultCode(201);
+
+ if ($isSubscription) {
+ if (!$this->caldavBackend instanceof Backend\SubscriptionSupport) {
+ throw new DAV\Exception\InvalidResourceType('This backend does not support subscriptions');
+ }
+ $this->caldavBackend->createSubscription($this->principalInfo['uri'], $name, $properties);
+
+ } elseif ($isCalendar) {
+ $this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties);
+
+ } else {
+ throw new DAV\Exception\InvalidResourceType('You can only create calendars and subscriptions in this collection');
+
}
- $this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties);
}
@@ -220,7 +284,7 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
*
* @return string|null
*/
- public function getOwner() {
+ function getOwner() {
return $this->principalInfo['uri'];
@@ -233,7 +297,7 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
*
* @return string|null
*/
- public function getGroup() {
+ function getGroup() {
return null;
@@ -251,36 +315,36 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
*
* @return array
*/
- public function getACL() {
+ function getACL() {
- return array(
- array(
+ return [
+ [
'privilege' => '{DAV:}read',
'principal' => $this->principalInfo['uri'],
'protected' => true,
- ),
- array(
+ ],
+ [
'privilege' => '{DAV:}write',
'principal' => $this->principalInfo['uri'],
'protected' => true,
- ),
- array(
+ ],
+ [
'privilege' => '{DAV:}read',
'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write',
'protected' => true,
- ),
- array(
+ ],
+ [
'privilege' => '{DAV:}write',
'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write',
'protected' => true,
- ),
- array(
+ ],
+ [
'privilege' => '{DAV:}read',
'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read',
'protected' => true,
- ),
+ ],
- );
+ ];
}
@@ -292,7 +356,7 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
* @param array $acl
* @return void
*/
- public function setACL(array $acl) {
+ function setACL(array $acl) {
throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported');
@@ -310,7 +374,7 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
*
* @return array|null
*/
- public function getSupportedPrivilegeSet() {
+ function getSupportedPrivilegeSet() {
return null;
@@ -329,7 +393,7 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
* @param string $summary A description of the reply
* @return null|string
*/
- public function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null) {
+ 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.');
@@ -339,4 +403,28 @@ class UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL {
}
+ /**
+ * Searches through all of a users calendars and calendar objects to find
+ * an object with a specific UID.
+ *
+ * This method should return the path to this object, relative to the
+ * calendar home, so this path usually only contains two parts:
+ *
+ * calendarpath/objectpath.ics
+ *
+ * If the uid is not found, return null.
+ *
+ * This method should only consider * objects that the principal owns, so
+ * any calendars owned by other principals that also appear in this
+ * collection should be ignored.
+ *
+ * @param string $uid
+ * @return string|null
+ */
+ function getCalendarObjectByUID($uid) {
+
+ return $this->caldavBackend->getCalendarObjectByUID($this->principalInfo['uri'], $uid);
+
+ }
+
}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/CalendarObject.php b/vendor/sabre/dav/lib/CalDAV/CalendarObject.php
index 2b9d2877e..393ca4cbd 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/CalendarObject.php
+++ b/vendor/sabre/dav/lib/CalDAV/CalendarObject.php
@@ -5,7 +5,7 @@ namespace Sabre\CalDAV;
/**
* The CalendarObject represents a single VEVENT or VTODO within a Calendar.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -35,17 +35,25 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\
/**
* Constructor
*
+ * The following properties may be passed within $objectData:
+ *
+ * * calendarid - This must refer to a calendarid from a caldavBackend
+ * * uri - A unique uri. Only the 'basename' must be passed.
+ * * calendardata (optional) - The iCalendar data
+ * * etag - (optional) The etag for this object, MUST be encloded with
+ * double-quotes.
+ * * size - (optional) The size of the data in bytes.
+ * * lastmodified - (optional) format as a unix timestamp.
+ * * acl - (optional) Use this to override the default ACL for the node.
+ *
* @param Backend\BackendInterface $caldavBackend
* @param array $calendarInfo
* @param array $objectData
*/
- public function __construct(Backend\BackendInterface $caldavBackend,array $calendarInfo,array $objectData) {
+ function __construct(Backend\BackendInterface $caldavBackend, array $calendarInfo, array $objectData) {
$this->caldavBackend = $caldavBackend;
- if (!isset($objectData['calendarid'])) {
- throw new \InvalidArgumentException('The objectData argument must contain a \'calendarid\' property');
- }
if (!isset($objectData['uri'])) {
throw new \InvalidArgumentException('The objectData argument must contain an \'uri\' property');
}
@@ -60,7 +68,7 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\
*
* @return string
*/
- public function getName() {
+ function getName() {
return $this->objectData['uri'];
@@ -71,12 +79,12 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\
*
* @return string
*/
- public function get() {
+ function get() {
// Pre-populating the 'calendardata' is optional, if we don't have it
// already we fetch it from the backend.
if (!isset($this->objectData['calendardata'])) {
- $this->objectData = $this->caldavBackend->getCalendarObject($this->objectData['calendarid'], $this->objectData['uri']);
+ $this->objectData = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $this->objectData['uri']);
}
return $this->objectData['calendardata'];
@@ -88,12 +96,12 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\
* @param string|resource $calendarData
* @return string
*/
- public function put($calendarData) {
+ function put($calendarData) {
if (is_resource($calendarData)) {
$calendarData = stream_get_contents($calendarData);
}
- $etag = $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'],$this->objectData['uri'],$calendarData);
+ $etag = $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'], $this->objectData['uri'], $calendarData);
$this->objectData['calendardata'] = $calendarData;
$this->objectData['etag'] = $etag;
@@ -106,9 +114,9 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\
*
* @return void
*/
- public function delete() {
+ function delete() {
- $this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'],$this->objectData['uri']);
+ $this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'], $this->objectData['uri']);
}
@@ -117,9 +125,13 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\
*
* @return string
*/
- public function getContentType() {
+ function getContentType() {
- return 'text/calendar; charset=utf-8';
+ $mime = 'text/calendar; charset=utf-8';
+ if (isset($this->objectData['component']) && $this->objectData['component']) {
+ $mime .= '; component=' . $this->objectData['component'];
+ }
+ return $mime;
}
@@ -130,12 +142,12 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\
*
* @return string
*/
- public function getETag() {
+ function getETag() {
if (isset($this->objectData['etag'])) {
return $this->objectData['etag'];
} else {
- return '"' . md5($this->get()). '"';
+ return '"' . md5($this->get()) . '"';
}
}
@@ -145,7 +157,7 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\
*
* @return int
*/
- public function getLastModified() {
+ function getLastModified() {
return $this->objectData['lastmodified'];
@@ -156,9 +168,9 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\
*
* @return int
*/
- public function getSize() {
+ function getSize() {
- if (array_key_exists('size',$this->objectData)) {
+ if (array_key_exists('size', $this->objectData)) {
return $this->objectData['size'];
} else {
return strlen($this->get());
@@ -173,7 +185,7 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\
*
* @return string|null
*/
- public function getOwner() {
+ function getOwner() {
return $this->calendarInfo['principaluri'];
@@ -186,7 +198,7 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\
*
* @return string|null
*/
- public function getGroup() {
+ function getGroup() {
return null;
@@ -204,7 +216,7 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\
*
* @return array
*/
- public function getACL() {
+ function getACL() {
// An alternative acl may be specified in the object data.
if (isset($this->objectData['acl'])) {
@@ -212,34 +224,34 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\
}
// The default ACL
- return array(
- array(
+ return [
+ [
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'],
'protected' => true,
- ),
- array(
+ ],
+ [
'privilege' => '{DAV:}write',
'principal' => $this->calendarInfo['principaluri'],
'protected' => true,
- ),
- array(
+ ],
+ [
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
'protected' => true,
- ),
- array(
+ ],
+ [
'privilege' => '{DAV:}write',
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
'protected' => true,
- ),
- array(
+ ],
+ [
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
'protected' => true,
- ),
+ ],
- );
+ ];
}
@@ -251,7 +263,7 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\
* @param array $acl
* @return void
*/
- public function setACL(array $acl) {
+ function setACL(array $acl) {
throw new \Sabre\DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported');
@@ -269,11 +281,10 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\
*
* @return array|null
*/
- public function getSupportedPrivilegeSet() {
+ function getSupportedPrivilegeSet() {
return null;
}
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/CalendarQueryValidator.php b/vendor/sabre/dav/lib/CalDAV/CalendarQueryValidator.php
index 494aed1a7..f3c7524d2 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/CalendarQueryValidator.php
+++ b/vendor/sabre/dav/lib/CalDAV/CalendarQueryValidator.php
@@ -14,7 +14,7 @@ use DateTime;
* This is used to determine which icalendar objects should be returned for a
* calendar-query REPORT request.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -29,7 +29,7 @@ class CalendarQueryValidator {
* @param array $filters
* @return bool
*/
- public function validate(VObject\Component $vObject,array $filters) {
+ function validate(VObject\Component\VCalendar $vObject, array $filters) {
// The top level object is always a component filter.
// We'll parse it manually, as it's pretty simple.
@@ -57,9 +57,9 @@ class CalendarQueryValidator {
*/
protected function validateCompFilters(VObject\Component $parent, array $filters) {
- foreach($filters as $filter) {
+ foreach ($filters as $filter) {
- $isDefined = isset($parent->$filter['name']);
+ $isDefined = isset($parent->{$filter['name']});
if ($filter['is-not-defined']) {
@@ -75,7 +75,7 @@ class CalendarQueryValidator {
}
if ($filter['time-range']) {
- foreach($parent->$filter['name'] as $subComponent) {
+ foreach ($parent->{$filter['name']} as $subComponent) {
if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) {
continue 2;
}
@@ -89,7 +89,7 @@ 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) {
+ foreach ($parent->{$filter['name']} as $subComponent) {
if (
$this->validateCompFilters($subComponent, $filter['comp-filters']) &&
@@ -126,9 +126,9 @@ class CalendarQueryValidator {
*/
protected function validatePropFilters(VObject\Component $parent, array $filters) {
- foreach($filters as $filter) {
+ foreach ($filters as $filter) {
- $isDefined = isset($parent->$filter['name']);
+ $isDefined = isset($parent->{$filter['name']});
if ($filter['is-not-defined']) {
@@ -144,7 +144,7 @@ class CalendarQueryValidator {
}
if ($filter['time-range']) {
- foreach($parent->$filter['name'] as $subComponent) {
+ foreach ($parent->{$filter['name']} as $subComponent) {
if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) {
continue 2;
}
@@ -158,9 +158,9 @@ 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) {
+ foreach ($parent->{$filter['name']} as $subComponent) {
- if(
+ if (
$this->validateParamFilters($subComponent, $filter['param-filters']) &&
(!$filter['text-match'] || $this->validateTextMatch($subComponent, $filter['text-match']))
) {
@@ -196,7 +196,7 @@ class CalendarQueryValidator {
*/
protected function validateParamFilters(VObject\Property $parent, array $filters) {
- foreach($filters as $filter) {
+ foreach ($filters as $filter) {
$isDefined = isset($parent[$filter['name']]);
@@ -217,30 +217,13 @@ class CalendarQueryValidator {
continue;
}
- if (version_compare(VObject\Version::VERSION, '3.0.0beta1', '>=')) {
-
- // 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 $subParam) {
-
- if($this->validateTextMatch($subParam,$filter['text-match'])) {
- // We had a match, so this param-filter succeeds
- continue 2;
- }
-
- }
-
- } else {
-
- // If there are sub-filters, we need to find at least one parameter
- // for which the subfilters hold true.
- foreach($parent[$filter['name']] as $subParam) {
-
- if($this->validateTextMatch($subParam,$filter['text-match'])) {
- // We had a match, so this param-filter succeeds
- continue 2;
- }
+ // If 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;
}
}
@@ -270,7 +253,7 @@ class CalendarQueryValidator {
protected function validateTextMatch($check, array $textMatch) {
if ($check instanceof VObject\Node) {
- $check = (string)$check;
+ $check = $check->getValue();
}
$isMatching = \Sabre\DAV\StringUtil::textMatch($check, $textMatch['value'], $textMatch['collation']);
@@ -299,7 +282,7 @@ class CalendarQueryValidator {
$end = new DateTime('3000-01-01');
}
- switch($component->name) {
+ switch ($component->name) {
case 'VEVENT' :
case 'VTODO' :
@@ -318,8 +301,8 @@ class CalendarQueryValidator {
if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) {
// Fire up the iterator!
- $it = new VObject\RecurrenceIterator($component->parent->parent, (string)$component->parent->UID);
- while($it->valid()) {
+ $it = new VObject\Recur\EventIterator($component->parent->parent, (string)$component->parent->UID);
+ while ($it->valid()) {
$expandedEvent = $it->getEventObject();
// We need to check from these expanded alarms, which
@@ -327,7 +310,7 @@ class CalendarQueryValidator {
// determine if we can 'give up' expanding events.
$firstAlarm = null;
if ($expandedEvent->VALARM !== null) {
- foreach($expandedEvent->VALARM as $expandedAlarm) {
+ foreach ($expandedEvent->VALARM as $expandedAlarm) {
$effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime();
if ($expandedAlarm->isInTimeRange($start, $end)) {
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/CalendarRootNode.php b/vendor/sabre/dav/lib/CalDAV/CalendarRoot.php
index 4f72ad444..0ac50e41d 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/CalendarRootNode.php
+++ b/vendor/sabre/dav/lib/CalDAV/CalendarRoot.php
@@ -10,11 +10,14 @@ use Sabre\DAVACL\PrincipalBackend;
* This object is responsible for generating a list of calendar-homes for each
* user.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * This is the top-most node for the calendars tree. In most servers this class
+ * represents the "/calendars" path.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class CalendarRootNode extends \Sabre\DAVACL\AbstractPrincipalCollection {
+class CalendarRoot extends \Sabre\DAVACL\AbstractPrincipalCollection {
/**
* CalDAV backend
@@ -37,7 +40,7 @@ class CalendarRootNode extends \Sabre\DAVACL\AbstractPrincipalCollection {
* @param Backend\BackendInterface $caldavBackend
* @param string $principalPrefix
*/
- public function __construct(PrincipalBackend\BackendInterface $principalBackend,Backend\BackendInterface $caldavBackend, $principalPrefix = 'principals') {
+ function __construct(PrincipalBackend\BackendInterface $principalBackend, Backend\BackendInterface $caldavBackend, $principalPrefix = 'principals') {
parent::__construct($principalBackend, $principalPrefix);
$this->caldavBackend = $caldavBackend;
@@ -52,7 +55,7 @@ class CalendarRootNode extends \Sabre\DAVACL\AbstractPrincipalCollection {
*
* @return string
*/
- public function getName() {
+ function getName() {
return Plugin::CALENDAR_ROOT;
@@ -68,9 +71,9 @@ class CalendarRootNode extends \Sabre\DAVACL\AbstractPrincipalCollection {
* @param array $principal
* @return \Sabre\DAV\INode
*/
- public function getChildForPrincipal(array $principal) {
+ function getChildForPrincipal(array $principal) {
- return new UserCalendars($this->caldavBackend, $principal);
+ return new CalendarHome($this->caldavBackend, $principal);
}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Exception/InvalidComponentType.php b/vendor/sabre/dav/lib/CalDAV/Exception/InvalidComponentType.php
index f2a64e33f..5ce8a93f5 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Exception/InvalidComponentType.php
+++ b/vendor/sabre/dav/lib/CalDAV/Exception/InvalidComponentType.php
@@ -8,7 +8,7 @@ use Sabre\CalDAV;
/**
* InvalidComponentType
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -23,11 +23,11 @@ class InvalidComponentType extends DAV\Exception\Forbidden {
* @param \DOMElement $errorNode
* @return void
*/
- public function serialize(DAV\Server $server, \DOMElement $errorNode) {
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
$doc = $errorNode->ownerDocument;
- $np = $doc->createElementNS(CalDAV\Plugin::NS_CALDAV,'cal:supported-calendar-component');
+ $np = $doc->createElementNS(CalDAV\Plugin::NS_CALDAV, 'cal:supported-calendar-component');
$errorNode->appendChild($np);
}
diff --git a/vendor/sabre/dav/lib/CalDAV/ICSExportPlugin.php b/vendor/sabre/dav/lib/CalDAV/ICSExportPlugin.php
new file mode 100644
index 000000000..8c296d50f
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/ICSExportPlugin.php
@@ -0,0 +1,366 @@
+<?php
+
+namespace Sabre\CalDAV;
+
+use DateTimeZone;
+use Sabre\DAV;
+use Sabre\VObject;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+use Sabre\DAV\Exception\BadRequest;
+use DateTime;
+
+/**
+ * ICS Exporter
+ *
+ * This plugin adds the ability to export entire calendars as .ics files.
+ * This is useful for clients that don't support CalDAV yet. They often do
+ * support ics files.
+ *
+ * To use this, point a http client to a caldav calendar, and add ?expand to
+ * the url.
+ *
+ * Further options that can be added to the url:
+ * start=123456789 - Only return events after the given unix timestamp
+ * end=123245679 - Only return events from before the given unix timestamp
+ * expand=1 - Strip timezone information and expand recurring events.
+ * If you'd like to expand, you _must_ also specify start
+ * and end.
+ *
+ * By default this plugin returns data in the text/calendar format (iCalendar
+ * 2.0). If you'd like to receive jCal data instead, you can use an Accept
+ * header:
+ *
+ * Accept: application/calendar+json
+ *
+ * Alternatively, you can also specify this in the url using
+ * accept=application/calendar+json, or accept=jcal for short. If the url
+ * parameter and Accept header is specified, the url parameter wins.
+ *
+ * Note that specifying a start or end data implies that only events will be
+ * returned. VTODO and VJOURNAL will be stripped.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ICSExportPlugin extends DAV\ServerPlugin {
+
+ /**
+ * Reference to Server class
+ *
+ * @var \Sabre\DAV\Server
+ */
+ protected $server;
+
+ /**
+ * Initializes the plugin and registers event handlers
+ *
+ * @param \Sabre\DAV\Server $server
+ * @return void
+ */
+ function initialize(DAV\Server $server) {
+
+ $this->server = $server;
+ $server->on('method:GET', [$this, 'httpGet'], 90);
+ $server->on('browserButtonActions', function($path, $node, &$actions) {
+ if ($node instanceof ICalendar) {
+ $actions .= '<a href="' . htmlspecialchars($path, ENT_QUOTES, 'UTF-8') . '?export"><span class="oi" data-glyph="calendar"></span></a>';
+ }
+ });
+
+ }
+
+ /**
+ * Intercepts GET requests on calendar urls ending with ?export.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpGet(RequestInterface $request, ResponseInterface $response) {
+
+ $queryParams = $request->getQueryParameters();
+ if (!array_key_exists('export', $queryParams)) return;
+
+ $path = $request->getPath();
+
+ $node = $this->server->getProperties($path, [
+ '{DAV:}resourcetype',
+ '{DAV:}displayname',
+ '{http://sabredav.org/ns}sync-token',
+ '{DAV:}sync-token',
+ '{http://apple.com/ns/ical/}calendar-color',
+ ]);
+
+ if (!isset($node['{DAV:}resourcetype']) || !$node['{DAV:}resourcetype']->is('{' . Plugin::NS_CALDAV . '}calendar')) {
+ return;
+ }
+ // Marking the transactionType, for logging purposes.
+ $this->server->transactionType = 'get-calendar-export';
+
+ $properties = $node;
+
+ $start = null;
+ $end = null;
+ $expand = false;
+ $componentType = false;
+ if (isset($queryParams['start'])) {
+ if (!ctype_digit($queryParams['start'])) {
+ throw new BadRequest('The start= parameter must contain a unix timestamp');
+ }
+ $start = DateTime::createFromFormat('U', $queryParams['start']);
+ }
+ if (isset($queryParams['end'])) {
+ if (!ctype_digit($queryParams['end'])) {
+ throw new BadRequest('The end= parameter must contain a unix timestamp');
+ }
+ $end = DateTime::createFromFormat('U', $queryParams['end']);
+ }
+ if (isset($queryParams['expand']) && !!$queryParams['expand']) {
+ if (!$start || !$end) {
+ throw new BadRequest('If you\'d like to expand recurrences, you must specify both a start= and end= parameter.');
+ }
+ $expand = true;
+ $componentType = 'VEVENT';
+ }
+ 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');
+ }
+ $componentType = $queryParams['componentType'];
+ }
+
+ $format = \Sabre\HTTP\Util::Negotiate(
+ $request->getHeader('Accept'),
+ [
+ 'text/calendar',
+ 'application/calendar+json',
+ ]
+ );
+
+ if (isset($queryParams['accept'])) {
+ if ($queryParams['accept'] === 'application/calendar+json' || $queryParams['accept'] === 'jcal') {
+ $format = 'application/calendar+json';
+ }
+ }
+ if (!$format) {
+ $format = 'text/calendar';
+ }
+
+ $this->generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, $response);
+
+ // Returning false to break the event chain
+ return false;
+
+ }
+
+ /**
+ * This method is responsible for generating the actual, full response.
+ *
+ * @param string $path
+ * @param DateTime|null $start
+ * @param DateTime|null $end
+ * @param bool $expand
+ * @param string $componentType
+ * @param string $format
+ * @param array $properties
+ * @param ResponseInterface $response
+ */
+ protected function generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, ResponseInterface $response) {
+
+ $calDataProp = '{' . Plugin::NS_CALDAV . '}calendar-data';
+
+ $blobs = [];
+ if ($start || $end || $componentType) {
+
+ // If there was a start or end filter, we need to enlist
+ // calendarQuery for speed.
+ $calendarNode = $this->server->tree->getNodeForPath($path);
+ $queryResult = $calendarNode->calendarQuery([
+ 'name' => 'VCALENDAR',
+ 'comp-filters' => [
+ [
+ 'name' => $componentType,
+ 'comp-filters' => [],
+ 'prop-filters' => [],
+ 'is-not-defined' => false,
+ 'time-range' => [
+ 'start' => $start,
+ 'end' => $end,
+ ],
+ ],
+ ],
+ 'prop-filters' => [],
+ 'is-not-defined' => false,
+ 'time-range' => null,
+ ]);
+
+ // queryResult is just a list of base urls. We need to prefix the
+ // calendar path.
+ $queryResult = array_map(
+ function($item) use ($path) {
+ return $path . '/' . $item;
+ },
+ $queryResult
+ );
+ $nodes = $this->server->getPropertiesForMultiplePaths($queryResult, [$calDataProp]);
+ unset($queryResult);
+
+ } else {
+ $nodes = $this->server->getPropertiesForPath($path, [$calDataProp], 1);
+ }
+
+ // Flattening the arrays
+ foreach ($nodes as $node) {
+ if (isset($node[200][$calDataProp])) {
+ $blobs[$node['href']] = $node[200][$calDataProp];
+ }
+ }
+ unset($nodes);
+
+ $mergedCalendar = $this->mergeObjects(
+ $properties,
+ $blobs
+ );
+
+ if ($expand) {
+ $calendarTimeZone = null;
+ // We're expanding, and for that we need to figure out the
+ // calendar's timezone.
+ $tzProp = '{' . Plugin::NS_CALDAV . '}calendar-timezone';
+ $tzResult = $this->server->getProperties($path, [$tzProp]);
+ if (isset($tzResult[$tzProp])) {
+ // This property contains a VCALENDAR with a single
+ // VTIMEZONE.
+ $vtimezoneObj = VObject\Reader::read($tzResult[$tzProp]);
+ $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
+ // Destroy circular references to PHP will GC the object.
+ $vtimezoneObj->destroy();
+ unset($vtimezoneObj);
+ } else {
+ // Defaulting to UTC.
+ $calendarTimeZone = new DateTimeZone('UTC');
+ }
+
+ $mergedCalendar = $mergedCalendar->expand($start, $end, $calendarTimeZone);
+ }
+
+ $response->setHeader('Content-Type', $format);
+
+ switch ($format) {
+ case 'text/calendar' :
+ $mergedCalendar = $mergedCalendar->serialize();
+ break;
+ case 'application/calendar+json' :
+ $mergedCalendar = json_encode($mergedCalendar->jsonSerialize());
+ break;
+ }
+
+ $response->setStatus(200);
+ $response->setBody($mergedCalendar);
+
+ }
+
+ /**
+ * Merges all calendar objects, and builds one big iCalendar blob.
+ *
+ * @param array $properties Some CalDAV properties
+ * @param array $inputObjects
+ * @return VObject\Component\VCalendar
+ */
+ function mergeObjects(array $properties, array $inputObjects) {
+
+ $calendar = new VObject\Component\VCalendar();
+ $calendar->version = '2.0';
+ if (DAV\Server::$exposeVersion) {
+ $calendar->prodid = '-//SabreDAV//SabreDAV ' . DAV\Version::VERSION . '//EN';
+ } else {
+ $calendar->prodid = '-//SabreDAV//SabreDAV//EN';
+ }
+ if (isset($properties['{DAV:}displayname'])) {
+ $calendar->{'X-WR-CALNAME'} = $properties['{DAV:}displayname'];
+ }
+ if (isset($properties['{http://apple.com/ns/ical/}calendar-color'])) {
+ $calendar->{'X-APPLE-CALENDAR-COLOR'} = $properties['{http://apple.com/ns/ical/}calendar-color'];
+ }
+
+ $collectedTimezones = [];
+
+ $timezones = [];
+ $objects = [];
+
+ foreach ($inputObjects as $href => $inputObject) {
+
+ $nodeComp = VObject\Reader::read($inputObject);
+
+ foreach ($nodeComp->children() as $child) {
+
+ switch ($child->name) {
+ case 'VEVENT' :
+ case 'VTODO' :
+ case 'VJOURNAL' :
+ $objects[] = clone $child;
+ break;
+
+ // VTIMEZONE is special, because we need to filter out the duplicates
+ case 'VTIMEZONE' :
+ // Naively just checking tzid.
+ if (in_array((string)$child->TZID, $collectedTimezones)) continue;
+
+ $timezones[] = 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);
+
+ return $calendar;
+
+ }
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using \Sabre\DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'ics-export';
+
+ }
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => 'Adds the ability to export CalDAV calendars as a single iCalendar file.',
+ 'link' => 'http://sabre.io/dav/ics-export-plugin/',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/ICalendar.php b/vendor/sabre/dav/lib/CalDAV/ICalendar.php
new file mode 100644
index 000000000..7cf4b1256
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/ICalendar.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Sabre\CalDAV;
+
+use Sabre\DAVACL;
+
+/**
+ * Calendar interface
+ *
+ * Implement this interface to allow a node to be recognized as an calendar.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface ICalendar extends ICalendarObjectContainer, DAVACL\IACL {
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/ICalendarObject.php b/vendor/sabre/dav/lib/CalDAV/ICalendarObject.php
index 0776abff0..b3a767b74 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/ICalendarObject.php
+++ b/vendor/sabre/dav/lib/CalDAV/ICalendarObject.php
@@ -1,6 +1,7 @@
<?php
namespace Sabre\CalDAV;
+
use Sabre\DAV;
/**
@@ -11,11 +12,10 @@ use Sabre\DAV;
*
* Calendar objects are resources such as Events, Todo's or Journals.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
interface ICalendarObject extends DAV\IFile {
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/ICalendar.php b/vendor/sabre/dav/lib/CalDAV/ICalendarObjectContainer.php
index 0f0547046..0308b8a55 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/ICalendar.php
+++ b/vendor/sabre/dav/lib/CalDAV/ICalendarObjectContainer.php
@@ -1,18 +1,21 @@
<?php
namespace Sabre\CalDAV;
-use Sabre\DAV;
/**
- * Calendar interface
+ * This interface represents a node that may contain calendar objects.
*
- * Implement this interface to allow a node to be recognized as an calendar.
+ * This is the shared parent for both the Inbox collection and calendars
+ * resources.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * In most cases you will likely want to look at ICalendar instead of this
+ * interface.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-interface ICalendar extends DAV\ICollection {
+interface ICalendarObjectContainer extends \Sabre\DAV\ICollection {
/**
* Performs a calendar-query on the contents of this calendar.
@@ -31,6 +34,6 @@ interface ICalendar extends DAV\ICollection {
* @param array $filters
* @return array
*/
- public function calendarQuery(array $filters);
+ function calendarQuery(array $filters);
}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/IShareableCalendar.php b/vendor/sabre/dav/lib/CalDAV/IShareableCalendar.php
index 4dd62e5c6..cfda91a55 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/IShareableCalendar.php
+++ b/vendor/sabre/dav/lib/CalDAV/IShareableCalendar.php
@@ -5,7 +5,7 @@ namespace Sabre\CalDAV;
/**
* This interface represents a Calendar that can be shared with other users.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/ISharedCalendar.php b/vendor/sabre/dav/lib/CalDAV/ISharedCalendar.php
index 917d1c498..84442ac21 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/ISharedCalendar.php
+++ b/vendor/sabre/dav/lib/CalDAV/ISharedCalendar.php
@@ -5,7 +5,7 @@ namespace Sabre\CalDAV;
/**
* This interface represents a Calendar that is shared by a different user.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/Collection.php b/vendor/sabre/dav/lib/CalDAV/Notifications/Collection.php
index 028b00d87..1fcc1171c 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/Collection.php
+++ b/vendor/sabre/dav/lib/CalDAV/Notifications/Collection.php
@@ -16,7 +16,7 @@ use Sabre\DAVACL;
* This collection should only return Sabre\CalDAV\Notifications\INode nodes as
* its children.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -42,7 +42,7 @@ class Collection extends DAV\Collection implements ICollection, DAVACL\IACL {
* @param CalDAV\Backend\NotificationSupport $caldavBackend
* @param string $principalUri
*/
- public function __construct(CalDAV\Backend\NotificationSupport $caldavBackend, $principalUri) {
+ function __construct(CalDAV\Backend\NotificationSupport $caldavBackend, $principalUri) {
$this->caldavBackend = $caldavBackend;
$this->principalUri = $principalUri;
@@ -54,12 +54,12 @@ class Collection extends DAV\Collection implements ICollection, DAVACL\IACL {
*
* @return array
*/
- public function getChildren() {
+ function getChildren() {
- $children = array();
+ $children = [];
$notifications = $this->caldavBackend->getNotificationsForPrincipal($this->principalUri);
- foreach($notifications as $notification) {
+ foreach ($notifications as $notification) {
$children[] = new Node(
$this->caldavBackend,
@@ -77,7 +77,7 @@ class Collection extends DAV\Collection implements ICollection, DAVACL\IACL {
*
* @return string
*/
- public function getName() {
+ function getName() {
return 'notifications';
@@ -90,7 +90,7 @@ class Collection extends DAV\Collection implements ICollection, DAVACL\IACL {
*
* @return string|null
*/
- public function getOwner() {
+ function getOwner() {
return $this->principalUri;
@@ -103,7 +103,7 @@ class Collection extends DAV\Collection implements ICollection, DAVACL\IACL {
*
* @return string|null
*/
- public function getGroup() {
+ function getGroup() {
return null;
@@ -121,20 +121,20 @@ class Collection extends DAV\Collection implements ICollection, DAVACL\IACL {
*
* @return array
*/
- public function getACL() {
+ function getACL() {
- return array(
- array(
+ return [
+ [
'principal' => $this->getOwner(),
'privilege' => '{DAV:}read',
'protected' => true,
- ),
- array(
+ ],
+ [
'principal' => $this->getOwner(),
'privilege' => '{DAV:}write',
'protected' => true,
- )
- );
+ ]
+ ];
}
@@ -146,7 +146,7 @@ class Collection extends DAV\Collection implements ICollection, DAVACL\IACL {
* @param array $acl
* @return void
*/
- public function setACL(array $acl) {
+ function setACL(array $acl) {
throw new DAV\Exception\NotImplemented('Updating ACLs is not implemented here');
@@ -164,7 +164,7 @@ class Collection extends DAV\Collection implements ICollection, DAVACL\IACL {
*
* @return array|null
*/
- public function getSupportedPrivilegeSet() {
+ function getSupportedPrivilegeSet() {
return null;
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/ICollection.php b/vendor/sabre/dav/lib/CalDAV/Notifications/ICollection.php
index 26e13b211..008e87435 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/ICollection.php
+++ b/vendor/sabre/dav/lib/CalDAV/Notifications/ICollection.php
@@ -14,11 +14,10 @@ use Sabre\DAV;
* This collection should only return Sabre\CalDAV\Notifications\INode nodes as
* its children.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
interface ICollection extends DAV\ICollection {
-
}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/INode.php b/vendor/sabre/dav/lib/CalDAV/Notifications/INode.php
index cd7fc5798..f9986b714 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/INode.php
+++ b/vendor/sabre/dav/lib/CalDAV/Notifications/INode.php
@@ -12,7 +12,7 @@ namespace Sabre\CalDAV\Notifications;
* For a complete example, check out the Notification class, which contains
* some helper functions.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/Node.php b/vendor/sabre/dav/lib/CalDAV/Notifications/Node.php
index 6367e9388..47e78d5de 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/Node.php
+++ b/vendor/sabre/dav/lib/CalDAV/Notifications/Node.php
@@ -4,6 +4,7 @@ namespace Sabre\CalDAV\Notifications;
use Sabre\DAV;
use Sabre\CalDAV;
+use Sabre\CalDAV\Xml\Notification\NotificationInterface;
use Sabre\DAVACL;
/**
@@ -12,8 +13,8 @@ use Sabre\DAVACL;
* The signature is mostly identical to that of Sabre\DAV\IFile, but the get() method
* MUST return an xml document that matches the requirements of the
* 'caldav-notifications.txt' spec.
-
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -45,9 +46,9 @@ class Node extends DAV\File implements INode, DAVACL\IACL {
*
* @param CalDAV\Backend\NotificationSupport $caldavBackend
* @param string $principalUri
- * @param CalDAV\Notifications\INotificationType $notification
+ * @param NotificationInterface $notification
*/
- public function __construct(CalDAV\Backend\NotificationSupport $caldavBackend, $principalUri, INotificationType $notification) {
+ function __construct(CalDAV\Backend\NotificationSupport $caldavBackend, $principalUri, NotificationInterface $notification) {
$this->caldavBackend = $caldavBackend;
$this->principalUri = $principalUri;
@@ -60,7 +61,7 @@ class Node extends DAV\File implements INode, DAVACL\IACL {
*
* @return id
*/
- public function getName() {
+ function getName() {
return $this->notification->getId() . '.xml';
@@ -73,7 +74,7 @@ class Node extends DAV\File implements INode, DAVACL\IACL {
*
* @return string
*/
- public function getETag() {
+ function getETag() {
return $this->notification->getETag();
@@ -85,7 +86,7 @@ class Node extends DAV\File implements INode, DAVACL\IACL {
*
* @return INotificationType
*/
- public function getNotificationType() {
+ function getNotificationType() {
return $this->notification;
@@ -96,7 +97,7 @@ class Node extends DAV\File implements INode, DAVACL\IACL {
*
* @return void
*/
- public function delete() {
+ function delete() {
$this->caldavBackend->deleteNotification($this->getOwner(), $this->notification);
@@ -109,7 +110,7 @@ class Node extends DAV\File implements INode, DAVACL\IACL {
*
* @return string|null
*/
- public function getOwner() {
+ function getOwner() {
return $this->principalUri;
@@ -122,7 +123,7 @@ class Node extends DAV\File implements INode, DAVACL\IACL {
*
* @return string|null
*/
- public function getGroup() {
+ function getGroup() {
return null;
@@ -140,20 +141,20 @@ class Node extends DAV\File implements INode, DAVACL\IACL {
*
* @return array
*/
- public function getACL() {
+ function getACL() {
- return array(
- array(
+ return [
+ [
'principal' => $this->getOwner(),
'privilege' => '{DAV:}read',
'protected' => true,
- ),
- array(
+ ],
+ [
'principal' => $this->getOwner(),
'privilege' => '{DAV:}write',
'protected' => true,
- )
- );
+ ]
+ ];
}
@@ -165,7 +166,7 @@ class Node extends DAV\File implements INode, DAVACL\IACL {
* @param array $acl
* @return void
*/
- public function setACL(array $acl) {
+ function setACL(array $acl) {
throw new DAV\Exception\NotImplemented('Updating ACLs is not implemented here');
@@ -183,7 +184,7 @@ class Node extends DAV\File implements INode, DAVACL\IACL {
*
* @return array|null
*/
- public function getSupportedPrivilegeSet() {
+ function getSupportedPrivilegeSet() {
return null;
diff --git a/vendor/sabre/dav/lib/CalDAV/Notifications/Plugin.php b/vendor/sabre/dav/lib/CalDAV/Notifications/Plugin.php
new file mode 100644
index 000000000..546bf927f
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Notifications/Plugin.php
@@ -0,0 +1,180 @@
+<?php
+
+namespace Sabre\CalDAV\Notifications;
+
+use Sabre\DAV;
+use Sabre\DAV\PropFind;
+use Sabre\DAV\INode as BaseINode;
+use Sabre\DAV\ServerPlugin;
+use Sabre\DAV\Server;
+use Sabre\DAVACL;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * Notifications plugin
+ *
+ * This plugin implements several features required by the caldav-notification
+ * draft specification.
+ *
+ * Before version 2.1.0 this functionality was part of Sabre\CalDAV\Plugin but
+ * this has since been split up.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Plugin extends ServerPlugin {
+
+ /**
+ * This is the namespace for the proprietary calendarserver extensions
+ */
+ const NS_CALENDARSERVER = 'http://calendarserver.org/ns/';
+
+ /**
+ * Reference to the main server object.
+ *
+ * @var Server
+ */
+ protected $server;
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using \Sabre\DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'notifications';
+
+ }
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by Sabre\DAV\Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ *
+ * @param Server $server
+ * @return void
+ */
+ function initialize(Server $server) {
+
+ $this->server = $server;
+ $server->on('method:GET', [$this, 'httpGet'], 90);
+ $server->on('propFind', [$this, 'propFind']);
+
+ $server->xml->namespaceMap[self::NS_CALENDARSERVER] = 'cs';
+ $server->resourceTypeMapping['\\Sabre\\CalDAV\\Notifications\\ICollection'] = '{' . self::NS_CALENDARSERVER . '}notification';
+
+ array_push($server->protectedProperties,
+ '{' . self::NS_CALENDARSERVER . '}notification-URL',
+ '{' . self::NS_CALENDARSERVER . '}notificationtype'
+ );
+
+ }
+
+ /**
+ * PropFind
+ *
+ * @param PropFind $propFind
+ * @param BaseINode $node
+ * @return void
+ */
+ function propFind(PropFind $propFind, BaseINode $node) {
+
+ $caldavPlugin = $this->server->getPlugin('caldav');
+
+ if ($node instanceof DAVACL\IPrincipal) {
+
+ $principalUrl = $node->getPrincipalUrl();
+
+ // notification-URL property
+ $propFind->handle('{' . self::NS_CALENDARSERVER . '}notification-URL', function() use ($principalUrl, $caldavPlugin) {
+
+ $notificationPath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl) . '/notifications/';
+ return new DAV\Xml\Property\Href($notificationPath);
+
+ });
+
+ }
+
+ if ($node instanceof INode) {
+
+ $propFind->handle(
+ '{' . self::NS_CALENDARSERVER . '}notificationtype',
+ [$node, 'getNotificationType']
+ );
+
+ }
+
+ }
+
+ /**
+ * This event is triggered before the usual GET request handler.
+ *
+ * We use this to intercept GET calls to notification nodes, and return the
+ * proper response.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ function httpGet(RequestInterface $request, ResponseInterface $response) {
+
+ $path = $request->getPath();
+
+ try {
+ $node = $this->server->tree->getNodeForPath($path);
+ } catch (DAV\Exception\NotFound $e) {
+ return;
+ }
+
+ if (!$node instanceof INode)
+ return;
+
+ $writer = $this->server->xml->getWriter();
+ $writer->contextUri = $this->server->getBaseUri();
+ $writer->openMemory();
+ $writer->startDocument('1.0', 'UTF-8');
+ $writer->startElement('{http://calendarserver.org/ns/}notification');
+ $node->getNotificationType()->xmlSerializeFull($writer);
+ $writer->endElement();
+
+ $response->setHeader('Content-Type', 'application/xml');
+ $response->setHeader('ETag', $node->getETag());
+ $response->setStatus(200);
+ $response->setBody($writer->outputMemory());
+
+ // Return false to break the event chain.
+ return false;
+
+ }
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => 'Adds support for caldav-notifications, which is required to enable caldav-sharing.',
+ 'link' => 'http://sabre.io/dav/caldav-sharing/',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Plugin.php b/vendor/sabre/dav/lib/CalDAV/Plugin.php
new file mode 100644
index 000000000..663490023
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Plugin.php
@@ -0,0 +1,1025 @@
+<?php
+
+namespace Sabre\CalDAV;
+
+use DateTimeZone;
+use Sabre\DAV;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\DAV\MkCol;
+use Sabre\DAV\Xml\Property\Href;
+use Sabre\DAVACL;
+use Sabre\VObject;
+use Sabre\HTTP;
+use Sabre\Uri;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * CalDAV plugin
+ *
+ * This plugin provides functionality added by CalDAV (RFC 4791)
+ * It implements new reports, and the MKCALENDAR method.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Plugin extends DAV\ServerPlugin {
+
+ /**
+ * This is the official CalDAV namespace
+ */
+ const NS_CALDAV = 'urn:ietf:params:xml:ns:caldav';
+
+ /**
+ * This is the namespace for the proprietary calendarserver extensions
+ */
+ const NS_CALENDARSERVER = 'http://calendarserver.org/ns/';
+
+ /**
+ * The hardcoded root for calendar objects. It is unfortunate
+ * that we're stuck with it, but it will have to do for now
+ */
+ const CALENDAR_ROOT = 'calendars';
+
+ /**
+ * Reference to server object
+ *
+ * @var DAV\Server
+ */
+ protected $server;
+
+ /**
+ * The default PDO storage uses a MySQL MEDIUMBLOB for iCalendar data,
+ * which can hold up to 2^24 = 16777216 bytes. This is plenty. We're
+ * capping it to 10M here.
+ */
+ protected $maxResourceSize = 10000000;
+
+ /**
+ * Use this method to tell the server this plugin defines additional
+ * HTTP methods.
+ *
+ * This method is passed a uri. It should only return HTTP methods that are
+ * available for the specified uri.
+ *
+ * @param string $uri
+ * @return array
+ */
+ function getHTTPMethods($uri) {
+
+ // The MKCALENDAR is only available on unmapped uri's, whose
+ // parents extend IExtendedCollection
+ list($parent, $name) = Uri\split($uri);
+
+ $node = $this->server->tree->getNodeForPath($parent);
+
+ if ($node instanceof DAV\IExtendedCollection) {
+ try {
+ $node->getChild($name);
+ } catch (DAV\Exception\NotFound $e) {
+ return ['MKCALENDAR'];
+ }
+ }
+ return [];
+
+ }
+
+ /**
+ * Returns the path to a principal's calendar home.
+ *
+ * The return url must not end with a slash.
+ * This function should return null in case a principal did not have
+ * a calendar home.
+ *
+ * @param string $principalUrl
+ * @return string
+ */
+ 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.
+ //
+ // This function assumes that there are two components in a principal
+ // path. If there's more, we don't return a calendar home. This
+ // 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];
+
+ }
+
+ /**
+ * Returns a list of features for the DAV: HTTP header.
+ *
+ * @return array
+ */
+ function getFeatures() {
+
+ return ['calendar-access', 'calendar-proxy'];
+
+ }
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'caldav';
+
+ }
+
+ /**
+ * Returns a list of reports this plugin supports.
+ *
+ * This will be used in the {DAV:}supported-report-set property.
+ * Note that you still need to subscribe to the 'report' event to actually
+ * implement them
+ *
+ * @param string $uri
+ * @return array
+ */
+ function getSupportedReportSet($uri) {
+
+ $node = $this->server->tree->getNodeForPath($uri);
+
+ $reports = [];
+ if ($node instanceof ICalendarObjectContainer || $node instanceof ICalendarObject) {
+ $reports[] = '{' . self::NS_CALDAV . '}calendar-multiget';
+ $reports[] = '{' . self::NS_CALDAV . '}calendar-query';
+ }
+ if ($node instanceof ICalendar) {
+ $reports[] = '{' . self::NS_CALDAV . '}free-busy-query';
+ }
+ // iCal has a bug where it assumes that sync support is enabled, only
+ // if we say we support it on the calendar-home, even though this is
+ // not actually the case.
+ if ($node instanceof CalendarHome && $this->server->getPlugin('sync')) {
+ $reports[] = '{DAV:}sync-collection';
+ }
+ return $reports;
+
+ }
+
+ /**
+ * Initializes the plugin
+ *
+ * @param DAV\Server $server
+ * @return void
+ */
+ function initialize(DAV\Server $server) {
+
+ $this->server = $server;
+
+ $server->on('method:MKCALENDAR', [$this, 'httpMkCalendar']);
+ $server->on('report', [$this, 'report']);
+ $server->on('propFind', [$this, 'propFind']);
+ $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']);
+ $server->on('beforeCreateFile', [$this, 'beforeCreateFile']);
+ $server->on('beforeWriteContent', [$this, 'beforeWriteContent']);
+ $server->on('afterMethod:GET', [$this, 'httpAfterGET']);
+
+ $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->resourceTypeMapping['\\Sabre\\CalDAV\\ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar';
+
+ $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read';
+ $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write';
+
+ array_push($server->protectedProperties,
+
+ '{' . self::NS_CALDAV . '}supported-calendar-component-set',
+ '{' . self::NS_CALDAV . '}supported-calendar-data',
+ '{' . self::NS_CALDAV . '}max-resource-size',
+ '{' . self::NS_CALDAV . '}min-date-time',
+ '{' . self::NS_CALDAV . '}max-date-time',
+ '{' . self::NS_CALDAV . '}max-instances',
+ '{' . self::NS_CALDAV . '}max-attendees-per-instance',
+ '{' . self::NS_CALDAV . '}calendar-home-set',
+ '{' . self::NS_CALDAV . '}supported-collation-set',
+ '{' . self::NS_CALDAV . '}calendar-data',
+
+ // CalendarServer extensions
+ '{' . self::NS_CALENDARSERVER . '}getctag',
+ '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for',
+ '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'
+
+ );
+
+ if ($aclPlugin = $server->getPlugin('acl')) {
+ $aclPlugin->principalSearchPropertySet['{' . self::NS_CALDAV . '}calendar-user-address-set'] = 'Calendar address';
+ }
+ }
+
+ /**
+ * This functions handles REPORT requests specific to CalDAV
+ *
+ * @param string $reportName
+ * @param mixed $report
+ * @return bool
+ */
+ function report($reportName, $report) {
+
+ switch ($reportName) {
+ case '{' . self::NS_CALDAV . '}calendar-multiget' :
+ $this->server->transactionType = 'report-calendar-multiget';
+ $this->calendarMultiGetReport($report);
+ return false;
+ case '{' . self::NS_CALDAV . '}calendar-query' :
+ $this->server->transactionType = 'report-calendar-query';
+ $this->calendarQueryReport($report);
+ return false;
+ 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 ResponseInterface $response
+ * @return bool
+ */
+ 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);
+ }
+ $properties = $mkcalendar->getProperties();
+
+ }
+
+ // iCal abuses MKCALENDAR since iCal 10.9.2 to create server-stored
+ // subscriptions. Before that it used MKCOL which was the correct way
+ // to do this.
+ //
+ // If the body had a {DAV:}resourcetype, it means we stumbled upon this
+ // request, and we simply use it instead of the pre-defined list.
+ if (isset($properties['{DAV:}resourcetype'])) {
+ $resourceType = $properties['{DAV:}resourcetype']->getValue();
+ } else {
+ $resourceType = ['{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar'];
+ }
+
+ $this->server->createCollection($path, new MkCol($resourceType, $properties));
+
+ $this->server->httpResponse->setStatus(201);
+ $this->server->httpResponse->setHeader('Content-Length', 0);
+
+ // This breaks the method chain.
+ return false;
+ }
+
+ /**
+ * PropFind
+ *
+ * This method handler is invoked before any after properties for a
+ * resource are fetched. This allows us to add in any CalDAV specific
+ * properties.
+ *
+ * @param DAV\PropFind $propFind
+ * @param DAV\INode $node
+ * @return void
+ */
+ function propFind(DAV\PropFind $propFind, DAV\INode $node) {
+
+ $ns = '{' . self::NS_CALDAV . '}';
+
+ if ($node instanceof ICalendarObjectContainer) {
+
+ $propFind->handle($ns . 'max-resource-size', $this->maxResourceSize);
+ $propFind->handle($ns . 'supported-calendar-data', function() {
+ return new Xml\Property\SupportedCalendarData();
+ });
+ $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) {
+
+ $calendarHomePath = $this->getCalendarHomeForPrincipal($principalUrl);
+ if (is_null($calendarHomePath)) return null;
+ return new Href($calendarHomePath . '/');
+
+ });
+ // The calendar-user-address-set property is basically mapped to
+ // the {DAV:}alternate-URI-set property.
+ $propFind->handle('{' . self::NS_CALDAV . '}calendar-user-address-set', function() use ($node) {
+ $addresses = $node->getAlternateUriSet();
+ $addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl() . '/';
+ return new Href($addresses, false);
+ });
+ // For some reason somebody thought it was a good idea to add
+ // another one of these properties. We're supporting it too.
+ $propFind->handle('{' . self::NS_CALENDARSERVER . '}email-address-set', function() use ($node) {
+ $addresses = $node->getAlternateUriSet();
+ $emails = [];
+ foreach ($addresses as $address) {
+ if (substr($address, 0, 7) === 'mailto:') {
+ $emails[] = substr($address, 7);
+ }
+ }
+ return new 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) {
+
+ $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] . '/';
+
+ // If the node is either ap proxy-read or proxy-write
+ // group, we grab the parent principal and add it to the
+ // list.
+ if ($groupNode instanceof Principal\IProxyRead) {
+ $readList[] = $listItem;
+ }
+ if ($groupNode instanceof Principal\IProxyWrite) {
+ $writeList[] = $listItem;
+ }
+
+ }
+
+ $propFind->set($propRead, new Href($readList));
+ $propFind->set($propWrite, new Href($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) {
+ $val = $node->get();
+ if (is_resource($val))
+ $val = stream_get_contents($val);
+
+ // Taking out \r to not screw up the xml output
+ return str_replace("\r", "", $val);
+
+ });
+
+ }
+
+ }
+
+ /**
+ * This function handles the calendar-multiget REPORT.
+ *
+ * This report is used by the client to fetch the content of a series
+ * of urls. Effectively avoiding a lot of redundant requests.
+ *
+ * @param CalendarMultiGetReport $report
+ * @return void
+ */
+ function calendarMultiGetReport($report) {
+
+ $needsJson = $report->contentType === 'application/calendar+json';
+
+ $timeZones = [];
+ $propertyList = [];
+
+ $paths = array_map(
+ [$this->server, 'calculateUri'],
+ $report->hrefs
+ );
+
+ 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 ($report->expand) {
+ // We're expanding, and for that we need to figure out the
+ // calendar's timezone.
+ list($calendarPath) = Uri\split($uri);
+ if (!isset($timeZones[$calendarPath])) {
+ // Checking the calendar-timezone property.
+ $tzProp = '{' . self::NS_CALDAV . '}calendar-timezone';
+ $tzResult = $this->server->getProperties($calendarPath, [$tzProp]);
+ if (isset($tzResult[$tzProp])) {
+ // This property contains a VCALENDAR with a single
+ // VTIMEZONE.
+ $vtimezoneObj = VObject\Reader::read($tzResult[$tzProp]);
+ $timeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
+ } else {
+ // Defaulting to UTC.
+ $timeZone = new DateTimeZone('UTC');
+ }
+ $timeZones[$calendarPath] = $timeZone;
+ }
+
+ $vObject = $vObject->expand($report->expand['start'], $report->expand['end'], $timeZones[$calendarPath]);
+ }
+ if ($needsJson) {
+ $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = json_encode($vObject->jsonSerialize());
+ } else {
+ $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
+ }
+ // Destroy circular references so PHP will garbage collect the
+ // object.
+ $vObject->destroy();
+ }
+
+ $propertyList[] = $objProps;
+
+ }
+
+ $prefer = $this->server->getHTTPPrefer();
+
+ $this->server->httpResponse->setStatus(207);
+ $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
+ $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer');
+ $this->server->httpResponse->setBody($this->server->generateMultiStatus($propertyList, $prefer['return'] === 'minimal'));
+
+ }
+
+ /**
+ * This function handles the calendar-query REPORT
+ *
+ * This report is used by clients to request calendar objects based on
+ * complex conditions.
+ *
+ * @param Xml\Request\CalendarQueryReport $report
+ * @return void
+ */
+ function calendarQueryReport($report) {
+
+ $path = $this->server->getRequestUri();
+
+ $needsJson = $report->contentType === 'application/calendar+json';
+
+ $node = $this->server->tree->getNodeForPath($this->server->getRequestUri());
+ $depth = $this->server->getHTTPDepth(0);
+
+ // The default result is an empty array
+ $result = [];
+
+ $calendarTimeZone = null;
+ if ($report->expand) {
+ // We're expanding, and for that we need to figure out the
+ // calendar's timezone.
+ $tzProp = '{' . self::NS_CALDAV . '}calendar-timezone';
+ $tzResult = $this->server->getProperties($path, [$tzProp]);
+ if (isset($tzResult[$tzProp])) {
+ // This property contains a VCALENDAR with a single
+ // VTIMEZONE.
+ $vtimezoneObj = VObject\Reader::read($tzResult[$tzProp]);
+ $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
+
+ // Destroy circular references so PHP will garbage collect the
+ // object.
+ $vtimezoneObj->destroy();
+ } else {
+ // Defaulting to UTC.
+ $calendarTimeZone = new DateTimeZone('UTC');
+ }
+ }
+
+ // The calendarobject was requested directly. In this case we handle
+ // this locally.
+ if ($depth == 0 && $node instanceof ICalendarObject) {
+
+ $requestedCalendarData = true;
+ $requestedProperties = $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';
+
+ // If calendar-data wasn't explicitly requested, we need to remove
+ // it after processing.
+ $requestedCalendarData = false;
+ }
+
+ $properties = $this->server->getPropertiesForPath(
+ $path,
+ $requestedProperties,
+ 0
+ );
+
+ // This array should have only 1 element, the first calendar
+ // object.
+ $properties = current($properties);
+
+ // If there wasn't any calendar-data returned somehow, we ignore
+ // this.
+ if (isset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) {
+
+ $validator = new CalendarQueryValidator();
+
+ $vObject = VObject\Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
+ if ($validator->validate($vObject, $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());
+ } elseif ($report->expand) {
+ $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) {
+ // 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.
+ //
+ // This targets at least the following clients:
+ // Windows 10
+ // Windows Phone 8, 10
+ $depth = 1;
+ } else {
+ throw new BadRequest('A calendar-query REPORT on a calendar with a Depth: 0 is undefined. Set Depth to 1');
+ }
+
+ }
+
+ // If we're dealing with a calendar, the calendar itself is responsible
+ // for the calendar-query.
+ if ($node instanceof ICalendarObjectContainer && $depth == 1) {
+
+ $nodePaths = $node->calendarQuery($report->filters);
+
+ foreach ($nodePaths as $path) {
+
+ list($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']);
+
+ 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());
+ } else {
+ $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
+ }
+
+ // Destroy circular references so PHP will garbage collect the
+ // object.
+ $vObject->destroy();
+ }
+ $result[] = $properties;
+
+ }
+
+ }
+
+ $prefer = $this->server->getHTTPPrefer();
+
+ $this->server->httpResponse->setStatus(207);
+ $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
+ $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer');
+ $this->server->httpResponse->setBody($this->server->generateMultiStatus($result, $prefer['return'] === 'minimal'));
+
+ }
+
+ /**
+ * This method is responsible for parsing the request and generating the
+ * response for the CALDAV:free-busy-query REPORT.
+ *
+ * @param Xml\Request\FreeBusyQueryReport $report
+ * @return void
+ */
+ 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');
+ }
+
+ $calendar = $this->server->tree->getNodeForPath($uri);
+ if (!$calendar instanceof ICalendar) {
+ throw new DAV\Exception\NotImplemented('The free-busy-query REPORT is only implemented on calendars');
+ }
+
+ $tzProp = '{' . self::NS_CALDAV . '}calendar-timezone';
+
+ // Figuring out the default timezone for the calendar, for floating
+ // times.
+ $calendarProps = $this->server->getProperties($uri, [$tzProp]);
+
+ if (isset($calendarProps[$tzProp])) {
+ $vtimezoneObj = VObject\Reader::read($calendarProps[$tzProp]);
+ $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
+ // Destroy circular references so PHP will garbage collect the object.
+ $vtimezoneObj->destroy();
+ } else {
+ $calendarTimeZone = new DateTimeZone('UTC');
+ }
+
+ // Doing a calendar-query first, to make sure we get the most
+ // performance.
+ $urls = $calendar->calendarQuery([
+ 'name' => 'VCALENDAR',
+ 'comp-filters' => [
+ [
+ 'name' => 'VEVENT',
+ 'comp-filters' => [],
+ 'prop-filters' => [],
+ 'is-not-defined' => false,
+ 'time-range' => [
+ 'start' => $report->start,
+ 'end' => $report->end,
+ ],
+ ],
+ ],
+ 'prop-filters' => [],
+ 'is-not-defined' => false,
+ 'time-range' => null,
+ ]);
+
+ $objects = array_map(function($url) use ($calendar) {
+ $obj = $calendar->getChild($url)->get();
+ return $obj;
+ }, $urls);
+
+ $generator = new VObject\FreeBusyGenerator();
+ $generator->setObjects($objects);
+ $generator->setTimeRange($report->start, $report->end);
+ $generator->setTimeZone($calendarTimeZone);
+ $result = $generator->getResult();
+ $result = $result->serialize();
+
+ $this->server->httpResponse->setStatus(200);
+ $this->server->httpResponse->setHeader('Content-Type', 'text/calendar');
+ $this->server->httpResponse->setHeader('Content-Length', strlen($result));
+ $this->server->httpResponse->setBody($result);
+
+ }
+
+ /**
+ * This method is triggered before a file gets updated with new content.
+ *
+ * This plugin uses this method to ensure that CalDAV objects receive
+ * valid calendar data.
+ *
+ * @param string $path
+ * @param DAV\IFile $node
+ * @param resource $data
+ * @param bool $modified Should be set to true, if this event handler
+ * changed &$data.
+ * @return void
+ */
+ function beforeWriteContent($path, DAV\IFile $node, &$data, &$modified) {
+
+ if (!$node instanceof ICalendarObject)
+ return;
+
+ // We're onyl interested in ICalendarObject nodes that are inside of a
+ // real calendar. This is to avoid triggering validation and scheduling
+ // for non-calendars (such as an inbox).
+ list($parent) = Uri\split($path);
+ $parentNode = $this->server->tree->getNodeForPath($parent);
+
+ if (!$parentNode instanceof ICalendar)
+ return;
+
+ $this->validateICalendar(
+ $data,
+ $path,
+ $modified,
+ $this->server->httpRequest,
+ $this->server->httpResponse,
+ false
+ );
+
+ }
+
+ /**
+ * This method is triggered before a new file is created.
+ *
+ * This plugin uses this method to ensure that newly created calendar
+ * objects contain valid calendar data.
+ *
+ * @param string $path
+ * @param resource $data
+ * @param DAV\ICollection $parentNode
+ * @param bool $modified Should be set to true, if this event handler
+ * changed &$data.
+ * @return void
+ */
+ function beforeCreateFile($path, &$data, DAV\ICollection $parentNode, &$modified) {
+
+ if (!$parentNode instanceof ICalendar)
+ return;
+
+ $this->validateICalendar(
+ $data,
+ $path,
+ $modified,
+ $this->server->httpRequest,
+ $this->server->httpResponse,
+ true
+ );
+
+ }
+
+ /**
+ * Checks if the submitted iCalendar data is in fact, valid.
+ *
+ * An exception is thrown if it's not.
+ *
+ * @param resource|string $data
+ * @param string $path
+ * @param bool $modified Should be set to true, if this event handler
+ * changed &$data.
+ * @param RequestInterface $request The http request.
+ * @param ResponseInterface $response The http response.
+ * @param bool $isNew Is the item a new one, or an update.
+ * @return void
+ */
+ protected function validateICalendar(&$data, $path, &$modified, RequestInterface $request, ResponseInterface $response, $isNew) {
+
+ // If it's a stream, we convert it to a string first.
+ if (is_resource($data)) {
+ $data = stream_get_contents($data);
+ }
+
+ $before = md5($data);
+ // Converting the data to unicode, if needed.
+ $data = DAV\StringUtil::ensureUTF8($data);
+
+ if ($before !== md5($data)) $modified = true;
+
+ try {
+
+ // If the data starts with a [, we can reasonably assume we're dealing
+ // with a jCal object.
+ if (substr($data, 0, 1) === '[') {
+ $vobj = VObject\Reader::readJson($data);
+
+ // Converting $data back to iCalendar, as that's what we
+ // technically support everywhere.
+ $data = $vobj->serialize();
+ $modified = true;
+ } else {
+ $vobj = VObject\Reader::read($data);
+ }
+
+ } catch (VObject\ParseException $e) {
+
+ throw new DAV\Exception\UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage());
+
+ }
+
+ if ($vobj->name !== 'VCALENDAR') {
+ throw new DAV\Exception\UnsupportedMediaType('This collection can only support iCalendar objects.');
+ }
+
+ $sCCS = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
+
+ // Get the Supported Components for the target calendar
+ list($parentPath) = Uri\split($path);
+ $calendarProperties = $this->server->getProperties($parentPath, [$sCCS]);
+
+ if (isset($calendarProperties[$sCCS])) {
+ $supportedComponents = $calendarProperties[$sCCS]->getValue();
+ } else {
+ $supportedComponents = ['VJOURNAL', 'VTODO', 'VEVENT'];
+ }
+
+ $foundType = null;
+ $foundUID = null;
+ foreach ($vobj->getComponents() as $component) {
+ switch ($component->name) {
+ case 'VTIMEZONE' :
+ continue 2;
+ case 'VEVENT' :
+ case 'VTODO' :
+ case 'VJOURNAL' :
+ if (is_null($foundType)) {
+ $foundType = $component->name;
+ if (!in_array($foundType, $supportedComponents)) {
+ throw new Exception\InvalidComponentType('This calendar only supports ' . implode(', ', $supportedComponents) . '. We found a ' . $foundType);
+ }
+ if (!isset($component->UID)) {
+ throw new DAV\Exception\BadRequest('Every ' . $component->name . ' component must have an UID');
+ }
+ $foundUID = (string)$component->UID;
+ } else {
+ if ($foundType !== $component->name) {
+ throw new DAV\Exception\BadRequest('A calendar object must only contain 1 component. We found a ' . $component->name . ' as well as a ' . $foundType);
+ }
+ if ($foundUID !== (string)$component->UID) {
+ throw new DAV\Exception\BadRequest('Every ' . $component->name . ' in this object must have identical UIDs');
+ }
+ }
+ break;
+ default :
+ throw new DAV\Exception\BadRequest('You are not allowed to create components of type: ' . $component->name . ' here');
+
+ }
+ }
+ if (!$foundType)
+ throw new DAV\Exception\BadRequest('iCalendar object must contain at least 1 of VEVENT, VTODO or VJOURNAL');
+
+ // We use an extra variable to allow event handles to tell us wether
+ // the object was modified or not.
+ //
+ // This helps us determine if we need to re-serialize the object.
+ $subModified = false;
+
+ $this->server->emit(
+ 'calendarObjectChange',
+ [
+ $request,
+ $response,
+ $vobj,
+ $parentPath,
+ &$subModified,
+ $isNew
+ ]
+ );
+
+ if ($subModified) {
+ // An event handler told us that it modified the object.
+ $data = $vobj->serialize();
+
+ // Using md5 to figure out if there was an *actual* change.
+ if (!$modified && $before !== md5($data)) {
+ $modified = true;
+ }
+
+ }
+
+ // Destroy circular references so PHP will garbage collect the object.
+ $vobj->destroy();
+
+ }
+
+
+ /**
+ * This method is used to generate HTML output for the
+ * DAV\Browser\Plugin. This allows us to generate an interface users
+ * can use to create new calendars.
+ *
+ * @param DAV\INode $node
+ * @param string $output
+ * @return bool
+ */
+ function htmlActionsPanel(DAV\INode $node, &$output) {
+
+ if (!$node instanceof CalendarHome)
+ return;
+
+ $output .= '<tr><td colspan="2"><form method="post" action="">
+ <h3>Create new calendar</h3>
+ <input type="hidden" name="sabreAction" value="mkcol" />
+ <input type="hidden" name="resourceType" value="{DAV:}collection,{' . self::NS_CALDAV . '}calendar" />
+ <label>Name (uri):</label> <input type="text" name="name" /><br />
+ <label>Display name:</label> <input type="text" name="{DAV:}displayname" /><br />
+ <input type="submit" value="create" />
+ </form>
+ </td></tr>';
+
+ return false;
+
+ }
+
+ /**
+ * This event is triggered after GET requests.
+ *
+ * This is used to transform data into jCal, if this was requested.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ function httpAfterGet(RequestInterface $request, ResponseInterface $response) {
+
+ if (strpos($response->getHeader('Content-Type'), 'text/calendar') === false) {
+ return;
+ }
+
+ $result = HTTP\Util::negotiate(
+ $request->getHeader('Accept'),
+ ['text/calendar', 'application/calendar+json']
+ );
+
+ if ($result !== 'application/calendar+json') {
+ // Do nothing
+ return;
+ }
+
+ // Transforming.
+ $vobj = VObject\Reader::read($response->getBody());
+
+ $jsonBody = json_encode($vobj->jsonSerialize());
+ $response->setBody($jsonBody);
+
+ // Destroy circular references so PHP will garbage collect the object.
+ $vobj->destroy();
+
+ $response->setHeader('Content-Type', 'application/calendar+json');
+ $response->setHeader('Content-Length', strlen($jsonBody));
+
+ }
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => 'Adds support for CalDAV (rfc4791)',
+ 'link' => 'http://sabre.io/dav/caldav/',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Principal/Collection.php b/vendor/sabre/dav/lib/CalDAV/Principal/Collection.php
index 8a747a644..e19719a76 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Principal/Collection.php
+++ b/vendor/sabre/dav/lib/CalDAV/Principal/Collection.php
@@ -1,6 +1,7 @@
<?php
namespace Sabre\CalDAV\Principal;
+
use Sabre\DAVACL;
/**
@@ -11,11 +12,11 @@ use Sabre\DAVACL;
* calendar-proxy-write sub-principals, as defined by the caldav-proxy
* specification.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class Collection extends DAVACL\AbstractPrincipalCollection {
+class Collection extends DAVACL\PrincipalCollection {
/**
* Returns a child object based on principal information
@@ -23,7 +24,7 @@ class Collection extends DAVACL\AbstractPrincipalCollection {
* @param array $principalInfo
* @return User
*/
- public function getChildForPrincipal(array $principalInfo) {
+ function getChildForPrincipal(array $principalInfo) {
return new User($this->principalBackend, $principalInfo);
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Principal/IProxyRead.php b/vendor/sabre/dav/lib/CalDAV/Principal/IProxyRead.php
index 548411fa8..7dd375932 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Principal/IProxyRead.php
+++ b/vendor/sabre/dav/lib/CalDAV/Principal/IProxyRead.php
@@ -10,7 +10,7 @@ use Sabre\DAVACL;
* Any principal node implementing this interface will be picked up as a 'proxy
* principal group'.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Principal/IProxyWrite.php b/vendor/sabre/dav/lib/CalDAV/Principal/IProxyWrite.php
index f0e6e47cb..eda87a4ff 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Principal/IProxyWrite.php
+++ b/vendor/sabre/dav/lib/CalDAV/Principal/IProxyWrite.php
@@ -10,7 +10,7 @@ use Sabre\DAVACL;
* Any principal node implementing this interface will be picked up as a 'proxy
* principal group'.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Principal/ProxyRead.php b/vendor/sabre/dav/lib/CalDAV/Principal/ProxyRead.php
index 62f66b98c..93f0fe095 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Principal/ProxyRead.php
+++ b/vendor/sabre/dav/lib/CalDAV/Principal/ProxyRead.php
@@ -1,6 +1,7 @@
<?php
namespace Sabre\CalDAV\Principal;
+
use Sabre\DAVACL;
use Sabre\DAV;
@@ -11,7 +12,7 @@ use Sabre\DAV;
* This is needed to implement 'Calendar delegation' support. This class is
* instantiated by User.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -39,7 +40,7 @@ class ProxyRead implements IProxyRead {
* @param DAVACL\PrincipalBackend\BackendInterface $principalBackend
* @param array $principalInfo
*/
- public function __construct(DAVACL\PrincipalBackend\BackendInterface $principalBackend, array $principalInfo) {
+ function __construct(DAVACL\PrincipalBackend\BackendInterface $principalBackend, array $principalInfo) {
$this->principalInfo = $principalInfo;
$this->principalBackend = $principalBackend;
@@ -51,7 +52,7 @@ class ProxyRead implements IProxyRead {
*
* @return string
*/
- public function getName() {
+ function getName() {
return 'calendar-proxy-read';
@@ -62,7 +63,7 @@ class ProxyRead implements IProxyRead {
*
* @return null
*/
- public function getLastModified() {
+ function getLastModified() {
return null;
@@ -74,7 +75,7 @@ class ProxyRead implements IProxyRead {
* @throws DAV\Exception\Forbidden
* @return void
*/
- public function delete() {
+ function delete() {
throw new DAV\Exception\Forbidden('Permission denied to delete node');
@@ -87,7 +88,7 @@ class ProxyRead implements IProxyRead {
* @param string $name The new name
* @return void
*/
- public function setName($name) {
+ function setName($name) {
throw new DAV\Exception\Forbidden('Permission denied to rename file');
@@ -101,9 +102,9 @@ class ProxyRead implements IProxyRead {
*
* @return array
*/
- public function getAlternateUriSet() {
+ function getAlternateUriSet() {
- return array();
+ return [];
}
@@ -112,7 +113,7 @@ class ProxyRead implements IProxyRead {
*
* @return string
*/
- public function getPrincipalUrl() {
+ function getPrincipalUrl() {
return $this->principalInfo['uri'] . '/' . $this->getName();
@@ -126,7 +127,7 @@ class ProxyRead implements IProxyRead {
*
* @return array
*/
- public function getGroupMemberSet() {
+ function getGroupMemberSet() {
return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl());
@@ -140,7 +141,7 @@ class ProxyRead implements IProxyRead {
*
* @return array
*/
- public function getGroupMembership() {
+ function getGroupMembership() {
return $this->principalBackend->getGroupMembership($this->getPrincipalUrl());
@@ -157,7 +158,7 @@ class ProxyRead implements IProxyRead {
* @param array $principals
* @return void
*/
- public function setGroupMemberSet(array $principals) {
+ function setGroupMemberSet(array $principals) {
$this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
@@ -171,7 +172,7 @@ class ProxyRead implements IProxyRead {
*
* @return string
*/
- public function getDisplayName() {
+ function getDisplayName() {
return $this->getName();
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Principal/ProxyWrite.php b/vendor/sabre/dav/lib/CalDAV/Principal/ProxyWrite.php
index 02cd81f70..8124c05e0 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Principal/ProxyWrite.php
+++ b/vendor/sabre/dav/lib/CalDAV/Principal/ProxyWrite.php
@@ -1,6 +1,7 @@
<?php
namespace Sabre\CalDAV\Principal;
+
use Sabre\DAVACL;
use Sabre\DAV;
@@ -11,7 +12,7 @@ use Sabre\DAV;
* This is needed to implement 'Calendar delegation' support. This class is
* instantiated by User.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -39,7 +40,7 @@ class ProxyWrite implements IProxyWrite {
* @param DAVACL\PrincipalBackend\BackendInterface $principalBackend
* @param array $principalInfo
*/
- public function __construct(DAVACL\PrincipalBackend\BackendInterface $principalBackend, array $principalInfo) {
+ function __construct(DAVACL\PrincipalBackend\BackendInterface $principalBackend, array $principalInfo) {
$this->principalInfo = $principalInfo;
$this->principalBackend = $principalBackend;
@@ -51,7 +52,7 @@ class ProxyWrite implements IProxyWrite {
*
* @return string
*/
- public function getName() {
+ function getName() {
return 'calendar-proxy-write';
@@ -62,7 +63,7 @@ class ProxyWrite implements IProxyWrite {
*
* @return null
*/
- public function getLastModified() {
+ function getLastModified() {
return null;
@@ -74,7 +75,7 @@ class ProxyWrite implements IProxyWrite {
* @throws DAV\Exception\Forbidden
* @return void
*/
- public function delete() {
+ function delete() {
throw new DAV\Exception\Forbidden('Permission denied to delete node');
@@ -87,7 +88,7 @@ class ProxyWrite implements IProxyWrite {
* @param string $name The new name
* @return void
*/
- public function setName($name) {
+ function setName($name) {
throw new DAV\Exception\Forbidden('Permission denied to rename file');
@@ -101,9 +102,9 @@ class ProxyWrite implements IProxyWrite {
*
* @return array
*/
- public function getAlternateUriSet() {
+ function getAlternateUriSet() {
- return array();
+ return [];
}
@@ -112,7 +113,7 @@ class ProxyWrite implements IProxyWrite {
*
* @return string
*/
- public function getPrincipalUrl() {
+ function getPrincipalUrl() {
return $this->principalInfo['uri'] . '/' . $this->getName();
@@ -126,7 +127,7 @@ class ProxyWrite implements IProxyWrite {
*
* @return array
*/
- public function getGroupMemberSet() {
+ function getGroupMemberSet() {
return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl());
@@ -140,7 +141,7 @@ class ProxyWrite implements IProxyWrite {
*
* @return array
*/
- public function getGroupMembership() {
+ function getGroupMembership() {
return $this->principalBackend->getGroupMembership($this->getPrincipalUrl());
@@ -157,7 +158,7 @@ class ProxyWrite implements IProxyWrite {
* @param array $principals
* @return void
*/
- public function setGroupMemberSet(array $principals) {
+ function setGroupMemberSet(array $principals) {
$this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
@@ -171,7 +172,7 @@ class ProxyWrite implements IProxyWrite {
*
* @return string
*/
- public function getDisplayName() {
+ function getDisplayName() {
return $this->getName();
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Principal/User.php b/vendor/sabre/dav/lib/CalDAV/Principal/User.php
index 6f3ccb868..6e97e7cca 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Principal/User.php
+++ b/vendor/sabre/dav/lib/CalDAV/Principal/User.php
@@ -1,6 +1,7 @@
<?php
namespace Sabre\CalDAV\Principal;
+
use Sabre\DAV;
use Sabre\DAVACL;
@@ -11,7 +12,7 @@ use Sabre\DAVACL;
* collection and returns the caldav-proxy-read and caldav-proxy-write child
* principals.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -25,7 +26,7 @@ class User extends DAVACL\Principal implements DAV\ICollection {
* @throws DAV\Exception\Forbidden
* @return void
*/
- public function createFile($name, $data = null) {
+ function createFile($name, $data = null) {
throw new DAV\Exception\Forbidden('Permission denied to create file (filename ' . $name . ')');
@@ -38,7 +39,7 @@ class User extends DAVACL\Principal implements DAV\ICollection {
* @throws DAV\Exception\Forbidden
* @return void
*/
- public function createDirectory($name) {
+ function createDirectory($name) {
throw new DAV\Exception\Forbidden('Permission denied to create directory');
@@ -50,7 +51,7 @@ class User extends DAVACL\Principal implements DAV\ICollection {
* @param string $name
* @return DAV\INode
*/
- public function getChild($name) {
+ function getChild($name) {
$principal = $this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/' . $name);
if (!$principal) {
@@ -69,11 +70,11 @@ class User extends DAVACL\Principal implements DAV\ICollection {
/**
* Returns an array with all the child nodes
*
- * @return DAV\INode[]
+ * @return DAV\INode[]
*/
- public function getChildren() {
+ function getChildren() {
- $r = array();
+ $r = [];
if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-read')) {
$r[] = new ProxyRead($this->principalBackend, $this->principalProperties);
}
@@ -91,7 +92,7 @@ class User extends DAVACL\Principal implements DAV\ICollection {
* @param string $name
* @return bool
*/
- public function childExists($name) {
+ function childExists($name) {
try {
$this->getChild($name);
@@ -114,19 +115,19 @@ class User extends DAVACL\Principal implements DAV\ICollection {
*
* @return array
*/
- public function getACL() {
+ function getACL() {
$acl = parent::getACL();
- $acl[] = array(
+ $acl[] = [
'privilege' => '{DAV:}read',
'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read',
'protected' => true,
- );
- $acl[] = array(
+ ];
+ $acl[] = [
'privilege' => '{DAV:}read',
'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write',
'protected' => true,
- );
+ ];
return $acl;
}
diff --git a/vendor/sabre/dav/lib/CalDAV/Schedule/IInbox.php b/vendor/sabre/dav/lib/CalDAV/Schedule/IInbox.php
new file mode 100644
index 000000000..c9fd77d93
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Schedule/IInbox.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Sabre\CalDAV\Schedule;
+
+/**
+ * Implement this interface to have a node be recognized as a CalDAV scheduling
+ * inbox.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface IInbox extends \Sabre\CalDAV\ICalendarObjectContainer, \Sabre\DAVACL\IACL {
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Schedule/IMipPlugin.php b/vendor/sabre/dav/lib/CalDAV/Schedule/IMipPlugin.php
new file mode 100644
index 000000000..ffb1fe45b
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Schedule/IMipPlugin.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace Sabre\CalDAV\Schedule;
+
+use Sabre\DAV;
+use Sabre\VObject\ITip;
+
+/**
+ * iMIP handler.
+ *
+ * This class is responsible for sending out iMIP messages. iMIP is the
+ * email-based transport for iTIP. iTIP deals with scheduling operations for
+ * iCalendar objects.
+ *
+ * If you want to customize the email that gets sent out, you can do so by
+ * extending this class and overriding the sendMessage method.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class IMipPlugin extends DAV\ServerPlugin {
+
+ /**
+ * Email address used in From: header.
+ *
+ * @var string
+ */
+ protected $senderEmail;
+
+ /**
+ * ITipMessage
+ *
+ * @var ITip\Message
+ */
+ protected $itipMessage;
+
+ /**
+ * Creates the email handler.
+ *
+ * @param string $senderEmail. The 'senderEmail' is the email that shows up
+ * in the 'From:' address. This should
+ * generally be some kind of no-reply email
+ * address you own.
+ */
+ function __construct($senderEmail) {
+
+ $this->senderEmail = $senderEmail;
+
+ }
+
+ /*
+ * This initializes the plugin.
+ *
+ * This function is called by Sabre\DAV\Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ *
+ * @param DAV\Server $server
+ * @return void
+ */
+ function initialize(DAV\Server $server) {
+
+ $server->on('schedule', [$this, 'schedule'], 120);
+
+ }
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using \Sabre\DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'imip';
+
+ }
+
+ /**
+ * Event handler for the 'schedule' event.
+ *
+ * @param ITip\Message $iTipMessage
+ * @return void
+ */
+ function schedule(ITip\Message $iTipMessage) {
+
+ // Not sending any emails if the system considers the update
+ // insignificant.
+ if (!$iTipMessage->significantChange) {
+ if (!$iTipMessage->scheduleStatus) {
+ $iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email';
+ }
+ return;
+ }
+
+ $summary = $iTipMessage->message->VEVENT->SUMMARY;
+
+ if (parse_url($iTipMessage->sender, PHP_URL_SCHEME) !== 'mailto')
+ return;
+
+ if (parse_url($iTipMessage->recipient, PHP_URL_SCHEME) !== 'mailto')
+ return;
+
+ $sender = substr($iTipMessage->sender, 7);
+ $recipient = substr($iTipMessage->recipient, 7);
+
+ if ($iTipMessage->senderName) {
+ $sender = $iTipMessage->senderName . ' <' . $sender . '>';
+ }
+ if ($iTipMessage->recipientName) {
+ $recipient = $iTipMessage->recipientName . ' <' . $recipient . '>';
+ }
+
+ $subject = 'SabreDAV iTIP message';
+ switch (strtoupper($iTipMessage->method)) {
+ case 'REPLY' :
+ $subject = 'Re: ' . $summary;
+ break;
+ case 'REQUEST' :
+ $subject = $summary;
+ break;
+ case 'CANCEL' :
+ $subject = 'Cancelled: ' . $summary;
+ break;
+ }
+
+ $headers = [
+ 'Reply-To: ' . $sender,
+ 'From: ' . $this->senderEmail,
+ 'Content-Type: text/calendar; charset=UTF-8; method=' . $iTipMessage->method,
+ ];
+ if (DAV\Server::$exposeVersion) {
+ $headers[] = 'X-Sabre-Version: ' . DAV\Version::VERSION;
+ }
+ $this->mail(
+ $recipient,
+ $subject,
+ $iTipMessage->message->serialize(),
+ $headers
+ );
+ $iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip';
+
+ }
+
+ // @codeCoverageIgnoreStart
+ // This is deemed untestable in a reasonable manner
+
+ /**
+ * This function is responsible for sending the actual email.
+ *
+ * @param string $to Recipient email address
+ * @param string $subject Subject of the email
+ * @param string $body iCalendar body
+ * @param array $headers List of headers
+ * @return void
+ */
+ protected function mail($to, $subject, $body, array $headers) {
+
+ mail($to, $subject, $body, implode("\r\n", $headers));
+
+ }
+
+ // @codeCoverageIgnoreEnd
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => 'Email delivery (rfc6037) for CalDAV scheduling',
+ 'link' => 'http://sabre.io/dav/scheduling/',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Schedule/IOutbox.php b/vendor/sabre/dav/lib/CalDAV/Schedule/IOutbox.php
index 7341eaa85..88fbdc411 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Schedule/IOutbox.php
+++ b/vendor/sabre/dav/lib/CalDAV/Schedule/IOutbox.php
@@ -6,11 +6,10 @@ namespace Sabre\CalDAV\Schedule;
* Implement this interface to have a node be recognized as a CalDAV scheduling
* outbox.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
interface IOutbox extends \Sabre\DAV\ICollection, \Sabre\DAVACL\IACL {
-
}
diff --git a/vendor/sabre/dav/lib/CalDAV/Schedule/ISchedulingObject.php b/vendor/sabre/dav/lib/CalDAV/Schedule/ISchedulingObject.php
new file mode 100644
index 000000000..b37cb40a1
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Schedule/ISchedulingObject.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Sabre\CalDAV\Schedule;
+
+/**
+ * The SchedulingObject represents a scheduling object in the Inbox collection
+ *
+ * @license http://sabre.io/license/ Modified BSD License
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ */
+interface ISchedulingObject extends \Sabre\CalDAV\ICalendarObject {
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Schedule/Inbox.php b/vendor/sabre/dav/lib/CalDAV/Schedule/Inbox.php
new file mode 100644
index 000000000..13212565e
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Schedule/Inbox.php
@@ -0,0 +1,261 @@
+<?php
+
+namespace Sabre\CalDAV\Schedule;
+
+use Sabre\DAV;
+use Sabre\CalDAV;
+use Sabre\DAVACL;
+use Sabre\CalDAV\Backend;
+use Sabre\VObject;
+
+/**
+ * The CalDAV scheduling inbox
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Inbox extends DAV\Collection implements IInbox {
+
+ /**
+ * CalDAV backend
+ *
+ * @var Backend\BackendInterface
+ */
+ protected $caldavBackend;
+
+ /**
+ * The principal Uri
+ *
+ * @var string
+ */
+ protected $principalUri;
+
+ /**
+ * Constructor
+ *
+ * @param Backend\SchedulingSupport $caldavBackend
+ * @param string $principalUri
+ */
+ function __construct(Backend\SchedulingSupport $caldavBackend, $principalUri) {
+
+ $this->caldavBackend = $caldavBackend;
+ $this->principalUri = $principalUri;
+
+ }
+
+ /**
+ * Returns the name of the node.
+ *
+ * This is used to generate the url.
+ *
+ * @return string
+ */
+ function getName() {
+
+ return 'inbox';
+
+ }
+
+ /**
+ * Returns an array with all the child nodes
+ *
+ * @return \Sabre\DAV\INode[]
+ */
+ function getChildren() {
+
+ $objs = $this->caldavBackend->getSchedulingObjects($this->principalUri);
+ $children = [];
+ foreach ($objs as $obj) {
+ //$obj['acl'] = $this->getACL();
+ $obj['principaluri'] = $this->principalUri;
+ $children[] = new SchedulingObject($this->caldavBackend, $obj);
+ }
+ return $children;
+
+ }
+
+ /**
+ * Creates a new file in the directory
+ *
+ * Data will either be supplied as a stream resource, or in certain cases
+ * as a string. Keep in mind that you may have to support either.
+ *
+ * After succesful creation of the file, you may choose to return the ETag
+ * of the new file here.
+ *
+ * The returned ETag must be surrounded by double-quotes (The quotes should
+ * be part of the actual string).
+ *
+ * If you cannot accurately determine the ETag, you should not return it.
+ * If you don't store the file exactly as-is (you're transforming it
+ * somehow) you should also not return an ETag.
+ *
+ * This means that if a subsequent GET to this new file does not exactly
+ * return the same contents of what was submitted here, you are strongly
+ * recommended to omit the ETag.
+ *
+ * @param string $name Name of the file
+ * @param resource|string $data Initial payload
+ * @return null|string
+ */
+ function createFile($name, $data = null) {
+
+ $this->caldavBackend->createSchedulingObject($this->principalUri, $name, $data);
+
+ }
+
+ /**
+ * Returns the owner principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getOwner() {
+
+ return $this->principalUri;
+
+ }
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getGroup() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+
+ return [
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => '{DAV:}authenticated',
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}write-properties',
+ 'principal' => $this->getOwner(),
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}unbind',
+ 'principal' => $this->getOwner(),
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}unbind',
+ 'principal' => $this->getOwner() . '/calendar-proxy-write',
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-deliver-invite',
+ 'principal' => '{DAV:}authenticated',
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-deliver-reply',
+ 'principal' => '{DAV:}authenticated',
+ 'protected' => true,
+ ],
+ ];
+
+ }
+
+ /**
+ * Updates the ACL
+ *
+ * This method will receive a list of new ACE's.
+ *
+ * @param array $acl
+ * @return void
+ */
+ function setACL(array $acl) {
+
+ throw new DAV\Exception\MethodNotAllowed('You\'re not allowed to update the ACL');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ function getSupportedPrivilegeSet() {
+
+ $ns = '{' . CalDAV\Plugin::NS_CALDAV . '}';
+
+ $default = DAVACL\Plugin::getDefaultSupportedPrivilegeSet();
+ $default['aggregates'][] = [
+ 'privilege' => $ns . 'schedule-deliver',
+ 'aggregates' => [
+ ['privilege' => $ns . 'schedule-deliver-invite'],
+ ['privilege' => $ns . 'schedule-deliver-reply'],
+ ],
+ ];
+ return $default;
+
+ }
+
+ /**
+ * Performs a calendar-query on the contents of this calendar.
+ *
+ * The calendar-query is defined in RFC4791 : CalDAV. Using the
+ * calendar-query it is possible for a client to request a specific set of
+ * object, based on contents of iCalendar properties, date-ranges and
+ * iCalendar component types (VTODO, VEVENT).
+ *
+ * This method should just return a list of (relative) urls that match this
+ * query.
+ *
+ * The list of filters are specified as an array. The exact array is
+ * documented by \Sabre\CalDAV\CalendarQueryParser.
+ *
+ * @param array $filters
+ * @return array
+ */
+ function calendarQuery(array $filters) {
+
+ $result = [];
+ $validator = new CalDAV\CalendarQueryValidator();
+
+ $objects = $this->caldavBackend->getSchedulingObjects($this->principalUri);
+ foreach ($objects as $object) {
+ $vObject = VObject\Reader::read($object['calendardata']);
+ if ($validator->validate($vObject, $filters)) {
+ $result[] = $object['uri'];
+ }
+
+ // Destroy circular references to PHP will GC the object.
+ $vObject->destroy();
+ }
+ return $result;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Schedule/Outbox.php b/vendor/sabre/dav/lib/CalDAV/Schedule/Outbox.php
index cf4500f69..dabaee2ca 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Schedule/Outbox.php
+++ b/vendor/sabre/dav/lib/CalDAV/Schedule/Outbox.php
@@ -1,6 +1,7 @@
<?php
namespace Sabre\CalDAV\Schedule;
+
use Sabre\DAV;
use Sabre\CalDAV;
use Sabre\DAVACL;
@@ -12,7 +13,7 @@ use Sabre\DAVACL;
* free-busy requests. This functionality is completely handled by the
* Scheduling plugin, so this object is actually mostly static.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -30,7 +31,7 @@ class Outbox extends DAV\Collection implements IOutbox {
*
* @param string $principalUri
*/
- public function __construct($principalUri) {
+ function __construct($principalUri) {
$this->principalUri = $principalUri;
@@ -43,7 +44,7 @@ class Outbox extends DAV\Collection implements IOutbox {
*
* @return string
*/
- public function getName() {
+ function getName() {
return 'outbox';
@@ -54,9 +55,9 @@ class Outbox extends DAV\Collection implements IOutbox {
*
* @return \Sabre\DAV\INode[]
*/
- public function getChildren() {
+ function getChildren() {
- return array();
+ return [];
}
@@ -67,7 +68,7 @@ class Outbox extends DAV\Collection implements IOutbox {
*
* @return string|null
*/
- public function getOwner() {
+ function getOwner() {
return $this->principalUri;
@@ -80,7 +81,7 @@ class Outbox extends DAV\Collection implements IOutbox {
*
* @return string|null
*/
- public function getGroup() {
+ function getGroup() {
return null;
@@ -98,25 +99,45 @@ class Outbox extends DAV\Collection implements IOutbox {
*
* @return array
*/
- public function getACL() {
+ function getACL() {
- return array(
- array(
+ return [
+ [
'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-query-freebusy',
'principal' => $this->getOwner(),
'protected' => true,
- ),
- array(
+ ],
+ [
'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-post-vevent',
'principal' => $this->getOwner(),
'protected' => true,
- ),
- array(
+ ],
+ [
'privilege' => '{DAV:}read',
'principal' => $this->getOwner(),
'protected' => true,
- ),
- );
+ ],
+ [
+ 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-query-freebusy',
+ 'principal' => $this->getOwner() . '/calendar-proxy-write',
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-post-vevent',
+ 'principal' => $this->getOwner() . '/calendar-proxy-write',
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->getOwner() . '/calendar-proxy-read',
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->getOwner() . '/calendar-proxy-write',
+ 'protected' => true,
+ ],
+ ];
}
@@ -128,7 +149,7 @@ class Outbox extends DAV\Collection implements IOutbox {
* @param array $acl
* @return void
*/
- public function setACL(array $acl) {
+ function setACL(array $acl) {
throw new DAV\Exception\MethodNotAllowed('You\'re not allowed to update the ACL');
@@ -146,15 +167,15 @@ class Outbox extends DAV\Collection implements IOutbox {
*
* @return array|null
*/
- public function getSupportedPrivilegeSet() {
+ function getSupportedPrivilegeSet() {
$default = DAVACL\Plugin::getDefaultSupportedPrivilegeSet();
- $default['aggregates'][] = array(
+ $default['aggregates'][] = [
'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-query-freebusy',
- );
- $default['aggregates'][] = array(
+ ];
+ $default['aggregates'][] = [
'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-post-vevent',
- );
+ ];
return $default;
diff --git a/vendor/sabre/dav/lib/CalDAV/Schedule/Plugin.php b/vendor/sabre/dav/lib/CalDAV/Schedule/Plugin.php
new file mode 100644
index 000000000..827d6209b
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Schedule/Plugin.php
@@ -0,0 +1,994 @@
+<?php
+
+namespace Sabre\CalDAV\Schedule;
+
+use DateTimeZone;
+use Sabre\DAV\Server;
+use Sabre\DAV\ServerPlugin;
+use Sabre\DAV\PropFind;
+use Sabre\DAV\PropPatch;
+use Sabre\DAV\INode;
+use Sabre\DAV\Xml\Property\Href;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+use Sabre\VObject;
+use Sabre\VObject\Reader;
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\ITip;
+use Sabre\VObject\ITip\Message;
+use Sabre\DAVACL;
+use Sabre\CalDAV\ICalendar;
+use Sabre\CalDAV\ICalendarObject;
+use Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp;
+use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\DAV\Exception\NotImplemented;
+
+/**
+ * CalDAV scheduling plugin.
+ * =========================
+ *
+ * This plugin provides the functionality added by the "Scheduling Extensions
+ * to CalDAV" standard, as defined in RFC6638.
+ *
+ * calendar-auto-schedule largely works by intercepting a users request to
+ * update their local calendar. If a user creates a new event with attendees,
+ * this plugin is supposed to grab the information from that event, and notify
+ * the attendees of this.
+ *
+ * There's 3 possible transports for this:
+ * * local delivery
+ * * delivery through email (iMip)
+ * * server-to-server delivery (iSchedule)
+ *
+ * iMip is simply, because we just need to add the iTip message as an email
+ * attachment. Local delivery is harder, because we both need to add this same
+ * message to a local DAV inbox, as well as live-update the relevant events.
+ *
+ * iSchedule is something for later.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Plugin extends ServerPlugin {
+
+ /**
+ * This is the official CalDAV namespace
+ */
+ const NS_CALDAV = 'urn:ietf:params:xml:ns:caldav';
+
+ /**
+ * Reference to main Server object.
+ *
+ * @var Server
+ */
+ protected $server;
+
+ /**
+ * Returns a list of features for the DAV: HTTP header.
+ *
+ * @return array
+ */
+ function getFeatures() {
+
+ return ['calendar-auto-schedule', 'calendar-availability'];
+
+ }
+
+ /**
+ * Returns the name of the plugin.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'caldav-schedule';
+
+ }
+
+ /**
+ * Initializes the plugin
+ *
+ * @param Server $server
+ * @return void
+ */
+ function initialize(Server $server) {
+
+ $this->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']);
+
+ $ns = '{' . self::NS_CALDAV . '}';
+
+ /**
+ * This information ensures that the {DAV:}resourcetype property has
+ * the correct values.
+ */
+ $server->resourceTypeMapping['\\Sabre\\CalDAV\\Schedule\\IOutbox'] = $ns . 'schedule-outbox';
+ $server->resourceTypeMapping['\\Sabre\\CalDAV\\Schedule\\IInbox'] = $ns . 'schedule-inbox';
+
+ /**
+ * Properties we protect are made read-only by the server.
+ */
+ array_push($server->protectedProperties,
+ $ns . 'schedule-inbox-URL',
+ $ns . 'schedule-outbox-URL',
+ $ns . 'calendar-user-address-set',
+ $ns . 'calendar-user-type',
+ $ns . 'schedule-default-calendar-URL'
+ );
+
+ }
+
+ /**
+ * Use this method to tell the server this plugin defines additional
+ * HTTP methods.
+ *
+ * This method is passed a uri. It should only return HTTP methods that are
+ * available for the specified uri.
+ *
+ * @param string $uri
+ * @return array
+ */
+ function getHTTPMethods($uri) {
+
+ try {
+ $node = $this->server->tree->getNodeForPath($uri);
+ } catch (NotFound $e) {
+ return [];
+ }
+
+ if ($node instanceof IOutbox) {
+ return ['POST'];
+ }
+
+ return [];
+
+ }
+
+ /**
+ * This method handles POST request for the outbox.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpPost(RequestInterface $request, ResponseInterface $response) {
+
+ // Checking if this is a text/calendar content type
+ $contentType = $request->getHeader('Content-Type');
+ if (strpos($contentType, 'text/calendar') !== 0) {
+ return;
+ }
+
+ $path = $request->getPath();
+
+ // Checking if we're talking to an outbox
+ try {
+ $node = $this->server->tree->getNodeForPath($path);
+ } catch (NotFound $e) {
+ return;
+ }
+ if (!$node instanceof IOutbox)
+ return;
+
+ $this->server->transactionType = 'post-caldav-outbox';
+ $this->outboxRequest($node, $request, $response);
+
+ // Returning false breaks the event chain and tells the server we've
+ // handled the request.
+ return false;
+
+ }
+
+ /**
+ * This method handler is invoked during fetching of properties.
+ *
+ * We use this event to add calendar-auto-schedule-specific properties.
+ *
+ * @param PropFind $propFind
+ * @param INode $node
+ * @return void
+ */
+ function propFind(PropFind $propFind, INode $node) {
+
+ if ($node instanceof DAVACL\IPrincipal) {
+
+ $caldavPlugin = $this->server->getPlugin('caldav');
+ $principalUrl = $node->getPrincipalUrl();
+
+ // schedule-outbox-URL property
+ $propFind->handle('{' . self::NS_CALDAV . '}schedule-outbox-URL', function() use ($principalUrl, $caldavPlugin) {
+
+ $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl);
+ if (!$calendarHomePath) {
+ return null;
+ }
+ $outboxPath = $calendarHomePath . '/outbox/';
+
+ return new Href($outboxPath);
+
+ });
+ // schedule-inbox-URL property
+ $propFind->handle('{' . self::NS_CALDAV . '}schedule-inbox-URL', function() use ($principalUrl, $caldavPlugin) {
+
+ $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl);
+ if (!$calendarHomePath) {
+ return null;
+ }
+ $inboxPath = $calendarHomePath . '/inbox/';
+
+ return new Href($inboxPath);
+
+ });
+
+ $propFind->handle('{' . self::NS_CALDAV . '}schedule-default-calendar-URL', function() use ($principalUrl, $caldavPlugin) {
+
+ // We don't support customizing this property yet, so in the
+ // meantime we just grab the first calendar in the home-set.
+ $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl);
+
+ if (!$calendarHomePath) {
+ return null;
+ }
+
+ $sccs = '{' . self::NS_CALDAV . '}supported-calendar-component-set';
+
+ $result = $this->server->getPropertiesForPath($calendarHomePath, [
+ '{DAV:}resourcetype',
+ $sccs,
+ ], 1);
+
+ foreach ($result as $child) {
+ if (!isset($child[200]['{DAV:}resourcetype']) || !$child[200]['{DAV:}resourcetype']->is('{' . self::NS_CALDAV . '}calendar') || $child[200]['{DAV:}resourcetype']->is('{http://calendarserver.org/ns/}shared')) {
+ // Node is either not a calendar or a shared instance.
+ continue;
+ }
+ if (!isset($child[200][$sccs]) || in_array('VEVENT', $child[200][$sccs]->getValue())) {
+ // Either there is no supported-calendar-component-set
+ // (which is fine) or we found one that supports VEVENT.
+ return new Href($child['href']);
+ }
+ }
+
+ });
+
+ // The server currently reports every principal to be of type
+ // 'INDIVIDUAL'
+ $propFind->handle('{' . self::NS_CALDAV . '}calendar-user-type', function() {
+
+ return 'INDIVIDUAL';
+
+ });
+
+ }
+
+ // 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
+ // old property to a different namespace.
+ $availProp = '{' . self::NS_CALDAV . '}calendar-availability';
+ $subPropFind = new PropFind(
+ $propFind->getPath(),
+ [$availProp]
+ );
+
+ $this->server->getPropertiesByNode(
+ $subPropFind,
+ $node
+ );
+
+ $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 PropPatch $propPatch
+ * @return void
+ */
+ 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';
+ $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
+ */
+ function calendarObjectChange(RequestInterface $request, ResponseInterface $response, VCalendar $vCal, $calendarPath, &$modified, $isNew) {
+
+ if (!$this->scheduleReply($this->server->httpRequest)) {
+ return;
+ }
+
+ $calendarNode = $this->server->tree->getNodeForPath($calendarPath);
+
+ $addresses = $this->getAddressesForPrincipal(
+ $calendarNode->getOwner()
+ );
+
+ if (!$isNew) {
+ $node = $this->server->tree->getNodeForPath($request->getPath());
+ $oldObj = Reader::read($node->get());
+ } else {
+ $oldObj = null;
+ }
+
+ $this->processICalendarChange($oldObj, $vCal, $addresses, [], $modified);
+
+ if ($oldObj) {
+ // 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) {
+
+ $this->server->emit('schedule', [$iTipMessage]);
+ if (!$iTipMessage->scheduleStatus) {
+ $iTipMessage->scheduleStatus = '5.2;There was no system capable of delivering the scheduling message';
+ }
+ // In case the change was considered 'insignificant', we are going to
+ // remove any error statuses, if any. See ticket #525.
+ list($baseCode) = explode('.', $iTipMessage->scheduleStatus);
+ if (!$iTipMessage->significantChange && in_array($baseCode, ['3', '5'])) {
+ $iTipMessage->scheduleStatus = null;
+ }
+
+ }
+
+ /**
+ * This method is triggered before a file gets deleted.
+ *
+ * We use this event to make sure that when this happens, attendees get
+ * cancellations, and organizers get 'DECLINED' statuses.
+ *
+ * @param string $path
+ * @return void
+ */
+ function beforeUnbind($path) {
+
+ // FIXME: We shouldn't trigger this functionality when we're issuing a
+ // MOVE. This is a hack.
+ if ($this->server->httpRequest->getMethod() === 'MOVE') return;
+
+ $node = $this->server->tree->getNodeForPath($path);
+
+ if (!$node instanceof ICalendarObject || $node instanceof ISchedulingObject) {
+ return;
+ }
+
+ if (!$this->scheduleReply($this->server->httpRequest)) {
+ return;
+ }
+
+ $addresses = $this->getAddressesForPrincipal(
+ $node->getOwner()
+ );
+
+ $broker = new ITip\Broker();
+ $messages = $broker->parseEvent(null, $addresses, $node->get());
+
+ foreach ($messages as $message) {
+ $this->deliver($message);
+ }
+
+ }
+
+ /**
+ * Event handler for the 'schedule' event.
+ *
+ * This handler attempts to look at local accounts to deliver the
+ * scheduling object.
+ *
+ * @param ITip\Message $iTipMessage
+ * @return void
+ */
+ function scheduleLocalDelivery(ITip\Message $iTipMessage) {
+
+ $aclPlugin = $this->server->getPlugin('acl');
+
+ // Local delivery is not available if the ACL plugin is not loaded.
+ if (!$aclPlugin) {
+ return;
+ }
+
+ $caldavNS = '{' . self::NS_CALDAV . '}';
+
+ $principalUri = $aclPlugin->getPrincipalByUri($iTipMessage->recipient);
+ if (!$principalUri) {
+ $iTipMessage->scheduleStatus = '3.7;Could not find principal.';
+ return;
+ }
+
+ // We found a principal URL, now we need to find its inbox.
+ // Unfortunately we may not have sufficient privileges to find this, so
+ // we are temporarily turning off ACL to let this come through.
+ //
+ // Once we support PHP 5.5, this should be wrapped in a try..finally
+ // block so we can ensure that this privilege gets added again after.
+ $this->server->removeListener('propFind', [$aclPlugin, 'propFind']);
+
+ $result = $this->server->getProperties(
+ $principalUri,
+ [
+ '{DAV:}principal-URL',
+ $caldavNS . 'calendar-home-set',
+ $caldavNS . 'schedule-inbox-URL',
+ $caldavNS . 'schedule-default-calendar-URL',
+ '{http://sabredav.org/ns}email-address',
+ ]
+ );
+
+ // Re-registering the ACL event
+ $this->server->on('propFind', [$aclPlugin, 'propFind'], 20);
+
+ if (!isset($result[$caldavNS . 'schedule-inbox-URL'])) {
+ $iTipMessage->scheduleStatus = '5.2;Could not find local inbox';
+ return;
+ }
+ if (!isset($result[$caldavNS . 'calendar-home-set'])) {
+ $iTipMessage->scheduleStatus = '5.2;Could not locate a calendar-home-set';
+ return;
+ }
+ if (!isset($result[$caldavNS . 'schedule-default-calendar-URL'])) {
+ $iTipMessage->scheduleStatus = '5.2;Could not find a schedule-default-calendar-URL property';
+ return;
+ }
+
+ $calendarPath = $result[$caldavNS . 'schedule-default-calendar-URL']->getHref();
+ $homePath = $result[$caldavNS . 'calendar-home-set']->getHref();
+ $inboxPath = $result[$caldavNS . 'schedule-inbox-URL']->getHref();
+
+ if ($iTipMessage->method === 'REPLY') {
+ $privilege = 'schedule-deliver-reply';
+ } else {
+ $privilege = 'schedule-deliver-invite';
+ }
+
+ if (!$aclPlugin->checkPrivileges($inboxPath, $caldavNS . $privilege, DAVACL\Plugin::R_PARENT, false)) {
+ $iTipMessage->scheduleStatus = '3.8;organizer did not have the ' . $privilege . ' privilege on the attendees inbox';
+ return;
+ }
+
+ // Next, we're going to find out if the item already exits in one of
+ // the users' calendars.
+ $uid = $iTipMessage->uid;
+
+ $newFileName = 'sabredav-' . \Sabre\DAV\UUIDUtil::getUUID() . '.ics';
+
+ $home = $this->server->tree->getNodeForPath($homePath);
+ $inbox = $this->server->tree->getNodeForPath($inboxPath);
+
+ $currentObject = null;
+ $objectNode = null;
+ $isNewNode = false;
+
+ $result = $home->getCalendarObjectByUID($uid);
+ if ($result) {
+ // There was an existing object, we need to update probably.
+ $objectPath = $homePath . '/' . $result;
+ $objectNode = $this->server->tree->getNodeForPath($objectPath);
+ $oldICalendarData = $objectNode->get();
+ $currentObject = Reader::read($oldICalendarData);
+ } else {
+ $isNewNode = true;
+ }
+
+ $broker = new ITip\Broker();
+ $newObject = $broker->processMessage($iTipMessage, $currentObject);
+
+ $inbox->createFile($newFileName, $iTipMessage->message->serialize());
+
+ if (!$newObject) {
+ // We received an iTip message referring to a UID that we don't
+ // have in any calendars yet, and processMessage did not give us a
+ // calendarobject back.
+ //
+ // The implication is that processMessage did not understand the
+ // iTip message.
+ $iTipMessage->scheduleStatus = '5.0;iTip message was not processed by the server, likely because we didn\'t understand it.';
+ return;
+ }
+
+ // Note that we are bypassing ACL on purpose by calling this directly.
+ // We may need to look a bit deeper into this later. Supporting ACL
+ // here would be nice.
+ if ($isNewNode) {
+ $calendar = $this->server->tree->getNodeForPath($calendarPath);
+ $calendar->createFile($newFileName, $newObject->serialize());
+ } else {
+ // If the message was a reply, we may have to inform other
+ // attendees of this attendees status. Therefore we're shooting off
+ // another itipMessage.
+ if ($iTipMessage->method === 'REPLY') {
+ $this->processICalendarChange(
+ $oldICalendarData,
+ $newObject,
+ [$iTipMessage->recipient],
+ [$iTipMessage->sender]
+ );
+ }
+ $objectNode->put($newObject->serialize());
+ }
+ $iTipMessage->scheduleStatus = '1.2;Message delivered locally';
+
+ }
+
+ /**
+ * This method looks at an old iCalendar object, a new iCalendar object and
+ * starts sending scheduling messages based on the changes.
+ *
+ * A list of addresses needs to be specified, so the system knows who made
+ * the update, because the behavior may be different based on if it's an
+ * attendee or an organizer.
+ *
+ * This method may update $newObject to add any status changes.
+ *
+ * @param VCalendar|string $oldObject
+ * @param VCalendar $newObject
+ * @param array $addresses
+ * @param array $ignore Any addresses to not send messages to.
+ * @param bool $modified A marker to indicate that the original object
+ * modified by this process.
+ * @return void
+ */
+ protected function processICalendarChange($oldObject = null, VCalendar $newObject, array $addresses, array $ignore = [], &$modified = false) {
+
+ $broker = new ITip\Broker();
+ $messages = $broker->parseEvent($newObject, $addresses, $oldObject);
+
+ if ($messages) $modified = true;
+
+ foreach ($messages as $message) {
+
+ if (in_array($message->recipient, $ignore)) {
+ continue;
+ }
+
+ $this->deliver($message);
+
+ if (isset($newObject->VEVENT->ORGANIZER) && ($newObject->VEVENT->ORGANIZER->getNormalizedValue() === $message->recipient)) {
+ if ($message->scheduleStatus) {
+ $newObject->VEVENT->ORGANIZER['SCHEDULE-STATUS'] = $message->getScheduleStatus();
+ }
+ unset($newObject->VEVENT->ORGANIZER['SCHEDULE-FORCE-SEND']);
+
+ } else {
+
+ if (isset($newObject->VEVENT->ATTENDEE)) foreach ($newObject->VEVENT->ATTENDEE as $attendee) {
+
+ if ($attendee->getNormalizedValue() === $message->recipient) {
+ if ($message->scheduleStatus) {
+ $attendee['SCHEDULE-STATUS'] = $message->getScheduleStatus();
+ }
+ unset($attendee['SCHEDULE-FORCE-SEND']);
+ break;
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ /**
+ * Returns a list of addresses that are associated with a principal.
+ *
+ * @param string $principal
+ * @return array
+ */
+ protected function getAddressesForPrincipal($principal) {
+
+ $CUAS = '{' . self::NS_CALDAV . '}calendar-user-address-set';
+
+ $properties = $this->server->getProperties(
+ $principal,
+ [$CUAS]
+ );
+
+ // If we can't find this information, we'll stop processing
+ if (!isset($properties[$CUAS])) {
+ return;
+ }
+
+ $addresses = $properties[$CUAS]->getHrefs();
+ return $addresses;
+
+ }
+
+ /**
+ * This method handles POST requests to the schedule-outbox.
+ *
+ * Currently, two types of requests are support:
+ * * FREEBUSY requests from RFC 6638
+ * * Simple iTIP messages from draft-desruisseaux-caldav-sched-04
+ *
+ * The latter is from an expired early draft of the CalDAV scheduling
+ * extensions, but iCal depends on a feature from that spec, so we
+ * implement it.
+ *
+ * @param IOutbox $outboxNode
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ function outboxRequest(IOutbox $outboxNode, RequestInterface $request, ResponseInterface $response) {
+
+ $outboxPath = $request->getPath();
+
+ // Parsing the request body
+ try {
+ $vObject = VObject\Reader::read($request->getBody());
+ } catch (VObject\ParseException $e) {
+ throw new BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage());
+ }
+
+ // The incoming iCalendar object must have a METHOD property, and a
+ // component. The combination of both determines what type of request
+ // this is.
+ $componentType = null;
+ foreach ($vObject->getComponents() as $component) {
+ if ($component->name !== 'VTIMEZONE') {
+ $componentType = $component->name;
+ break;
+ }
+ }
+ if (is_null($componentType)) {
+ throw new BadRequest('We expected at least one VTODO, VJOURNAL, VFREEBUSY or VEVENT component');
+ }
+
+ // Validating the METHOD
+ $method = strtoupper((string)$vObject->METHOD);
+ if (!$method) {
+ throw new BadRequest('A METHOD property must be specified in iTIP messages');
+ }
+
+ // So we support one type of request:
+ //
+ // REQUEST with a VFREEBUSY component
+
+ $acl = $this->server->getPlugin('acl');
+
+ if ($componentType === 'VFREEBUSY' && $method === 'REQUEST') {
+
+ $acl && $acl->checkPrivileges($outboxPath, '{' . self::NS_CALDAV . '}schedule-query-freebusy');
+ $this->handleFreeBusyRequest($outboxNode, $vObject, $request, $response);
+
+ // 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 VObject\Component $vObject
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return string
+ */
+ protected function handleFreeBusyRequest(IOutbox $outbox, VObject\Component $vObject, RequestInterface $request, ResponseInterface $response) {
+
+ $vFreeBusy = $vObject->VFREEBUSY;
+ $organizer = $vFreeBusy->organizer;
+
+ $organizer = (string)$organizer;
+
+ // Validating if the organizer matches the owner of the inbox.
+ $owner = $outbox->getOwner();
+
+ $caldavNS = '{' . self::NS_CALDAV . '}';
+
+ $uas = $caldavNS . 'calendar-user-address-set';
+ $props = $this->server->getProperties($owner, [$uas]);
+
+ if (empty($props[$uas]) || !in_array($organizer, $props[$uas]->getHrefs())) {
+ throw new Forbidden('The organizer in the request did not match any of the addresses for the owner of this inbox');
+ }
+
+ if (!isset($vFreeBusy->ATTENDEE)) {
+ throw new BadRequest('You must at least specify 1 attendee');
+ }
+
+ $attendees = [];
+ foreach ($vFreeBusy->ATTENDEE as $attendee) {
+ $attendees[] = (string)$attendee;
+ }
+
+
+ if (!isset($vFreeBusy->DTSTART) || !isset($vFreeBusy->DTEND)) {
+ throw new BadRequest('DTSTART and DTEND must both be specified');
+ }
+
+ $startRange = $vFreeBusy->DTSTART->getDateTime();
+ $endRange = $vFreeBusy->DTEND->getDateTime();
+
+ $results = [];
+ foreach ($attendees as $attendee) {
+ $results[] = $this->getFreeBusyForEmail($attendee, $startRange, $endRange, $vObject);
+ }
+
+ $dom = new \DOMDocument('1.0', 'utf-8');
+ $dom->formatOutput = true;
+ $scheduleResponse = $dom->createElement('cal:schedule-response');
+ foreach ($this->server->xml->namespaceMap as $namespace => $prefix) {
+
+ $scheduleResponse->setAttribute('xmlns:' . $prefix, $namespace);
+
+ }
+ $dom->appendChild($scheduleResponse);
+
+ foreach ($results as $result) {
+ $xresponse = $dom->createElement('cal:response');
+
+ $recipient = $dom->createElement('cal:recipient');
+ $recipientHref = $dom->createElement('d:href');
+
+ $recipientHref->appendChild($dom->createTextNode($result['href']));
+ $recipient->appendChild($recipientHref);
+ $xresponse->appendChild($recipient);
+
+ $reqStatus = $dom->createElement('cal:request-status');
+ $reqStatus->appendChild($dom->createTextNode($result['request-status']));
+ $xresponse->appendChild($reqStatus);
+
+ if (isset($result['calendar-data'])) {
+
+ $calendardata = $dom->createElement('cal:calendar-data');
+ $calendardata->appendChild($dom->createTextNode(str_replace("\r\n", "\n", $result['calendar-data']->serialize())));
+ $xresponse->appendChild($calendardata);
+
+ }
+ $scheduleResponse->appendChild($xresponse);
+ }
+
+ $response->setStatus(200);
+ $response->setHeader('Content-Type', 'application/xml');
+ $response->setBody($dom->saveXML());
+
+ }
+
+ /**
+ * Returns free-busy information for a specific address. The returned
+ * data is an array containing the following properties:
+ *
+ * calendar-data : A VFREEBUSY VObject
+ * request-status : an iTip status code.
+ * href: The principal's email address, as requested
+ *
+ * The following request status codes may be returned:
+ * * 2.0;description
+ * * 3.7;description
+ *
+ * @param string $email address
+ * @param DateTimeInterface $start
+ * @param DateTimeInterface $end
+ * @param VObject\Component $request
+ * @return array
+ */
+ 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);
+
+ $result = $aclPlugin->principalSearch(
+ ['{http://sabredav.org/ns}email-address' => $email],
+ [
+ '{DAV:}principal-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,
+ ];
+ }
+
+ if (!isset($result[0][200][$caldavNS . 'calendar-home-set'])) {
+ return [
+ 'request-status' => '3.7;No calendar-home-set property found',
+ 'href' => 'mailto:' . $email,
+ ];
+ }
+ if (!isset($result[0][200][$caldavNS . 'schedule-inbox-URL'])) {
+ return [
+ 'request-status' => '3.7;No schedule-inbox-URL property found',
+ 'href' => 'mailto:' . $email,
+ ];
+ }
+ $homeSet = $result[0][200][$caldavNS . 'calendar-home-set']->getHref();
+ $inboxUrl = $result[0][200][$caldavNS . 'schedule-inbox-URL']->getHref();
+
+ // Grabbing the calendar list
+ $objects = [];
+ $calendarTimeZone = new DateTimeZone('UTC');
+
+ foreach ($this->server->tree->getNodeForPath($homeSet)->getChildren() as $node) {
+ if (!$node instanceof ICalendar) {
+ continue;
+ }
+
+ $sct = $caldavNS . 'schedule-calendar-transp';
+ $ctz = $caldavNS . 'calendar-timezone';
+ $props = $node->getProperties([$sct, $ctz]);
+
+ if (isset($props[$sct]) && $props[$sct]->getValue() == ScheduleCalendarTransp::TRANSPARENT) {
+ // If a calendar is marked as 'transparent', it means we must
+ // ignore it for free-busy purposes.
+ continue;
+ }
+
+ $aclPlugin->checkPrivileges($homeSet . $node->getName(), $caldavNS . 'read-free-busy');
+
+ if (isset($props[$ctz])) {
+ $vtimezoneObj = VObject\Reader::read($props[$ctz]);
+ $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
+
+ // 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',
+ 'comp-filters' => [
+ [
+ 'name' => 'VEVENT',
+ 'comp-filters' => [],
+ 'prop-filters' => [],
+ 'is-not-defined' => false,
+ 'time-range' => [
+ 'start' => $start,
+ 'end' => $end,
+ ],
+ ],
+ ],
+ 'prop-filters' => [],
+ 'is-not-defined' => false,
+ 'time-range' => null,
+ ]);
+
+ $calObjects = array_map(function($url) use ($node) {
+ $obj = $node->getChild($url)->get();
+ return $obj;
+ }, $urls);
+
+ $objects = array_merge($objects, $calObjects);
+
+ }
+
+ $inboxProps = $this->server->getProperties(
+ $inboxUrl,
+ $caldavNS . 'calendar-availability'
+ );
+
+ $vcalendar = new VObject\Component\VCalendar();
+ $vcalendar->METHOD = 'REPLY';
+
+ $generator = new VObject\FreeBusyGenerator();
+ $generator->setObjects($objects);
+ $generator->setTimeRange($start, $end);
+ $generator->setBaseObject($vcalendar);
+ $generator->setTimeZone($calendarTimeZone);
+
+ if ($inboxProps) {
+ $generator->setVAvailability(
+ VObject\Reader::read(
+ $inboxProps[$caldavNS . 'calendar-availability']
+ )
+ );
+ }
+
+ $result = $generator->getResult();
+
+ $vcalendar->VFREEBUSY->ATTENDEE = 'mailto:' . $email;
+ $vcalendar->VFREEBUSY->UID = (string)$request->VFREEBUSY->UID;
+ $vcalendar->VFREEBUSY->ORGANIZER = clone $request->VFREEBUSY->ORGANIZER;
+
+ return [
+ 'calendar-data' => $result,
+ 'request-status' => '2.0;Success',
+ 'href' => 'mailto:' . $email,
+ ];
+ }
+
+ /**
+ * This method checks the 'Schedule-Reply' header
+ * and returns false if it's 'F', otherwise true.
+ *
+ * @param RequestInterface $request
+ * @return bool
+ */
+ private function scheduleReply(RequestInterface $request) {
+
+ $scheduleReply = $request->getHeader('Schedule-Reply');
+ return $scheduleReply !== 'F';
+
+ }
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => 'Adds calendar-auto-schedule, as defined in rf6868',
+ 'link' => 'http://sabre.io/dav/scheduling/',
+ ];
+
+ }
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Schedule/SchedulingObject.php b/vendor/sabre/dav/lib/CalDAV/Schedule/SchedulingObject.php
new file mode 100644
index 000000000..a36646e6c
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Schedule/SchedulingObject.php
@@ -0,0 +1,165 @@
+<?php
+
+namespace Sabre\CalDAV\Schedule;
+
+use Sabre\CalDAV\Backend;
+use Sabre\DAV\Exception\MethodNotAllowed;
+
+/**
+ * The SchedulingObject represents a scheduling object in the Inbox collection
+ *
+ * @author Brett (https://github.com/bretten)
+ * @license http://sabre.io/license/ Modified BSD License
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ */
+class SchedulingObject extends \Sabre\CalDAV\CalendarObject implements ISchedulingObject {
+
+ /**
+ /* The CalDAV backend
+ *
+ * @var Backend\SchedulingSupport
+ */
+ protected $caldavBackend;
+
+ /**
+ * Array with information about this SchedulingObject
+ *
+ * @var array
+ */
+ protected $objectData;
+
+ /**
+ * Constructor
+ *
+ * The following properties may be passed within $objectData:
+ *
+ * * uri - A unique uri. Only the 'basename' must be passed.
+ * * principaluri - the principal that owns the object.
+ * * calendardata (optional) - The iCalendar data
+ * * etag - (optional) The etag for this object, MUST be encloded with
+ * double-quotes.
+ * * size - (optional) The size of the data in bytes.
+ * * lastmodified - (optional) format as a unix timestamp.
+ * * acl - (optional) Use this to override the default ACL for the node.
+ *
+ * @param Backend\BackendInterface $caldavBackend
+ * @param array $objectData
+ */
+ function __construct(Backend\SchedulingSupport $caldavBackend, array $objectData) {
+
+ $this->caldavBackend = $caldavBackend;
+
+ if (!isset($objectData['uri'])) {
+ throw new \InvalidArgumentException('The objectData argument must contain an \'uri\' property');
+ }
+
+ $this->objectData = $objectData;
+
+ }
+
+ /**
+ * Returns the ICalendar-formatted object
+ *
+ * @return string
+ */
+ function get() {
+
+ // Pre-populating the 'calendardata' is optional, if we don't have it
+ // already we fetch it from the backend.
+ if (!isset($this->objectData['calendardata'])) {
+ $this->objectData = $this->caldavBackend->getSchedulingObject($this->objectData['principaluri'], $this->objectData['uri']);
+ }
+ return $this->objectData['calendardata'];
+
+ }
+
+ /**
+ * Updates the ICalendar-formatted object
+ *
+ * @param string|resource $calendarData
+ * @return string
+ */
+ function put($calendarData) {
+
+ throw new MethodNotAllowed('Updating scheduling objects is not supported');
+
+ }
+
+ /**
+ * Deletes the scheduling message
+ *
+ * @return void
+ */
+ function delete() {
+
+ $this->caldavBackend->deleteSchedulingObject($this->objectData['principaluri'], $this->objectData['uri']);
+
+ }
+
+ /**
+ * Returns the owner principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getOwner() {
+
+ return $this->objectData['principaluri'];
+
+ }
+
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+
+ // An alternative acl may be specified in the object data.
+ //
+
+ if (isset($this->objectData['acl'])) {
+ return $this->objectData['acl'];
+ }
+
+ // The default ACL
+ return [
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->objectData['principaluri'],
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->objectData['principaluri'],
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->objectData['principaluri'] . '/calendar-proxy-write',
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->objectData['principaluri'] . '/calendar-proxy-write',
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->objectData['principaluri'] . '/calendar-proxy-read',
+ 'protected' => true,
+ ],
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/ShareableCalendar.php b/vendor/sabre/dav/lib/CalDAV/ShareableCalendar.php
index cabf7eb95..c11695d5f 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/ShareableCalendar.php
+++ b/vendor/sabre/dav/lib/CalDAV/ShareableCalendar.php
@@ -6,7 +6,7 @@ namespace Sabre\CalDAV;
* This object represents a CalDAV calendar that can be shared with other
* users.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -30,7 +30,7 @@ class ShareableCalendar extends Calendar implements IShareableCalendar {
* @param array $remove
* @return void
*/
- public function updateShares(array $add, array $remove) {
+ function updateShares(array $add, array $remove) {
$this->caldavBackend->updateShares($this->calendarInfo['id'], $add, $remove);
@@ -48,7 +48,7 @@ class ShareableCalendar extends Calendar implements IShareableCalendar {
*
* @return array
*/
- public function getShares() {
+ function getShares() {
return $this->caldavBackend->getShares($this->calendarInfo['id']);
@@ -63,7 +63,7 @@ class ShareableCalendar extends Calendar implements IShareableCalendar {
* @param bool $value
* @return void
*/
- public function setPublishStatus($value) {
+ function setPublishStatus($value) {
$this->caldavBackend->setPublishStatus($this->calendarInfo['id'], $value);
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/SharedCalendar.php b/vendor/sabre/dav/lib/CalDAV/SharedCalendar.php
index 79eda43ab..7973eff9c 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/SharedCalendar.php
+++ b/vendor/sabre/dav/lib/CalDAV/SharedCalendar.php
@@ -2,12 +2,10 @@
namespace Sabre\CalDAV;
-use Sabre\DAVACL;
-
/**
* This object represents a CalDAV calendar that is shared by a different user.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -19,14 +17,14 @@ class SharedCalendar extends Calendar implements ISharedCalendar {
* @param Backend\BackendInterface $caldavBackend
* @param array $calendarInfo
*/
- public function __construct(Backend\BackendInterface $caldavBackend, $calendarInfo) {
+ function __construct(Backend\BackendInterface $caldavBackend, $calendarInfo) {
- $required = array(
+ $required = [
'{http://calendarserver.org/ns/}shared-url',
'{http://sabredav.org/ns}owner-principal',
'{http://sabredav.org/ns}read-only',
- );
- foreach($required as $r) {
+ ];
+ foreach ($required as $r) {
if (!isset($calendarInfo[$r])) {
throw new \InvalidArgumentException('The ' . $r . ' property must be specified for SharedCalendar(s)');
}
@@ -42,7 +40,7 @@ class SharedCalendar extends Calendar implements ISharedCalendar {
*
* @return string
*/
- public function getSharedUrl() {
+ function getSharedUrl() {
return $this->calendarInfo['{http://calendarserver.org/ns/}shared-url'];
@@ -55,7 +53,7 @@ class SharedCalendar extends Calendar implements ISharedCalendar {
*
* @return string|null
*/
- public function getOwner() {
+ function getOwner() {
return $this->calendarInfo['{http://sabredav.org/ns}owner-principal'];
@@ -73,28 +71,62 @@ class SharedCalendar extends Calendar implements ISharedCalendar {
*
* @return array
*/
- public function getACL() {
+ function getACL() {
// The top-level ACL only contains access information for the true
// owner of the calendar, so we need to add the information for the
// sharee.
$acl = parent::getACL();
- $acl[] = array(
+ $acl[] = [
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'],
'protected' => true,
- );
+ ];
+ if ($this->calendarInfo['{http://sabredav.org/ns}read-only']) {
+ $acl[] = [
+ 'privilege' => '{DAV:}write-properties',
+ 'principal' => $this->calendarInfo['principaluri'],
+ 'protected' => true,
+ ];
+ } else {
+ $acl[] = [
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->calendarInfo['principaluri'],
+ 'protected' => true,
+ ];
+ }
+ return $acl;
+
+ }
+
+ /**
+ * This method returns the ACL's for calendar objects in this calendar.
+ * The result of this method automatically gets passed to the
+ * calendar-object nodes in the calendar.
+ *
+ * @return array
+ */
+ function getChildACL() {
+
+ $acl = parent::getChildACL();
+ $acl[] = [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->calendarInfo['principaluri'],
+ 'protected' => true,
+ ];
+
if (!$this->calendarInfo['{http://sabredav.org/ns}read-only']) {
- $acl[] = array(
+ $acl[] = [
'privilege' => '{DAV:}write',
'principal' => $this->calendarInfo['principaluri'],
'protected' => true,
- );
+ ];
}
return $acl;
}
+
/**
* Returns the list of people whom this calendar is shared with.
*
@@ -107,7 +139,7 @@ class SharedCalendar extends Calendar implements ISharedCalendar {
*
* @return array
*/
- public function getShares() {
+ function getShares() {
return $this->caldavBackend->getShares($this->calendarInfo['id']);
diff --git a/vendor/sabre/dav/lib/CalDAV/SharingPlugin.php b/vendor/sabre/dav/lib/CalDAV/SharingPlugin.php
new file mode 100644
index 000000000..5154fb1de
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/SharingPlugin.php
@@ -0,0 +1,426 @@
+<?php
+
+namespace Sabre\CalDAV;
+
+use Sabre\DAV;
+use Sabre\DAV\Xml\Property\Href;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * This plugin implements support for caldav sharing.
+ *
+ * This spec is defined at:
+ * http://svn.calendarserver.org/repository/calendarserver/CalendarServer/trunk/doc/Extensions/caldav-sharing.txt
+ *
+ * See:
+ * Sabre\CalDAV\Backend\SharingSupport for all the documentation.
+ *
+ * Note: This feature is experimental, and may change in between different
+ * SabreDAV versions.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class SharingPlugin extends DAV\ServerPlugin {
+
+ /**
+ * These are the various status constants used by sharing-messages.
+ */
+ const STATUS_ACCEPTED = 1;
+ const STATUS_DECLINED = 2;
+ const STATUS_DELETED = 3;
+ const STATUS_NORESPONSE = 4;
+ const STATUS_INVALID = 5;
+
+ /**
+ * Reference to SabreDAV server object.
+ *
+ * @var Sabre\DAV\Server
+ */
+ protected $server;
+
+ /**
+ * This method should return a list of server-features.
+ *
+ * This is for example 'versioning' and is added to the DAV: header
+ * in an OPTIONS response.
+ *
+ * @return array
+ */
+ function getFeatures() {
+
+ return ['calendarserver-sharing'];
+
+ }
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using Sabre\DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'caldav-sharing';
+
+ }
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by Sabre\DAV\Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ *
+ * @param DAV\Server $server
+ * @return void
+ */
+ function initialize(DAV\Server $server) {
+
+ $this->server = $server;
+ $server->resourceTypeMapping['Sabre\\CalDAV\\ISharedCalendar'] = '{' . Plugin::NS_CALENDARSERVER . '}shared';
+
+ array_push(
+ $this->server->protectedProperties,
+ '{' . Plugin::NS_CALENDARSERVER . '}invite',
+ '{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes',
+ '{' . Plugin::NS_CALENDARSERVER . '}shared-url'
+ );
+
+ $this->server->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 event is triggered when properties are requested for a certain
+ * node.
+ *
+ * This allows us to inject any properties early.
+ *
+ * @param DAV\PropFind $propFind
+ * @param DAV\INode $node
+ * @return void
+ */
+ function propFindEarly(DAV\PropFind $propFind, DAV\INode $node) {
+
+ if ($node instanceof IShareableCalendar) {
+
+ $propFind->handle('{' . Plugin::NS_CALENDARSERVER . '}invite', function() use ($node) {
+ return new Xml\Property\Invite(
+ $node->getShares()
+ );
+ });
+
+ }
+
+ if ($node instanceof ISharedCalendar) {
+
+ $propFind->handle('{' . Plugin::NS_CALENDARSERVER . '}shared-url', function() use ($node) {
+ return new Href(
+ $node->getSharedUrl()
+ );
+ });
+
+ $propFind->handle('{' . Plugin::NS_CALENDARSERVER . '}invite', function() use ($node) {
+
+ // Fetching owner information
+ $props = $this->server->getPropertiesForPath($node->getOwner(), [
+ '{http://sabredav.org/ns}email-address',
+ '{DAV:}displayname',
+ ], 0);
+
+ $ownerInfo = [
+ 'href' => $node->getOwner(),
+ ];
+
+ if (isset($props[0][200])) {
+
+ // We're mapping the internal webdav properties to the
+ // elements caldav-sharing expects.
+ if (isset($props[0][200]['{http://sabredav.org/ns}email-address'])) {
+ $ownerInfo['href'] = 'mailto:' . $props[0][200]['{http://sabredav.org/ns}email-address'];
+ }
+ if (isset($props[0][200]['{DAV:}displayname'])) {
+ $ownerInfo['commonName'] = $props[0][200]['{DAV:}displayname'];
+ }
+
+ }
+
+ return new Xml\Property\Invite(
+ $node->getShares(),
+ $ownerInfo
+ );
+
+ });
+
+ }
+
+ }
+
+ /**
+ * This method is triggered *after* all properties have been retrieved.
+ * This allows us to inject the correct resourcetype for calendars that
+ * have been shared.
+ *
+ * @param DAV\PropFind $propFind
+ * @param DAV\INode $node
+ * @return void
+ */
+ function propFindLate(DAV\PropFind $propFind, DAV\INode $node) {
+
+ if ($node instanceof IShareableCalendar) {
+ if ($rt = $propFind->get('{DAV:}resourcetype')) {
+ if (count($node->getShares()) > 0) {
+ $rt->add('{' . Plugin::NS_CALENDARSERVER . '}shared-owner');
+ }
+ }
+ $propFind->handle('{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes', function() {
+ return new Xml\Property\AllowedSharingModes(true, false);
+ });
+
+ }
+
+ }
+
+ /**
+ * This method is trigged when a user attempts to update a node's
+ * properties.
+ *
+ * A previous draft of the sharing spec stated that it was possible to use
+ * PROPPATCH to remove 'shared-owner' from the resourcetype, thus unsharing
+ * the calendar.
+ *
+ * Even though this is no longer in the current spec, we keep this around
+ * because OS X 10.7 may still make use of this feature.
+ *
+ * @param string $path
+ * @param DAV\PropPatch $propPatch
+ * @return void
+ */
+ function propPatch($path, DAV\PropPatch $propPatch) {
+
+ $node = $this->server->tree->getNodeForPath($path);
+ if (!$node instanceof IShareableCalendar)
+ return;
+
+ $propPatch->handle('{DAV:}resourcetype', function($value) use ($node) {
+ if ($value->is('{' . Plugin::NS_CALENDARSERVER . '}shared-owner')) return false;
+ $shares = $node->getShares();
+ $remove = [];
+ foreach ($shares as $share) {
+ $remove[] = $share['href'];
+ }
+ $node->updateShares([], $remove);
+
+ return true;
+
+ });
+
+ }
+
+ /**
+ * We intercept this to handle POST requests on calendars.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return null|bool
+ */
+ function httpPost(RequestInterface $request, ResponseInterface $response) {
+
+ $path = $request->getPath();
+
+ // Only handling xml
+ $contentType = $request->getHeader('Content-Type');
+ if (strpos($contentType, 'application/xml') === false && strpos($contentType, 'text/xml') === false)
+ return;
+
+ // Making sure the node exists
+ try {
+ $node = $this->server->tree->getNodeForPath($path);
+ } catch (DAV\Exception\NotFound $e) {
+ return;
+ }
+
+ $requestBody = $request->getBodyAsString();
+
+ // If this request handler could not deal with this POST request, it
+ // will return 'null' and other plugins get a chance to handle the
+ // request.
+ //
+ // However, we already requested the full body. This is a problem,
+ // because a body can only be read once. This is why we preemptively
+ // re-populated the request body with the existing data.
+ $request->setBody($requestBody);
+
+ $message = $this->server->xml->parse($requestBody, $request->getUrl(), $documentType);
+
+ switch ($documentType) {
+
+ // Dealing with the 'share' document, which modified invitees on a
+ // calendar.
+ case '{' . Plugin::NS_CALENDARSERVER . '}share' :
+
+ // We can only deal with IShareableCalendar objects
+ if (!$node instanceof IShareableCalendar) {
+ return;
+ }
+
+ $this->server->transactionType = 'post-calendar-share';
+
+ // Getting ACL info
+ $acl = $this->server->getPlugin('acl');
+
+ // If there's no ACL support, we allow everything
+ if ($acl) {
+ $acl->checkPrivileges($path, '{DAV:}write');
+ }
+
+ $node->updateShares($message->set, $message->remove);
+
+ $response->setStatus(200);
+ // Adding this because sending a response body may cause issues,
+ // and I wanted some type of indicator the response was handled.
+ $response->setHeader('X-Sabre-Status', 'everything-went-well');
+
+ // Breaking the event chain
+ return false;
+
+ // The invite-reply document is sent when the user replies to an
+ // invitation of a calendar share.
+ case '{' . Plugin::NS_CALENDARSERVER . '}invite-reply' :
+
+ // This only works on the calendar-home-root node.
+ if (!$node instanceof CalendarHome) {
+ return;
+ }
+ $this->server->transactionType = 'post-invite-reply';
+
+ // Getting ACL info
+ $acl = $this->server->getPlugin('acl');
+
+ // If there's no ACL support, we allow everything
+ if ($acl) {
+ $acl->checkPrivileges($path, '{DAV:}write');
+ }
+
+ $url = $node->shareReply(
+ $message->href,
+ $message->status,
+ $message->calendarUri,
+ $message->inReplyTo,
+ $message->summary
+ );
+
+ $response->setStatus(200);
+ // Adding this because sending a response body may cause issues,
+ // and I wanted some type of indicator the response was handled.
+ $response->setHeader('X-Sabre-Status', 'everything-went-well');
+
+ if ($url) {
+ $writer = $this->server->xml->getWriter($this->server->getBaseUri());
+ $writer->openMemory();
+ $writer->startDocument();
+ $writer->startElement('{' . Plugin::NS_CALENDARSERVER . '}shared-as');
+ $writer->write(new Href($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' :
+
+ // We can only deal with IShareableCalendar objects
+ if (!$node instanceof IShareableCalendar) {
+ return;
+ }
+ $this->server->transactionType = 'post-publish-calendar';
+
+ // Getting ACL info
+ $acl = $this->server->getPlugin('acl');
+
+ // If there's no ACL support, we allow everything
+ if ($acl) {
+ $acl->checkPrivileges($path, '{DAV:}write');
+ }
+
+ $node->setPublishStatus(true);
+
+ // iCloud sends back the 202, so we will too.
+ $response->setStatus(202);
+
+ // Adding this because sending a response body may cause issues,
+ // and I wanted some type of indicator the response was handled.
+ $response->setHeader('X-Sabre-Status', 'everything-went-well');
+
+ // Breaking the event chain
+ return false;
+
+ case '{' . Plugin::NS_CALENDARSERVER . '}unpublish-calendar' :
+
+ // We can only deal with IShareableCalendar objects
+ if (!$node instanceof IShareableCalendar) {
+ return;
+ }
+ $this->server->transactionType = 'post-unpublish-calendar';
+
+ // Getting ACL info
+ $acl = $this->server->getPlugin('acl');
+
+ // If there's no ACL support, we allow everything
+ if ($acl) {
+ $acl->checkPrivileges($path, '{DAV:}write');
+ }
+
+ $node->setPublishStatus(false);
+
+ $response->setStatus(200);
+
+ // Adding this because sending a response body may cause issues,
+ // and I wanted some type of indicator the response was handled.
+ $response->setHeader('X-Sabre-Status', 'everything-went-well');
+
+ // Breaking the event chain
+ return false;
+
+ }
+
+
+
+ }
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => 'Adds support for caldav-sharing.',
+ 'link' => 'http://sabre.io/dav/caldav-sharing/',
+ ];
+
+ }
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Subscriptions/ISubscription.php b/vendor/sabre/dav/lib/CalDAV/Subscriptions/ISubscription.php
new file mode 100644
index 000000000..7ba259c7b
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Subscriptions/ISubscription.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Sabre\CalDAV\Subscriptions;
+
+use Sabre\DAV\ICollection;
+use Sabre\DAV\IProperties;
+
+/**
+ * ISubscription
+ *
+ * Nodes implementing this interface represent calendar subscriptions.
+ *
+ * The subscription node doesn't do much, other than returning and updating
+ * subscription-related properties.
+ *
+ * The following properties should be supported:
+ *
+ * 1. {DAV:}displayname
+ * 2. {http://apple.com/ns/ical/}refreshrate
+ * 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos
+ * should not be stripped).
+ * 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms
+ * should not be stripped).
+ * 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if
+ * attachments should not be stripped).
+ * 6. {http://calendarserver.org/ns/}source (Must be a
+ * Sabre\DAV\Property\Href).
+ * 7. {http://apple.com/ns/ical/}calendar-color
+ * 8. {http://apple.com/ns/ical/}calendar-order
+ *
+ * It is recommended to support every property.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface ISubscription extends ICollection, IProperties {
+
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Subscriptions/Plugin.php b/vendor/sabre/dav/lib/CalDAV/Subscriptions/Plugin.php
new file mode 100644
index 000000000..7abbfb1f9
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Subscriptions/Plugin.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace Sabre\CalDAV\Subscriptions;
+
+use Sabre\DAV\INode;
+use Sabre\DAV\PropFind;
+use Sabre\DAV\ServerPlugin;
+use Sabre\DAV\Server;
+
+/**
+ * This plugin adds calendar-subscription support to your CalDAV server.
+ *
+ * Some clients support 'managed subscriptions' server-side. This is basically
+ * a list of subscription urls a user is using.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Plugin extends ServerPlugin {
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by Sabre\DAV\Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ *
+ * @param Server $server
+ * @return void
+ */
+ function initialize(Server $server) {
+
+ $server->resourceTypeMapping['Sabre\\CalDAV\\Subscriptions\\ISubscription'] =
+ '{http://calendarserver.org/ns/}subscribed';
+
+ $server->xml->elementMap['{http://calendarserver.org/ns/}source'] =
+ 'Sabre\\DAV\\Xml\\Property\\Href';
+
+ $server->on('propFind', [$this, 'propFind'], 150);
+
+ }
+
+ /**
+ * This method should return a list of server-features.
+ *
+ * This is for example 'versioning' and is added to the DAV: header
+ * in an OPTIONS response.
+ *
+ * @return array
+ */
+ function getFeatures() {
+
+ return ['calendarserver-subscribed'];
+
+ }
+
+ /**
+ * Triggered after properties have been fetched.
+ *
+ * @param PropFind $propFind
+ * @param INode $node
+ * @return void
+ */
+ function propFind(PropFind $propFind, INode $node) {
+
+ // There's a bunch of properties that must appear as a self-closing
+ // xml-element. This event handler ensures that this will be the case.
+ $props = [
+ '{http://calendarserver.org/ns/}subscribed-strip-alarms',
+ '{http://calendarserver.org/ns/}subscribed-strip-attachments',
+ '{http://calendarserver.org/ns/}subscribed-strip-todos',
+ ];
+
+ foreach ($props as $prop) {
+
+ if ($propFind->getStatus($prop) === 200) {
+ $propFind->set($prop, '', 200);
+ }
+
+ }
+
+ }
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using \Sabre\DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'subscriptions';
+
+ }
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => 'This plugin allows users to store iCalendar subscriptions in their calendar-home.',
+ 'link' => null,
+ ];
+
+ }
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Subscriptions/Subscription.php b/vendor/sabre/dav/lib/CalDAV/Subscriptions/Subscription.php
new file mode 100644
index 000000000..ee53da2c6
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Subscriptions/Subscription.php
@@ -0,0 +1,274 @@
+<?php
+
+namespace Sabre\CalDAV\Subscriptions;
+
+use Sabre\DAV\Collection;
+use Sabre\DAV\Xml\Property\Href;
+use Sabre\DAV\PropPatch;
+use Sabre\DAV\Exception\MethodNotAllowed;
+use Sabre\DAVACL\IACL;
+use Sabre\CalDAV\Backend\SubscriptionSupport;
+
+/**
+ * Subscription Node
+ *
+ * This node represents a subscription.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Subscription extends Collection implements ISubscription, IACL {
+
+ /**
+ * caldavBackend
+ *
+ * @var SupportsSubscriptions
+ */
+ protected $caldavBackend;
+
+ /**
+ * subscriptionInfo
+ *
+ * @var array
+ */
+ protected $subscriptionInfo;
+
+ /**
+ * Constructor
+ *
+ * @param SubscriptionSupport $caldavBackend
+ * @param array $calendarInfo
+ */
+ function __construct(SubscriptionSupport $caldavBackend, array $subscriptionInfo) {
+
+ $this->caldavBackend = $caldavBackend;
+ $this->subscriptionInfo = $subscriptionInfo;
+
+ $required = [
+ 'id',
+ 'uri',
+ 'principaluri',
+ 'source',
+ ];
+
+ foreach ($required as $r) {
+ if (!isset($subscriptionInfo[$r])) {
+ throw new \InvalidArgumentException('The ' . $r . ' field is required when creating a subscription node');
+ }
+ }
+
+ }
+
+ /**
+ * Returns the name of the node.
+ *
+ * This is used to generate the url.
+ *
+ * @return string
+ */
+ function getName() {
+
+ return $this->subscriptionInfo['uri'];
+
+ }
+
+ /**
+ * Returns the last modification time
+ *
+ * @return int
+ */
+ function getLastModified() {
+
+ if (isset($this->subscriptionInfo['lastmodified'])) {
+ return $this->subscriptionInfo['lastmodified'];
+ }
+
+ }
+
+ /**
+ * Deletes the current node
+ *
+ * @return void
+ */
+ function delete() {
+
+ $this->caldavBackend->deleteSubscription(
+ $this->subscriptionInfo['id']
+ );
+
+ }
+
+ /**
+ * Returns an array with all the child nodes
+ *
+ * @return DAV\INode[]
+ */
+ function getChildren() {
+
+ return [];
+
+ }
+
+ /**
+ * Updates properties on this node.
+ *
+ * This method received a PropPatch object, which contains all the
+ * information about the update.
+ *
+ * To update specific properties, call the 'handle' method on this object.
+ * Read the PropPatch documentation for more information.
+ *
+ * @param PropPatch $propPatch
+ * @return void
+ */
+ function propPatch(PropPatch $propPatch) {
+
+ return $this->caldavBackend->updateSubscription(
+ $this->subscriptionInfo['id'],
+ $propPatch
+ );
+
+ }
+
+ /**
+ * Returns a list of properties for this nodes.
+ *
+ * The properties list is a list of propertynames the client requested,
+ * encoded in clark-notation {xmlnamespace}tagname.
+ *
+ * If the array is empty, it means 'all properties' were requested.
+ *
+ * Note that it's fine to liberally give properties back, instead of
+ * conforming to the list of requested properties.
+ * The Server class will filter out the extra.
+ *
+ * @param array $properties
+ * @return void
+ */
+ function getProperties($properties) {
+
+ $r = [];
+
+ foreach ($properties as $prop) {
+
+ switch ($prop) {
+ case '{http://calendarserver.org/ns/}source' :
+ $r[$prop] = new Href($this->subscriptionInfo['source'], false);
+ break;
+ default :
+ if (array_key_exists($prop, $this->subscriptionInfo)) {
+ $r[$prop] = $this->subscriptionInfo[$prop];
+ }
+ break;
+ }
+
+ }
+
+ return $r;
+
+ }
+
+ /**
+ * Returns the owner principal.
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getOwner() {
+
+ return $this->subscriptionInfo['principaluri'];
+
+ }
+
+ /**
+ * Returns a group principal.
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getGroup() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+
+ return [
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->getOwner(),
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->getOwner(),
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->getOwner() . '/calendar-proxy-write',
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->getOwner() . '/calendar-proxy-write',
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->getOwner() . '/calendar-proxy-read',
+ 'protected' => true,
+ ]
+ ];
+
+ }
+
+ /**
+ * Updates the ACL.
+ *
+ * This method will receive a list of new ACE's.
+ *
+ * @param array $acl
+ * @return void
+ */
+ function setACL(array $acl) {
+
+ throw new MethodNotAllowed('Changing ACL is not yet supported');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See \Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Xml/Filter/CalendarData.php b/vendor/sabre/dav/lib/CalDAV/Xml/Filter/CalendarData.php
new file mode 100644
index 000000000..9babcf15c
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Filter/CalendarData.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Filter;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\CalDAV\Plugin;
+use Sabre\VObject\DateTimeParser;
+
+/**
+ * CalendarData parser.
+ *
+ * This class parses the {urn:ietf:params:xml:ns:caldav}calendar-data XML
+ * element, as defined in:
+ *
+ * https://tools.ietf.org/html/rfc4791#section-9.6
+ *
+ * This element is used in three distinct places in the caldav spec, but in
+ * this case, this element class only implements the calendar-data element as
+ * it appears in a DAV:prop element, in a calendar-query or calendar-multiget
+ * REPORT request.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class CalendarData implements XmlDeserializable {
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $result = [
+ 'contentType' => $reader->getAttribute('content-type') ?: 'text/calendar',
+ 'version' => $reader->getAttribute('version') ?: '2.0',
+ ];
+
+ $elems = (array)$reader->parseInnerTree();
+ foreach ($elems as $elem) {
+
+ switch ($elem['name']) {
+ 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,
+ ];
+
+ if (!$result['expand']['start'] || !$result['expand']['end']) {
+ throw new BadRequest('The "start" and "end" attributes are required when expanding calendar-data');
+ }
+ if ($result['expand']['end'] <= $result['expand']['start']) {
+ throw new BadRequest('The end-date must be larger than the start-date when expanding calendar-data');
+ }
+ break;
+ }
+
+ }
+
+ return $result;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Xml/Filter/CompFilter.php b/vendor/sabre/dav/lib/CalDAV/Xml/Filter/CompFilter.php
new file mode 100644
index 000000000..c9b27dbfd
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Filter/CompFilter.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Filter;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\CalDAV\Plugin;
+use Sabre\VObject\DateTimeParser;
+
+/**
+ * CompFilter parser.
+ *
+ * This class parses the {urn:ietf:params:xml:ns:caldav}comp-filter XML
+ * element, as defined in:
+ *
+ * https://tools.ietf.org/html/rfc4791#section-9.6
+ *
+ * The result will be spit out as an array.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class CompFilter implements XmlDeserializable {
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $result = [
+ 'name' => null,
+ 'is-not-defined' => false,
+ 'comp-filters' => [],
+ 'prop-filters' => [],
+ 'time-range' => false,
+ ];
+
+ $att = $reader->parseAttributes();
+ $result['name'] = $att['name'];
+
+ $elems = $reader->parseInnerTree();
+
+ 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' :
+ $result['prop-filters'][] = $elem['value'];
+ break;
+ case '{' . Plugin::NS_CALDAV . '}is-not-defined' :
+ $result['is-not-defined'] = true;
+ break;
+ case '{' . Plugin::NS_CALDAV . '}time-range' :
+ if ($result['name'] === 'VCALENDAR') {
+ 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,
+ ];
+ 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/vendor/sabre/dav/lib/CalDAV/Xml/Filter/ParamFilter.php b/vendor/sabre/dav/lib/CalDAV/Xml/Filter/ParamFilter.php
new file mode 100644
index 000000000..eb7f564df
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Filter/ParamFilter.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Filter;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+use Sabre\CalDAV\Plugin;
+
+/**
+ * PropFilter parser.
+ *
+ * This class parses the {urn:ietf:params:xml:ns:caldav}param-filter XML
+ * element, as defined in:
+ *
+ * https://tools.ietf.org/html/rfc4791#section-9.7.3
+ *
+ * The result will be spit out as an array.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ParamFilter implements XmlDeserializable {
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * Important note 2: You are responsible for advancing the reader to the
+ * next element. Not doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $result = [
+ 'name' => null,
+ 'is-not-defined' => false,
+ 'text-match' => null,
+ ];
+
+ $att = $reader->parseAttributes();
+ $result['name'] = $att['name'];
+
+ $elems = $reader->parseInnerTree();
+
+ 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' :
+ $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'],
+ ];
+ break;
+
+ }
+
+ }
+
+ return $result;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Xml/Filter/PropFilter.php b/vendor/sabre/dav/lib/CalDAV/Xml/Filter/PropFilter.php
new file mode 100644
index 000000000..4c2e1b172
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Filter/PropFilter.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Filter;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\CalDAV\Plugin;
+use Sabre\VObject\DateTimeParser;
+
+/**
+ * PropFilter parser.
+ *
+ * This class parses the {urn:ietf:params:xml:ns:caldav}prop-filter XML
+ * element, as defined in:
+ *
+ * https://tools.ietf.org/html/rfc4791#section-9.7.2
+ *
+ * The result will be spit out as an array.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PropFilter implements XmlDeserializable {
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $result = [
+ 'name' => null,
+ 'is-not-defined' => false,
+ 'param-filters' => [],
+ 'text-match' => null,
+ 'time-range' => false,
+ ];
+
+ $att = $reader->parseAttributes();
+ $result['name'] = $att['name'];
+
+ $elems = $reader->parseInnerTree();
+
+ 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' :
+ $result['is-not-defined'] = true;
+ break;
+ 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,
+ ];
+ 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' :
+ $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'],
+ ];
+ break;
+
+ }
+
+ }
+
+ return $result;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/Notification/Invite.php b/vendor/sabre/dav/lib/CalDAV/Xml/Notification/Invite.php
index 8d6974d45..7fb022e33 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/Notification/Invite.php
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Notification/Invite.php
@@ -1,19 +1,22 @@
<?php
-namespace Sabre\CalDAV\Notifications\Notification;
+namespace Sabre\CalDAV\Xml\Notification;
+use Sabre\Xml\Writer;
use Sabre\CalDAV\SharingPlugin as SharingPlugin;
-use Sabre\DAV;
use Sabre\CalDAV;
/**
* This class represents the cs:invite-notification notification element.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * This element is defined here:
+ * http://svn.calendarserver.org/repository/calendarserver/CalendarServer/trunk/doc/Extensions/caldav-sharing.txt
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class Invite extends DAV\Property implements CalDAV\Notifications\INotificationType {
+class Invite implements NotificationInterface {
/**
* A unique id for the message
@@ -134,9 +137,9 @@ class Invite extends DAV\Property implements CalDAV\Notifications\INotificationT
*
* @param array $values All the options
*/
- public function __construct(array $values) {
+ function __construct(array $values) {
- $required = array(
+ $required = [
'id',
'etag',
'href',
@@ -145,14 +148,14 @@ class Invite extends DAV\Property implements CalDAV\Notifications\INotificationT
'readOnly',
'hostUrl',
'organizer',
- );
- foreach($required as $item) {
+ ];
+ foreach ($required as $item) {
if (!isset($values[$item])) {
throw new \InvalidArgumentException($item . ' is a required constructor option');
}
}
- foreach($values as $key=>$value) {
+ foreach ($values as $key => $value) {
if (!property_exists($this, $key)) {
throw new \InvalidArgumentException('Unknown option: ' . $key);
}
@@ -162,19 +165,27 @@ class Invite extends DAV\Property implements CalDAV\Notifications\INotificationT
}
/**
- * Serializes the notification as a single property.
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing element.
*
- * You should usually just encode the single top-level element of the
- * notification.
+ * This allows serializers to be re-used for different element names.
*
- * @param DAV\Server $server
- * @param \DOMElement $node
+ * If you are opening new elements, you must also close them again.
+ *
+ * @param Writer $writer
* @return void
*/
- public function serialize(DAV\Server $server, \DOMElement $node) {
+ function xmlSerialize(Writer $writer) {
- $prop = $node->ownerDocument->createElement('cs:invite-notification');
- $node->appendChild($prop);
+ $writer->writeElement('{' . CalDAV\Plugin::NS_CALENDARSERVER . '}invite-notification');
}
@@ -182,116 +193,88 @@ class Invite extends DAV\Property implements CalDAV\Notifications\INotificationT
* This method serializes the entire notification, as it is used in the
* response body.
*
- * @param DAV\Server $server
- * @param \DOMElement $node
+ * @param Writer $writer
* @return void
*/
- public function serializeBody(DAV\Server $server, \DOMElement $node) {
+ function xmlSerializeFull(Writer $writer) {
- $doc = $node->ownerDocument;
+ $cs = '{' . CalDAV\Plugin::NS_CALENDARSERVER . '}';
- $dt = $doc->createElement('cs:dtstamp');
$this->dtStamp->setTimezone(new \DateTimezone('GMT'));
- $dt->appendChild($doc->createTextNode($this->dtStamp->format('Ymd\\THis\\Z')));
- $node->appendChild($dt);
-
- $prop = $doc->createElement('cs:invite-notification');
- $node->appendChild($prop);
+ $writer->writeElement($cs . 'dtstamp', $this->dtStamp->format('Ymd\\THis\\Z'));
- $uid = $doc->createElement('cs:uid');
- $uid->appendChild( $doc->createTextNode($this->id) );
- $prop->appendChild($uid);
+ $writer->startElement($cs . 'invite-notification');
- $href = $doc->createElement('d:href');
- $href->appendChild( $doc->createTextNode( $this->href ) );
- $prop->appendChild($href);
+ $writer->writeElement($cs . 'uid', $this->id);
+ $writer->writeElement('{DAV:}href', $this->href);
- $nodeName = null;
- switch($this->type) {
+ switch ($this->type) {
case SharingPlugin::STATUS_ACCEPTED :
- $nodeName = 'cs:invite-accepted';
+ $writer->writeElement($cs . 'invite-accepted');
break;
case SharingPlugin::STATUS_DECLINED :
- $nodeName = 'cs:invite-declined';
+ $writer->writeElement($cs . 'invite-declined');
break;
case SharingPlugin::STATUS_DELETED :
- $nodeName = 'cs:invite-deleted';
+ $writer->writeElement($cs . 'invite-deleted');
break;
case SharingPlugin::STATUS_NORESPONSE :
- $nodeName = 'cs:invite-noresponse';
+ $writer->writeElement($cs . 'invite-noresponse');
break;
}
- $prop->appendChild(
- $doc->createElement($nodeName)
- );
- $hostHref = $doc->createElement('d:href', $server->getBaseUri() . $this->hostUrl);
- $hostUrl = $doc->createElement('cs:hosturl');
- $hostUrl->appendChild($hostHref);
- $prop->appendChild($hostUrl);
-
- $access = $doc->createElement('cs:access');
+
+ $writer->writeElement($cs . 'hosturl', [
+ '{DAV:}href' => $writer->contextUri . $this->hostUrl
+ ]);
+
+ if ($this->summary) {
+ $writer->writeElement($cs . 'summary', $this->summary);
+ }
+
+ $writer->startElement($cs . 'access');
if ($this->readOnly) {
- $access->appendChild($doc->createElement('cs:read'));
+ $writer->writeElement($cs . 'read');
} else {
- $access->appendChild($doc->createElement('cs:read-write'));
+ $writer->writeElement($cs . 'read-write');
}
- $prop->appendChild($access);
+ $writer->endElement(); // access
- $organizerUrl = $doc->createElement('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:') {
- $organizerHref = new DAV\Property\Href($this->organizer, false);
+ if (strtolower(substr($this->organizer, 0, 7)) === 'mailto:') {
+ $writer->writeElement('{DAV:}href', $this->organizer);
} else {
- $organizerHref = new DAV\Property\Href($this->organizer, true);
+ $writer->writeElement('{DAV:}href', $writer->contextUri . $this->organizer);
}
- $organizerHref->serialize($server, $organizerUrl);
-
if ($this->commonName) {
- $commonName = $doc->createElement('cs:common-name');
- $commonName->appendChild($doc->createTextNode($this->commonName));
- $organizerUrl->appendChild($commonName);
-
- $commonNameOld = $doc->createElement('cs:organizer-cn');
- $commonNameOld->appendChild($doc->createTextNode($this->commonName));
- $prop->appendChild($commonNameOld);
-
+ $writer->writeElement($cs . 'common-name', $this->commonName);
}
if ($this->firstName) {
- $firstName = $doc->createElement('cs:first-name');
- $firstName->appendChild($doc->createTextNode($this->firstName));
- $organizerUrl->appendChild($firstName);
-
- $firstNameOld = $doc->createElement('cs:organizer-first');
- $firstNameOld->appendChild($doc->createTextNode($this->firstName));
- $prop->appendChild($firstNameOld);
+ $writer->writeElement($cs . 'first-name', $this->firstName);
}
if ($this->lastName) {
- $lastName = $doc->createElement('cs:last-name');
- $lastName->appendChild($doc->createTextNode($this->lastName));
- $organizerUrl->appendChild($lastName);
-
- $lastNameOld = $doc->createElement('cs:organizer-last');
- $lastNameOld->appendChild($doc->createTextNode($this->lastName));
- $prop->appendChild($lastNameOld);
+ $writer->writeElement($cs . 'last-name', $this->lastName);
}
- $prop->appendChild($organizerUrl);
+ $writer->endElement(); // organizer
- if ($this->summary) {
- $summary = $doc->createElement('cs:summary');
- $summary->appendChild($doc->createTextNode($this->summary));
- $prop->appendChild($summary);
+ if ($this->commonName) {
+ $writer->writeElement($cs . 'organizer-cn', $this->commonName);
+ }
+ if ($this->firstName) {
+ $writer->writeElement($cs . 'organizer-first', $this->firstName);
+ }
+ if ($this->lastName) {
+ $writer->writeElement($cs . 'organizer-last', $this->lastName);
}
if ($this->supportedComponents) {
-
- $xcomp = $doc->createElement('cal:supported-calendar-component-set');
- $this->supportedComponents->serialize($server, $xcomp);
- $prop->appendChild($xcomp);
-
+ $writer->writeElement('{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set', $this->supportedComponents);
}
+ $writer->endElement(); // invite-notification
+
}
/**
@@ -302,7 +285,7 @@ class Invite extends DAV\Property implements CalDAV\Notifications\INotificationT
*
* @return string
*/
- public function getId() {
+ function getId() {
return $this->id;
@@ -315,7 +298,7 @@ class Invite extends DAV\Property implements CalDAV\Notifications\INotificationT
*
* @return string
*/
- public function getETag() {
+ function getETag() {
return $this->etag;
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/Notification/InviteReply.php b/vendor/sabre/dav/lib/CalDAV/Xml/Notification/InviteReply.php
index e40751346..945323fed 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/Notification/InviteReply.php
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Notification/InviteReply.php
@@ -1,19 +1,19 @@
<?php
-namespace Sabre\CalDAV\Notifications\Notification;
+namespace Sabre\CalDAV\Xml\Notification;
-use Sabre\CalDAV\SharingPlugin as SharingPlugin;
-use Sabre\DAV;
+use Sabre\Xml\Writer;
use Sabre\CalDAV;
+use Sabre\CalDAV\SharingPlugin;
/**
* This class represents the cs:invite-reply notification element.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class InviteReply extends DAV\Property implements CalDAV\Notifications\INotificationType {
+class InviteReply implements NotificationInterface {
/**
* A unique id for the message
@@ -86,10 +86,12 @@ class InviteReply extends DAV\Property implements CalDAV\Notifications\INotifica
* * hostUrl - A url to the shared calendar.
* * summary - Description of the share, can be the same as the
* calendar, but may also be modified (optional).
+ *
+ * @param array $values
*/
- public function __construct(array $values) {
+ function __construct(array $values) {
- $required = array(
+ $required = [
'id',
'etag',
'href',
@@ -97,14 +99,14 @@ class InviteReply extends DAV\Property implements CalDAV\Notifications\INotifica
'inReplyTo',
'type',
'hostUrl',
- );
- foreach($required as $item) {
+ ];
+ foreach ($required as $item) {
if (!isset($values[$item])) {
throw new \InvalidArgumentException($item . ' is a required constructor option');
}
}
- foreach($values as $key=>$value) {
+ foreach ($values as $key => $value) {
if (!property_exists($this, $key)) {
throw new \InvalidArgumentException('Unknown option: ' . $key);
}
@@ -114,19 +116,27 @@ class InviteReply extends DAV\Property implements CalDAV\Notifications\INotifica
}
/**
- * Serializes the notification as a single property.
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing element.
+ *
+ * This allows serializers to be re-used for different element names.
*
- * You should usually just encode the single top-level element of the
- * notification.
+ * If you are opening new elements, you must also close them again.
*
- * @param DAV\Server $server
- * @param \DOMElement $node
+ * @param Writer $writer
* @return void
*/
- public function serialize(DAV\Server $server, \DOMElement $node) {
+ function xmlSerialize(Writer $writer) {
- $prop = $node->ownerDocument->createElement('cs:invite-reply');
- $node->appendChild($prop);
+ $writer->writeElement('{' . CalDAV\Plugin::NS_CALENDARSERVER . '}invite-reply');
}
@@ -134,58 +144,41 @@ class InviteReply extends DAV\Property implements CalDAV\Notifications\INotifica
* This method serializes the entire notification, as it is used in the
* response body.
*
- * @param DAV\Server $server
- * @param \DOMElement $node
+ * @param Writer $writer
* @return void
*/
- public function serializeBody(DAV\Server $server, \DOMElement $node) {
+ function xmlSerializeFull(Writer $writer) {
- $doc = $node->ownerDocument;
+ $cs = '{' . CalDAV\Plugin::NS_CALENDARSERVER . '}';
- $dt = $doc->createElement('cs:dtstamp');
$this->dtStamp->setTimezone(new \DateTimezone('GMT'));
- $dt->appendChild($doc->createTextNode($this->dtStamp->format('Ymd\\THis\\Z')));
- $node->appendChild($dt);
+ $writer->writeElement($cs . 'dtstamp', $this->dtStamp->format('Ymd\\THis\\Z'));
- $prop = $doc->createElement('cs:invite-reply');
- $node->appendChild($prop);
+ $writer->startElement($cs . 'invite-reply');
- $uid = $doc->createElement('cs:uid');
- $uid->appendChild($doc->createTextNode($this->id));
- $prop->appendChild($uid);
+ $writer->writeElement($cs . 'uid', $this->id);
+ $writer->writeElement($cs . 'in-reply-to', $this->inReplyTo);
+ $writer->writeElement('{DAV:}href', $this->href);
- $inReplyTo = $doc->createElement('cs:in-reply-to');
- $inReplyTo->appendChild( $doc->createTextNode($this->inReplyTo) );
- $prop->appendChild($inReplyTo);
-
- $href = $doc->createElement('d:href');
- $href->appendChild( $doc->createTextNode($this->href) );
- $prop->appendChild($href);
-
- $nodeName = null;
- switch($this->type) {
+ switch ($this->type) {
case SharingPlugin::STATUS_ACCEPTED :
- $nodeName = 'cs:invite-accepted';
+ $writer->writeElement($cs . 'invite-accepted');
break;
case SharingPlugin::STATUS_DECLINED :
- $nodeName = 'cs:invite-declined';
+ $writer->writeElement($cs . 'invite-declined');
break;
}
- $prop->appendChild(
- $doc->createElement($nodeName)
- );
- $hostHref = $doc->createElement('d:href', $server->getBaseUri() . $this->hostUrl);
- $hostUrl = $doc->createElement('cs:hosturl');
- $hostUrl->appendChild($hostHref);
- $prop->appendChild($hostUrl);
+
+ $writer->writeElement($cs . 'hosturl', [
+ '{DAV:}href' => $writer->contextUri . $this->hostUrl
+ ]);
if ($this->summary) {
- $summary = $doc->createElement('cs:summary');
- $summary->appendChild($doc->createTextNode($this->summary));
- $prop->appendChild($summary);
+ $writer->writeElement($cs . 'summary', $this->summary);
}
+ $writer->endElement(); // invite-reply
}
@@ -197,7 +190,7 @@ class InviteReply extends DAV\Property implements CalDAV\Notifications\INotifica
*
* @return string
*/
- public function getId() {
+ function getId() {
return $this->id;
@@ -210,9 +203,10 @@ class InviteReply extends DAV\Property implements CalDAV\Notifications\INotifica
*
* @return string
*/
- public function getETag() {
+ function getETag() {
return $this->etag;
}
+
}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/INotificationType.php b/vendor/sabre/dav/lib/CalDAV/Xml/Notification/NotificationInterface.php
index b646399b9..1c08f12fd 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/INotificationType.php
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Notification/NotificationInterface.php
@@ -1,26 +1,27 @@
<?php
-namespace Sabre\CalDAV\Notifications;
-use Sabre\DAV;
+namespace Sabre\CalDAV\Xml\Notification;
+
+use Sabre\Xml\XmlSerializable;
+use Sabre\Xml\Writer;
/**
* This interface reflects a single notification type.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-interface INotificationType extends DAV\PropertyInterface {
+interface NotificationInterface extends XmlSerializable {
/**
* This method serializes the entire notification, as it is used in the
* response body.
*
- * @param DAV\Server $server
- * @param \DOMElement $node
+ * @param Writer $writer
* @return void
*/
- function serializeBody(DAV\Server $server, \DOMElement $node);
+ function xmlSerializeFull(Writer $writer);
/**
* Returns a unique id for this notification
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/Notification/SystemStatus.php b/vendor/sabre/dav/lib/CalDAV/Xml/Notification/SystemStatus.php
index 608892dab..d41702e07 100644
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Notifications/Notification/SystemStatus.php
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Notification/SystemStatus.php
@@ -1,9 +1,9 @@
<?php
-namespace Sabre\CalDAV\Notifications\Notification;
+namespace Sabre\CalDAV\Xml\Notification;
-use Sabre\DAV;
-use Sabre\CalDAV;
+use Sabre\Xml\Writer;
+use Sabre\CalDAV\Plugin;
/**
* SystemStatus notification
@@ -11,11 +11,11 @@ use Sabre\CalDAV;
* This notification can be used to indicate to the user that the system is
* down.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class SystemStatus extends DAV\Property implements CalDAV\Notifications\INotificationType {
+class SystemStatus implements NotificationInterface {
const TYPE_LOW = 1;
const TYPE_MEDIUM = 2;
@@ -68,7 +68,7 @@ class SystemStatus extends DAV\Property implements CalDAV\Notifications\INotific
* @param string $description
* @param string $href
*/
- public function __construct($id, $etag, $type = self::TYPE_HIGH, $description = null, $href = null) {
+ function __construct($id, $etag, $type = self::TYPE_HIGH, $description = null, $href = null) {
$this->id = $id;
$this->type = $type;
@@ -79,18 +79,23 @@ class SystemStatus extends DAV\Property implements CalDAV\Notifications\INotific
}
/**
- * Serializes the notification as a single property.
+ * The serialize method is called during xml writing.
*
- * You should usually just encode the single top-level element of the
- * notification.
+ * It should use the $writer argument to encode this object into XML.
*
- * @param DAV\Server $server
- * @param \DOMElement $node
+ * Important note: it is not needed to create the parent element. The
+ * parent element is already created, and we only have to worry about
+ * attributes, child elements and text (if any).
+ *
+ * Important note 2: If you are writing any new elements, you are also
+ * responsible for closing them.
+ *
+ * @param Writer $writer
* @return void
*/
- public function serialize(DAV\Server $server, \DOMElement $node) {
+ function xmlSerialize(Writer $writer) {
- switch($this->type) {
+ switch ($this->type) {
case self::TYPE_LOW :
$type = 'low';
break;
@@ -103,10 +108,9 @@ class SystemStatus extends DAV\Property implements CalDAV\Notifications\INotific
break;
}
- $prop = $node->ownerDocument->createElement('cs:systemstatus');
- $prop->setAttribute('type', $type);
-
- $node->appendChild($prop);
+ $writer->startElement('{' . Plugin::NS_CALENDARSERVER . '}systemstatus');
+ $writer->writeAttribute('type', $type);
+ $writer->endElement();
}
@@ -114,13 +118,13 @@ class SystemStatus extends DAV\Property implements CalDAV\Notifications\INotific
* This method serializes the entire notification, as it is used in the
* response body.
*
- * @param DAV\Server $server
- * @param \DOMElement $node
+ * @param Writer $writer
* @return void
*/
- public function serializeBody(DAV\Server $server, \DOMElement $node) {
+ function xmlSerializeFull(Writer $writer) {
- switch($this->type) {
+ $cs = '{' . Plugin::NS_CALENDARSERVER . '}';
+ switch ($this->type) {
case self::TYPE_LOW :
$type = 'low';
break;
@@ -133,23 +137,18 @@ class SystemStatus extends DAV\Property implements CalDAV\Notifications\INotific
break;
}
- $prop = $node->ownerDocument->createElement('cs:systemstatus');
- $prop->setAttribute('type', $type);
+ $writer->startElement($cs . 'systemstatus');
+ $writer->writeAttribute('type', $type);
+
if ($this->description) {
- $text = $node->ownerDocument->createTextNode($this->description);
- $desc = $node->ownerDocument->createElement('cs:description');
- $desc->appendChild($text);
- $prop->appendChild($desc);
+ $writer->writeElement($cs . 'description', $this->description);
}
if ($this->href) {
- $text = $node->ownerDocument->createTextNode($this->href);
- $href = $node->ownerDocument->createElement('d:href');
- $href->appendChild($text);
- $prop->appendChild($href);
+ $writer->writeElement('{DAV:}href', $this->href);
}
- $node->appendChild($prop);
+ $writer->endElement(); // systemstatus
}
@@ -161,7 +160,7 @@ class SystemStatus extends DAV\Property implements CalDAV\Notifications\INotific
*
* @return string
*/
- public function getId() {
+ function getId() {
return $this->id;
@@ -174,9 +173,10 @@ class SystemStatus extends DAV\Property implements CalDAV\Notifications\INotific
*
* @return string
*/
- public function getETag() {
+ function getETag() {
return $this->etag;
}
+
}
diff --git a/vendor/sabre/dav/lib/CalDAV/Xml/Property/AllowedSharingModes.php b/vendor/sabre/dav/lib/CalDAV/Xml/Property/AllowedSharingModes.php
new file mode 100644
index 000000000..c2a2d565e
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Property/AllowedSharingModes.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Property;
+
+use Sabre\Xml\XmlSerializable;
+use Sabre\Xml\Writer;
+use Sabre\CalDAV\Plugin;
+
+/**
+ * AllowedSharingModes
+ *
+ * This property encodes the 'allowed-sharing-modes' property, as defined by
+ * the 'caldav-sharing-02' spec, in the http://calendarserver.org/ns/
+ * namespace.
+ *
+ * This property is a representation of the supported-calendar_component-set
+ * property in the CalDAV namespace. It simply requires an array of components,
+ * such as VEVENT, VTODO
+ *
+ * @see https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-sharing-02.txt
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class AllowedSharingModes implements XmlSerializable {
+
+ /**
+ * Whether or not a calendar can be shared with another user
+ *
+ * @var bool
+ */
+ protected $canBeShared;
+
+ /**
+ * Whether or not the calendar can be placed on a public url.
+ *
+ * @var bool
+ */
+ protected $canBePublished;
+
+ /**
+ * Constructor
+ *
+ * @param bool $canBeShared
+ * @param bool $canBePublished
+ * @return void
+ */
+ function __construct($canBeShared, $canBePublished) {
+
+ $this->canBeShared = $canBeShared;
+ $this->canBePublished = $canBePublished;
+
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ if ($this->canBeShared) {
+ $writer->writeElement('{' . Plugin::NS_CALENDARSERVER . '}can-be-shared');
+ }
+ if ($this->canBePublished) {
+ $writer->writeElement('{' . Plugin::NS_CALENDARSERVER . '}can-be-published');
+ }
+
+ }
+
+
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Xml/Property/EmailAddressSet.php b/vendor/sabre/dav/lib/CalDAV/Xml/Property/EmailAddressSet.php
new file mode 100644
index 000000000..f577a9919
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Property/EmailAddressSet.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Property;
+
+use Sabre\Xml\Writer;
+use Sabre\Xml\XmlSerializable;
+
+/**
+ * email-address-set property
+ *
+ * This property represents the email-address-set property in the
+ * http://calendarserver.org/ns/ namespace.
+ *
+ * It's a list of email addresses associated with a user.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class EmailAddressSet implements XmlSerializable {
+
+ /**
+ * emails
+ *
+ * @var array
+ */
+ private $emails;
+
+ /**
+ * __construct
+ *
+ * @param array $emails
+ */
+ function __construct(array $emails) {
+
+ $this->emails = $emails;
+
+ }
+
+ /**
+ * Returns the email addresses
+ *
+ * @return array
+ */
+ function getValue() {
+
+ return $this->emails;
+
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ foreach ($this->emails as $email) {
+
+ $writer->writeElement('{http://calendarserver.org/ns/}email-address', $email);
+
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Xml/Property/Invite.php b/vendor/sabre/dav/lib/CalDAV/Xml/Property/Invite.php
new file mode 100644
index 000000000..3ee053214
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Property/Invite.php
@@ -0,0 +1,252 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Property;
+
+use Sabre\Xml\Element;
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+use Sabre\CalDAV\Plugin;
+use Sabre\CalDAV\SharingPlugin;
+
+/**
+ * Invite property
+ *
+ * This property encodes the 'invite' property, as defined by
+ * the 'caldav-sharing-02' spec, in the http://calendarserver.org/ns/
+ * namespace.
+ *
+ * @see https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-sharing-02.txt
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Invite implements Element {
+
+ /**
+ * The list of users a calendar has been shared to.
+ *
+ * @var array
+ */
+ protected $users;
+
+ /**
+ * The organizer contains information about the person who shared the
+ * object.
+ *
+ * @var array
+ */
+ protected $organizer;
+
+ /**
+ * Creates the property.
+ *
+ * Users is an array. Each element of the array has the following
+ * properties:
+ *
+ * * href - Often a mailto: address
+ * * commonName - Optional, for example a first and lastname for a user.
+ * * status - One of the SharingPlugin::STATUS_* constants.
+ * * readOnly - true or false
+ * * summary - Optional, description of the share
+ *
+ * The organizer key is optional to specify. It's only useful when a
+ * 'sharee' requests the sharing information.
+ *
+ * The organizer may have the following properties:
+ * * href - Often a mailto: address.
+ * * commonName - Optional human-readable name.
+ * * firstName - Optional first name.
+ * * lastName - Optional last name.
+ *
+ * If you wonder why these two structures are so different, I guess a
+ * valid answer is that the current spec is still a draft.
+ *
+ * @param array $users
+ */
+ function __construct(array $users, array $organizer = null) {
+
+ $this->users = $users;
+ $this->organizer = $organizer;
+
+ }
+
+ /**
+ * Returns the list of users, as it was passed to the constructor.
+ *
+ * @return array
+ */
+ function getValue() {
+
+ return $this->users;
+
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ $cs = '{' . Plugin::NS_CALENDARSERVER . '}';
+
+ if (!is_null($this->organizer)) {
+
+ $writer->startElement($cs . 'organizer');
+ $writer->writeElement('{DAV:}href', $this->organizer['href']);
+
+ if (isset($this->organizer['commonName']) && $this->organizer['commonName']) {
+ $writer->writeElement($cs . 'common-name', $this->organizer['commonName']);
+ }
+ if (isset($this->organizer['firstName']) && $this->organizer['firstName']) {
+ $writer->writeElement($cs . 'first-name', $this->organizer['firstName']);
+ }
+ if (isset($this->organizer['lastName']) && $this->organizer['lastName']) {
+ $writer->writeElement($cs . 'last-name', $this->organizer['lastName']);
+ }
+ $writer->endElement(); // organizer
+
+ }
+
+ foreach ($this->users as $user) {
+
+ $writer->startElement($cs . 'user');
+ $writer->writeElement('{DAV:}href', $user['href']);
+ if (isset($user['commonName']) && $user['commonName']) {
+ $writer->writeElement($cs . 'common-name', $user['commonName']);
+ }
+ switch ($user['status']) {
+
+ case SharingPlugin::STATUS_ACCEPTED :
+ $writer->writeElement($cs . 'invite-accepted');
+ break;
+ case SharingPlugin::STATUS_DECLINED :
+ $writer->writeElement($cs . 'invite-declined');
+ break;
+ case SharingPlugin::STATUS_NORESPONSE :
+ $writer->writeElement($cs . 'invite-noresponse');
+ break;
+ case SharingPlugin::STATUS_INVALID :
+ $writer->writeElement($cs . 'invite-invalid');
+ break;
+ }
+
+ $writer->startElement($cs . 'access');
+ if ($user['readOnly']) {
+ $writer->writeElement($cs . 'read');
+ } else {
+ $writer->writeElement($cs . 'read-write');
+ }
+ $writer->endElement(); // access
+
+ if (isset($user['summary']) && $user['summary']) {
+ $writer->writeElement($cs . 'summary', $user['summary']);
+ }
+
+ $writer->endElement(); //user
+
+ }
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $cs = '{' . Plugin::NS_CALENDARSERVER . '}';
+
+ $users = [];
+
+ foreach ($reader->parseInnerTree() as $elem) {
+
+ if ($elem['name'] !== $cs . 'user')
+ continue;
+
+ $user = [
+ 'href' => null,
+ 'commonName' => null,
+ 'readOnly' => null,
+ 'summary' => null,
+ 'status' => null,
+ ];
+
+ foreach ($elem['value'] as $userElem) {
+
+ switch ($userElem['name']) {
+ case $cs . 'invite-accepted' :
+ $user['status'] = SharingPlugin::STATUS_ACCEPTED;
+ break;
+ case $cs . 'invite-declined' :
+ $user['status'] = SharingPlugin::STATUS_DECLINED;
+ break;
+ case $cs . 'invite-noresponse' :
+ $user['status'] = SharingPlugin::STATUS_NORESPONSE;
+ break;
+ case $cs . 'invite-invalid' :
+ $user['status'] = SharingPlugin::STATUS_INVALID;
+ break;
+ case '{DAV:}href' :
+ $user['href'] = $userElem['value'];
+ break;
+ case $cs . 'common-name' :
+ $user['commonName'] = $userElem['value'];
+ break;
+ case $cs . 'access' :
+ foreach ($userElem['value'] as $accessHref) {
+ if ($accessHref['name'] === $cs . 'read') {
+ $user['readOnly'] = true;
+ }
+ }
+ break;
+ case $cs . 'summary' :
+ $user['summary'] = $userElem['value'];
+ break;
+
+ }
+
+ }
+ if (!$user['status']) {
+ throw new \InvalidArgumentException('Every user must have one of cs:invite-accepted, cs:invite-declined, cs:invite-noresponse or cs:invite-invalid');
+ }
+
+ $users[] = $user;
+
+ }
+
+ return new self($users);
+
+ }
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Xml/Property/ScheduleCalendarTransp.php b/vendor/sabre/dav/lib/CalDAV/Xml/Property/ScheduleCalendarTransp.php
new file mode 100644
index 000000000..4a253e008
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Property/ScheduleCalendarTransp.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Property;
+
+use Sabre\Xml\Element;
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+use Sabre\Xml\Element\Elements;
+use Sabre\CalDAV\Plugin;
+
+/**
+ * schedule-calendar-transp property.
+ *
+ * This property is a representation of the schedule-calendar-transp property.
+ * This property is defined in:
+ *
+ * http://tools.ietf.org/html/rfc6638#section-9.1
+ *
+ * Its values are either 'transparent' or 'opaque'. If it's transparent, it
+ * means that this calendar will not be taken into consideration when a
+ * different user queries for free-busy information. If it's 'opaque', it will.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ScheduleCalendarTransp implements Element {
+
+ const TRANSPARENT = 'transparent';
+ const OPAQUE = 'opaque';
+
+ /**
+ * value
+ *
+ * @var string
+ */
+ protected $value;
+
+ /**
+ * Creates the property
+ *
+ * @param string $value
+ */
+ function __construct($value) {
+
+ if ($value !== self::TRANSPARENT && $value !== self::OPAQUE) {
+ throw new \InvalidArgumentException('The value must either be specified as "transparent" or "opaque"');
+ }
+ $this->value = $value;
+
+ }
+
+ /**
+ * Returns the current value
+ *
+ * @return string
+ */
+ function getValue() {
+
+ return $this->value;
+
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ switch ($this->value) {
+ case self::TRANSPARENT :
+ $writer->writeElement('{' . Plugin::NS_CALDAV . '}transparent');
+ break;
+ case self::OPAQUE :
+ $writer->writeElement('{' . Plugin::NS_CALDAV . '}opaque');
+ break;
+ }
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $elems = Elements::xmlDeserialize($reader);
+
+ $value = null;
+
+ foreach ($elems as $elem) {
+ switch ($elem) {
+ case '{' . Plugin::NS_CALDAV . '}opaque' :
+ $value = self::OPAQUE;
+ break;
+ case '{' . Plugin::NS_CALDAV . '}transparent' :
+ $value = self::TRANSPARENT;
+ break;
+ }
+ }
+ if (is_null($value))
+ return null;
+
+ return new self($value);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarComponentSet.php b/vendor/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarComponentSet.php
new file mode 100644
index 000000000..7a26e767e
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarComponentSet.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Property;
+
+use Sabre\Xml\Element;
+use Sabre\Xml\ParseException;
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+use Sabre\CalDAV\Plugin;
+
+/**
+ * SupportedCalendarComponentSet property.
+ *
+ * This class represents the
+ * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set property, as
+ * defined in:
+ *
+ * https://tools.ietf.org/html/rfc4791#section-5.2.3
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class SupportedCalendarComponentSet implements Element {
+
+ /**
+ * List of supported components.
+ *
+ * This array will contain values such as VEVENT, VTODO and VJOURNAL.
+ *
+ * @var array
+ */
+ protected $components = [];
+
+ /**
+ * Creates the property.
+ *
+ * @param array $components
+ */
+ function __construct(array $components) {
+
+ $this->components = $components;
+
+ }
+
+ /**
+ * Returns the list of supported components
+ *
+ * @return array
+ */
+ function getValue() {
+
+ return $this->components;
+
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ foreach ($this->components as $component) {
+
+ $writer->startElement('{' . Plugin::NS_CALDAV . '}comp');
+ $writer->writeAttributes(['name' => $component]);
+ $writer->endElement();
+
+ }
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $elems = $reader->parseInnerTree();
+
+ $components = [];
+
+ foreach ((array)$elems as $elem) {
+ if ($elem['name'] === '{' . Plugin::NS_CALDAV . '}comp') {
+ $components[] = $elem['attributes']['name'];
+ }
+ }
+
+ if (!$components) {
+ throw new ParseException('supported-calendar-component-set must have at least one CALDAV:comp element');
+ }
+
+ return new self($components);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarData.php b/vendor/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarData.php
new file mode 100644
index 000000000..b0c407fd6
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarData.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Property;
+
+use Sabre\Xml\XmlSerializable;
+use Sabre\Xml\Writer;
+use Sabre\CalDAV\Plugin;
+
+/**
+ * Supported-calendar-data property
+ *
+ * This property is a representation of the supported-calendar-data property
+ * in the CalDAV namespace. SabreDAV only has support for text/calendar;2.0
+ * so the value is currently hardcoded.
+ *
+ * This property is defined in:
+ * http://tools.ietf.org/html/rfc4791#section-5.2.4
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class SupportedCalendarData implements XmlSerializable {
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ $writer->startElement('{' . Plugin::NS_CALDAV . '}calendar-data');
+ $writer->writeAttributes([
+ 'content-type' => 'text/calendar',
+ 'version' => '2.0',
+ ]);
+ $writer->endElement(); // calendar-data
+ $writer->startElement('{' . Plugin::NS_CALDAV . '}calendar-data');
+ $writer->writeAttributes([
+ 'content-type' => 'application/calendar+json',
+ ]);
+ $writer->endElement(); // calendar-data
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Xml/Property/SupportedCollationSet.php b/vendor/sabre/dav/lib/CalDAV/Xml/Property/SupportedCollationSet.php
new file mode 100644
index 000000000..71de25a62
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Property/SupportedCollationSet.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Property;
+
+use Sabre\Xml\Writer;
+use Sabre\Xml\XmlSerializable;
+use Sabre\CalDAV\Plugin;
+
+/**
+ * supported-collation-set property
+ *
+ * This property is a representation of the supported-collation-set property
+ * in the CalDAV namespace.
+ *
+ * This property is defined in:
+ * http://tools.ietf.org/html/rfc4791#section-7.5.1
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class SupportedCollationSet implements XmlSerializable {
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ $collations = [
+ 'i;ascii-casemap',
+ 'i;octet',
+ 'i;unicode-casemap'
+ ];
+
+ foreach ($collations as $collation) {
+ $writer->writeElement('{' . Plugin::NS_CALDAV . '}supported-collation', $collation);
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php b/vendor/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php
new file mode 100644
index 000000000..79b3bb3ac
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php
@@ -0,0 +1,124 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Request;
+
+use Sabre\Xml\XmlDeserializable;
+use Sabre\Xml\Reader;
+use Sabre\CalDAV\Plugin;
+use Sabre\Uri;
+
+/**
+ * CalendarMultiGetReport request parser.
+ *
+ * This class parses the {urn:ietf:params:xml:ns:caldav}calendar-multiget
+ * REPORT, as defined in:
+ *
+ * https://tools.ietf.org/html/rfc4791#section-7.9
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class CalendarMultiGetReport implements XmlDeserializable {
+
+ /**
+ * An array with requested properties.
+ *
+ * @var array
+ */
+ public $properties;
+
+ /**
+ * This is an array with the urls that are being requested.
+ *
+ * @var array
+ */
+ public $hrefs;
+
+ /**
+ * If the calendar data must be expanded, this will contain an array with 2
+ * elements: start and end.
+ *
+ * Each may be a DateTime or null.
+ *
+ * @var array|null
+ */
+ public $expand = null;
+
+ /**
+ * The mimetype of the content that should be returend. Usually
+ * text/calendar.
+ *
+ * @var string
+ */
+ public $contentType = null;
+
+ /**
+ * The version of calendar-data that should be returned. Usually '2.0',
+ * referring to iCalendar 2.0.
+ *
+ * @var string
+ */
+ public $version = null;
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $elems = $reader->parseInnerTree([
+ '{urn:ietf:params:xml:ns:caldav}calendar-data' => 'Sabre\\CalDAV\\Xml\\Filter\\CalendarData',
+ '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue',
+ ]);
+
+ $newProps = [
+ 'hrefs' => [],
+ 'properties' => [],
+ ];
+
+ foreach ($elems as $elem) {
+
+ switch ($elem['name']) {
+
+ 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'];
+ }
+ break;
+ 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/vendor/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php b/vendor/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php
new file mode 100644
index 000000000..848a4dc46
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Request;
+
+use Sabre\Xml\XmlDeserializable;
+use Sabre\Xml\Reader;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\CalDAV\Plugin;
+
+/**
+ * CalendarQueryReport request parser.
+ *
+ * This class parses the {urn:ietf:params:xml:ns:caldav}calendar-query
+ * REPORT, as defined in:
+ *
+ * https://tools.ietf.org/html/rfc4791#section-7.9
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class CalendarQueryReport implements XmlDeserializable {
+
+ /**
+ * An array with requested properties.
+ *
+ * @var array
+ */
+ public $properties;
+
+ /**
+ * List of property/component filters.
+ *
+ * @var array
+ */
+ public $filters;
+
+ /**
+ * If the calendar data must be expanded, this will contain an array with 2
+ * elements: start and end.
+ *
+ * Each may be a DateTime or null.
+ *
+ * @var array|null
+ */
+ public $expand = null;
+
+ /**
+ * The mimetype of the content that should be returend. Usually
+ * text/calendar.
+ *
+ * @var string
+ */
+ public $contentType = null;
+
+ /**
+ * The version of calendar-data that should be returned. Usually '2.0',
+ * referring to iCalendar 2.0.
+ *
+ * @var string
+ */
+ public $version = null;
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $elems = $reader->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}calendar-data' => 'Sabre\\CalDAV\\Xml\\Filter\\CalendarData',
+ '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue',
+ ]);
+
+ $newProps = [
+ 'filters' => null,
+ 'properties' => [],
+ ];
+
+ if (!is_array($elems)) $elems = [];
+
+ foreach ($elems as $elem) {
+
+ switch ($elem['name']) {
+
+ 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'];
+ }
+ break;
+ case '{' . Plugin::NS_CALDAV . '}filter' :
+ foreach ($elem['value'] as $subElem) {
+ 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');
+ }
+ $newProps['filters'] = $subElem['value'];
+ }
+ }
+ break;
+
+ }
+
+ }
+
+ if (is_null($newProps['filters'])) {
+ 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/vendor/sabre/dav/lib/CalDAV/Xml/Request/FreeBusyQueryReport.php b/vendor/sabre/dav/lib/CalDAV/Xml/Request/FreeBusyQueryReport.php
new file mode 100644
index 000000000..e3b27d08e
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Request/FreeBusyQueryReport.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Request;
+
+use Sabre\CalDAV\Plugin;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\VObject\DateTimeParser;
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+
+/**
+ * FreeBusyQueryReport
+ *
+ * This class parses the {DAV:}free-busy-query REPORT, as defined in:
+ *
+ * http://tools.ietf.org/html/rfc3253#section-3.8
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class FreeBusyQueryReport implements XmlDeserializable {
+
+ /**
+ * Starttime of report
+ *
+ * @var DateTime|null
+ */
+ public $start;
+
+ /**
+ * End time of report
+ *
+ * @var DateTime|null
+ */
+ public $end;
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $timeRange = '{' . Plugin::NS_CALDAV . '}time-range';
+
+ $start = null;
+ $end = null;
+
+ 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');
+ }
+ if ($start) {
+ $start = DateTimeParser::parseDateTime($start);
+ }
+ if ($end) {
+ $end = DateTimeParser::parseDateTime($end);
+ }
+ $result = new self();
+ $result->start = $start;
+ $result->end = $end;
+
+ return $result;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Xml/Request/InviteReply.php b/vendor/sabre/dav/lib/CalDAV/Xml/Request/InviteReply.php
new file mode 100644
index 000000000..ec627156f
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Request/InviteReply.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Request;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+use Sabre\Xml\Element\KeyValue;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\CalDAV\Plugin;
+use Sabre\CalDAV\SharingPlugin;
+
+/**
+ * Invite-reply POST request parser
+ *
+ * This class parses the invite-reply POST request, as defined in:
+ *
+ * http://svn.calendarserver.org/repository/calendarserver/CalendarServer/trunk/doc/Extensions/caldav-sharing.txt
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class InviteReply implements XmlDeserializable {
+
+ /**
+ * The sharee calendar user address.
+ *
+ * This is the address that the original invite was set to
+ *
+ * @var string
+ */
+ public $href;
+
+ /**
+ * The uri to the calendar that was being shared.
+ *
+ * @var string
+ */
+ public $calendarUri;
+
+ /**
+ * The id of the invite message that's being responded to
+ *
+ * @var string
+ */
+ public $inReplyTo;
+
+ /**
+ * An optional message
+ *
+ * @var string
+ */
+ public $summary;
+
+ /**
+ * Either SharingPlugin::STATUS_ACCEPTED or SharingPlugin::STATUS_DECLINED.
+ *
+ * @var int
+ */
+ public $status;
+
+ /**
+ * Constructor
+ *
+ * @param string $href
+ * @param string $calendarUri
+ * @param string $inReplyTo
+ * @param string $summary
+ * @param int $status
+ */
+ function __construct($href, $calendarUri, $inReplyTo, $summary, $status) {
+
+ $this->href = $href;
+ $this->calendarUri = $calendarUri;
+ $this->inReplyTo = $inReplyTo;
+ $this->summary = $summary;
+ $this->status = $status;
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $elems = KeyValue::xmlDeserialize($reader);
+
+ $href = null;
+ $calendarUri = null;
+ $inReplyTo = null;
+ $summary = null;
+ $status = null;
+
+ foreach ($elems as $name => $value) {
+
+ switch ($name) {
+
+ case '{' . Plugin::NS_CALENDARSERVER . '}hosturl' :
+ foreach ($value as $bla) {
+ if ($bla['name'] === '{DAV:}href') {
+ $calendarUri = $bla['value'];
+ }
+ }
+ break;
+ case '{' . Plugin::NS_CALENDARSERVER . '}invite-accepted' :
+ $status = SharingPlugin::STATUS_ACCEPTED;
+ break;
+ case '{' . Plugin::NS_CALENDARSERVER . '}invite-declined' :
+ $status = SharingPlugin::STATUS_DECLINED;
+ break;
+ case '{' . Plugin::NS_CALENDARSERVER . '}in-reply-to' :
+ $inReplyTo = $value;
+ break;
+ case '{' . Plugin::NS_CALENDARSERVER . '}summary' :
+ $summary = $value;
+ break;
+ 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/vendor/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php b/vendor/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php
new file mode 100644
index 000000000..7b745db55
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Request;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+
+/**
+ * MKCALENDAR parser.
+ *
+ * This class parses the MKCALENDAR request, as defined in:
+ *
+ * https://tools.ietf.org/html/rfc4791#section-5.3.1
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class MkCalendar implements XmlDeserializable {
+
+ /**
+ * The list of properties that will be set.
+ *
+ * @var array
+ */
+ public $properties = [];
+
+ /**
+ * Returns the list of properties the calendar will be initialized with.
+ *
+ * @return array
+ */
+ function getProperties() {
+
+ return $this->properties;
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $self = new self();
+
+ $elementMap = $reader->elementMap;
+ $elementMap['{DAV:}prop'] = 'Sabre\DAV\Xml\Element\Prop';
+ $elementMap['{DAV:}set'] = 'Sabre\Xml\Element\KeyValue';
+ $elems = $reader->parseInnerTree($elementMap);
+
+ foreach ($elems as $elem) {
+ if ($elem['name'] === '{DAV:}set') {
+ $self->properties = array_merge($self->properties, $elem['value']['{DAV:}prop']);
+ }
+ }
+
+ return $self;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CalDAV/Xml/Request/Share.php b/vendor/sabre/dav/lib/CalDAV/Xml/Request/Share.php
new file mode 100644
index 000000000..dacc5dc94
--- /dev/null
+++ b/vendor/sabre/dav/lib/CalDAV/Xml/Request/Share.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace Sabre\CalDAV\Xml\Request;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+use Sabre\CalDAV\Plugin;
+
+/**
+ * Share POST request parser
+ *
+ * This class parses the share POST request, as defined in:
+ *
+ * http://svn.calendarserver.org/repository/calendarserver/CalendarServer/trunk/doc/Extensions/caldav-sharing.txt
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Share implements XmlDeserializable {
+
+ /**
+ * The list of new people added or updated.
+ *
+ * Every element has the following keys:
+ * 1. href - An email address
+ * 2. commonName - Some name
+ * 3. summary - An optional description of the share
+ * 4. readOnly - true or false
+ *
+ * @var array
+ */
+ public $set = [];
+
+ /**
+ * List of people removed from the share list.
+ *
+ * The list is a flat list of email addresses (including mailto:).
+ *
+ * @var array
+ */
+ public $remove = [];
+
+ /**
+ * Constructor
+ *
+ * @param array $set
+ * @param array $remove
+ */
+ function __construct(array $set, array $remove) {
+
+ $this->set = $set;
+ $this->remove = $remove;
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $elems = $reader->parseInnerTree([
+ '{' . Plugin::NS_CALENDARSERVER . '}set' => 'Sabre\\Xml\\Element\\KeyValue',
+ '{' . Plugin::NS_CALENDARSERVER . '}remove' => 'Sabre\\Xml\\Element\\KeyValue',
+ ]);
+
+ $set = [];
+ $remove = [];
+
+ foreach ($elems as $elem) {
+ switch ($elem['name']) {
+
+ case '{' . Plugin::NS_CALENDARSERVER . '}set' :
+ $sharee = $elem['value'];
+
+ $sumElem = '{' . Plugin::NS_CALENDARSERVER . '}summary';
+ $commonName = '{' . Plugin::NS_CALENDARSERVER . '}common-name';
+
+ $set[] = [
+ 'href' => $sharee['{DAV:}href'],
+ 'commonName' => isset($sharee[$commonName]) ? $sharee[$commonName] : null,
+ 'summary' => isset($sharee[$sumElem]) ? $sharee[$sumElem] : null,
+ 'readOnly' => !array_key_exists('{' . Plugin::NS_CALENDARSERVER . '}read-write', $sharee),
+ ];
+ break;
+
+ case '{' . Plugin::NS_CALENDARSERVER . '}remove' :
+ $remove[] = $elem['value']['{DAV:}href'];
+ break;
+
+ }
+ }
+
+ return new self($set, $remove);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CardDAV/AddressBook.php b/vendor/sabre/dav/lib/CardDAV/AddressBook.php
new file mode 100644
index 000000000..70bec8760
--- /dev/null
+++ b/vendor/sabre/dav/lib/CardDAV/AddressBook.php
@@ -0,0 +1,433 @@
+<?php
+
+namespace Sabre\CardDAV;
+
+use Sabre\DAV;
+use Sabre\DAVACL;
+
+/**
+ * The AddressBook class represents a CardDAV addressbook, owned by a specific user
+ *
+ * The AddressBook can contain multiple vcards
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class AddressBook extends DAV\Collection implements IAddressBook, DAV\IProperties, DAVACL\IACL, DAV\Sync\ISyncCollection, DAV\IMultiGet {
+
+ /**
+ * This is an array with addressbook information
+ *
+ * @var array
+ */
+ protected $addressBookInfo;
+
+ /**
+ * CardDAV backend
+ *
+ * @var Backend\BackendInterface
+ */
+ protected $carddavBackend;
+
+ /**
+ * Constructor
+ *
+ * @param Backend\BackendInterface $carddavBackend
+ * @param array $addressBookInfo
+ */
+ function __construct(Backend\BackendInterface $carddavBackend, array $addressBookInfo) {
+
+ $this->carddavBackend = $carddavBackend;
+ $this->addressBookInfo = $addressBookInfo;
+
+ }
+
+ /**
+ * Returns the name of the addressbook
+ *
+ * @return string
+ */
+ function getName() {
+
+ return $this->addressBookInfo['uri'];
+
+ }
+
+ /**
+ * Returns a card
+ *
+ * @param string $name
+ * @return \ICard
+ */
+ function getChild($name) {
+
+ $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'], $name);
+ if (!$obj) throw new DAV\Exception\NotFound('Card not found');
+ return new Card($this->carddavBackend, $this->addressBookInfo, $obj);
+
+ }
+
+ /**
+ * Returns the full list of cards
+ *
+ * @return array
+ */
+ function getChildren() {
+
+ $objs = $this->carddavBackend->getCards($this->addressBookInfo['id']);
+ $children = [];
+ foreach ($objs as $obj) {
+ $obj['acl'] = $this->getChildACL();
+ $children[] = new Card($this->carddavBackend, $this->addressBookInfo, $obj);
+ }
+ return $children;
+
+ }
+
+ /**
+ * This method receives a list of paths in it's first argument.
+ * It must return an array with Node objects.
+ *
+ * If any children are not found, you do not have to return them.
+ *
+ * @param string[] $paths
+ * @return array
+ */
+ function getMultipleChildren(array $paths) {
+
+ $objs = $this->carddavBackend->getMultipleCards($this->addressBookInfo['id'], $paths);
+ $children = [];
+ foreach ($objs as $obj) {
+ $obj['acl'] = $this->getChildACL();
+ $children[] = new Card($this->carddavBackend, $this->addressBookInfo, $obj);
+ }
+ return $children;
+
+ }
+
+ /**
+ * Creates a new directory
+ *
+ * We actually block this, as subdirectories are not allowed in addressbooks.
+ *
+ * @param string $name
+ * @return void
+ */
+ function createDirectory($name) {
+
+ throw new DAV\Exception\MethodNotAllowed('Creating collections in addressbooks is not allowed');
+
+ }
+
+ /**
+ * Creates a new file
+ *
+ * The contents of the new file must be a valid VCARD.
+ *
+ * This method may return an ETag.
+ *
+ * @param string $name
+ * @param resource $vcardData
+ * @return string|null
+ */
+ function createFile($name, $vcardData = null) {
+
+ if (is_resource($vcardData)) {
+ $vcardData = stream_get_contents($vcardData);
+ }
+ // Converting to UTF-8, if needed
+ $vcardData = DAV\StringUtil::ensureUTF8($vcardData);
+
+ return $this->carddavBackend->createCard($this->addressBookInfo['id'], $name, $vcardData);
+
+ }
+
+ /**
+ * Deletes the entire addressbook.
+ *
+ * @return void
+ */
+ function delete() {
+
+ $this->carddavBackend->deleteAddressBook($this->addressBookInfo['id']);
+
+ }
+
+ /**
+ * Renames the addressbook
+ *
+ * @param string $newName
+ * @return void
+ */
+ function setName($newName) {
+
+ throw new DAV\Exception\MethodNotAllowed('Renaming addressbooks is not yet supported');
+
+ }
+
+ /**
+ * Returns the last modification date as a unix timestamp.
+ *
+ * @return void
+ */
+ function getLastModified() {
+
+ return null;
+
+ }
+
+ /**
+ * Updates properties on this node.
+ *
+ * This method received a PropPatch object, which contains all the
+ * information about the update.
+ *
+ * To update specific properties, call the 'handle' method on this object.
+ * Read the PropPatch documentation for more information.
+ *
+ * @param DAV\PropPatch $propPatch
+ * @return void
+ */
+ function propPatch(DAV\PropPatch $propPatch) {
+
+ return $this->carddavBackend->updateAddressBook($this->addressBookInfo['id'], $propPatch);
+
+ }
+
+ /**
+ * Returns a list of properties for this nodes.
+ *
+ * The properties list is a list of propertynames the client requested,
+ * encoded in clark-notation {xmlnamespace}tagname
+ *
+ * If the array is empty, it means 'all properties' were requested.
+ *
+ * @param array $properties
+ * @return array
+ */
+ function getProperties($properties) {
+
+ $response = [];
+ foreach ($properties as $propertyName) {
+
+ if (isset($this->addressBookInfo[$propertyName])) {
+
+ $response[$propertyName] = $this->addressBookInfo[$propertyName];
+
+ }
+
+ }
+
+ return $response;
+
+ }
+
+ /**
+ * Returns the owner principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getOwner() {
+
+ return $this->addressBookInfo['principaluri'];
+
+ }
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getGroup() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+
+ return [
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->getOwner(),
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->getOwner(),
+ 'protected' => true,
+ ],
+
+ ];
+
+ }
+
+ /**
+ * This method returns the ACL's for card nodes in this address book.
+ * The result of this method automatically gets passed to the
+ * card nodes in this address book.
+ *
+ * @return array
+ */
+ function getChildACL() {
+
+ return [
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->getOwner(),
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->getOwner(),
+ 'protected' => true,
+ ],
+ ];
+
+ }
+
+ /**
+ * Updates the ACL
+ *
+ * This method will receive a list of new ACE's.
+ *
+ * @param array $acl
+ * @return void
+ */
+ function setACL(array $acl) {
+
+ throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+ /**
+ * This method returns the current sync-token for this collection.
+ * This can be any string.
+ *
+ * If null is returned from this function, the plugin assumes there's no
+ * sync information available.
+ *
+ * @return string|null
+ */
+ function getSyncToken() {
+
+ if (
+ $this->carddavBackend instanceof Backend\SyncSupport &&
+ isset($this->addressBookInfo['{DAV:}sync-token'])
+ ) {
+ return $this->addressBookInfo['{DAV:}sync-token'];
+ }
+ if (
+ $this->carddavBackend instanceof Backend\SyncSupport &&
+ isset($this->addressBookInfo['{http://sabredav.org/ns}sync-token'])
+ ) {
+ return $this->addressBookInfo['{http://sabredav.org/ns}sync-token'];
+ }
+
+ }
+
+ /**
+ * The getChanges method returns all the changes that have happened, since
+ * the specified syncToken and the current collection.
+ *
+ * This function should return an array, such as the following:
+ *
+ * [
+ * 'syncToken' => 'The current synctoken',
+ * 'added' => [
+ * 'new.txt',
+ * ],
+ * 'modified' => [
+ * 'modified.txt',
+ * ],
+ * 'deleted' => [
+ * 'foo.php.bak',
+ * 'old.txt'
+ * ]
+ * ];
+ *
+ * The syncToken property should reflect the *current* syncToken of the
+ * collection, as reported getSyncToken(). This is needed here too, to
+ * ensure the operation is atomic.
+ *
+ * If the syncToken is specified as null, this is an initial sync, and all
+ * members should be reported.
+ *
+ * The modified property is an array of nodenames that have changed since
+ * the last token.
+ *
+ * The deleted property is an array with nodenames, that have been deleted
+ * from collection.
+ *
+ * The second argument is basically the 'depth' of the report. If it's 1,
+ * you only have to report changes that happened only directly in immediate
+ * descendants. If it's 2, it should also include changes from the nodes
+ * below the child collections. (grandchildren)
+ *
+ * The third (optional) argument allows a client to specify how many
+ * results should be returned at most. If the limit is not specified, it
+ * should be treated as infinite.
+ *
+ * If the limit (infinite or not) is higher than you're willing to return,
+ * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
+ *
+ * If the syncToken is expired (due to data cleanup) or unknown, you must
+ * return null.
+ *
+ * The limit is 'suggestive'. You are free to ignore it.
+ *
+ * @param string $syncToken
+ * @param int $syncLevel
+ * @param int $limit
+ * @return array
+ */
+ function getChanges($syncToken, $syncLevel, $limit = null) {
+
+ if (!$this->carddavBackend instanceof Backend\SyncSupport) {
+ return null;
+ }
+
+ return $this->carddavBackend->getChangesForAddressBook(
+ $this->addressBookInfo['id'],
+ $syncToken,
+ $syncLevel,
+ $limit
+ );
+
+ }
+}
diff --git a/vendor/sabre/dav/lib/Sabre/CardDAV/UserAddressBooks.php b/vendor/sabre/dav/lib/CardDAV/AddressBookHome.php
index b4af86147..ebc251832 100644
--- a/vendor/sabre/dav/lib/Sabre/CardDAV/UserAddressBooks.php
+++ b/vendor/sabre/dav/lib/CardDAV/AddressBookHome.php
@@ -3,18 +3,20 @@
namespace Sabre\CardDAV;
use Sabre\DAV;
+use Sabre\DAV\MkCol;
use Sabre\DAVACL;
+use Sabre\Uri;
/**
- * UserAddressBooks class
+ * AddressBook Home class
*
- * The UserAddressBooks collection contains a list of addressbooks associated with a user
+ * This collection contains a list of addressbooks associated with one user.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class UserAddressBooks extends DAV\Collection implements DAV\IExtendedCollection, DAVACL\IACL {
+class AddressBookHome extends DAV\Collection implements DAV\IExtendedCollection, DAVACL\IACL {
/**
* Principal uri
@@ -36,7 +38,7 @@ class UserAddressBooks extends DAV\Collection implements DAV\IExtendedCollection
* @param Backend\BackendInterface $carddavBackend
* @param string $principalUri
*/
- public function __construct(Backend\BackendInterface $carddavBackend, $principalUri) {
+ function __construct(Backend\BackendInterface $carddavBackend, $principalUri) {
$this->carddavBackend = $carddavBackend;
$this->principalUri = $principalUri;
@@ -48,9 +50,9 @@ class UserAddressBooks extends DAV\Collection implements DAV\IExtendedCollection
*
* @return string
*/
- public function getName() {
+ function getName() {
- list(,$name) = DAV\URLUtil::splitPath($this->principalUri);
+ list(, $name) = Uri\split($this->principalUri);
return $name;
}
@@ -61,7 +63,7 @@ class UserAddressBooks extends DAV\Collection implements DAV\IExtendedCollection
* @param string $name
* @return void
*/
- public function setName($name) {
+ function setName($name) {
throw new DAV\Exception\MethodNotAllowed();
@@ -72,7 +74,7 @@ class UserAddressBooks extends DAV\Collection implements DAV\IExtendedCollection
*
* @return void
*/
- public function delete() {
+ function delete() {
throw new DAV\Exception\MethodNotAllowed();
@@ -83,7 +85,7 @@ class UserAddressBooks extends DAV\Collection implements DAV\IExtendedCollection
*
* @return int
*/
- public function getLastModified() {
+ function getLastModified() {
return null;
@@ -98,7 +100,7 @@ class UserAddressBooks extends DAV\Collection implements DAV\IExtendedCollection
* @param resource $data
* @return void
*/
- public function createFile($filename, $data=null) {
+ function createFile($filename, $data = null) {
throw new DAV\Exception\MethodNotAllowed('Creating new files in this collection is not supported');
@@ -112,23 +114,23 @@ class UserAddressBooks extends DAV\Collection implements DAV\IExtendedCollection
* @param string $filename
* @return void
*/
- public function createDirectory($filename) {
+ 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 addressbook, by name
*
* @param string $name
* @todo needs optimizing
* @return \AddressBook
*/
- public function getChild($name) {
+ function getChild($name) {
- foreach($this->getChildren() as $child) {
- if ($name==$child->getName())
+ foreach ($this->getChildren() as $child) {
+ if ($name == $child->getName())
return $child;
}
@@ -141,11 +143,11 @@ class UserAddressBooks extends DAV\Collection implements DAV\IExtendedCollection
*
* @return array
*/
- public function getChildren() {
+ function getChildren() {
- $addressbooks = $this->carddavBackend->getAddressbooksForUser($this->principalUri);
- $objs = array();
- foreach($addressbooks as $addressbook) {
+ $addressbooks = $this->carddavBackend->getAddressBooksForUser($this->principalUri);
+ $objs = [];
+ foreach ($addressbooks as $addressbook) {
$objs[] = new AddressBook($this->carddavBackend, $addressbook);
}
return $objs;
@@ -153,18 +155,20 @@ class UserAddressBooks extends DAV\Collection implements DAV\IExtendedCollection
}
/**
- * Creates a new addressbook
+ * Creates a new address book.
*
* @param string $name
- * @param array $resourceType
- * @param array $properties
+ * @param MkCol $mkCol
+ * @throws DAV\Exception\InvalidResourceType
* @return void
*/
- public function createExtendedCollection($name, array $resourceType, array $properties) {
+ function createExtendedCollection($name, MkCol $mkCol) {
- if (!in_array('{'.Plugin::NS_CARDDAV.'}addressbook',$resourceType) || count($resourceType)!==2) {
+ 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);
}
@@ -176,7 +180,7 @@ class UserAddressBooks extends DAV\Collection implements DAV\IExtendedCollection
*
* @return string|null
*/
- public function getOwner() {
+ function getOwner() {
return $this->principalUri;
@@ -189,7 +193,7 @@ class UserAddressBooks extends DAV\Collection implements DAV\IExtendedCollection
*
* @return string|null
*/
- public function getGroup() {
+ function getGroup() {
return null;
@@ -207,21 +211,20 @@ class UserAddressBooks extends DAV\Collection implements DAV\IExtendedCollection
*
* @return array
*/
- public function getACL() {
+ function getACL() {
- return array(
- array(
+ return [
+ [
'privilege' => '{DAV:}read',
'principal' => $this->principalUri,
'protected' => true,
- ),
- array(
+ ],
+ [
'privilege' => '{DAV:}write',
'principal' => $this->principalUri,
'protected' => true,
- ),
-
- );
+ ],
+ ];
}
@@ -233,7 +236,7 @@ class UserAddressBooks extends DAV\Collection implements DAV\IExtendedCollection
* @param array $acl
* @return void
*/
- public function setACL(array $acl) {
+ function setACL(array $acl) {
throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported');
@@ -251,7 +254,7 @@ class UserAddressBooks extends DAV\Collection implements DAV\IExtendedCollection
*
* @return array|null
*/
- public function getSupportedPrivilegeSet() {
+ function getSupportedPrivilegeSet() {
return null;
diff --git a/vendor/sabre/dav/lib/Sabre/CardDAV/AddressBookRoot.php b/vendor/sabre/dav/lib/CardDAV/AddressBookRoot.php
index 789abbc5d..4a33df4ec 100644
--- a/vendor/sabre/dav/lib/Sabre/CardDAV/AddressBookRoot.php
+++ b/vendor/sabre/dav/lib/CardDAV/AddressBookRoot.php
@@ -9,7 +9,7 @@ use Sabre\DAVACL;
*
* This object lists a collection of users, which can contain addressbooks.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -43,7 +43,7 @@ class AddressBookRoot extends DAVACL\AbstractPrincipalCollection {
* @param Backend\BackendInterface $carddavBackend
* @param string $principalPrefix
*/
- public function __construct(DAVACL\PrincipalBackend\BackendInterface $principalBackend,Backend\BackendInterface $carddavBackend, $principalPrefix = 'principals') {
+ function __construct(DAVACL\PrincipalBackend\BackendInterface $principalBackend, Backend\BackendInterface $carddavBackend, $principalPrefix = 'principals') {
$this->carddavBackend = $carddavBackend;
parent::__construct($principalBackend, $principalPrefix);
@@ -55,7 +55,7 @@ class AddressBookRoot extends DAVACL\AbstractPrincipalCollection {
*
* @return string
*/
- public function getName() {
+ function getName() {
return Plugin::ADDRESSBOOK_ROOT;
@@ -71,9 +71,9 @@ class AddressBookRoot extends DAVACL\AbstractPrincipalCollection {
* @param array $principal
* @return \Sabre\DAV\INode
*/
- public function getChildForPrincipal(array $principal) {
+ function getChildForPrincipal(array $principal) {
- return new UserAddressBooks($this->carddavBackend, $principal['uri']);
+ return new AddressBookHome($this->carddavBackend, $principal['uri']);
}
diff --git a/vendor/sabre/dav/lib/CardDAV/Backend/AbstractBackend.php b/vendor/sabre/dav/lib/CardDAV/Backend/AbstractBackend.php
new file mode 100644
index 000000000..03d2346da
--- /dev/null
+++ b/vendor/sabre/dav/lib/CardDAV/Backend/AbstractBackend.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Sabre\CardDAV\Backend;
+
+/**
+ * CardDAV abstract Backend
+ *
+ * This class serves as a base-class for addressbook backends
+ *
+ * This class doesn't do much, but it was added for consistency.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class AbstractBackend implements BackendInterface {
+
+ /**
+ * Returns a list of cards.
+ *
+ * This method should work identical to getCard, but instead return all the
+ * cards in the list as an array.
+ *
+ * If the backend supports this, it may allow for some speed-ups.
+ *
+ * @param mixed $addressBookId
+ * @param array $uris
+ * @return array
+ */
+ function getMultipleCards($addressBookId, array $uris) {
+
+ return array_map(function($uri) use ($addressBookId) {
+ return $this->getCard($addressBookId, $uri);
+ }, $uris);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/CardDAV/Backend/BackendInterface.php b/vendor/sabre/dav/lib/CardDAV/Backend/BackendInterface.php
index 982da3a0f..b9691b906 100644
--- a/vendor/sabre/dav/lib/Sabre/CardDAV/Backend/BackendInterface.php
+++ b/vendor/sabre/dav/lib/CardDAV/Backend/BackendInterface.php
@@ -11,7 +11,7 @@ namespace Sabre\CardDAV\Backend;
* class. The value of the addressBookId is completely up to you, it can be any
* arbitrary value you can use as an unique identifier.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -34,20 +34,25 @@ interface BackendInterface {
* @param string $principalUri
* @return array
*/
- public function getAddressBooksForUser($principalUri);
+ function getAddressBooksForUser($principalUri);
/**
- * Updates an addressbook's properties
+ * Updates properties for an address book.
*
- * See Sabre\DAV\IProperties for a description of the mutations array, as
- * well as the return value.
+ * The list of mutations is stored in a Sabre\DAV\PropPatch object.
+ * To do the actual updates, you must tell this object which properties
+ * you're going to process with the handle() method.
*
- * @param mixed $addressBookId
- * @param array $mutations
- * @see Sabre\DAV\IProperties::updateProperties
- * @return bool|array
+ * Calling the handle method is like telling the PropPatch object "I
+ * promise I can handle updating this property".
+ *
+ * Read the PropPatch documenation for more info and examples.
+ *
+ * @param string $addressBookId
+ * @param \Sabre\DAV\PropPatch $propPatch
+ * @return void
*/
- public function updateAddressBook($addressBookId, array $mutations);
+ function updateAddressBook($addressBookId, \Sabre\DAV\PropPatch $propPatch);
/**
* Creates a new address book
@@ -57,7 +62,7 @@ interface BackendInterface {
* @param array $properties
* @return void
*/
- public function createAddressBook($principalUri, $url, array $properties);
+ function createAddressBook($principalUri, $url, array $properties);
/**
* Deletes an entire addressbook and all its contents
@@ -65,7 +70,7 @@ interface BackendInterface {
* @param mixed $addressBookId
* @return void
*/
- public function deleteAddressBook($addressBookId);
+ function deleteAddressBook($addressBookId);
/**
* Returns all cards for a specific addressbook id.
@@ -86,7 +91,7 @@ interface BackendInterface {
* @param mixed $addressbookId
* @return array
*/
- public function getCards($addressbookId);
+ function getCards($addressbookId);
/**
* Returns a specfic card.
@@ -94,17 +99,33 @@ interface BackendInterface {
* The same set of properties must be returned as with getCards. The only
* exception is that 'carddata' is absolutely required.
*
+ * If the card does not exist, you must return false.
+ *
* @param mixed $addressBookId
* @param string $cardUri
* @return array
*/
- public function getCard($addressBookId, $cardUri);
+ function getCard($addressBookId, $cardUri);
+
+ /**
+ * Returns a list of cards.
+ *
+ * This method should work identical to getCard, but instead return all the
+ * cards in the list as an array.
+ *
+ * If the backend supports this, it may allow for some speed-ups.
+ *
+ * @param mixed $addressBookId
+ * @param array $uris
+ * @return array
+ */
+ function getMultipleCards($addressBookId, array $uris);
/**
* Creates a new card.
*
* The addressbook id will be passed as the first argument. This is the
- * same id as it is returned from the getAddressbooksForUser method.
+ * same id as it is returned from the getAddressBooksForUser method.
*
* The cardUri is a base uri, and doesn't include the full path. The
* cardData argument is the vcard body, and is passed as a string.
@@ -125,13 +146,13 @@ interface BackendInterface {
* @param string $cardData
* @return string|null
*/
- public function createCard($addressBookId, $cardUri, $cardData);
+ function createCard($addressBookId, $cardUri, $cardData);
/**
* Updates a card.
*
* The addressbook id will be passed as the first argument. This is the
- * same id as it is returned from the getAddressbooksForUser method.
+ * same id as it is returned from the getAddressBooksForUser method.
*
* The cardUri is a base uri, and doesn't include the full path. The
* cardData argument is the vcard body, and is passed as a string.
@@ -152,7 +173,7 @@ interface BackendInterface {
* @param string $cardData
* @return string|null
*/
- public function updateCard($addressBookId, $cardUri, $cardData);
+ function updateCard($addressBookId, $cardUri, $cardData);
/**
* Deletes a card
@@ -161,6 +182,6 @@ interface BackendInterface {
* @param string $cardUri
* @return bool
*/
- public function deleteCard($addressBookId, $cardUri);
+ function deleteCard($addressBookId, $cardUri);
}
diff --git a/vendor/sabre/dav/lib/CardDAV/Backend/PDO.php b/vendor/sabre/dav/lib/CardDAV/Backend/PDO.php
new file mode 100644
index 000000000..5509ddc02
--- /dev/null
+++ b/vendor/sabre/dav/lib/CardDAV/Backend/PDO.php
@@ -0,0 +1,545 @@
+<?php
+
+namespace Sabre\CardDAV\Backend;
+
+use Sabre\CardDAV;
+use Sabre\DAV;
+
+/**
+ * PDO CardDAV backend
+ *
+ * This CardDAV backend uses PDO to store addressbooks
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PDO extends AbstractBackend implements SyncSupport {
+
+ /**
+ * PDO connection
+ *
+ * @var PDO
+ */
+ protected $pdo;
+
+ /**
+ * The PDO table name used to store addressbooks
+ */
+ public $addressBooksTableName = 'addressbooks';
+
+ /**
+ * The PDO table name used to store cards
+ */
+ public $cardsTableName = 'cards';
+
+ /**
+ * The table name that will be used for tracking changes in address books.
+ *
+ * @var string
+ */
+ public $addressBookChangesTableName = 'addressbookchanges';
+
+ /**
+ * Sets up the object
+ *
+ * @param \PDO $pdo
+ */
+ function __construct(\PDO $pdo) {
+
+ $this->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 = ?');
+ $stmt->execute([$principalUri]);
+
+ $addressBooks = [];
+
+ foreach ($stmt->fetchAll() as $row) {
+
+ $addressBooks[] = [
+ 'id' => $row['id'],
+ 'uri' => $row['uri'],
+ 'principaluri' => $row['principaluri'],
+ '{DAV:}displayname' => $row['displayname'],
+ '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
+ '{http://calendarserver.org/ns/}getctag' => $row['synctoken'],
+ '{http://sabredav.org/ns}sync-token' => $row['synctoken'] ? $row['synctoken'] : '0',
+ ];
+
+ }
+
+ return $addressBooks;
+
+ }
+
+
+ /**
+ * Updates properties for an address book.
+ *
+ * The list of mutations is stored in a Sabre\DAV\PropPatch object.
+ * To do the actual updates, you must tell this object which properties
+ * you're going to process with the handle() method.
+ *
+ * Calling the handle method is like telling the PropPatch object "I
+ * promise I can handle updating this property".
+ *
+ * Read the PropPatch documenation for more info and examples.
+ *
+ * @param string $addressBookId
+ * @param \Sabre\DAV\PropPatch $propPatch
+ * @return void
+ */
+ function updateAddressBook($addressBookId, \Sabre\DAV\PropPatch $propPatch) {
+
+ $supportedProperties = [
+ '{DAV:}displayname',
+ '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description',
+ ];
+
+ $propPatch->handle($supportedProperties, function($mutations) use ($addressBookId) {
+
+ $updates = [];
+ foreach ($mutations as $property => $newValue) {
+
+ switch ($property) {
+ case '{DAV:}displayname' :
+ $updates['displayname'] = $newValue;
+ break;
+ case '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' :
+ $updates['description'] = $newValue;
+ break;
+ }
+ }
+ $query = 'UPDATE ' . $this->addressBooksTableName . ' SET ';
+ $first = true;
+ foreach ($updates as $key => $value) {
+ if ($first) {
+ $first = false;
+ } else {
+ $query .= ', ';
+ }
+ $query .= ' `' . $key . '` = :' . $key . ' ';
+ }
+ $query .= ' WHERE id = :addressbookid';
+
+ $stmt = $this->pdo->prepare($query);
+ $updates['addressbookid'] = $addressBookId;
+
+ $stmt->execute($updates);
+
+ $this->addChange($addressBookId, "", 2);
+
+ return true;
+
+ });
+
+ }
+
+ /**
+ * Creates a new address book
+ *
+ * @param string $principalUri
+ * @param string $url Just the 'basename' of the url.
+ * @param array $properties
+ * @return int Last insert id
+ */
+ function createAddressBook($principalUri, $url, array $properties) {
+
+ $values = [
+ 'displayname' => null,
+ 'description' => null,
+ 'principaluri' => $principalUri,
+ 'uri' => $url,
+ ];
+
+ foreach ($properties as $property => $newValue) {
+
+ switch ($property) {
+ case '{DAV:}displayname' :
+ $values['displayname'] = $newValue;
+ break;
+ case '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' :
+ $values['description'] = $newValue;
+ break;
+ default :
+ throw new DAV\Exception\BadRequest('Unknown property: ' . $property);
+ }
+
+ }
+
+ $query = 'INSERT INTO ' . $this->addressBooksTableName . ' (uri, displayname, description, principaluri, synctoken) VALUES (:uri, :displayname, :description, :principaluri, 1)';
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute($values);
+ return $this->pdo->lastInsertId();
+
+ }
+
+ /**
+ * Deletes an entire addressbook and all its contents
+ *
+ * @param int $addressBookId
+ * @return void
+ */
+ function deleteAddressBook($addressBookId) {
+
+ $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?');
+ $stmt->execute([$addressBookId]);
+
+ $stmt = $this->pdo->prepare('DELETE FROM ' . $this->addressBooksTableName . ' WHERE id = ?');
+ $stmt->execute([$addressBookId]);
+
+ $stmt = $this->pdo->prepare('DELETE FROM ' . $this->addressBookChangesTableName . ' WHERE addressbookid = ?');
+ $stmt->execute([$addressBookId]);
+
+ }
+
+ /**
+ * Returns all cards for a specific addressbook id.
+ *
+ * This method should return the following properties for each card:
+ * * carddata - raw vcard data
+ * * uri - Some unique url
+ * * lastmodified - A unix timestamp
+ *
+ * It's recommended to also return the following properties:
+ * * etag - A unique etag. This must change every time the card changes.
+ * * size - The size of the card in bytes.
+ *
+ * If these last two properties are provided, less time will be spent
+ * calculating them. If they are specified, you can also ommit carddata.
+ * This may speed up certain requests, especially with large cards.
+ *
+ * @param mixed $addressbookId
+ * @return array
+ */
+ function getCards($addressbookId) {
+
+ $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, size FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?');
+ $stmt->execute([$addressbookId]);
+
+ $result = [];
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ $row['etag'] = '"' . $row['etag'] . '"';
+ $result[] = $row;
+ }
+ return $result;
+
+ }
+
+ /**
+ * Returns a specfic card.
+ *
+ * The same set of properties must be returned as with getCards. The only
+ * exception is that 'carddata' is absolutely required.
+ *
+ * If the card does not exist, you must return false.
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @return array
+ */
+ function getCard($addressBookId, $cardUri) {
+
+ $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified, etag, size FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ? LIMIT 1');
+ $stmt->execute([$addressBookId, $cardUri]);
+
+ $result = $stmt->fetch(\PDO::FETCH_ASSOC);
+
+ if (!$result) return false;
+
+ $result['etag'] = '"' . $result['etag'] . '"';
+ return $result;
+
+ }
+
+ /**
+ * Returns a list of cards.
+ *
+ * This method should work identical to getCard, but instead return all the
+ * cards in the list as an array.
+ *
+ * If the backend supports this, it may allow for some speed-ups.
+ *
+ * @param mixed $addressBookId
+ * @param array $uris
+ * @return array
+ */
+ function getMultipleCards($addressBookId, array $uris) {
+
+ $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 .= ')';
+
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute(array_merge([$addressBookId], $uris));
+ $result = [];
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ $row['etag'] = '"' . $row['etag'] . '"';
+ $result[] = $row;
+ }
+ return $result;
+
+ }
+
+ /**
+ * Creates a new card.
+ *
+ * The addressbook id will be passed as the first argument. This is the
+ * same id as it is returned from the getAddressBooksForUser method.
+ *
+ * The cardUri is a base uri, and doesn't include the full path. The
+ * cardData argument is the vcard body, and is passed as a string.
+ *
+ * It is possible to return an ETag from this method. This ETag is for the
+ * newly created resource, and must be enclosed with double quotes (that
+ * is, the string itself must contain the double quotes).
+ *
+ * You should only return the ETag if you store the carddata as-is. If a
+ * subsequent GET request on the same card does not have the same body,
+ * byte-by-byte and you did return an ETag here, clients tend to get
+ * confused.
+ *
+ * If you don't return an ETag, you can just return null.
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @param string $cardData
+ * @return string|null
+ */
+ function createCard($addressBookId, $cardUri, $cardData) {
+
+ $stmt = $this->pdo->prepare('INSERT INTO ' . $this->cardsTableName . ' (carddata, uri, lastmodified, addressbookid, size, etag) VALUES (?, ?, ?, ?, ?, ?)');
+
+ $etag = md5($cardData);
+
+ $stmt->execute([
+ $cardData,
+ $cardUri,
+ time(),
+ $addressBookId,
+ strlen($cardData),
+ $etag,
+ ]);
+
+ $this->addChange($addressBookId, $cardUri, 1);
+
+ return '"' . $etag . '"';
+
+ }
+
+ /**
+ * Updates a card.
+ *
+ * The addressbook id will be passed as the first argument. This is the
+ * same id as it is returned from the getAddressBooksForUser method.
+ *
+ * The cardUri is a base uri, and doesn't include the full path. The
+ * cardData argument is the vcard body, and is passed as a string.
+ *
+ * It is possible to return an ETag from this method. This ETag should
+ * match that of the updated resource, and must be enclosed with double
+ * quotes (that is: the string itself must contain the actual quotes).
+ *
+ * You should only return the ETag if you store the carddata as-is. If a
+ * subsequent GET request on the same card does not have the same body,
+ * byte-by-byte and you did return an ETag here, clients tend to get
+ * confused.
+ *
+ * If you don't return an ETag, you can just return null.
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @param string $cardData
+ * @return string|null
+ */
+ function updateCard($addressBookId, $cardUri, $cardData) {
+
+ $stmt = $this->pdo->prepare('UPDATE ' . $this->cardsTableName . ' SET carddata = ?, lastmodified = ?, size = ?, etag = ? WHERE uri = ? AND addressbookid =?');
+
+ $etag = md5($cardData);
+ $stmt->execute([
+ $cardData,
+ time(),
+ strlen($cardData),
+ $etag,
+ $cardUri,
+ $addressBookId
+ ]);
+
+ $this->addChange($addressBookId, $cardUri, 2);
+
+ return '"' . $etag . '"';
+
+ }
+
+ /**
+ * Deletes a card
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @return bool
+ */
+ function deleteCard($addressBookId, $cardUri) {
+
+ $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ?');
+ $stmt->execute([$addressBookId, $cardUri]);
+
+ $this->addChange($addressBookId, $cardUri, 3);
+
+ return $stmt->rowCount() === 1;
+
+ }
+
+ /**
+ * The getChanges method returns all the changes that have happened, since
+ * the specified syncToken in the specified address book.
+ *
+ * This function should return an array, such as the following:
+ *
+ * [
+ * 'syncToken' => 'The current synctoken',
+ * 'added' => [
+ * 'new.txt',
+ * ],
+ * 'modified' => [
+ * 'updated.txt',
+ * ],
+ * 'deleted' => [
+ * 'foo.php.bak',
+ * 'old.txt'
+ * ]
+ * ];
+ *
+ * The returned syncToken property should reflect the *current* syncToken
+ * of the addressbook, as reported in the {http://sabredav.org/ns}sync-token
+ * property. This is needed here too, to ensure the operation is atomic.
+ *
+ * If the $syncToken argument is specified as null, this is an initial
+ * sync, and all members should be reported.
+ *
+ * The modified property is an array of nodenames that have changed since
+ * the last token.
+ *
+ * The deleted property is an array with nodenames, that have been deleted
+ * from collection.
+ *
+ * The $syncLevel argument is basically the 'depth' of the report. If it's
+ * 1, you only have to report changes that happened only directly in
+ * immediate descendants. If it's 2, it should also include changes from
+ * the nodes below the child collections. (grandchildren)
+ *
+ * The $limit argument allows a client to specify how many results should
+ * be returned at most. If the limit is not specified, it should be treated
+ * as infinite.
+ *
+ * If the limit (infinite or not) is higher than you're willing to return,
+ * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
+ *
+ * If the syncToken is expired (due to data cleanup) or unknown, you must
+ * return null.
+ *
+ * The limit is 'suggestive'. You are free to ignore it.
+ *
+ * @param string $addressBookId
+ * @param string $syncToken
+ * @param int $syncLevel
+ * @param int $limit
+ * @return array
+ */
+ function getChangesForAddressBook($addressBookId, $syncToken, $syncLevel, $limit = null) {
+
+ // Current synctoken
+ $stmt = $this->pdo->prepare('SELECT synctoken FROM ' . $this->addressBooksTableName . ' WHERE id = ?');
+ $stmt->execute([ $addressBookId ]);
+ $currentToken = $stmt->fetchColumn(0);
+
+ if (is_null($currentToken)) return null;
+
+ $result = [
+ 'syncToken' => $currentToken,
+ 'added' => [],
+ 'modified' => [],
+ 'deleted' => [],
+ ];
+
+ if ($syncToken) {
+
+ $query = "SELECT uri, operation FROM " . $this->addressBookChangesTableName . " WHERE synctoken >= ? AND synctoken < ? AND addressbookid = ? ORDER BY synctoken";
+ if ($limit > 0) $query .= " LIMIT " . (int)$limit;
+
+ // Fetching all changes
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute([$syncToken, $currentToken, $addressBookId]);
+
+ $changes = [];
+
+ // This loop ensures that any duplicates are overwritten, only the
+ // last change on a node is relevant.
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+
+ $changes[$row['uri']] = $row['operation'];
+
+ }
+
+ foreach ($changes as $uri => $operation) {
+
+ switch ($operation) {
+ case 1:
+ $result['added'][] = $uri;
+ break;
+ case 2:
+ $result['modified'][] = $uri;
+ break;
+ case 3:
+ $result['deleted'][] = $uri;
+ break;
+ }
+
+ }
+ } else {
+ // No synctoken supplied, this is the initial sync.
+ $query = "SELECT uri FROM " . $this->cardsTableName . " WHERE addressbookid = ?";
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute([$addressBookId]);
+
+ $result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN);
+ }
+ return $result;
+
+ }
+
+ /**
+ * Adds a change record to the addressbookchanges table.
+ *
+ * @param mixed $addressBookId
+ * @param string $objectUri
+ * @param int $operation 1 = add, 2 = modify, 3 = delete
+ * @return void
+ */
+ protected function addChange($addressBookId, $objectUri, $operation) {
+
+ $stmt = $this->pdo->prepare('INSERT INTO ' . $this->addressBookChangesTableName . ' (uri, synctoken, addressbookid, operation) SELECT ?, synctoken, ?, ? FROM ' . $this->addressBooksTableName . ' WHERE id = ?');
+ $stmt->execute([
+ $objectUri,
+ $addressBookId,
+ $operation,
+ $addressBookId
+ ]);
+ $stmt = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET synctoken = synctoken + 1 WHERE id = ?');
+ $stmt->execute([
+ $addressBookId
+ ]);
+
+ }
+}
diff --git a/vendor/sabre/dav/lib/CardDAV/Backend/SyncSupport.php b/vendor/sabre/dav/lib/CardDAV/Backend/SyncSupport.php
new file mode 100644
index 000000000..f80618a8e
--- /dev/null
+++ b/vendor/sabre/dav/lib/CardDAV/Backend/SyncSupport.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Sabre\CardDAV\Backend;
+
+/**
+ * WebDAV-sync support for CardDAV backends.
+ *
+ * In order for backends to advertise support for WebDAV-sync, this interface
+ * must be implemented.
+ *
+ * Implementing this can result in a significant reduction of bandwidth and CPU
+ * time.
+ *
+ * For this to work, you _must_ return a {http://sabredav.org/ns}sync-token
+ * property from getAddressBooksForUser.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface SyncSupport extends BackendInterface {
+
+ /**
+ * The getChanges method returns all the changes that have happened, since
+ * the specified syncToken in the specified address book.
+ *
+ * This function should return an array, such as the following:
+ *
+ * [
+ * 'syncToken' => 'The current synctoken',
+ * 'added' => [
+ * 'new.txt',
+ * ],
+ * 'modified' => [
+ * 'modified.txt',
+ * ],
+ * 'deleted' => [
+ * 'foo.php.bak',
+ * 'old.txt'
+ * ]
+ * ];
+ *
+ * The returned syncToken property should reflect the *current* syncToken
+ * of the calendar, as reported in the {http://sabredav.org/ns}sync-token
+ * property. This is needed here too, to ensure the operation is atomic.
+ *
+ * If the $syncToken argument is specified as null, this is an initial
+ * sync, and all members should be reported.
+ *
+ * The modified property is an array of nodenames that have changed since
+ * the last token.
+ *
+ * The deleted property is an array with nodenames, that have been deleted
+ * from collection.
+ *
+ * The $syncLevel argument is basically the 'depth' of the report. If it's
+ * 1, you only have to report changes that happened only directly in
+ * immediate descendants. If it's 2, it should also include changes from
+ * the nodes below the child collections. (grandchildren)
+ *
+ * The $limit argument allows a client to specify how many results should
+ * be returned at most. If the limit is not specified, it should be treated
+ * as infinite.
+ *
+ * If the limit (infinite or not) is higher than you're willing to return,
+ * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
+ *
+ * If the syncToken is expired (due to data cleanup) or unknown, you must
+ * return null.
+ *
+ * The limit is 'suggestive'. You are free to ignore it.
+ *
+ * @param string $addressBookId
+ * @param string $syncToken
+ * @param int $syncLevel
+ * @param int $limit
+ * @return array
+ */
+ function getChangesForAddressBook($addressBookId, $syncToken, $syncLevel, $limit = null);
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/CardDAV/Card.php b/vendor/sabre/dav/lib/CardDAV/Card.php
index cc65f7600..8da672502 100644
--- a/vendor/sabre/dav/lib/Sabre/CardDAV/Card.php
+++ b/vendor/sabre/dav/lib/CardDAV/Card.php
@@ -5,11 +5,10 @@ namespace Sabre\CardDAV;
use Sabre\DAVACL;
use Sabre\DAV;
-
/**
* The Card object represents a single Card from an addressbook
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -43,7 +42,7 @@ class Card extends DAV\File implements ICard, DAVACL\IACL {
* @param array $addressBookInfo
* @param array $cardData
*/
- public function __construct(Backend\BackendInterface $carddavBackend,array $addressBookInfo,array $cardData) {
+ function __construct(Backend\BackendInterface $carddavBackend, array $addressBookInfo, array $cardData) {
$this->carddavBackend = $carddavBackend;
$this->addressBookInfo = $addressBookInfo;
@@ -56,7 +55,7 @@ class Card extends DAV\File implements ICard, DAVACL\IACL {
*
* @return string
*/
- public function getName() {
+ function getName() {
return $this->cardData['uri'];
@@ -67,7 +66,7 @@ class Card extends DAV\File implements ICard, DAVACL\IACL {
*
* @return string
*/
- public function get() {
+ function get() {
// Pre-populating 'carddata' is optional. If we don't yet have it
// already, we fetch it from the backend.
@@ -84,7 +83,7 @@ class Card extends DAV\File implements ICard, DAVACL\IACL {
* @param string $cardData
* @return string|null
*/
- public function put($cardData) {
+ function put($cardData) {
if (is_resource($cardData))
$cardData = stream_get_contents($cardData);
@@ -92,7 +91,7 @@ class Card extends DAV\File implements ICard, DAVACL\IACL {
// Converting to UTF-8, if needed
$cardData = DAV\StringUtil::ensureUTF8($cardData);
- $etag = $this->carddavBackend->updateCard($this->addressBookInfo['id'],$this->cardData['uri'],$cardData);
+ $etag = $this->carddavBackend->updateCard($this->addressBookInfo['id'], $this->cardData['uri'], $cardData);
$this->cardData['carddata'] = $cardData;
$this->cardData['etag'] = $etag;
@@ -105,9 +104,9 @@ class Card extends DAV\File implements ICard, DAVACL\IACL {
*
* @return void
*/
- public function delete() {
+ function delete() {
- $this->carddavBackend->deleteCard($this->addressBookInfo['id'],$this->cardData['uri']);
+ $this->carddavBackend->deleteCard($this->addressBookInfo['id'], $this->cardData['uri']);
}
@@ -116,9 +115,9 @@ class Card extends DAV\File implements ICard, DAVACL\IACL {
*
* @return string
*/
- public function getContentType() {
+ function getContentType() {
- return 'text/x-vcard; charset=utf-8';
+ return 'text/vcard; charset=utf-8';
}
@@ -127,7 +126,7 @@ class Card extends DAV\File implements ICard, DAVACL\IACL {
*
* @return string
*/
- public function getETag() {
+ function getETag() {
if (isset($this->cardData['etag'])) {
return $this->cardData['etag'];
@@ -148,9 +147,9 @@ class Card extends DAV\File implements ICard, DAVACL\IACL {
*
* @return int
*/
- public function getLastModified() {
+ function getLastModified() {
- return isset($this->cardData['lastmodified'])?$this->cardData['lastmodified']:null;
+ return isset($this->cardData['lastmodified']) ? $this->cardData['lastmodified'] : null;
}
@@ -159,7 +158,7 @@ class Card extends DAV\File implements ICard, DAVACL\IACL {
*
* @return int
*/
- public function getSize() {
+ function getSize() {
if (array_key_exists('size', $this->cardData)) {
return $this->cardData['size'];
@@ -176,7 +175,7 @@ class Card extends DAV\File implements ICard, DAVACL\IACL {
*
* @return string|null
*/
- public function getOwner() {
+ function getOwner() {
return $this->addressBookInfo['principaluri'];
@@ -189,7 +188,7 @@ class Card extends DAV\File implements ICard, DAVACL\IACL {
*
* @return string|null
*/
- public function getGroup() {
+ function getGroup() {
return null;
@@ -207,20 +206,25 @@ class Card extends DAV\File implements ICard, DAVACL\IACL {
*
* @return array
*/
- public function getACL() {
+ function getACL() {
+
+ // An alternative acl may be specified through the cardData array.
+ if (isset($this->cardData['acl'])) {
+ return $this->cardData['acl'];
+ }
- return array(
- array(
+ return [
+ [
'privilege' => '{DAV:}read',
'principal' => $this->addressBookInfo['principaluri'],
'protected' => true,
- ),
- array(
+ ],
+ [
'privilege' => '{DAV:}write',
'principal' => $this->addressBookInfo['principaluri'],
'protected' => true,
- ),
- );
+ ],
+ ];
}
@@ -232,7 +236,7 @@ class Card extends DAV\File implements ICard, DAVACL\IACL {
* @param array $acl
* @return void
*/
- public function setACL(array $acl) {
+ function setACL(array $acl) {
throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported');
@@ -250,11 +254,10 @@ class Card extends DAV\File implements ICard, DAVACL\IACL {
*
* @return array|null
*/
- public function getSupportedPrivilegeSet() {
+ function getSupportedPrivilegeSet() {
return null;
}
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/CardDAV/IAddressBook.php b/vendor/sabre/dav/lib/CardDAV/IAddressBook.php
index e9e990cbd..f80e05575 100644
--- a/vendor/sabre/dav/lib/Sabre/CardDAV/IAddressBook.php
+++ b/vendor/sabre/dav/lib/CardDAV/IAddressBook.php
@@ -9,12 +9,10 @@ use Sabre\DAV;
*
* Implement this interface to allow a node to be recognized as an addressbook.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
interface IAddressBook extends DAV\ICollection {
-
-
}
diff --git a/vendor/sabre/dav/lib/Sabre/CardDAV/ICard.php b/vendor/sabre/dav/lib/CardDAV/ICard.php
index e9a633132..a974cbd8f 100644
--- a/vendor/sabre/dav/lib/Sabre/CardDAV/ICard.php
+++ b/vendor/sabre/dav/lib/CardDAV/ICard.php
@@ -10,11 +10,10 @@ use Sabre\DAV;
* Extend the ICard interface to allow your custom nodes to be picked up as
* 'Cards'.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
interface ICard extends DAV\IFile {
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/CardDAV/IDirectory.php b/vendor/sabre/dav/lib/CardDAV/IDirectory.php
index c2774cb45..d991a1cc8 100644
--- a/vendor/sabre/dav/lib/Sabre/CardDAV/IDirectory.php
+++ b/vendor/sabre/dav/lib/CardDAV/IDirectory.php
@@ -11,11 +11,10 @@ namespace Sabre\CardDAV;
* A full description can be found in the IETF draft:
* - draft-daboo-carddav-directory-gateway
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
interface IDirectory extends IAddressBook {
-
}
diff --git a/vendor/sabre/dav/lib/CardDAV/Plugin.php b/vendor/sabre/dav/lib/CardDAV/Plugin.php
new file mode 100644
index 000000000..b8bded098
--- /dev/null
+++ b/vendor/sabre/dav/lib/CardDAV/Plugin.php
@@ -0,0 +1,871 @@
+<?php
+
+namespace Sabre\CardDAV;
+
+use Sabre\DAV;
+use Sabre\DAV\Exception\ReportNotSupported;
+use Sabre\DAV\Xml\Property\Href;
+use Sabre\DAVACL;
+use Sabre\HTTP;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+use Sabre\VObject;
+
+/**
+ * CardDAV plugin
+ *
+ * The CardDAV plugin adds CardDAV functionality to the WebDAV server
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Plugin extends DAV\ServerPlugin {
+
+ /**
+ * Url to the addressbooks
+ */
+ const ADDRESSBOOK_ROOT = 'addressbooks';
+
+ /**
+ * xml namespace for CardDAV elements
+ */
+ const NS_CARDDAV = 'urn:ietf:params:xml:ns:carddav';
+
+ /**
+ * Add urls to this property to have them automatically exposed as
+ * 'directories' to the user.
+ *
+ * @var array
+ */
+ public $directories = [];
+
+ /**
+ * Server class
+ *
+ * @var Sabre\DAV\Server
+ */
+ protected $server;
+
+ /**
+ * The default PDO storage uses a MySQL MEDIUMBLOB for iCalendar data,
+ * which can hold up to 2^24 = 16777216 bytes. This is plenty. We're
+ * capping it to 10M here.
+ */
+ protected $maxResourceSize = 10000000;
+
+ /**
+ * Initializes the plugin
+ *
+ * @param DAV\Server $server
+ * @return void
+ */
+ function initialize(DAV\Server $server) {
+
+ /* Events */
+ $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';
+
+ /* Mapping Interfaces to {DAV:}resourcetype values */
+ $server->resourceTypeMapping['Sabre\\CardDAV\\IAddressBook'] = '{' . self::NS_CARDDAV . '}addressbook';
+ $server->resourceTypeMapping['Sabre\\CardDAV\\IDirectory'] = '{' . self::NS_CARDDAV . '}directory';
+
+ /* Adding properties that may never be changed */
+ $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-address-data';
+ $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}max-resource-size';
+ $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}addressbook-home-set';
+ $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-collation-set';
+
+ $server->xml->elementMap['{http://calendarserver.org/ns/}me-card'] = 'Sabre\\DAV\\Xml\\Property\\Href';
+
+ $this->server = $server;
+
+ }
+
+ /**
+ * Returns a list of supported features.
+ *
+ * This is used in the DAV: header in the OPTIONS and PROPFIND requests.
+ *
+ * @return array
+ */
+ function getFeatures() {
+
+ return ['addressbook'];
+
+ }
+
+ /**
+ * Returns a list of reports this plugin supports.
+ *
+ * This will be used in the {DAV:}supported-report-set property.
+ * Note that you still need to subscribe to the 'report' event to actually
+ * implement them
+ *
+ * @param string $uri
+ * @return array
+ */
+ function getSupportedReportSet($uri) {
+
+ $node = $this->server->tree->getNodeForPath($uri);
+ if ($node instanceof IAddressBook || $node instanceof ICard) {
+ return [
+ '{' . self::NS_CARDDAV . '}addressbook-multiget',
+ '{' . self::NS_CARDDAV . '}addressbook-query',
+ ];
+ }
+ return [];
+
+ }
+
+
+ /**
+ * Adds all CardDAV-specific properties
+ *
+ * @param DAV\PropFind $propFind
+ * @param DAV\INode $node
+ * @return void
+ */
+ function propFindEarly(DAV\PropFind $propFind, DAV\INode $node) {
+
+ $ns = '{' . self::NS_CARDDAV . '}';
+
+ if ($node instanceof IAddressBook) {
+
+ $propFind->handle($ns . 'max-resource-size', $this->maxResourceSize);
+ $propFind->handle($ns . 'supported-address-data', function() {
+ return new Xml\Property\SupportedAddressData();
+ });
+ $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 Href($this->getAddressBookHomeForPrincipal($path) . '/');
+ });
+
+ if ($this->directories) $propFind->handle('{' . self::NS_CARDDAV . '}directory-gateway', function() {
+ return new Href($this->directories);
+ });
+
+ }
+
+ if ($node instanceof ICard) {
+
+ // The address-data property is not supposed to be a 'real'
+ // property, but in large chunks of the spec it does act as such.
+ // Therefore we simply expose it as a property.
+ $propFind->handle('{' . self::NS_CARDDAV . '}address-data', function() use ($node) {
+ $val = $node->get();
+ if (is_resource($val))
+ $val = stream_get_contents($val);
+
+ return $val;
+
+ });
+
+ }
+
+ }
+
+ /**
+ * This functions handles REPORT requests specific to CardDAV
+ *
+ * @param string $reportName
+ * @param \DOMNode $dom
+ * @param mixed $path
+ * @return bool
+ */
+ function report($reportName, $dom, $path) {
+
+ switch ($reportName) {
+ case '{' . self::NS_CARDDAV . '}addressbook-multiget' :
+ $this->server->transactionType = 'report-addressbook-multiget';
+ $this->addressbookMultiGetReport($dom);
+ return false;
+ case '{' . self::NS_CARDDAV . '}addressbook-query' :
+ $this->server->transactionType = 'report-addressbook-query';
+ $this->addressBookQueryReport($dom);
+ return false;
+ default :
+ return;
+
+ }
+
+
+ }
+
+ /**
+ * Returns the addressbook home for a given principal
+ *
+ * @param string $principal
+ * @return string
+ */
+ protected function getAddressbookHomeForPrincipal($principal) {
+
+ list(, $principalId) = \Sabre\HTTP\URLUtil::splitPath($principal);
+ return self::ADDRESSBOOK_ROOT . '/' . $principalId;
+
+ }
+
+
+ /**
+ * This function handles the addressbook-multiget REPORT.
+ *
+ * This report is used by the client to fetch the content of a series
+ * of urls. Effectively avoiding a lot of redundant requests.
+ *
+ * @param Xml\Request\AddressBookMultiGetReport $report
+ * @return void
+ */
+ function addressbookMultiGetReport($report) {
+
+ $contentType = $report->contentType;
+ $version = $report->version;
+ if ($version) {
+ $contentType .= '; version=' . $version;
+ }
+
+ $vcardType = $this->negotiateVCard(
+ $contentType
+ );
+
+ $propertyList = [];
+ $paths = array_map(
+ [$this->server, 'calculateUri'],
+ $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'],
+ $vcardType
+ );
+
+ }
+ $propertyList[] = $props;
+
+ }
+
+ $prefer = $this->server->getHTTPPrefer();
+
+ $this->server->httpResponse->setStatus(207);
+ $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
+ $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer');
+ $this->server->httpResponse->setBody($this->server->generateMultiStatus($propertyList, $prefer['return'] === 'minimal'));
+
+ }
+
+ /**
+ * This method is triggered before a file gets updated with new content.
+ *
+ * This plugin uses this method to ensure that Card nodes receive valid
+ * vcard data.
+ *
+ * @param string $path
+ * @param DAV\IFile $node
+ * @param resource $data
+ * @param bool $modified Should be set to true, if this event handler
+ * changed &$data.
+ * @return void
+ */
+ function beforeWriteContent($path, DAV\IFile $node, &$data, &$modified) {
+
+ if (!$node instanceof ICard)
+ return;
+
+ $this->validateVCard($data, $modified);
+
+ }
+
+ /**
+ * This method is triggered before a new file is created.
+ *
+ * This plugin uses this method to ensure that Card nodes receive valid
+ * vcard data.
+ *
+ * @param string $path
+ * @param resource $data
+ * @param DAV\ICollection $parentNode
+ * @param bool $modified Should be set to true, if this event handler
+ * changed &$data.
+ * @return void
+ */
+ function beforeCreateFile($path, &$data, DAV\ICollection $parentNode, &$modified) {
+
+ if (!$parentNode instanceof IAddressBook)
+ return;
+
+ $this->validateVCard($data, $modified);
+
+ }
+
+ /**
+ * Checks if the submitted iCalendar data is in fact, valid.
+ *
+ * An exception is thrown if it's not.
+ *
+ * @param resource|string $data
+ * @param bool $modified Should be set to true, if this event handler
+ * changed &$data.
+ * @return void
+ */
+ protected function validateVCard(&$data, &$modified) {
+
+ // If it's a stream, we convert it to a string first.
+ if (is_resource($data)) {
+ $data = stream_get_contents($data);
+ }
+
+ $before = md5($data);
+
+ // Converting the data to unicode, if needed.
+ $data = DAV\StringUtil::ensureUTF8($data);
+
+ if (md5($data) !== $before) $modified = true;
+
+ try {
+
+ // If the data starts with a [, we can reasonably assume we're dealing
+ // with a jCal object.
+ if (substr($data, 0, 1) === '[') {
+ $vobj = VObject\Reader::readJson($data);
+
+ // Converting $data back to iCalendar, as that's what we
+ // technically support everywhere.
+ $data = $vobj->serialize();
+ $modified = true;
+ } else {
+ $vobj = VObject\Reader::read($data);
+ }
+
+ } catch (VObject\ParseException $e) {
+
+ throw new DAV\Exception\UnsupportedMediaType('This resource only supports valid vCard or jCard data. Parse error: ' . $e->getMessage());
+
+ }
+
+ if ($vobj->name !== 'VCARD') {
+ throw new DAV\Exception\UnsupportedMediaType('This collection can only support vcard objects.');
+ }
+
+ if (!isset($vobj->UID)) {
+ // No UID in vcards is invalid, but we'll just add it in anyway.
+ $vobj->add('UID', DAV\UUIDUtil::getUUID());
+ $data = $vobj->serialize();
+ $modified = true;
+ }
+
+ // Destroy circular references to PHP will GC the object.
+ $vobj->destroy();
+ }
+
+
+ /**
+ * 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) {
+
+ $depth = $this->server->getHTTPDepth(0);
+
+ if ($depth == 0) {
+ $candidateNodes = [
+ $this->server->tree->getNodeForPath($this->server->getRequestUri())
+ ];
+ if (!$candidateNodes[0] instanceof ICard) {
+ throw new ReportNotSupported('The addressbook-query report is not supported on this url with Depth: 0');
+ }
+ } else {
+ $candidateNodes = $this->server->tree->getChildren($this->server->getRequestUri());
+ }
+
+ $contentType = $report->contentType;
+ if ($report->version) {
+ $contentType .= '; version=' . $report->version;
+ }
+
+ $vcardType = $this->negotiateVCard(
+ $contentType
+ );
+
+ $validNodes = [];
+ foreach ($candidateNodes as $node) {
+
+ if (!$node instanceof ICard)
+ continue;
+
+ $blob = $node->get();
+ if (is_resource($blob)) {
+ $blob = stream_get_contents($blob);
+ }
+
+ if (!$this->validateFilters($blob, $report->filters, $report->test)) {
+ continue;
+ }
+
+ $validNodes[] = $node;
+
+ if ($report->limit && $report->limit <= count($validNodes)) {
+ // We hit the maximum number of items, we can stop now.
+ break;
+ }
+
+ }
+
+ $result = [];
+ foreach ($validNodes as $validNode) {
+
+ if ($depth == 0) {
+ $href = $this->server->getRequestUri();
+ } else {
+ $href = $this->server->getRequestUri() . '/' . $validNode->getName();
+ }
+
+ list($props) = $this->server->getPropertiesForPath($href, $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'],
+ $vcardType
+ );
+
+ }
+ $result[] = $props;
+
+ }
+
+ $prefer = $this->server->getHTTPPrefer();
+
+ $this->server->httpResponse->setStatus(207);
+ $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
+ $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer');
+ $this->server->httpResponse->setBody($this->server->generateMultiStatus($result, $prefer['return'] === 'minimal'));
+
+ }
+
+ /**
+ * Validates if a vcard makes it throught a list of filters.
+ *
+ * @param string $vcardData
+ * @param array $filters
+ * @param string $test anyof or allof (which means OR or AND)
+ * @return bool
+ */
+ function validateFilters($vcardData, array $filters, $test) {
+
+
+ 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) {
+ $success = false;
+ } else {
+ $success = true;
+ }
+ } elseif ((!$filter['param-filters'] && !$filter['text-matches']) || !$isDefined) {
+
+ // We only need to check for existence
+ $success = $isDefined;
+
+ } else {
+
+ $vProperties = $vcard->select($filter['name']);
+
+ $results = [];
+ if ($filter['param-filters']) {
+ $results[] = $this->validateParamFilters($vProperties, $filter['param-filters'], $filter['test']);
+ }
+ if ($filter['text-matches']) {
+ $texts = [];
+ foreach ($vProperties as $vProperty)
+ $texts[] = $vProperty->getValue();
+
+ $results[] = $this->validateTextMatches($texts, $filter['text-matches'], $filter['test']);
+ }
+
+ if (count($results) === 1) {
+ $success = $results[0];
+ } else {
+ if ($filter['test'] === 'anyof') {
+ $success = $results[0] || $results[1];
+ } else {
+ $success = $results[0] && $results[1];
+ }
+ }
+
+ } // else
+
+ // There are two conditions where we can already determine whether
+ // or not this filter succeeds.
+ if ($test === 'anyof' && $success) {
+
+ // Destroy circular references to PHP will GC the object.
+ $vcard->destroy();
+
+ return true;
+ }
+ if ($test === 'allof' && !$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();
+
+ // If we got all the way here, it means we haven't been able to
+ // determine early if the test failed or not.
+ //
+ // This implies for 'anyof' that the test failed, and for 'allof' that
+ // we succeeded. Sounds weird, but makes sense.
+ return $test === 'allof';
+
+ }
+
+ /**
+ * Validates if a param-filter can be applied to a specific property.
+ *
+ * @todo currently we're only validating the first parameter of the passed
+ * property. Any subsequence parameters with the same name are
+ * ignored.
+ * @param array $vProperties
+ * @param array $filters
+ * @param string $test
+ * @return bool
+ */
+ protected function validateParamFilters(array $vProperties, array $filters, $test) {
+
+ foreach ($filters as $filter) {
+
+ $isDefined = false;
+ foreach ($vProperties as $vProperty) {
+ $isDefined = isset($vProperty[$filter['name']]);
+ if ($isDefined) break;
+ }
+
+ if ($filter['is-not-defined']) {
+ if ($isDefined) {
+ $success = false;
+ } else {
+ $success = true;
+ }
+
+ // If there's no text-match, we can just check for existence
+ } elseif (!$filter['text-match'] || !$isDefined) {
+
+ $success = $isDefined;
+
+ } else {
+
+ $success = false;
+ foreach ($vProperties as $vProperty) {
+ // If we got all the way here, we'll need to validate the
+ // text-match filter.
+ $success = DAV\StringUtil::textMatch($vProperty[$filter['name']]->getValue(), $filter['text-match']['value'], $filter['text-match']['collation'], $filter['text-match']['match-type']);
+ if ($success) break;
+ }
+ if ($filter['text-match']['negate-condition']) {
+ $success = !$success;
+ }
+
+ } // else
+
+ // There are two conditions where we can already determine whether
+ // or not this filter succeeds.
+ if ($test === 'anyof' && $success) {
+ return true;
+ }
+ if ($test === 'allof' && !$success) {
+ return false;
+ }
+
+ }
+
+ // If we got all the way here, it means we haven't been able to
+ // determine early if the test failed or not.
+ //
+ // This implies for 'anyof' that the test failed, and for 'allof' that
+ // we succeeded. Sounds weird, but makes sense.
+ return $test === 'allof';
+
+ }
+
+ /**
+ * Validates if a text-filter can be applied to a specific property.
+ *
+ * @param array $texts
+ * @param array $filters
+ * @param string $test
+ * @return bool
+ */
+ protected function validateTextMatches(array $texts, array $filters, $test) {
+
+ foreach ($filters as $filter) {
+
+ $success = false;
+ foreach ($texts as $haystack) {
+ $success = DAV\StringUtil::textMatch($haystack, $filter['value'], $filter['collation'], $filter['match-type']);
+
+ // Breaking on the first match
+ if ($success) break;
+ }
+ if ($filter['negate-condition']) {
+ $success = !$success;
+ }
+
+ if ($success && $test === 'anyof')
+ return true;
+
+ if (!$success && $test == 'allof')
+ return false;
+
+
+ }
+
+ // If we got all the way here, it means we haven't been able to
+ // determine early if the test failed or not.
+ //
+ // This implies for 'anyof' that the test failed, and for 'allof' that
+ // we succeeded. Sounds weird, but makes sense.
+ return $test === 'allof';
+
+ }
+
+ /**
+ * This event is triggered when fetching properties.
+ *
+ * This event is scheduled late in the process, after most work for
+ * propfind has been done.
+ *
+ * @param DAV\PropFind $propFind
+ * @param DAV\INode $node
+ * @return void
+ */
+ function propFindLate(DAV\PropFind $propFind, DAV\INode $node) {
+
+ // If the request was made using the SOGO connector, we must rewrite
+ // the content-type property. By default SabreDAV will send back
+ // text/x-vcard; charset=utf-8, but for SOGO we must strip that last
+ // part.
+ if (strpos($this->server->httpRequest->getHeader('User-Agent'), 'Thunderbird') === false) {
+ return;
+ }
+ $contentType = $propFind->get('{DAV:}getcontenttype');
+ list($part) = explode(';', $contentType);
+ if ($part === 'text/x-vcard' || $part === 'text/vcard') {
+ $propFind->set('{DAV:}getcontenttype', 'text/x-vcard');
+ }
+
+ }
+
+ /**
+ * This method is used to generate HTML output for the
+ * Sabre\DAV\Browser\Plugin. This allows us to generate an interface users
+ * can use to create new addressbooks.
+ *
+ * @param DAV\INode $node
+ * @param string $output
+ * @return bool
+ */
+ function htmlActionsPanel(DAV\INode $node, &$output) {
+
+ if (!$node instanceof AddressBookHome)
+ return;
+
+ $output .= '<tr><td colspan="2"><form method="post" action="">
+ <h3>Create new address book</h3>
+ <input type="hidden" name="sabreAction" value="mkcol" />
+ <input type="hidden" name="resourceType" value="{DAV:}collection,{' . self::NS_CARDDAV . '}addressbook" />
+ <label>Name (uri):</label> <input type="text" name="name" /><br />
+ <label>Display name:</label> <input type="text" name="{DAV:}displayname" /><br />
+ <input type="submit" value="create" />
+ </form>
+ </td></tr>';
+
+ return false;
+
+ }
+
+ /**
+ * This event is triggered after GET requests.
+ *
+ * This is used to transform data into jCal, if this was requested.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ function httpAfterGet(RequestInterface $request, ResponseInterface $response) {
+
+ if (strpos($response->getHeader('Content-Type'), 'text/vcard') === false) {
+ return;
+ }
+
+ $target = $this->negotiateVCard($request->getHeader('Accept'), $mimeType);
+
+ $newBody = $this->convertVCard(
+ $response->getBody(),
+ $target
+ );
+
+ $response->setBody($newBody);
+ $response->setHeader('Content-Type', $mimeType . '; charset=utf-8');
+ $response->setHeader('Content-Length', strlen($newBody));
+
+ }
+
+ /**
+ * This helper function performs the content-type negotiation for vcards.
+ *
+ * It will return one of the following strings:
+ * 1. vcard3
+ * 2. vcard4
+ * 3. jcard
+ *
+ * It defaults to vcard3.
+ *
+ * @param string $input
+ * @param string $mimeType
+ * @return string
+ */
+ protected function negotiateVCard($input, &$mimeType = null) {
+
+ $result = HTTP\Util::negotiate(
+ $input,
+ [
+ // Most often used mime-type. Version 3
+ 'text/x-vcard',
+ // The correct standard mime-type. Defaults to version 3 as
+ // well.
+ 'text/vcard',
+ // vCard 4
+ 'text/vcard; version=4.0',
+ // vCard 3
+ 'text/vcard; version=3.0',
+ // jCard
+ 'application/vcard+json',
+ ]
+ );
+
+ $mimeType = $result;
+ switch ($result) {
+
+ default :
+ case 'text/x-vcard' :
+ case 'text/vcard' :
+ case 'text/vcard; version=3.0' :
+ $mimeType = 'text/vcard';
+ return 'vcard3';
+ case 'text/vcard; version=4.0' :
+ return 'vcard4';
+ case 'application/vcard+json' :
+ return 'jcard';
+
+ // @codeCoverageIgnoreStart
+ }
+ // @codeCoverageIgnoreEnd
+
+ }
+
+ /**
+ * Converts a vcard blob to a different version, or jcard.
+ *
+ * @param string $data
+ * @param string $target
+ * @return string
+ */
+ protected function convertVCard($data, $target) {
+
+ $data = VObject\Reader::read($data);
+ switch ($target) {
+ default :
+ case 'vcard3' :
+ $data = $data->convert(VObject\Document::VCARD30);
+ $newResult = $data->serialize();
+ break;
+ case 'vcard4' :
+ $data = $data->convert(VObject\Document::VCARD40);
+ $newResult = $data->serialize();
+ break;
+ case 'jcard' :
+ $data = $data->convert(VObject\Document::VCARD40);
+ $newResult = json_encode($data->jsonSerialize());
+ break;
+
+ }
+ // Destroy circular references to PHP will GC the object.
+ $data->destroy();
+
+ return $newResult;
+
+ }
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'carddav';
+
+ }
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => 'Adds support for CardDAV (rfc6352)',
+ 'link' => 'http://sabre.io/dav/carddav/',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CardDAV/VCFExportPlugin.php b/vendor/sabre/dav/lib/CardDAV/VCFExportPlugin.php
new file mode 100644
index 000000000..de8b3bb84
--- /dev/null
+++ b/vendor/sabre/dav/lib/CardDAV/VCFExportPlugin.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace Sabre\CardDAV;
+
+use Sabre\DAV;
+use Sabre\VObject;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * VCF Exporter
+ *
+ * This plugin adds the ability to export entire address books as .vcf files.
+ * This is useful for clients that don't support CardDAV yet. They often do
+ * support vcf files.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @author Thomas Tanghus (http://tanghus.net/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VCFExportPlugin extends DAV\ServerPlugin {
+
+ /**
+ * Reference to Server class
+ *
+ * @var Sabre\DAV\Server
+ */
+ protected $server;
+
+ /**
+ * Initializes the plugin and registers event handlers
+ *
+ * @param DAV\Server $server
+ * @return void
+ */
+ function initialize(DAV\Server $server) {
+
+ $this->server = $server;
+ $this->server->on('method:GET', [$this, 'httpGet'], 90);
+ $server->on('browserButtonActions', function($path, $node, &$actions) {
+ if ($node instanceof IAddressBook) {
+ $actions .= '<a href="' . htmlspecialchars($path, ENT_QUOTES, 'UTF-8') . '?export"><span class="oi" data-glyph="book"></span></a>';
+ }
+ });
+ }
+
+ /**
+ * Intercepts GET requests on addressbook urls ending with ?export.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpGet(RequestInterface $request, ResponseInterface $response) {
+
+ $queryParams = $request->getQueryParameters();
+ if (!array_key_exists('export', $queryParams)) return;
+
+ $path = $request->getPath();
+
+ $node = $this->server->tree->getNodeForPath($path);
+
+ if (!($node instanceof IAddressBook)) return;
+
+ $this->server->transactionType = 'get-addressbook-export';
+
+ // Checking ACL, if available.
+ if ($aclPlugin = $this->server->getPlugin('acl')) {
+ $aclPlugin->checkPrivileges($path, '{DAV:}read');
+ }
+
+ $response->setHeader('Content-Type', 'text/directory');
+ $response->setStatus(200);
+
+ $nodes = $this->server->getPropertiesForPath($path, [
+ '{' . Plugin::NS_CARDDAV . '}address-data',
+ ], 1);
+
+ $response->setBody($this->generateVCF($nodes));
+
+ // Returning false to break the event chain
+ return false;
+
+ }
+
+ /**
+ * Merges all vcard objects, and builds one big vcf export
+ *
+ * @param array $nodes
+ * @return string
+ */
+ function generateVCF(array $nodes) {
+
+ $output = "";
+
+ foreach ($nodes as $node) {
+
+ if (!isset($node[200]['{' . Plugin::NS_CARDDAV . '}address-data'])) {
+ continue;
+ }
+ $nodeData = $node[200]['{' . Plugin::NS_CARDDAV . '}address-data'];
+
+ // Parsing this node so VObject can clean up the output.
+ $vcard = VObject\Reader::read($nodeData);
+ $output .= $vcard->serialize();
+
+ // Destroy circular references to PHP will GC the object.
+ $vcard->destroy();
+
+ }
+
+ return $output;
+
+ }
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using \Sabre\DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'vcf-export';
+
+ }
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => 'Adds the ability to export CardDAV addressbooks as a single vCard file.',
+ 'link' => 'http://sabre.io/dav/vcf-export-plugin/',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CardDAV/Xml/Filter/AddressData.php b/vendor/sabre/dav/lib/CardDAV/Xml/Filter/AddressData.php
new file mode 100644
index 000000000..34028db85
--- /dev/null
+++ b/vendor/sabre/dav/lib/CardDAV/Xml/Filter/AddressData.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Sabre\CardDAV\Xml\Filter;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+
+/**
+ * AddressData parser.
+ *
+ * This class parses the {urn:ietf:params:xml:ns:carddav}address-data XML
+ * element, as defined in:
+ *
+ * http://tools.ietf.org/html/rfc6352#section-10.4
+ *
+ * This element is used in two distinct places, but this one specifically
+ * encodes the address-data element as it appears in the addressbook-query
+ * adressbook-multiget REPORT requests.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class AddressData implements XmlDeserializable {
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $result = [
+ 'contentType' => $reader->getAttribute('content-type') ?: 'text/vcard',
+ 'version' => $reader->getAttribute('version') ?: '3.0',
+ ];
+
+ $reader->next();
+ return $result;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CardDAV/Xml/Filter/ParamFilter.php b/vendor/sabre/dav/lib/CardDAV/Xml/Filter/ParamFilter.php
new file mode 100644
index 000000000..9646ae3e6
--- /dev/null
+++ b/vendor/sabre/dav/lib/CardDAV/Xml/Filter/ParamFilter.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Sabre\CardDAV\Xml\Filter;
+
+use Sabre\Xml\Element;
+use Sabre\Xml\Reader;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\CardDAV\Plugin;
+
+/**
+ * ParamFilter parser.
+ *
+ * This class parses the {urn:ietf:params:xml:ns:carddav}param-filter XML
+ * element, as defined in:
+ *
+ * http://tools.ietf.org/html/rfc6352#section-10.5.2
+ *
+ * The result will be spit out as an array.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class ParamFilter implements Element {
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $result = [
+ 'name' => null,
+ 'is-not-defined' => false,
+ 'text-match' => null,
+ ];
+
+ $att = $reader->parseAttributes();
+ $result['name'] = $att['name'];
+
+ $elems = $reader->parseInnerTree();
+
+ 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' :
+ $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);
+ }
+ $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,
+ ];
+ break;
+
+ }
+
+ }
+
+ return $result;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CardDAV/Xml/Filter/PropFilter.php b/vendor/sabre/dav/lib/CardDAV/Xml/Filter/PropFilter.php
new file mode 100644
index 000000000..c162da160
--- /dev/null
+++ b/vendor/sabre/dav/lib/CardDAV/Xml/Filter/PropFilter.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Sabre\CardDAV\Xml\Filter;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\CardDAV\Plugin;
+
+/**
+ * PropFilter parser.
+ *
+ * This class parses the {urn:ietf:params:xml:ns:carddav}prop-filter XML
+ * element, as defined in:
+ *
+ * http://tools.ietf.org/html/rfc6352#section-10.5.1
+ *
+ * The result will be spit out as an array.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PropFilter implements XmlDeserializable {
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $result = [
+ 'name' => null,
+ 'test' => 'anyof',
+ 'is-not-defined' => false,
+ 'param-filters' => [],
+ 'text-matches' => [],
+ ];
+
+ $att = $reader->parseAttributes();
+ $result['name'] = $att['name'];
+
+ if (isset($att['test']) && $att['test'] === 'allof') {
+ $result['test'] = 'allof';
+ }
+
+ $elems = $reader->parseInnerTree();
+
+ 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' :
+ $result['is-not-defined'] = true;
+ break;
+ 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);
+ }
+ $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,
+ ];
+ break;
+
+ }
+
+ }
+
+ return $result;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CardDAV/Xml/Property/SupportedAddressData.php b/vendor/sabre/dav/lib/CardDAV/Xml/Property/SupportedAddressData.php
new file mode 100644
index 000000000..6ff57b6e3
--- /dev/null
+++ b/vendor/sabre/dav/lib/CardDAV/Xml/Property/SupportedAddressData.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Sabre\CardDAV\Xml\Property;
+
+use Sabre\Xml\Writer;
+use Sabre\Xml\XmlSerializable;
+use Sabre\CardDAV\Plugin;
+
+/**
+ * Supported-address-data property
+ *
+ * This property is a representation of the supported-address-data property
+ * in the CardDAV namespace.
+ *
+ * This property is defined in:
+ *
+ * http://tools.ietf.org/html/rfc6352#section-6.2.2
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class SupportedAddressData implements XmlSerializable {
+
+ /**
+ * supported versions
+ *
+ * @var array
+ */
+ protected $supportedData = [];
+
+ /**
+ * Creates the property
+ *
+ * @param array|null $supportedData
+ */
+ function __construct(array $supportedData = null) {
+
+ if (is_null($supportedData)) {
+ $supportedData = [
+ ['contentType' => 'text/vcard', 'version' => '3.0'],
+ ['contentType' => 'text/vcard', 'version' => '4.0'],
+ ['contentType' => 'application/vcard+json', 'version' => '4.0'],
+ ];
+ }
+
+ $this->supportedData = $supportedData;
+
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ foreach ($this->supportedData as $supported) {
+ $writer->startElement('{' . Plugin::NS_CARDDAV . '}address-data-type');
+ $writer->writeAttributes([
+ 'content-type' => $supported['contentType'],
+ 'version' => $supported['version']
+ ]);
+ $writer->endElement(); // address-data-type
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CardDAV/Xml/Property/SupportedCollationSet.php b/vendor/sabre/dav/lib/CardDAV/Xml/Property/SupportedCollationSet.php
new file mode 100644
index 000000000..1fc064900
--- /dev/null
+++ b/vendor/sabre/dav/lib/CardDAV/Xml/Property/SupportedCollationSet.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Sabre\CardDAV\Xml\Property;
+
+use Sabre\Xml\Writer;
+use Sabre\Xml\XmlSerializable;
+
+/**
+ * supported-collation-set property
+ *
+ * This property is a representation of the supported-collation-set property
+ * in the CardDAV namespace.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class SupportedCollationSet implements XmlSerializable {
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ foreach (['i;ascii-casemap', 'i;octet', 'i;unicode-casemap'] as $coll) {
+ $writer->writeElement('{urn:ietf:params:xml:ns:carddav}supported-collation', $coll);
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php b/vendor/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php
new file mode 100644
index 000000000..c97c5eb4f
--- /dev/null
+++ b/vendor/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace Sabre\CardDAV\Xml\Request;
+
+use Sabre\CardDAV\Plugin;
+use Sabre\Uri;
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+
+/**
+ * AddressBookMultiGetReport request parser.
+ *
+ * This class parses the {urn:ietf:params:xml:ns:carddav}addressbook-multiget
+ * REPORT, as defined in:
+ *
+ * http://tools.ietf.org/html/rfc6352#section-8.7
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class AddressBookMultiGetReport implements XmlDeserializable {
+
+ /**
+ * An array with requested properties.
+ *
+ * @var array
+ */
+ public $properties;
+
+ /**
+ * This is an array with the urls that are being requested.
+ *
+ * @var array
+ */
+ public $hrefs;
+
+ /**
+ * The mimetype of the content that should be returend. Usually
+ * text/vcard.
+ *
+ * @var string
+ */
+ public $contentType = null;
+
+ /**
+ * The version of vcard data that should be returned. Usually 3.0,
+ * referring to vCard 3.0.
+ *
+ * @var string
+ */
+ public $version = null;
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $elems = $reader->parseInnerTree([
+ '{urn:ietf:params:xml:ns:carddav}address-data' => 'Sabre\\CardDAV\\Xml\\Filter\\AddressData',
+ '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue',
+ ]);
+
+ $newProps = [
+ 'hrefs' => [],
+ 'properties' => []
+ ];
+
+ foreach ($elems as $elem) {
+
+ switch ($elem['name']) {
+
+ 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'];
+ }
+ break;
+ 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/vendor/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php b/vendor/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php
new file mode 100644
index 000000000..a68ac5800
--- /dev/null
+++ b/vendor/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php
@@ -0,0 +1,192 @@
+<?php
+
+namespace Sabre\CardDAV\Xml\Request;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\CardDAV\Plugin;
+
+/**
+ * AddressBookQueryReport request parser.
+ *
+ * This class parses the {urn:ietf:params:xml:ns:carddav}addressbook-query
+ * REPORT, as defined in:
+ *
+ * http://tools.ietf.org/html/rfc6352#section-8.6
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class AddressBookQueryReport implements XmlDeserializable {
+
+ /**
+ * An array with requested properties.
+ *
+ * @var array
+ */
+ public $properties;
+
+ /**
+ * List of property/component filters.
+ *
+ * This is an array with filters. Every item is a property filter. Every
+ * property filter has the following keys:
+ * * name - name of the component to filter on
+ * * test - anyof or allof
+ * * is-not-defined - Test for non-existence
+ * * param-filters - A list of parameter filters on the property
+ * * text-matches - A list of text values the filter needs to match
+ *
+ * Each param-filter has the following keys:
+ * * name - name of the parameter
+ * * is-not-defined - Test for non-existence
+ * * text-match - Match the parameter value
+ *
+ * Each text-match in property filters, and the single text-match in
+ * param-filters have the following keys:
+ *
+ * * value - value to match
+ * * match-type - contains, starts-with, ends-with, equals
+ * * negate-condition - Do the opposite match
+ * * collation - Usually i;unicode-casemap
+ *
+ * @var array
+ */
+ public $filters;
+
+ /**
+ * The number of results the client wants
+ *
+ * null means it wasn't specified, which in most cases means 'all results'.
+ *
+ * @var int|null
+ */
+ public $limit;
+
+ /**
+ * Either 'anyof' or 'allof'
+ *
+ * @var string
+ */
+ public $test;
+
+ /**
+ * The mimetype of the content that should be returend. Usually
+ * text/vcard.
+ *
+ * @var string
+ */
+ public $contentType = null;
+
+ /**
+ * The version of vcard data that should be returned. Usually 3.0,
+ * referring to vCard 3.0.
+ *
+ * @var string
+ */
+ public $version = null;
+
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ 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',
+ ]);
+
+ $newProps = [
+ 'filters' => null,
+ 'properties' => [],
+ 'test' => 'anyof',
+ 'limit' => null,
+ ];
+
+ if (!is_array($elems)) $elems = [];
+
+ foreach ($elems as $elem) {
+
+ switch ($elem['name']) {
+
+ 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'];
+ }
+ break;
+ case '{' . Plugin::NS_CARDDAV . '}filter' :
+
+ if (!is_null($newProps['filters'])) {
+ 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') {
+ 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') {
+ $newProps['filters'][] = $subElem['value'];
+ }
+ }
+ break;
+ case '{' . Plugin::NS_CARDDAV . '}limit' :
+ foreach ($elem['value'] as $child) {
+ if ($child['name'] === '{' . Plugin::NS_CARDDAV . '}nresults') {
+ $newProps['limit'] = (int)$child['value'];
+ }
+ }
+ break;
+
+ }
+
+ }
+
+ if (is_null($newProps['filters'])) {
+ /*
+ * We are supposed to throw this error, but KDE sometimes does not
+ * include the filter element, and we need to treat it as if no
+ * filters are supplied
+ */
+ //throw new BadRequest('The {' . Plugin::NS_CARDDAV . '}filter element is required for this request');
+ $newProps['filters'] = [];
+
+ }
+
+ $obj = new self();
+ foreach ($newProps as $key => $value) {
+ $obj->$key = $value;
+ }
+
+ return $obj;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractBasic.php b/vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractBasic.php
new file mode 100644
index 000000000..40a95f8bf
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractBasic.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace Sabre\DAV\Auth\Backend;
+
+use Sabre\DAV;
+use Sabre\HTTP;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * HTTP Basic authentication backend class
+ *
+ * This class can be used by authentication objects wishing to use HTTP Basic
+ * Most of the digest logic is handled, implementors just need to worry about
+ * the validateUserPass method.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author James David Low (http://jameslow.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class AbstractBasic implements BackendInterface {
+
+ /**
+ * Authentication Realm.
+ *
+ * The realm is often displayed by browser clients when showing the
+ * authentication dialog.
+ *
+ * @var string
+ */
+ protected $realm = 'sabre/dav';
+
+ /**
+ * This is the prefix that will be used to generate principal urls.
+ *
+ * @var string
+ */
+ protected $principalPrefix = 'principals/';
+
+ /**
+ * 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
+ */
+ abstract protected function validateUserPass($username, $password);
+
+ /**
+ * Sets the authentication realm for this backend.
+ *
+ * @param string $realm
+ * @return void
+ */
+ function setRealm($realm) {
+
+ $this->realm = $realm;
+
+ }
+
+ /**
+ * When this method is called, the backend must check if authentication was
+ * successful.
+ *
+ * The returned value must be one of the following
+ *
+ * [true, "principals/username"]
+ * [false, "reason for failure"]
+ *
+ * If authentication was successful, it's expected that the authentication
+ * backend returns a so-called principal url.
+ *
+ * Examples of a principal url:
+ *
+ * principals/admin
+ * principals/user1
+ * principals/users/joe
+ * principals/uid/123457
+ *
+ * If you don't use WebDAV ACL (RFC3744) we recommend that you simply
+ * return a string such as:
+ *
+ * principals/users/[username]
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return array
+ */
+ function check(RequestInterface $request, ResponseInterface $response) {
+
+ $auth = new HTTP\Auth\Basic(
+ $this->realm,
+ $request,
+ $response
+ );
+
+ $userpass = $auth->getCredentials();
+ if (!$userpass) {
+ 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 [true, $this->principalPrefix . $userpass[0]];
+
+ }
+
+ /**
+ * This method is called when a user could not be authenticated, and
+ * authentication was required for the current request.
+ *
+ * This gives you the opportunity to set authentication headers. The 401
+ * status code will already be set.
+ *
+ * In this case of Basic Auth, this would for example mean that the
+ * following header needs to be set:
+ *
+ * $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV');
+ *
+ * Keep in mind that in the case of multiple authentication backends, other
+ * WWW-Authenticate headers may already have been set, and you'll want to
+ * append your own WWW-Authenticate header instead of overwriting the
+ * existing one.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ function challenge(RequestInterface $request, ResponseInterface $response) {
+
+ $auth = new HTTP\Auth\Basic(
+ $this->realm,
+ $request,
+ $response
+ );
+ $auth->requireLogin();
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractBearer.php b/vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractBearer.php
new file mode 100644
index 000000000..ae7a8a12f
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractBearer.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace Sabre\DAV\Auth\Backend;
+
+use Sabre\DAV;
+use Sabre\HTTP;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * HTTP Bearer authentication backend class
+ *
+ * This class can be used by authentication objects wishing to use HTTP Bearer
+ * Most of the digest logic is handled, implementors just need to worry about
+ * the validateBearerToken method.
+ *
+ * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/).
+ * @author François Kooman (https://tuxed.net/)
+ * @author James David Low (http://jameslow.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class AbstractBearer implements BackendInterface {
+
+ /**
+ * Authentication Realm.
+ *
+ * The realm is often displayed by browser clients when showing the
+ * authentication dialog.
+ *
+ * @var string
+ */
+ protected $realm = 'sabre/dav';
+
+ /**
+ * Validates a Bearer token
+ *
+ * This method should return the full principal url, or false if the
+ * token was incorrect.
+ *
+ * @param string $bearerToken
+ * @return string|false
+ */
+ abstract protected function validateBearerToken($bearerToken);
+
+ /**
+ * Sets the authentication realm for this backend.
+ *
+ * @param string $realm
+ * @return void
+ */
+ function setRealm($realm) {
+
+ $this->realm = $realm;
+
+ }
+
+ /**
+ * When this method is called, the backend must check if authentication was
+ * successful.
+ *
+ * The returned value must be one of the following
+ *
+ * [true, "principals/username"]
+ * [false, "reason for failure"]
+ *
+ * If authentication was successful, it's expected that the authentication
+ * backend returns a so-called principal url.
+ *
+ * Examples of a principal url:
+ *
+ * principals/admin
+ * principals/user1
+ * principals/users/joe
+ * principals/uid/123457
+ *
+ * If you don't use WebDAV ACL (RFC3744) we recommend that you simply
+ * return a string such as:
+ *
+ * principals/users/[username]
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return array
+ */
+ function check(RequestInterface $request, ResponseInterface $response) {
+
+ $auth = new HTTP\Auth\Bearer(
+ $this->realm,
+ $request,
+ $response
+ );
+
+ $bearerToken = $auth->getToken($request);
+ if (!$bearerToken) {
+ return [false, "No 'Authorization: Bearer' header found. Either the client didn't send one, or the server is mis-configured"];
+ }
+ $principalUrl = $this->validateBearerToken($bearerToken);
+ if (!$principalUrl) {
+ return [false, "Bearer token was incorrect"];
+ }
+ return [true, $principalUrl];
+
+ }
+
+ /**
+ * This method is called when a user could not be authenticated, and
+ * authentication was required for the current request.
+ *
+ * This gives you the opportunity to set authentication headers. The 401
+ * status code will already be set.
+ *
+ * In this case of Bearer Auth, this would for example mean that the
+ * following header needs to be set:
+ *
+ * $response->addHeader('WWW-Authenticate', 'Bearer realm=SabreDAV');
+ *
+ * Keep in mind that in the case of multiple authentication backends, other
+ * WWW-Authenticate headers may already have been set, and you'll want to
+ * append your own WWW-Authenticate header instead of overwriting the
+ * existing one.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ function challenge(RequestInterface $request, ResponseInterface $response) {
+
+ $auth = new HTTP\Auth\Bearer(
+ $this->realm,
+ $request,
+ $response
+ );
+ $auth->requireLogin();
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php b/vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php
new file mode 100644
index 000000000..0251decc1
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace Sabre\DAV\Auth\Backend;
+
+use Sabre\HTTP;
+use Sabre\DAV;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * HTTP Digest authentication backend class
+ *
+ * This class can be used by authentication objects wishing to use HTTP Digest
+ * Most of the digest logic is handled, implementors just need to worry about
+ * the getDigestHash method
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class AbstractDigest implements BackendInterface {
+
+ /**
+ * Authentication Realm.
+ *
+ * The realm is often displayed by browser clients when showing the
+ * authentication dialog.
+ *
+ * @var string
+ */
+ protected $realm = 'SabreDAV';
+
+ /**
+ * This is the prefix that will be used to generate principal urls.
+ *
+ * @var string
+ */
+ protected $principalPrefix = 'principals/';
+
+ /**
+ * Sets the authentication realm for this backend.
+ *
+ * Be aware that for Digest authentication, the realm influences the digest
+ * hash. Choose the realm wisely, because if you change it later, all the
+ * existing hashes will break and nobody can authenticate.
+ *
+ * @param string $realm
+ * @return void
+ */
+ function setRealm($realm) {
+
+ $this->realm = $realm;
+
+ }
+
+ /**
+ * Returns a users digest hash based on the username and realm.
+ *
+ * If the user was not known, null must be returned.
+ *
+ * @param string $realm
+ * @param string $username
+ * @return string|null
+ */
+ abstract function getDigestHash($realm, $username);
+
+ /**
+ * When this method is called, the backend must check if authentication was
+ * successful.
+ *
+ * The returned value must be one of the following
+ *
+ * [true, "principals/username"]
+ * [false, "reason for failure"]
+ *
+ * If authentication was successful, it's expected that the authentication
+ * backend returns a so-called principal url.
+ *
+ * Examples of a principal url:
+ *
+ * principals/admin
+ * principals/user1
+ * principals/users/joe
+ * principals/uid/123457
+ *
+ * If you don't use WebDAV ACL (RFC3744) we recommend that you simply
+ * return a string such as:
+ *
+ * principals/users/[username]
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return array
+ */
+ function check(RequestInterface $request, ResponseInterface $response) {
+
+ $digest = new HTTP\Auth\Digest(
+ $this->realm,
+ $request,
+ $response
+ );
+ $digest->init();
+
+ $username = $digest->getUsername();
+
+ // No username was given
+ if (!$username) {
+ return [false, "No 'Authorization: Digest' header found. Either the client didn't send one, or the server is misconfigured"];
+ }
+
+ $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 (!is_string($hash)) {
+ throw new DAV\Exception('The returned value from getDigestHash must be a string or null');
+ }
+
+ // If this was false, the password or part of the hash was incorrect.
+ if (!$digest->validateA1($hash)) {
+ return [false, "Username or password was incorrect"];
+ }
+
+ return [true, $this->principalPrefix . $username];
+
+ }
+
+ /**
+ * This method is called when a user could not be authenticated, and
+ * authentication was required for the current request.
+ *
+ * This gives you the opportunity to set authentication headers. The 401
+ * status code will already be set.
+ *
+ * In this case of Basic Auth, this would for example mean that the
+ * following header needs to be set:
+ *
+ * $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV');
+ *
+ * Keep in mind that in the case of multiple authentication backends, other
+ * WWW-Authenticate headers may already have been set, and you'll want to
+ * append your own WWW-Authenticate header instead of overwriting the
+ * existing one.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ function challenge(RequestInterface $request, ResponseInterface $response) {
+
+ $auth = new HTTP\Auth\Digest(
+ $this->realm,
+ $request,
+ $response
+ );
+ $auth->init();
+ $auth->requireLogin();
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Auth/Backend/Apache.php b/vendor/sabre/dav/lib/DAV/Auth/Backend/Apache.php
new file mode 100644
index 000000000..e203d2685
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Auth/Backend/Apache.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Sabre\DAV\Auth\Backend;
+
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * Apache authenticator
+ *
+ * This authentication backend assumes that authentication has been
+ * configured in apache, rather than within SabreDAV.
+ *
+ * Make sure apache is properly configured for this to work.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Apache implements BackendInterface {
+
+ /**
+ * This is the prefix that will be used to generate principal urls.
+ *
+ * @var string
+ */
+ protected $principalPrefix = 'principals/';
+
+ /**
+ * When this method is called, the backend must check if authentication was
+ * successful.
+ *
+ * The returned value must be one of the following
+ *
+ * [true, "principals/username"]
+ * [false, "reason for failure"]
+ *
+ * If authentication was successful, it's expected that the authentication
+ * backend returns a so-called principal url.
+ *
+ * Examples of a principal url:
+ *
+ * principals/admin
+ * principals/user1
+ * principals/users/joe
+ * principals/uid/123457
+ *
+ * If you don't use WebDAV ACL (RFC3744) we recommend that you simply
+ * return a string such as:
+ *
+ * principals/users/[username]
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return array
+ */
+ function check(RequestInterface $request, ResponseInterface $response) {
+
+ $remoteUser = $request->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'];
+ }
+
+ return [true, $this->principalPrefix . $remoteUser];
+
+ }
+
+ /**
+ * This method is called when a user could not be authenticated, and
+ * authentication was required for the current request.
+ *
+ * This gives you the opportunity to set authentication headers. The 401
+ * status code will already be set.
+ *
+ * In this case of Basic Auth, this would for example mean that the
+ * following header needs to be set:
+ *
+ * $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV');
+ *
+ * Keep in mind that in the case of multiple authentication backends, other
+ * WWW-Authenticate headers may already have been set, and you'll want to
+ * append your own WWW-Authenticate header instead of overwriting the
+ * existing one.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ function challenge(RequestInterface $request, ResponseInterface $response) {
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Auth/Backend/BackendInterface.php b/vendor/sabre/dav/lib/DAV/Auth/Backend/BackendInterface.php
new file mode 100644
index 000000000..0fb2210f4
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Auth/Backend/BackendInterface.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Sabre\DAV\Auth\Backend;
+
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * This is the base class for any authentication object.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface BackendInterface {
+
+ /**
+ * When this method is called, the backend must check if authentication was
+ * successful.
+ *
+ * The returned value must be one of the following
+ *
+ * [true, "principals/username"]
+ * [false, "reason for failure"]
+ *
+ * If authentication was successful, it's expected that the authentication
+ * backend returns a so-called principal url.
+ *
+ * Examples of a principal url:
+ *
+ * principals/admin
+ * principals/user1
+ * principals/users/joe
+ * principals/uid/123457
+ *
+ * If you don't use WebDAV ACL (RFC3744) we recommend that you simply
+ * return a string such as:
+ *
+ * principals/users/[username]
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return array
+ */
+ function check(RequestInterface $request, ResponseInterface $response);
+
+ /**
+ * This method is called when a user could not be authenticated, and
+ * authentication was required for the current request.
+ *
+ * This gives you the opportunity to set authentication headers. The 401
+ * status code will already be set.
+ *
+ * In this case of Basic Auth, this would for example mean that the
+ * following header needs to be set:
+ *
+ * $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV');
+ *
+ * Keep in mind that in the case of multiple authentication backends, other
+ * WWW-Authenticate headers may already have been set, and you'll want to
+ * append your own WWW-Authenticate header instead of overwriting the
+ * existing one.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ function challenge(RequestInterface $request, ResponseInterface $response);
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Auth/Backend/BasicCallBack.php b/vendor/sabre/dav/lib/DAV/Auth/Backend/BasicCallBack.php
new file mode 100644
index 000000000..7ad8f48b2
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Auth/Backend/BasicCallBack.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Sabre\DAV\Auth\Backend;
+
+/**
+ * Extremely simply HTTP Basic auth backend.
+ *
+ * This backend basically works by calling a callback, which receives a
+ * username and password.
+ * The callback must return true or false depending on if authentication was
+ * correct.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class BasicCallBack extends AbstractBasic {
+
+ /**
+ * Callback
+ *
+ * @var callable
+ */
+ protected $callBack;
+
+ /**
+ * Creates the backend.
+ *
+ * A callback must be provided to handle checking the username and
+ * password.
+ *
+ * @param callable $callBack
+ * @return void
+ */
+ function __construct(callable $callBack) {
+
+ $this->callBack = $callBack;
+
+ }
+
+ /**
+ * Validates a username and password
+ *
+ * This method should return true or false depending on if login
+ * succeeded.
+ *
+ * @param string $username
+ * @param string $password
+ * @return bool
+ */
+ protected function validateUserPass($username, $password) {
+
+ $cb = $this->callBack;
+ return $cb($username, $password);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/File.php b/vendor/sabre/dav/lib/DAV/Auth/Backend/File.php
index a8e913614..6756e68df 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/File.php
+++ b/vendor/sabre/dav/lib/DAV/Auth/Backend/File.php
@@ -9,7 +9,7 @@ use Sabre\DAV;
*
* The backend file must conform to Apache's htdigest format
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -20,7 +20,7 @@ class File extends AbstractDigest {
*
* @var array
*/
- protected $users = array();
+ protected $users = [];
/**
* Creates the backend object.
@@ -29,7 +29,7 @@ class File extends AbstractDigest {
*
* @param string|null $filename
*/
- public function __construct($filename=null) {
+ function __construct($filename = null) {
if (!is_null($filename))
$this->loadFile($filename);
@@ -43,14 +43,14 @@ class File extends AbstractDigest {
* @param string $filename
* @return void
*/
- public function loadFile($filename) {
+ function loadFile($filename) {
- foreach(file($filename,FILE_IGNORE_NEW_LINES) as $line) {
+ foreach (file($filename, FILE_IGNORE_NEW_LINES) as $line) {
if (substr_count($line, ":") !== 2)
throw new DAV\Exception('Malformed htdigest file. Every line should contain 2 colons');
- list($username,$realm,$A1) = explode(':',$line);
+ list($username, $realm, $A1) = explode(':', $line);
if (!preg_match('/^[a-zA-Z0-9]{32}$/', $A1))
throw new DAV\Exception('Malformed htdigest file. Invalid md5 hash');
@@ -68,9 +68,9 @@ class File extends AbstractDigest {
* @param string $username
* @return string
*/
- public function getDigestHash($realm, $username) {
+ function getDigestHash($realm, $username) {
- return isset($this->users[$realm . ':' . $username])?$this->users[$realm . ':' . $username]:false;
+ return isset($this->users[$realm . ':' . $username]) ? $this->users[$realm . ':' . $username] : false;
}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/PDO.php b/vendor/sabre/dav/lib/DAV/Auth/Backend/PDO.php
index f153d8429..76ad89391 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/PDO.php
+++ b/vendor/sabre/dav/lib/DAV/Auth/Backend/PDO.php
@@ -3,11 +3,9 @@
namespace Sabre\DAV\Auth\Backend;
/**
- * This is an authentication backend that uses a file to manage passwords.
+ * This is an authentication backend that uses a database to manage passwords.
*
- * The backend file must conform to Apache's htdigest format
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -25,7 +23,7 @@ class PDO extends AbstractDigest {
*
* @var string
*/
- protected $tableName;
+ public $tableName = 'users';
/**
@@ -34,12 +32,10 @@ class PDO extends AbstractDigest {
* If the filename argument is passed in, it will parse out the specified file fist.
*
* @param PDO $pdo
- * @param string $tableName The PDO table name to use
*/
- public function __construct(\PDO $pdo, $tableName = 'users') {
+ function __construct(\PDO $pdo) {
$this->pdo = $pdo;
- $this->tableName = $tableName;
}
@@ -50,15 +46,11 @@ class PDO extends AbstractDigest {
* @param string $username
* @return string|null
*/
- public function getDigestHash($realm,$username) {
-
- $stmt = $this->pdo->prepare('SELECT username, digesta1 FROM '.$this->tableName.' WHERE username = ?');
- $stmt->execute(array($username));
- $result = $stmt->fetchAll();
-
- if (!count($result)) return;
+ function getDigestHash($realm, $username) {
- return $result[0]['digesta1'];
+ $stmt = $this->pdo->prepare('SELECT digesta1 FROM ' . $this->tableName . ' WHERE username = ?');
+ $stmt->execute([$username]);
+ return $stmt->fetchColumn() ?: null;
}
diff --git a/vendor/sabre/dav/lib/DAV/Auth/Plugin.php b/vendor/sabre/dav/lib/DAV/Auth/Plugin.php
new file mode 100644
index 000000000..818d8a4ad
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Auth/Plugin.php
@@ -0,0 +1,213 @@
+<?php
+
+namespace Sabre\DAV\Auth;
+
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+use Sabre\HTTP\URLUtil;
+use Sabre\DAV\Exception\NotAuthenticated;
+use Sabre\DAV\Server;
+use Sabre\DAV\ServerPlugin;
+
+/**
+ * This plugin provides Authentication for a WebDAV server.
+ *
+ * It works by providing a Auth\Backend class. Several examples of these
+ * classes can be found in the Backend directory.
+ *
+ * It's possible to provide more than one backend to this plugin. If more than
+ * one backend was provided, each backend will attempt to authenticate. Only if
+ * all backends fail, we throw a 401.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Plugin extends ServerPlugin {
+
+ /**
+ * authentication backends
+ */
+ protected $backends;
+
+ /**
+ * The currently logged in principal. Will be `null` if nobody is currently
+ * logged in.
+ *
+ * @var string|null
+ */
+ protected $currentPrincipal;
+
+ /**
+ * Creates the authentication plugin
+ *
+ * @param Backend\BackendInterface $authBackend
+ */
+ function __construct(Backend\BackendInterface $authBackend = null) {
+
+ if (!is_null($authBackend)) {
+ $this->addBackend($authBackend);
+ }
+
+ }
+
+ /**
+ * Adds an authentication backend to the plugin.
+ *
+ * @param Backend\BackendInterface $authBackend
+ * @return void
+ */
+ function addBackend(Backend\BackendInterface $authBackend) {
+
+ $this->backends[] = $authBackend;
+
+ }
+
+ /**
+ * 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);
+
+ }
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'auth';
+
+ }
+
+ /**
+ * Returns the currently logged-in principal.
+ *
+ * This will return a string such as:
+ *
+ * principals/username
+ * principals/users/username
+ *
+ * This method will return null if nobody is logged in.
+ *
+ * @return string|null
+ */
+ function getCurrentPrincipal() {
+
+ return $this->currentPrincipal;
+
+ }
+
+ /**
+ * Returns the current username.
+ *
+ * This method is deprecated and is only kept for backwards compatibility
+ * purposes. Please switch to getCurrentPrincipal().
+ *
+ * @deprecated Will be removed in a future version!
+ * @return string|null
+ */
+ function getCurrentUser() {
+
+ // We just do a 'basename' on the principal to give back a sane value
+ // here.
+ list(, $userName) = URLUtil::splitPath(
+ $this->getCurrentPrincipal()
+ );
+
+ return $userName;
+
+ }
+
+ /**
+ * This method is called before any HTTP method and forces users to be authenticated
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function beforeMethod(RequestInterface $request, ResponseInterface $response) {
+
+ 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.
+ //
+ // We don't want to authenticate users twice, so we simply don't do
+ // anything here. See Issue #700 for additional reasoning.
+ //
+ // This is not a perfect solution, but will be fixed once the
+ // "currently authenticated principal" is information that's not
+ // not associated with the plugin, but rather per-request.
+ //
+ // See issue #580 for more information about that.
+ return;
+
+ }
+ 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])) {
+ throw new \Sabre\DAV\Exception('The authentication backend did not return a correct value from the check() method.');
+ }
+
+ if ($result[0]) {
+ $this->currentPrincipal = $result[1];
+ // Exit early
+ return;
+ }
+ $reasons[] = $result[1];
+
+ }
+
+ // If we got here, it means that no authentication backend was
+ // successful in authenticating the user.
+ $this->currentPrincipal = null;
+
+ foreach ($this->backends as $backend) {
+ $backend->challenge($request, $response);
+ }
+ throw new NotAuthenticated(implode(', ', $reasons));
+
+ }
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => 'Generic authentication plugin',
+ 'link' => 'http://sabre.io/dav/authentication/',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Browser/GuessContentType.php b/vendor/sabre/dav/lib/DAV/Browser/GuessContentType.php
index 9fd47b930..01cddc230 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Browser/GuessContentType.php
+++ b/vendor/sabre/dav/lib/DAV/Browser/GuessContentType.php
@@ -2,7 +2,10 @@
namespace Sabre\DAV\Browser;
+use Sabre\HTTP\URLUtil;
use Sabre\DAV;
+use Sabre\DAV\PropFind;
+use Sabre\DAV\Inode;
/**
* GuessContentType plugin
@@ -15,7 +18,7 @@ use Sabre\DAV;
* so this extension does what the rest of the world does, and guesses it based
* on the file extension.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -28,7 +31,7 @@ class GuessContentType extends DAV\ServerPlugin {
*
* @var array
*/
- public $extensionMap = array(
+ public $extensionMap = [
// images
'jpg' => 'image/jpeg',
@@ -37,12 +40,12 @@ class GuessContentType extends DAV\ServerPlugin {
// groupware
'ics' => 'text/calendar',
- 'vcf' => 'text/x-vcard',
+ 'vcf' => 'text/vcard',
// text
'txt' => 'text/plain',
- );
+ ];
/**
* Initializes the plugin
@@ -50,34 +53,31 @@ class GuessContentType extends DAV\ServerPlugin {
* @param DAV\Server $server
* @return void
*/
- public function initialize(DAV\Server $server) {
+ function initialize(DAV\Server $server) {
// Using a relatively low priority (200) to allow other extensions
// to set the content-type first.
- $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties'),200);
+ $server->on('propFind', [$this, 'propFind'], 200);
}
/**
- * Handler for teh afterGetProperties event
+ * Our PROPFIND handler
*
- * @param string $path
- * @param array $properties
+ * Here we set a contenttype, if the node didn't already have one.
+ *
+ * @param PropFind $propFind
+ * @param INode $node
* @return void
*/
- public function afterGetProperties($path, &$properties) {
-
- if (array_key_exists('{DAV:}getcontenttype', $properties[404])) {
+ function propFind(PropFind $propFind, INode $node) {
- list(, $fileName) = DAV\URLUtil::splitPath($path);
- $contentType = $this->getContentType($fileName);
+ $propFind->handle('{DAV:}getcontenttype', function() use ($propFind) {
- if ($contentType) {
- $properties[200]['{DAV:}getcontenttype'] = $contentType;
- unset($properties[404]['{DAV:}getcontenttype']);
- }
+ list(, $fileName) = URLUtil::splitPath($propFind->getPath());
+ return $this->getContentType($fileName);
- }
+ });
}
@@ -90,9 +90,11 @@ class GuessContentType extends DAV\ServerPlugin {
protected function getContentType($fileName) {
// Just grabbing the extension
- $extension = strtolower(substr($fileName,strrpos($fileName,'.')+1));
- if (isset($this->extensionMap[$extension]))
+ $extension = strtolower(substr($fileName, strrpos($fileName, '.') + 1));
+ if (isset($this->extensionMap[$extension])) {
return $this->extensionMap[$extension];
+ }
+ return 'application/octet-stream';
}
diff --git a/vendor/sabre/dav/lib/DAV/Browser/HtmlOutput.php b/vendor/sabre/dav/lib/DAV/Browser/HtmlOutput.php
new file mode 100644
index 000000000..f4be6b348
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Browser/HtmlOutput.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Sabre\DAV\Browser;
+
+/**
+ * WebDAV properties that implement this interface are able to generate their
+ * own html output for the browser plugin.
+ *
+ * This is only useful for display purposes, and might make it a bit easier for
+ * people to read and understand the value of some properties.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface HtmlOutput {
+
+ /**
+ * Generate html representation for this value.
+ *
+ * The html output is 100% trusted, and no effort is being made to sanitize
+ * it. It's up to the implementor to sanitize user provided values.
+ *
+ * The output must be in UTF-8.
+ *
+ * The baseUri parameter is a url to the root of the application, and can
+ * be used to construct local links.
+ *
+ * @param HtmlOutputHelper $html
+ * @return string
+ */
+ function toHtml(HtmlOutputHelper $html);
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Browser/HtmlOutputHelper.php b/vendor/sabre/dav/lib/DAV/Browser/HtmlOutputHelper.php
new file mode 100644
index 000000000..249d54047
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Browser/HtmlOutputHelper.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace Sabre\DAV\Browser;
+
+use Sabre\Uri;
+use Sabre\Xml\Service as XmlService;
+
+/**
+ * This class provides a few utility functions for easily generating HTML for
+ * the browser plugin.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class HtmlOutputHelper {
+
+ /**
+ * Link to the root of the application.
+ *
+ * @var string
+ */
+ protected $baseUri;
+
+ /**
+ * List of xml namespaces.
+ *
+ * @var array
+ */
+ protected $namespaceMap;
+
+ /**
+ * Creates the object.
+ *
+ * baseUri must point to the root of the application. This will be used to
+ * easily generate links.
+ *
+ * The namespaceMap contains an array with the list of xml namespaces and
+ * their prefixes. WebDAV uses a lot of XML with complex namespaces, so
+ * that can be used to make output a lot shorter.
+ *
+ * @param string $baseUri
+ * @param array $namespaceMap
+ */
+ function __construct($baseUri, array $namespaceMap) {
+
+ $this->baseUri = $baseUri;
+ $this->namespaceMap = $namespaceMap;
+
+ }
+
+ /**
+ * Generates a 'full' url based on a relative one.
+ *
+ * For relative urls, the base of the application is taken as the reference
+ * url, not the 'current url of the current request'.
+ *
+ * Absolute urls are left alone.
+ *
+ * @param string $path
+ * @return string
+ */
+ function fullUrl($path) {
+
+ return Uri\resolve($this->baseUri, $path);
+
+ }
+
+ /**
+ * Escape string for HTML output.
+ *
+ * @param string $input
+ * @return string
+ */
+ function h($input) {
+
+ return htmlspecialchars($input, ENT_COMPAT, 'UTF-8');
+
+ }
+
+ /**
+ * Generates a full <a>-tag.
+ *
+ * Url is automatically expanded. If label is not specified, we re-use the
+ * url.
+ *
+ * @param string $url
+ * @param string $label
+ * @return string
+ */
+ function link($url, $label = null) {
+
+ $url = $this->h($this->fullUrl($url));
+ return '<a href="' . $url . '">' . ($label ? $this->h($label) : $url) . '</a>';
+
+ }
+
+ /**
+ * This method takes an xml element in clark-notation, and turns it into a
+ * shortened version with a prefix, if it was a known namespace.
+ *
+ * @param string $element
+ * @return string
+ */
+ function xmlName($element) {
+
+ list($ns, $localName) = XmlService::parseClarkNotation($element);
+ if (isset($this->namespaceMap[$ns])) {
+ $propName = $this->namespaceMap[$ns] . ':' . $localName;
+ } else {
+ $propName = $element;
+ }
+ return "<span title=\"" . $this->h($element) . "\">" . $this->h($propName) . "</span>";
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Browser/MapGetToPropFind.php b/vendor/sabre/dav/lib/DAV/Browser/MapGetToPropFind.php
index 881c063b9..38ee63bcd 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Browser/MapGetToPropFind.php
+++ b/vendor/sabre/dav/lib/DAV/Browser/MapGetToPropFind.php
@@ -3,6 +3,8 @@
namespace Sabre\DAV\Browser;
use Sabre\DAV;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
/**
* This is a simple plugin that will map any GET request for non-files to
@@ -10,7 +12,7 @@ use Sabre\DAV;
*
* This should allow easy debugging of PROPFIND
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -29,27 +31,28 @@ class MapGetToPropFind extends DAV\ServerPlugin {
* @param DAV\Server $server
* @return void
*/
- public function initialize(DAV\Server $server) {
+ function initialize(DAV\Server $server) {
$this->server = $server;
- $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor'));
+ $this->server->on('method:GET', [$this, 'httpGet'], 90);
}
/**
* This method intercepts GET requests to non-files, and changes it into an HTTP PROPFIND request
*
- * @param string $method
- * @param string $uri
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
* @return bool
*/
- public function httpGetInterceptor($method, $uri) {
+ function httpGet(RequestInterface $request, ResponseInterface $response) {
- if ($method!='GET') return true;
-
- $node = $this->server->tree->getNodeForPath($uri);
+ $node = $this->server->tree->getNodeForPath($request->getPath());
if ($node instanceof DAV\IFile) return;
- $this->server->invokeMethod('PROPFIND',$uri);
+ $subRequest = clone $request;
+ $subRequest->setMethod('PROPFIND');
+
+ $this->server->invokeMethod($subRequest, $response);
return false;
}
diff --git a/vendor/sabre/dav/lib/DAV/Browser/Plugin.php b/vendor/sabre/dav/lib/DAV/Browser/Plugin.php
new file mode 100644
index 000000000..07ca6c3e5
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Browser/Plugin.php
@@ -0,0 +1,797 @@
+<?php
+
+namespace Sabre\DAV\Browser;
+
+use Sabre\DAV;
+use Sabre\DAV\MkCol;
+use Sabre\HTTP\URLUtil;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * Browser Plugin
+ *
+ * This plugin provides a html representation, so that a WebDAV server may be accessed
+ * using a browser.
+ *
+ * The class intercepts GET requests to collection resources and generates a simple
+ * html index.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Plugin extends DAV\ServerPlugin {
+
+ /**
+ * reference to server class
+ *
+ * @var Sabre\DAV\Server
+ */
+ protected $server;
+
+ /**
+ * enablePost turns on the 'actions' panel, which allows people to create
+ * folders and upload files straight from a browser.
+ *
+ * @var bool
+ */
+ protected $enablePost = true;
+
+ /**
+ * A list of properties that are usually not interesting. This can cut down
+ * the browser output a bit by removing the properties that most people
+ * will likely not want to see.
+ *
+ * @var array
+ */
+ public $uninterestingProperties = [
+ '{DAV:}supportedlock',
+ '{DAV:}acl-restrictions',
+ '{DAV:}supported-privilege-set',
+ '{DAV:}supported-method-set',
+ ];
+
+ /**
+ * Creates the object.
+ *
+ * By default it will allow file creation and uploads.
+ * Specify the first argument as false to disable this
+ *
+ * @param bool $enablePost
+ */
+ function __construct($enablePost = true) {
+
+ $this->enablePost = $enablePost;
+
+ }
+
+ /**
+ * Initializes the plugin and subscribes to events
+ *
+ * @param DAV\Server $server
+ * @return void
+ */
+ function initialize(DAV\Server $server) {
+
+ $this->server = $server;
+ $this->server->on('method:GET', [$this, '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']);
+ }
+
+ /**
+ * This method intercepts GET requests that have ?sabreAction=info
+ * appended to the URL
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpGetEarly(RequestInterface $request, ResponseInterface $response) {
+
+ $params = $request->getQueryParameters();
+ if (isset($params['sabreAction']) && $params['sabreAction'] === 'info') {
+ return $this->httpGet($request, $response);
+ }
+
+ }
+
+ /**
+ * This method intercepts GET requests to collections and returns the html
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpGet(RequestInterface $request, ResponseInterface $response) {
+
+ // We're not using straight-up $_GET, because we want everything to be
+ // unit testable.
+ $getVars = $request->getQueryParameters();
+
+ // CSP headers
+ $this->server->httpResponse->setHeader('Content-Security-Policy', "img-src 'self'; style-src 'self';");
+
+ $sabreAction = isset($getVars['sabreAction']) ? $getVars['sabreAction'] : null;
+
+ switch ($sabreAction) {
+
+ case 'asset' :
+ // Asset handling, such as images
+ $this->serveAsset(isset($getVars['assetName']) ? $getVars['assetName'] : null);
+ return false;
+ default :
+ case 'info' :
+ try {
+ $this->server->tree->getNodeForPath($request->getPath());
+ } catch (DAV\Exception\NotFound $e) {
+ // We're simply stopping when the file isn't found to not interfere
+ // with other plugins.
+ return;
+ }
+
+ $response->setStatus(200);
+ $response->setHeader('Content-Type', 'text/html; charset=utf-8');
+
+ $response->setBody(
+ $this->generateDirectoryIndex($request->getPath())
+ );
+
+ return false;
+
+ case 'plugins' :
+ $response->setStatus(200);
+ $response->setHeader('Content-Type', 'text/html; charset=utf-8');
+
+ $response->setBody(
+ $this->generatePluginListing()
+ );
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * Handles POST requests for tree operations.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpPOST(RequestInterface $request, ResponseInterface $response) {
+
+ $contentType = $request->getHeader('Content-Type');
+ list($contentType) = explode(';', $contentType);
+ if ($contentType !== 'application/x-www-form-urlencoded' &&
+ $contentType !== 'multipart/form-data') {
+ return;
+ }
+ $postVars = $request->getPostData();
+
+ if (!isset($postVars['sabreAction']))
+ return;
+
+ $uri = $request->getPath();
+
+ if ($this->server->emit('onBrowserPostAction', [$uri, $postVars['sabreAction'], $postVars])) {
+
+ switch ($postVars['sabreAction']) {
+
+ case 'mkcol' :
+ if (isset($postVars['name']) && trim($postVars['name'])) {
+ // Using basename() because we won't allow slashes
+ list(, $folderName) = URLUtil::splitPath(trim($postVars['name']));
+
+ if (isset($postVars['resourceType'])) {
+ $resourceType = explode(',', $postVars['resourceType']);
+ } else {
+ $resourceType = ['{DAV:}collection'];
+ }
+
+ $properties = [];
+ foreach ($postVars as $varName => $varValue) {
+ // Any _POST variable in clark notation is treated
+ // like a property.
+ if ($varName[0] === '{') {
+ // PHP will convert any dots to underscores.
+ // This leaves us with no way to differentiate
+ // the two.
+ // Therefore we replace the string *DOT* with a
+ // real dot. * is not allowed in uris so we
+ // should be good.
+ $varName = str_replace('*DOT*', '.', $varName);
+ $properties[$varName] = $varValue;
+ }
+ }
+
+ $mkCol = new MkCol(
+ $resourceType,
+ $properties
+ );
+ $this->server->createCollection($uri . '/' . $folderName, $mkCol);
+ }
+ break;
+
+ // @codeCoverageIgnoreStart
+ case 'put' :
+
+ if ($_FILES) $file = current($_FILES);
+ else break;
+
+ list(, $newName) = URLUtil::splitPath(trim($file['name']));
+ if (isset($postVars['name']) && trim($postVars['name']))
+ $newName = trim($postVars['name']);
+
+ // Making sure we only have a 'basename' component
+ list(, $newName) = URLUtil::splitPath($newName);
+
+ if (is_uploaded_file($file['tmp_name'])) {
+ $this->server->createFile($uri . '/' . $newName, fopen($file['tmp_name'], 'r'));
+ }
+ break;
+ // @codeCoverageIgnoreEnd
+
+ }
+
+ }
+ $response->setHeader('Location', $request->getUrl());
+ $response->setStatus(302);
+ return false;
+
+ }
+
+ /**
+ * Escapes a string for html.
+ *
+ * @param string $value
+ * @return string
+ */
+ function escapeHTML($value) {
+
+ return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
+
+ }
+
+ /**
+ * Generates the html directory index for a given url
+ *
+ * @param string $path
+ * @return string
+ */
+ function generateDirectoryIndex($path) {
+
+ $html = $this->generateHeader($path ? $path : '/', $path);
+
+ $node = $this->server->tree->getNodeForPath($path);
+ if ($node instanceof DAV\ICollection) {
+
+ $html .= "<section><h1>Nodes</h1>\n";
+ $html .= "<table class=\"nodeTable\">";
+
+ $subNodes = $this->server->getPropertiesForChildren($path, [
+ '{DAV:}displayname',
+ '{DAV:}resourcetype',
+ '{DAV:}getcontenttype',
+ '{DAV:}getcontentlength',
+ '{DAV:}getlastmodified',
+ ]);
+
+ foreach ($subNodes as $subPath => $subProps) {
+
+ $subNode = $this->server->tree->getNodeForPath($subPath);
+ $fullPath = $this->server->getBaseUri() . URLUtil::encodePath($subPath);
+ list(, $displayPath) = URLUtil::splitPath($subPath);
+
+ $subNodes[$subPath]['subNode'] = $subNode;
+ $subNodes[$subPath]['fullPath'] = $fullPath;
+ $subNodes[$subPath]['displayPath'] = $displayPath;
+ }
+ uasort($subNodes, [$this, 'compareNodes']);
+
+ foreach ($subNodes as $subProps) {
+ $type = [
+ 'string' => 'Unknown',
+ 'icon' => 'cog',
+ ];
+ if (isset($subProps['{DAV:}resourcetype'])) {
+ $type = $this->mapResourceType($subProps['{DAV:}resourcetype']->getValue(), $subProps['subNode']);
+ }
+
+ $html .= '<tr>';
+ $html .= '<td class="nameColumn"><a href="' . $this->escapeHTML($subProps['fullPath']) . '"><span class="oi" data-glyph="' . $this->escapeHTML($type['icon']) . '"></span> ' . $this->escapeHTML($subProps['displayPath']) . '</a></td>';
+ $html .= '<td class="typeColumn">' . $this->escapeHTML($type['string']) . '</td>';
+ $html .= '<td>';
+ if (isset($subProps['{DAV:}getcontentlength'])) {
+ $html .= $this->escapeHTML($subProps['{DAV:}getcontentlength'] . ' bytes');
+ }
+ $html .= '</td><td>';
+ if (isset($subProps['{DAV:}getlastmodified'])) {
+ $lastMod = $subProps['{DAV:}getlastmodified']->getTime();
+ $html .= $this->escapeHTML($lastMod->format('F j, Y, g:i a'));
+ }
+ $html .= '</td>';
+
+ $buttonActions = '';
+ if ($subProps['subNode'] instanceof DAV\IFile) {
+ $buttonActions = '<a href="' . $this->escapeHTML($subProps['fullPath']) . '?sabreAction=info"><span class="oi" data-glyph="info"></span></a>';
+ }
+ $this->server->emit('browserButtonActions', [$subProps['fullPath'], $subProps['subNode'], &$buttonActions]);
+
+ $html .= '<td>' . $buttonActions . '</td>';
+ $html .= '</tr>';
+ }
+
+ $html .= '</table>';
+
+ }
+
+ $html .= "</section>";
+ $html .= "<section><h1>Properties</h1>";
+ $html .= "<table class=\"propTable\">";
+
+ // Allprops request
+ $propFind = new PropFindAll($path);
+ $properties = $this->server->getPropertiesByNode($propFind, $node);
+
+ $properties = $propFind->getResultForMultiStatus()[200];
+
+ foreach ($properties as $propName => $propValue) {
+ if (!in_array($propName, $this->uninterestingProperties)) {
+ $html .= $this->drawPropertyRow($propName, $propValue);
+ }
+
+ }
+
+
+ $html .= "</table>";
+ $html .= "</section>";
+
+ /* Start of generating actions */
+
+ $output = '';
+ if ($this->enablePost) {
+ $this->server->emit('onHTMLActionsPanel', [$node, &$output]);
+ }
+
+ if ($output) {
+
+ $html .= "<section><h1>Actions</h1>";
+ $html .= "<div class=\"actions\">\n";
+ $html .= $output;
+ $html .= "</div>\n";
+ $html .= "</section>\n";
+ }
+
+ $html .= $this->generateFooter();
+
+ $this->server->httpResponse->setHeader('Content-Security-Policy', "img-src 'self'; style-src 'self';");
+
+ return $html;
+
+ }
+
+ /**
+ * Generates the 'plugins' page.
+ *
+ * @return string
+ */
+ function generatePluginListing() {
+
+ $html = $this->generateHeader('Plugins');
+
+ $html .= "<section><h1>Plugins</h1>";
+ $html .= "<table class=\"propTable\">";
+ foreach ($this->server->getPlugins() as $plugin) {
+ $info = $plugin->getPluginInfo();
+ $html .= '<tr><th>' . $info['name'] . '</th>';
+ $html .= '<td>' . $info['description'] . '</td>';
+ $html .= '<td>';
+ if (isset($info['link']) && $info['link']) {
+ $html .= '<a href="' . $this->escapeHTML($info['link']) . '"><span class="oi" data-glyph="book"></span></a>';
+ }
+ $html .= '</td></tr>';
+ }
+ $html .= "</table>";
+ $html .= "</section>";
+
+ /* Start of generating actions */
+
+ $html .= $this->generateFooter();
+
+ return $html;
+
+ }
+
+ /**
+ * Generates the first block of HTML, including the <head> tag and page
+ * header.
+ *
+ * Returns footer.
+ *
+ * @param string $title
+ * @param string $path
+ * @return void
+ */
+ function generateHeader($title, $path = null) {
+
+ $version = DAV\Version::VERSION;
+
+ $vars = [
+ '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(),
+ ];
+
+ $html = <<<HTML
+<!DOCTYPE html>
+<html>
+<head>
+ <title>$vars[title] - sabre/dav $version</title>
+ <link rel="shortcut icon" href="$vars[favicon]" type="image/vnd.microsoft.icon" />
+ <link rel="stylesheet" href="$vars[style]" type="text/css" />
+ <link rel="stylesheet" href="$vars[iconstyle]" type="text/css" />
+
+</head>
+<body>
+ <header>
+ <div class="logo">
+ <a href="$vars[baseUrl]"><img src="$vars[logo]" alt="sabre/dav" /> $vars[title]</a>
+ </div>
+ </header>
+
+ <nav>
+HTML;
+
+ // If the path is empty, there's no parent.
+ if ($path) {
+ list($parentUri) = URLUtil::splitPath($path);
+ $fullPath = $this->server->getBaseUri() . URLUtil::encodePath($parentUri);
+ $html .= '<a href="' . $fullPath . '" class="btn">⇤ Go to parent</a>';
+ } else {
+ $html .= '<span class="btn disabled">⇤ Go to parent</span>';
+ }
+
+ $html .= ' <a href="?sabreAction=plugins" class="btn"><span class="oi" data-glyph="puzzle-piece"></span> Plugins</a>';
+
+ $html .= "</nav>";
+
+ return $html;
+
+ }
+
+ /**
+ * Generates the page footer.
+ *
+ * Returns html.
+ *
+ * @return string
+ */
+ function generateFooter() {
+
+ $version = DAV\Version::VERSION;
+ return <<<HTML
+<footer>Generated by SabreDAV $version (c)2007-2015 <a href="http://sabre.io/">http://sabre.io/</a></footer>
+</body>
+</html>
+HTML;
+
+ }
+
+ /**
+ * This method is used to generate the 'actions panel' output for
+ * collections.
+ *
+ * This specifically generates the interfaces for creating new files, and
+ * creating new directories.
+ *
+ * @param DAV\INode $node
+ * @param mixed $output
+ * @return void
+ */
+ function htmlActionsPanel(DAV\INode $node, &$output) {
+
+ if (!$node instanceof DAV\ICollection)
+ return;
+
+ // We also know fairly certain that if an object is a non-extended
+ // SimpleCollection, we won't need to show the panel either.
+ if (get_class($node) === 'Sabre\\DAV\\SimpleCollection')
+ return;
+
+ ob_start();
+ echo '<form method="post" action="">
+ <h3>Create new folder</h3>
+ <input type="hidden" name="sabreAction" value="mkcol" />
+ <label>Name:</label> <input type="text" name="name" /><br />
+ <input type="submit" value="create" />
+ </form>
+ <form method="post" action="" enctype="multipart/form-data">
+ <h3>Upload file</h3>
+ <input type="hidden" name="sabreAction" value="put" />
+ <label>Name (optional):</label> <input type="text" name="name" /><br />
+ <label>File:</label> <input type="file" name="file" /><br />
+ <input type="submit" value="upload" />
+ </form>
+ ';
+
+ $output .= ob_get_clean();
+
+ }
+
+ /**
+ * This method takes a path/name of an asset and turns it into url
+ * suiteable for http access.
+ *
+ * @param string $assetName
+ * @return string
+ */
+ protected function getAssetUrl($assetName) {
+
+ return $this->server->getBaseUri() . '?sabreAction=asset&assetName=' . urlencode($assetName);
+
+ }
+
+ /**
+ * This method returns a local pathname to an asset.
+ *
+ * @param string $assetName
+ * @return string
+ * @throws DAV\Exception\NotFound
+ */
+ protected function getLocalAssetPath($assetName) {
+
+ $assetDir = __DIR__ . '/assets/';
+ $path = $assetDir . $assetName;
+
+ // Making sure people aren't trying to escape from the base path.
+ $path = str_replace('\\', '/', $path);
+ if (strpos($path, '/../') !== false || strrchr($path, '/') === '/..') {
+ throw new DAV\Exception\NotFound('Path does not exist, or escaping from the base path was detected');
+ }
+ if (strpos(realpath($path), realpath($assetDir)) === 0 && file_exists($path)) {
+ return $path;
+ }
+ throw new DAV\Exception\NotFound('Path does not exist, or escaping from the base path was detected');
+ }
+
+ /**
+ * This method reads an asset from disk and generates a full http response.
+ *
+ * @param string $assetName
+ * @return void
+ */
+ protected function serveAsset($assetName) {
+
+ $assetPath = $this->getLocalAssetPath($assetName);
+
+ // Rudimentary mime type detection
+ $mime = 'application/octet-stream';
+ $map = [
+ 'ico' => 'image/vnd.microsoft.icon',
+ 'png' => 'image/png',
+ 'css' => 'text/css',
+ ];
+
+ $ext = substr($assetName, strrpos($assetName, '.') + 1);
+ if (isset($map[$ext])) {
+ $mime = $map[$ext];
+ }
+
+ $this->server->httpResponse->setHeader('Content-Type', $mime);
+ $this->server->httpResponse->setHeader('Content-Length', filesize($assetPath));
+ $this->server->httpResponse->setHeader('Cache-Control', 'public, max-age=1209600');
+ $this->server->httpResponse->setStatus(200);
+ $this->server->httpResponse->setBody(fopen($assetPath, 'r'));
+
+ }
+
+ /**
+ * Sort helper function: compares two directory entries based on type and
+ * display name. Collections sort above other types.
+ *
+ * @param array $a
+ * @param array $b
+ * @return int
+ */
+ protected function compareNodes($a, $b) {
+
+ $typeA = (isset($a['{DAV:}resourcetype']))
+ ? (in_array('{DAV:}collection', $a['{DAV:}resourcetype']->getValue()))
+ : false;
+
+ $typeB = (isset($b['{DAV:}resourcetype']))
+ ? (in_array('{DAV:}collection', $b['{DAV:}resourcetype']->getValue()))
+ : false;
+
+ // If same type, sort alphabetically by filename:
+ if ($typeA === $typeB) {
+ return strnatcasecmp($a['displayPath'], $b['displayPath']);
+ }
+ return (($typeA < $typeB) ? 1 : -1);
+
+ }
+
+ /**
+ * Maps a resource type to a human-readable string and icon.
+ *
+ * @param array $resourceTypes
+ * @param INode $node
+ * @return array
+ */
+ private function mapResourceType(array $resourceTypes, $node) {
+
+ if (!$resourceTypes) {
+ if ($node instanceof DAV\IFile) {
+ return [
+ 'string' => 'File',
+ 'icon' => 'file',
+ ];
+ } else {
+ return [
+ 'string' => 'Unknown',
+ 'icon' => 'cog',
+ ];
+ }
+ }
+
+ $types = [
+ '{http://calendarserver.org/ns/}calendar-proxy-write' => [
+ 'string' => 'Proxy-Write',
+ 'icon' => 'people',
+ ],
+ '{http://calendarserver.org/ns/}calendar-proxy-read' => [
+ 'string' => 'Proxy-Read',
+ 'icon' => 'people',
+ ],
+ '{urn:ietf:params:xml:ns:caldav}schedule-outbox' => [
+ 'string' => 'Outbox',
+ 'icon' => 'inbox',
+ ],
+ '{urn:ietf:params:xml:ns:caldav}schedule-inbox' => [
+ 'string' => 'Inbox',
+ 'icon' => 'inbox',
+ ],
+ '{urn:ietf:params:xml:ns:caldav}calendar' => [
+ 'string' => 'Calendar',
+ 'icon' => 'calendar',
+ ],
+ '{http://calendarserver.org/ns/}shared-owner' => [
+ 'string' => 'Shared',
+ 'icon' => 'calendar',
+ ],
+ '{http://calendarserver.org/ns/}subscribed' => [
+ 'string' => 'Subscription',
+ 'icon' => 'calendar',
+ ],
+ '{urn:ietf:params:xml:ns:carddav}directory' => [
+ 'string' => 'Directory',
+ 'icon' => 'globe',
+ ],
+ '{urn:ietf:params:xml:ns:carddav}addressbook' => [
+ 'string' => 'Address book',
+ 'icon' => 'book',
+ ],
+ '{DAV:}principal' => [
+ 'string' => 'Principal',
+ 'icon' => 'person',
+ ],
+ '{DAV:}collection' => [
+ 'string' => 'Collection',
+ 'icon' => 'folder',
+ ],
+ ];
+
+ $info = [
+ 'string' => [],
+ 'icon' => 'cog',
+ ];
+ foreach ($resourceTypes as $k => $resourceType) {
+ if (isset($types[$resourceType])) {
+ $info['string'][] = $types[$resourceType]['string'];
+ } else {
+ $info['string'][] = $resourceType;
+ }
+ }
+ foreach ($types as $key => $resourceInfo) {
+ if (in_array($key, $resourceTypes)) {
+ $info['icon'] = $resourceInfo['icon'];
+ break;
+ }
+ }
+ $info['string'] = implode(', ', $info['string']);
+
+ return $info;
+
+ }
+
+ /**
+ * Draws a table row for a property
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return string
+ */
+ private function drawPropertyRow($name, $value) {
+
+ $html = new HtmlOutputHelper(
+ $this->server->getBaseUri(),
+ $this->server->xml->namespaceMap
+ );
+
+ return "<tr><th>" . $html->xmlName($name) . "</th><td>" . $this->drawPropertyValue($html, $value) . "</td></tr>";
+
+ }
+
+ /**
+ * Draws a table row for a property
+ *
+ * @param HtmlOutputHelper $html
+ * @param mixed $value
+ * @return string
+ */
+ 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());
+ // removing first and last line, as they contain our root
+ // element.
+ $xml = explode("\n", $xml);
+ $xml = array_slice($xml, 2, -2);
+ return "<pre>" . $html->h(implode("\n", $xml)) . "</pre>";
+
+ } else {
+ return "<em>unknown</em>";
+ }
+
+ }
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins;
+ * using \Sabre\DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'browser';
+
+ }
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => 'Generates HTML indexes and debug information for your sabre/dav server',
+ 'link' => 'http://sabre.io/dav/browser-plugin/',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Browser/PropFindAll.php b/vendor/sabre/dav/lib/DAV/Browser/PropFindAll.php
new file mode 100644
index 000000000..1ac439672
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Browser/PropFindAll.php
@@ -0,0 +1,132 @@
+<?php
+
+namespace Sabre\DAV\Browser;
+
+use Sabre\DAV\PropFind;
+
+/**
+ * This class is used by the browser plugin to trick the system in returning
+ * every defined property.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PropFindAll extends PropFind {
+
+ /**
+ * Creates the PROPFIND object
+ *
+ * @param string $path
+ */
+ function __construct($path) {
+
+ parent::__construct($path, []);
+
+ }
+
+ /**
+ * Handles a specific property.
+ *
+ * This method checks wether the specified property was requested in this
+ * PROPFIND request, and if so, it will call the callback and use the
+ * return value for it's value.
+ *
+ * Example:
+ *
+ * $propFind->handle('{DAV:}displayname', function() {
+ * return 'hello';
+ * });
+ *
+ * Note that handle will only work the first time. If null is returned, the
+ * value is ignored.
+ *
+ * It's also possible to not pass a callback, but immediately pass a value
+ *
+ * @param string $propertyName
+ * @param mixed $valueOrCallBack
+ * @return void
+ */
+ function handle($propertyName, $valueOrCallBack) {
+
+ if (is_callable($valueOrCallBack)) {
+ $value = $valueOrCallBack();
+ } else {
+ $value = $valueOrCallBack;
+ }
+ if (!is_null($value)) {
+ $this->result[$propertyName] = [200, $value];
+ }
+
+ }
+
+ /**
+ * Sets the value of the property
+ *
+ * If status is not supplied, the status will default to 200 for non-null
+ * properties, and 404 for null properties.
+ *
+ * @param string $propertyName
+ * @param mixed $value
+ * @param int $status
+ * @return void
+ */
+ function set($propertyName, $value, $status = null) {
+
+ if (is_null($status)) {
+ $status = is_null($value) ? 404 : 200;
+ }
+ $this->result[$propertyName] = [$status, $value];
+
+ }
+
+ /**
+ * Returns the current value for a property.
+ *
+ * @param string $propertyName
+ * @return mixed
+ */
+ function get($propertyName) {
+
+ return isset($this->result[$propertyName]) ? $this->result[$propertyName][1] : null;
+
+ }
+
+ /**
+ * Returns the current status code for a property name.
+ *
+ * If the property does not appear in the list of requested properties,
+ * null will be returned.
+ *
+ * @param string $propertyName
+ * @return int|null
+ */
+ function getStatus($propertyName) {
+
+ return isset($this->result[$propertyName]) ? $this->result[$propertyName][0] : 404;
+
+ }
+
+ /**
+ * Returns all propertynames that have a 404 status, and thus don't have a
+ * value yet.
+ *
+ * @return array
+ */
+ function get404Properties() {
+
+ $result = [];
+ foreach ($this->result as $propertyName => $stuff) {
+ if ($stuff[0] === 404) {
+ $result[] = $propertyName;
+ }
+ }
+ // If there's nothing in this list, we're adding one fictional item.
+ if (!$result) {
+ $result[] = '{http://sabredav.org/ns}idk';
+ }
+ return $result;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/ICON-LICENSE b/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/ICON-LICENSE
new file mode 100644
index 000000000..2199f4a69
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/ICON-LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Waybury
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. \ No newline at end of file
diff --git a/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.css b/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.css
new file mode 100644
index 000000000..e74867400
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.css
@@ -0,0 +1,510 @@
+@font-face {
+ font-family: 'Icons';
+ src: url('?sabreAction=asset&assetName=openiconic/open-iconic.eot');
+ src: url('?sabreAction=asset&assetName=openiconic/open-iconic.eot?#iconic-sm') format('embedded-opentype'), url('?sabreAction=asset&assetName=openiconic/open-iconic.woff') format('woff'), url('?sabreAction=asset&assetName=openiconic/open-iconic.ttf') format('truetype'), url('?sabreAction=asset&assetName=openiconic/open-iconic.otf') format('opentype'), url('?sabreAction=asset&assetName=openiconic/open-iconic.svg#iconic-sm') format('svg');
+ font-weight: normal;
+ font-style: normal;
+}
+
+.oi[data-glyph].oi-text-replace {
+ font-size: 0;
+ line-height: 0;
+}
+
+.oi[data-glyph].oi-text-replace:before {
+ width: 1em;
+ text-align: center;
+}
+
+.oi[data-glyph]:before {
+ font-family: 'Icons';
+ display: inline-block;
+ speak: none;
+ line-height: 1;
+ vertical-align: baseline;
+ font-weight: normal;
+ font-style: normal;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.oi[data-glyph]:empty:before {
+ width: 1em;
+ text-align: center;
+ box-sizing: content-box;
+}
+
+.oi[data-glyph].oi-align-left:before {
+ text-align: left;
+}
+
+.oi[data-glyph].oi-align-right:before {
+ text-align: right;
+}
+
+.oi[data-glyph].oi-align-center:before {
+ text-align: center;
+}
+
+.oi[data-glyph].oi-flip-horizontal:before {
+ -webkit-transform: scale(-1, 1);
+ -ms-transform: scale(-1, 1);
+ transform: scale(-1, 1);
+}
+.oi[data-glyph].oi-flip-vertical:before {
+ -webkit-transform: scale(1, -1);
+ -ms-transform: scale(-1, 1);
+ transform: scale(1, -1);
+}
+.oi[data-glyph].oi-flip-horizontal-vertical:before {
+ -webkit-transform: scale(-1, -1);
+ -ms-transform: scale(-1, 1);
+ transform: scale(-1, -1);
+}
+
+
+.oi[data-glyph=account-login]:before { content:'\e000'; }
+
+.oi[data-glyph=account-logout]:before { content:'\e001'; }
+
+.oi[data-glyph=action-redo]:before { content:'\e002'; }
+
+.oi[data-glyph=action-undo]:before { content:'\e003'; }
+
+.oi[data-glyph=align-center]:before { content:'\e004'; }
+
+.oi[data-glyph=align-left]:before { content:'\e005'; }
+
+.oi[data-glyph=align-right]:before { content:'\e006'; }
+
+.oi[data-glyph=aperture]:before { content:'\e007'; }
+
+.oi[data-glyph=arrow-bottom]:before { content:'\e008'; }
+
+.oi[data-glyph=arrow-circle-bottom]:before { content:'\e009'; }
+
+.oi[data-glyph=arrow-circle-left]:before { content:'\e00a'; }
+
+.oi[data-glyph=arrow-circle-right]:before { content:'\e00b'; }
+
+.oi[data-glyph=arrow-circle-top]:before { content:'\e00c'; }
+
+.oi[data-glyph=arrow-left]:before { content:'\e00d'; }
+
+.oi[data-glyph=arrow-right]:before { content:'\e00e'; }
+
+.oi[data-glyph=arrow-thick-bottom]:before { content:'\e00f'; }
+
+.oi[data-glyph=arrow-thick-left]:before { content:'\e010'; }
+
+.oi[data-glyph=arrow-thick-right]:before { content:'\e011'; }
+
+.oi[data-glyph=arrow-thick-top]:before { content:'\e012'; }
+
+.oi[data-glyph=arrow-top]:before { content:'\e013'; }
+
+.oi[data-glyph=audio-spectrum]:before { content:'\e014'; }
+
+.oi[data-glyph=audio]:before { content:'\e015'; }
+
+.oi[data-glyph=badge]:before { content:'\e016'; }
+
+.oi[data-glyph=ban]:before { content:'\e017'; }
+
+.oi[data-glyph=bar-chart]:before { content:'\e018'; }
+
+.oi[data-glyph=basket]:before { content:'\e019'; }
+
+.oi[data-glyph=battery-empty]:before { content:'\e01a'; }
+
+.oi[data-glyph=battery-full]:before { content:'\e01b'; }
+
+.oi[data-glyph=beaker]:before { content:'\e01c'; }
+
+.oi[data-glyph=bell]:before { content:'\e01d'; }
+
+.oi[data-glyph=bluetooth]:before { content:'\e01e'; }
+
+.oi[data-glyph=bold]:before { content:'\e01f'; }
+
+.oi[data-glyph=bolt]:before { content:'\e020'; }
+
+.oi[data-glyph=book]:before { content:'\e021'; }
+
+.oi[data-glyph=bookmark]:before { content:'\e022'; }
+
+.oi[data-glyph=box]:before { content:'\e023'; }
+
+.oi[data-glyph=briefcase]:before { content:'\e024'; }
+
+.oi[data-glyph=british-pound]:before { content:'\e025'; }
+
+.oi[data-glyph=browser]:before { content:'\e026'; }
+
+.oi[data-glyph=brush]:before { content:'\e027'; }
+
+.oi[data-glyph=bug]:before { content:'\e028'; }
+
+.oi[data-glyph=bullhorn]:before { content:'\e029'; }
+
+.oi[data-glyph=calculator]:before { content:'\e02a'; }
+
+.oi[data-glyph=calendar]:before { content:'\e02b'; }
+
+.oi[data-glyph=camera-slr]:before { content:'\e02c'; }
+
+.oi[data-glyph=caret-bottom]:before { content:'\e02d'; }
+
+.oi[data-glyph=caret-left]:before { content:'\e02e'; }
+
+.oi[data-glyph=caret-right]:before { content:'\e02f'; }
+
+.oi[data-glyph=caret-top]:before { content:'\e030'; }
+
+.oi[data-glyph=cart]:before { content:'\e031'; }
+
+.oi[data-glyph=chat]:before { content:'\e032'; }
+
+.oi[data-glyph=check]:before { content:'\e033'; }
+
+.oi[data-glyph=chevron-bottom]:before { content:'\e034'; }
+
+.oi[data-glyph=chevron-left]:before { content:'\e035'; }
+
+.oi[data-glyph=chevron-right]:before { content:'\e036'; }
+
+.oi[data-glyph=chevron-top]:before { content:'\e037'; }
+
+.oi[data-glyph=circle-check]:before { content:'\e038'; }
+
+.oi[data-glyph=circle-x]:before { content:'\e039'; }
+
+.oi[data-glyph=clipboard]:before { content:'\e03a'; }
+
+.oi[data-glyph=clock]:before { content:'\e03b'; }
+
+.oi[data-glyph=cloud-download]:before { content:'\e03c'; }
+
+.oi[data-glyph=cloud-upload]:before { content:'\e03d'; }
+
+.oi[data-glyph=cloud]:before { content:'\e03e'; }
+
+.oi[data-glyph=cloudy]:before { content:'\e03f'; }
+
+.oi[data-glyph=code]:before { content:'\e040'; }
+
+.oi[data-glyph=cog]:before { content:'\e041'; }
+
+.oi[data-glyph=collapse-down]:before { content:'\e042'; }
+
+.oi[data-glyph=collapse-left]:before { content:'\e043'; }
+
+.oi[data-glyph=collapse-right]:before { content:'\e044'; }
+
+.oi[data-glyph=collapse-up]:before { content:'\e045'; }
+
+.oi[data-glyph=command]:before { content:'\e046'; }
+
+.oi[data-glyph=comment-square]:before { content:'\e047'; }
+
+.oi[data-glyph=compass]:before { content:'\e048'; }
+
+.oi[data-glyph=contrast]:before { content:'\e049'; }
+
+.oi[data-glyph=copywriting]:before { content:'\e04a'; }
+
+.oi[data-glyph=credit-card]:before { content:'\e04b'; }
+
+.oi[data-glyph=crop]:before { content:'\e04c'; }
+
+.oi[data-glyph=dashboard]:before { content:'\e04d'; }
+
+.oi[data-glyph=data-transfer-download]:before { content:'\e04e'; }
+
+.oi[data-glyph=data-transfer-upload]:before { content:'\e04f'; }
+
+.oi[data-glyph=delete]:before { content:'\e050'; }
+
+.oi[data-glyph=dial]:before { content:'\e051'; }
+
+.oi[data-glyph=document]:before { content:'\e052'; }
+
+.oi[data-glyph=dollar]:before { content:'\e053'; }
+
+.oi[data-glyph=double-quote-sans-left]:before { content:'\e054'; }
+
+.oi[data-glyph=double-quote-sans-right]:before { content:'\e055'; }
+
+.oi[data-glyph=double-quote-serif-left]:before { content:'\e056'; }
+
+.oi[data-glyph=double-quote-serif-right]:before { content:'\e057'; }
+
+.oi[data-glyph=droplet]:before { content:'\e058'; }
+
+.oi[data-glyph=eject]:before { content:'\e059'; }
+
+.oi[data-glyph=elevator]:before { content:'\e05a'; }
+
+.oi[data-glyph=ellipses]:before { content:'\e05b'; }
+
+.oi[data-glyph=envelope-closed]:before { content:'\e05c'; }
+
+.oi[data-glyph=envelope-open]:before { content:'\e05d'; }
+
+.oi[data-glyph=euro]:before { content:'\e05e'; }
+
+.oi[data-glyph=excerpt]:before { content:'\e05f'; }
+
+.oi[data-glyph=expand-down]:before { content:'\e060'; }
+
+.oi[data-glyph=expand-left]:before { content:'\e061'; }
+
+.oi[data-glyph=expand-right]:before { content:'\e062'; }
+
+.oi[data-glyph=expand-up]:before { content:'\e063'; }
+
+.oi[data-glyph=external-link]:before { content:'\e064'; }
+
+.oi[data-glyph=eye]:before { content:'\e065'; }
+
+.oi[data-glyph=eyedropper]:before { content:'\e066'; }
+
+.oi[data-glyph=file]:before { content:'\e067'; }
+
+.oi[data-glyph=fire]:before { content:'\e068'; }
+
+.oi[data-glyph=flag]:before { content:'\e069'; }
+
+.oi[data-glyph=flash]:before { content:'\e06a'; }
+
+.oi[data-glyph=folder]:before { content:'\e06b'; }
+
+.oi[data-glyph=fork]:before { content:'\e06c'; }
+
+.oi[data-glyph=fullscreen-enter]:before { content:'\e06d'; }
+
+.oi[data-glyph=fullscreen-exit]:before { content:'\e06e'; }
+
+.oi[data-glyph=globe]:before { content:'\e06f'; }
+
+.oi[data-glyph=graph]:before { content:'\e070'; }
+
+.oi[data-glyph=grid-four-up]:before { content:'\e071'; }
+
+.oi[data-glyph=grid-three-up]:before { content:'\e072'; }
+
+.oi[data-glyph=grid-two-up]:before { content:'\e073'; }
+
+.oi[data-glyph=hard-drive]:before { content:'\e074'; }
+
+.oi[data-glyph=header]:before { content:'\e075'; }
+
+.oi[data-glyph=headphones]:before { content:'\e076'; }
+
+.oi[data-glyph=heart]:before { content:'\e077'; }
+
+.oi[data-glyph=home]:before { content:'\e078'; }
+
+.oi[data-glyph=image]:before { content:'\e079'; }
+
+.oi[data-glyph=inbox]:before { content:'\e07a'; }
+
+.oi[data-glyph=infinity]:before { content:'\e07b'; }
+
+.oi[data-glyph=info]:before { content:'\e07c'; }
+
+.oi[data-glyph=italic]:before { content:'\e07d'; }
+
+.oi[data-glyph=justify-center]:before { content:'\e07e'; }
+
+.oi[data-glyph=justify-left]:before { content:'\e07f'; }
+
+.oi[data-glyph=justify-right]:before { content:'\e080'; }
+
+.oi[data-glyph=key]:before { content:'\e081'; }
+
+.oi[data-glyph=laptop]:before { content:'\e082'; }
+
+.oi[data-glyph=layers]:before { content:'\e083'; }
+
+.oi[data-glyph=lightbulb]:before { content:'\e084'; }
+
+.oi[data-glyph=link-broken]:before { content:'\e085'; }
+
+.oi[data-glyph=link-intact]:before { content:'\e086'; }
+
+.oi[data-glyph=list-rich]:before { content:'\e087'; }
+
+.oi[data-glyph=list]:before { content:'\e088'; }
+
+.oi[data-glyph=location]:before { content:'\e089'; }
+
+.oi[data-glyph=lock-locked]:before { content:'\e08a'; }
+
+.oi[data-glyph=lock-unlocked]:before { content:'\e08b'; }
+
+.oi[data-glyph=loop-circular]:before { content:'\e08c'; }
+
+.oi[data-glyph=loop-square]:before { content:'\e08d'; }
+
+.oi[data-glyph=loop]:before { content:'\e08e'; }
+
+.oi[data-glyph=magnifying-glass]:before { content:'\e08f'; }
+
+.oi[data-glyph=map-marker]:before { content:'\e090'; }
+
+.oi[data-glyph=map]:before { content:'\e091'; }
+
+.oi[data-glyph=media-pause]:before { content:'\e092'; }
+
+.oi[data-glyph=media-play]:before { content:'\e093'; }
+
+.oi[data-glyph=media-record]:before { content:'\e094'; }
+
+.oi[data-glyph=media-skip-backward]:before { content:'\e095'; }
+
+.oi[data-glyph=media-skip-forward]:before { content:'\e096'; }
+
+.oi[data-glyph=media-step-backward]:before { content:'\e097'; }
+
+.oi[data-glyph=media-step-forward]:before { content:'\e098'; }
+
+.oi[data-glyph=media-stop]:before { content:'\e099'; }
+
+.oi[data-glyph=medical-cross]:before { content:'\e09a'; }
+
+.oi[data-glyph=menu]:before { content:'\e09b'; }
+
+.oi[data-glyph=microphone]:before { content:'\e09c'; }
+
+.oi[data-glyph=minus]:before { content:'\e09d'; }
+
+.oi[data-glyph=monitor]:before { content:'\e09e'; }
+
+.oi[data-glyph=moon]:before { content:'\e09f'; }
+
+.oi[data-glyph=move]:before { content:'\e0a0'; }
+
+.oi[data-glyph=musical-note]:before { content:'\e0a1'; }
+
+.oi[data-glyph=paperclip]:before { content:'\e0a2'; }
+
+.oi[data-glyph=pencil]:before { content:'\e0a3'; }
+
+.oi[data-glyph=people]:before { content:'\e0a4'; }
+
+.oi[data-glyph=person]:before { content:'\e0a5'; }
+
+.oi[data-glyph=phone]:before { content:'\e0a6'; }
+
+.oi[data-glyph=pie-chart]:before { content:'\e0a7'; }
+
+.oi[data-glyph=pin]:before { content:'\e0a8'; }
+
+.oi[data-glyph=play-circle]:before { content:'\e0a9'; }
+
+.oi[data-glyph=plus]:before { content:'\e0aa'; }
+
+.oi[data-glyph=power-standby]:before { content:'\e0ab'; }
+
+.oi[data-glyph=print]:before { content:'\e0ac'; }
+
+.oi[data-glyph=project]:before { content:'\e0ad'; }
+
+.oi[data-glyph=pulse]:before { content:'\e0ae'; }
+
+.oi[data-glyph=puzzle-piece]:before { content:'\e0af'; }
+
+.oi[data-glyph=question-mark]:before { content:'\e0b0'; }
+
+.oi[data-glyph=rain]:before { content:'\e0b1'; }
+
+.oi[data-glyph=random]:before { content:'\e0b2'; }
+
+.oi[data-glyph=reload]:before { content:'\e0b3'; }
+
+.oi[data-glyph=resize-both]:before { content:'\e0b4'; }
+
+.oi[data-glyph=resize-height]:before { content:'\e0b5'; }
+
+.oi[data-glyph=resize-width]:before { content:'\e0b6'; }
+
+.oi[data-glyph=rss-alt]:before { content:'\e0b7'; }
+
+.oi[data-glyph=rss]:before { content:'\e0b8'; }
+
+.oi[data-glyph=script]:before { content:'\e0b9'; }
+
+.oi[data-glyph=share-boxed]:before { content:'\e0ba'; }
+
+.oi[data-glyph=share]:before { content:'\e0bb'; }
+
+.oi[data-glyph=shield]:before { content:'\e0bc'; }
+
+.oi[data-glyph=signal]:before { content:'\e0bd'; }
+
+.oi[data-glyph=signpost]:before { content:'\e0be'; }
+
+.oi[data-glyph=sort-ascending]:before { content:'\e0bf'; }
+
+.oi[data-glyph=sort-descending]:before { content:'\e0c0'; }
+
+.oi[data-glyph=spreadsheet]:before { content:'\e0c1'; }
+
+.oi[data-glyph=star]:before { content:'\e0c2'; }
+
+.oi[data-glyph=sun]:before { content:'\e0c3'; }
+
+.oi[data-glyph=tablet]:before { content:'\e0c4'; }
+
+.oi[data-glyph=tag]:before { content:'\e0c5'; }
+
+.oi[data-glyph=tags]:before { content:'\e0c6'; }
+
+.oi[data-glyph=target]:before { content:'\e0c7'; }
+
+.oi[data-glyph=task]:before { content:'\e0c8'; }
+
+.oi[data-glyph=terminal]:before { content:'\e0c9'; }
+
+.oi[data-glyph=text]:before { content:'\e0ca'; }
+
+.oi[data-glyph=thumb-down]:before { content:'\e0cb'; }
+
+.oi[data-glyph=thumb-up]:before { content:'\e0cc'; }
+
+.oi[data-glyph=timer]:before { content:'\e0cd'; }
+
+.oi[data-glyph=transfer]:before { content:'\e0ce'; }
+
+.oi[data-glyph=trash]:before { content:'\e0cf'; }
+
+.oi[data-glyph=underline]:before { content:'\e0d0'; }
+
+.oi[data-glyph=vertical-align-bottom]:before { content:'\e0d1'; }
+
+.oi[data-glyph=vertical-align-center]:before { content:'\e0d2'; }
+
+.oi[data-glyph=vertical-align-top]:before { content:'\e0d3'; }
+
+.oi[data-glyph=video]:before { content:'\e0d4'; }
+
+.oi[data-glyph=volume-high]:before { content:'\e0d5'; }
+
+.oi[data-glyph=volume-low]:before { content:'\e0d6'; }
+
+.oi[data-glyph=volume-off]:before { content:'\e0d7'; }
+
+.oi[data-glyph=warning]:before { content:'\e0d8'; }
+
+.oi[data-glyph=wifi]:before { content:'\e0d9'; }
+
+.oi[data-glyph=wrench]:before { content:'\e0da'; }
+
+.oi[data-glyph=x]:before { content:'\e0db'; }
+
+.oi[data-glyph=yen]:before { content:'\e0dc'; }
+
+.oi[data-glyph=zoom-in]:before { content:'\e0dd'; }
+
+.oi[data-glyph=zoom-out]:before { content:'\e0de'; }
diff --git a/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.eot b/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.eot
new file mode 100644
index 000000000..7ca7c170f
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.eot
Binary files differ
diff --git a/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.otf b/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.otf
new file mode 100644
index 000000000..d79fb13a1
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.otf
Binary files differ
diff --git a/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.svg b/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.svg
new file mode 100644
index 000000000..0792c003a
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.svg
@@ -0,0 +1,543 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<!--
+2014-4-30: Created.
+-->
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata>
+Created by FontForge 20120731 at Wed Apr 30 22:56:47 2014
+ By P.J. Onori
+Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)
+</metadata>
+<defs>
+<font id="open-iconic" horiz-adv-x="800" >
+ <font-face
+ font-family="Icons"
+ font-weight="400"
+ font-stretch="normal"
+ units-per-em="800"
+ panose-1="2 0 5 3 0 0 0 0 0 0"
+ ascent="800"
+ descent="0"
+ bbox="-0.25 -101 802 800.126"
+ underline-thickness="50"
+ underline-position="-100"
+ unicode-range="U+E000-E0DE"
+ />
+ <missing-glyph />
+ <glyph glyph-name="" unicode="&#xe000;"
+d="M300 700h500v-700h-500v100h400v500h-400v100zM400 500l200 -150l-200 -150v100h-400v100h400v100z" />
+ <glyph glyph-name="1" unicode="&#xe001;"
+d="M300 700h500v-700h-500v100h400v500h-400v100zM200 500v-100h400v-100h-400v-100l-200 150z" />
+ <glyph glyph-name="2" unicode="&#xe002;"
+d="M350 700c193 0 350 -157 350 -350v-50h100l-200 -200l-200 200h100v50c0 138 -112 250 -250 250s-250 -112 -250 -250c0 193 157 350 350 350z" />
+ <glyph glyph-name="3" unicode="&#xe003;"
+d="M450 700c193 0 350 -157 350 -350c0 138 -112 250 -250 250s-250 -112 -250 -250v-50h100l-200 -200l-200 200h100v50c0 193 157 350 350 350z" />
+ <glyph glyph-name="4" unicode="&#xe004;"
+d="M0 700h800v-100h-800v100zM100 500h600v-100h-600v100zM0 300h800v-100h-800v100zM100 100h600v-100h-600v100z" />
+ <glyph glyph-name="5" unicode="&#xe005;"
+d="M0 700h800v-100h-800v100zM0 500h600v-100h-600v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100z" />
+ <glyph glyph-name="6" unicode="&#xe006;"
+d="M0 700h800v-100h-800v100zM200 500h600v-100h-600v100zM0 300h800v-100h-800v100zM200 100h600v-100h-600v100z" />
+ <glyph glyph-name="7" unicode="&#xe007;"
+d="M400 700c75 0 144 -23 203 -59l-72 -225l-322 234c57 31 122 50 191 50zM125 588l191 -138l-310 -222c-4 23 -6 47 -6 72c0 114 49 215 125 288zM688 575c69 -72 112 -168 112 -275c0 -35 -4 -68 -12 -100h-222zM216 256l112 -350c-128 23 -232 109 -287 222zM372 100
+h372c-64 -109 -177 -186 -310 -197z" />
+ <glyph glyph-name="8" unicode="&#xe008;" horiz-adv-x="600"
+d="M200 800h100v-500h200l-247 -300l-253 300h200v500z" />
+ <glyph glyph-name="9" unicode="&#xe009;"
+d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM300 700v-300h-200l300 -300l300 300h-200v300h-200z" />
+ <glyph glyph-name="a" unicode="&#xe00a;"
+d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700l-300 -300l300 -300v200h300v200h-300v200z" />
+ <glyph glyph-name="b" unicode="&#xe00b;"
+d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700v-200h-300v-200h300v-200l300 300z" />
+ <glyph glyph-name="c" unicode="&#xe00c;"
+d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700l-300 -300h200v-300h200v300h200z" />
+ <glyph glyph-name="d" unicode="&#xe00d;"
+d="M300 600v-200h500v-100h-500v-200l-300 247z" />
+ <glyph glyph-name="e" unicode="&#xe00e;"
+d="M500 600l300 -247l-300 -253v200h-500v100h500v200z" />
+ <glyph glyph-name="f" unicode="&#xe00f;" horiz-adv-x="600"
+d="M200 800h200v-500h200l-297 -300l-303 300h200v500z" />
+ <glyph glyph-name="10" unicode="&#xe010;"
+d="M300 700v-200h500v-200h-500v-200l-300 297z" />
+ <glyph glyph-name="11" unicode="&#xe011;"
+d="M500 700l300 -297l-300 -303v200h-500v200h500v200z" />
+ <glyph glyph-name="12" unicode="&#xe012;" horiz-adv-x="600"
+d="M297 800l303 -300h-200v-500h-200v500h-200z" />
+ <glyph glyph-name="13" unicode="&#xe013;" horiz-adv-x="600"
+d="M247 800l253 -300h-200v-500h-100v500h-200z" />
+ <glyph glyph-name="14" unicode="&#xe014;"
+d="M400 800h100v-800h-100v800zM200 700h100v-600h-100v600zM600 600h100v-400h-100v400zM0 500h100v-200h-100v200z" />
+ <glyph glyph-name="15" unicode="&#xe015;"
+d="M119 600l69 -72c-55 -54 -88 -130 -88 -212s33 -156 88 -210l-69 -72c-73 72 -119 172 -119 282s46 212 119 284zM681 600c73 -73 119 -174 119 -284s-46 -210 -119 -282l-69 72c55 54 88 126 88 210s-33 157 -88 212zM259 460l69 -72c-18 -18 -28 -45 -28 -72
+s10 -51 28 -69l-69 -72c-36 36 -59 86 -59 141s23 108 59 144zM541 459c36 -36 59 -87 59 -143s-23 -105 -59 -141l-69 72c18 18 28 41 28 69s-10 54 -28 72z" />
+ <glyph glyph-name="16" unicode="&#xe016;" horiz-adv-x="400"
+d="M200 800c110 0 200 -90 200 -200s-90 -200 -200 -200s-200 90 -200 200s90 200 200 200zM100 319c31 -11 65 -19 100 -19s69 8 100 19v-319l-100 100l-100 -100v319z" />
+ <glyph glyph-name="17" unicode="&#xe017;"
+d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300c0 -66 21 -126 56 -175l419 419c-49 35 -109 56 -175 56zM644 575l-419 -419c49 -35 109 -56 175 -56c166 0 300 134 300 300
+c0 66 -21 126 -56 175z" />
+ <glyph glyph-name="18" unicode="&#xe018;"
+d="M0 700h100v-600h700v-100h-800v700zM500 700h200v-500h-200v500zM200 500h200v-300h-200v300z" />
+ <glyph glyph-name="19" unicode="&#xe019;"
+d="M397 800c13 1 24 -4 34 -13c2 -1 214 -254 241 -287h128v-100h-100v-366c0 -18 -16 -34 -34 -34h-532c-18 0 -34 16 -34 34v366h-100v100h128l234 281c8 10 22 18 35 19zM400 672l-144 -172h288zM250 300c-28 0 -50 -22 -50 -50v-100c0 -28 22 -50 50 -50s50 22 50 50
+v100c0 28 -22 50 -50 50zM550 300c-28 0 -50 -22 -50 -50v-100c0 -28 22 -50 50 -50s50 22 50 50v100c0 28 -22 50 -50 50z" />
+ <glyph glyph-name="1a" unicode="&#xe01a;"
+d="M9 700h682c6 0 9 -4 9 -10v-190h100v-200h-100v-191c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v582c0 6 3 9 9 9zM100 600v-400h500v400h-500z" />
+ <glyph glyph-name="1b" unicode="&#xe01b;"
+d="M9 700h682c6 0 9 -4 9 -10v-190h100v-200h-100v-191c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v582c0 6 3 9 9 9z" />
+ <glyph glyph-name="1c" unicode="&#xe01c;"
+d="M92 650c0 23 19 50 45 50h3h5h5h500c28 0 50 -22 50 -50s-22 -50 -50 -50h-50v-141c9 -17 120 -231 166 -309c15 -26 34 -61 34 -106c0 -39 -15 -77 -41 -103c-27 -27 -65 -41 -103 -41h-512c-39 0 -77 15 -103 41c-27 27 -41 65 -41 103c0 45 19 79 34 106
+c46 78 157 292 166 309v141h-50c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51zM500 600h-200v-162l-6 -10s-65 -123 -122 -228h456c-57 105 -122 228 -122 228l-6 10v162z" />
+ <glyph glyph-name="1d" unicode="&#xe01d;"
+d="M400 800c110 0 200 -90 200 -200c0 -104 52 -198 134 -266c42 -34 66 -82 66 -134h-800c0 52 24 100 66 134c82 68 134 162 134 266c0 110 90 200 200 200zM300 100h200c0 -55 -45 -100 -100 -100s-100 45 -100 100z" />
+ <glyph glyph-name="1e" unicode="&#xe01e;" horiz-adv-x="600"
+d="M150 800h50l350 -250l-225 -147l225 -153l-350 -250h-50v250l-75 -75l-75 75l150 150l-150 150l75 75l75 -75v250zM250 650v-200l150 100zM250 350v-200l150 100z" />
+ <glyph glyph-name="1f" unicode="&#xe01f;"
+d="M0 800h500c110 0 200 -90 200 -200c0 -47 -17 -91 -44 -125c85 -40 144 -125 144 -225c0 -138 -112 -250 -250 -250h-550v100c55 0 100 45 100 100v400c0 55 -45 100 -100 100v100zM300 700v-200h100c55 0 100 45 100 100s-45 100 -100 100h-100zM300 400v-300h150
+c83 0 150 67 150 150s-67 150 -150 150h-150z" />
+ <glyph glyph-name="20" unicode="&#xe020;" horiz-adv-x="600"
+d="M300 800v-300h200l-300 -500v300h-200z" />
+ <glyph glyph-name="21" unicode="&#xe021;"
+d="M100 800h300v-300l100 100l100 -100v300h50c28 0 50 -22 50 -50v-550h-550c-28 0 -50 -22 -50 -50s22 -50 50 -50h550v-100h-550c-83 0 -150 67 -150 150v550l3 19c8 39 39 70 78 78z" />
+ <glyph glyph-name="22" unicode="&#xe022;" horiz-adv-x="400"
+d="M0 800h400v-800l-200 200l-200 -200v800z" />
+ <glyph glyph-name="23" unicode="&#xe023;"
+d="M0 800h800v-100h-800v100zM0 600h300v-103h203v103h297v-591c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v591z" />
+ <glyph glyph-name="24" unicode="&#xe024;"
+d="M300 800h200c55 0 100 -46 100 -100v-100h191c6 0 9 -3 9 -9v-241c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v241c0 6 3 9 9 9h191v100c0 54 45 100 100 100zM300 700v-100h200v100h-200zM0 209c16 -5 32 -9 50 -9h700c18 0 34 4 50 9v-200c0 -6 -3 -9 -9 -9h-782
+c-6 0 -9 3 -9 9v200z" />
+ <glyph glyph-name="25" unicode="&#xe025;" horiz-adv-x="600"
+d="M300 800c58 0 110 -16 147 -53s53 -89 53 -147h-100c0 39 -11 61 -25 75s-36 25 -75 25c-35 0 -55 -10 -72 -31s-28 -55 -28 -94c0 -51 20 -107 28 -175h172v-100h-178c-14 -60 -49 -127 -113 -200h491v-100h-600v122l16 12c69 69 95 121 106 166h-122v100h125
+c-8 50 -25 106 -25 175c0 58 16 113 50 156s88 69 150 69z" />
+ <glyph glyph-name="26" unicode="&#xe026;"
+d="M34 700h4h3h4h5h700c28 0 50 -22 50 -50v-700c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v700v2c0 20 15 42 34 48zM150 600c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50zM350 600c-28 0 -50 -22 -50 -50s22 -50 50 -50h300c28 0 50 22 50 50
+s-22 50 -50 50h-300zM100 400v-400h600v400h-600z" />
+ <glyph glyph-name="27" unicode="&#xe027;"
+d="M744 797l9 -3l41 -41c4 -4 3 -11 0 -15l-266 -375c-3 -5 -10 -11 -15 -13l-25 -12c-23 72 -78 127 -150 150l12 25l13 15l375 266zM266 400c74 0 134 -61 134 -134c0 -148 -118 -266 -266 -266c-48 0 -94 15 -134 38c80 46 134 129 134 228c0 73 59 134 132 134z" />
+ <glyph glyph-name="28" unicode="&#xe028;"
+d="M9 451c0 23 19 50 46 50c8 0 19 -3 26 -7l131 -66l29 22c-79 81 -1 250 118 250s197 -167 119 -250l28 -22l131 66c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-115 -56c9 -16 19 -33 25 -50h68c28 0 50 -22 50 -50s-22 -50 -50 -50h-50
+c0 -23 -2 -45 -6 -66l78 -40c21 -5 37 -28 37 -49c0 -28 -22 -50 -50 -50c-10 0 -23 5 -31 11l-65 35c-24 -46 -59 -83 -100 -107c-35 19 -63 42 -63 69v135v4v5v6v5v5v87c0 28 -22 50 -50 50c-25 0 -46 -17 -50 -40c1 -3 1 -8 1 -11s0 -8 -1 -11v-82v-4v-5v-144
+c0 -28 -27 -53 -62 -72c-41 25 -76 64 -100 110l-66 -35c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50c0 21 16 44 37 49l78 40c-4 21 -6 43 -6 66h-50h-5c-28 0 -50 22 -50 50c0 26 22 50 50 50h5h69c6 17 16 34 25 50l-116 56c-16 7 -28 27 -28 45z" />
+ <glyph glyph-name="29" unicode="&#xe029;"
+d="M610 700h81c6 0 9 -3 9 -9v-582c0 -6 -3 -9 -9 -9h-91v597zM210 503l290 147v-500l-250 125v-3c-14 0 -25 -11 -28 -25l72 -178c11 -25 0 -55 -25 -66s-55 0 -66 25l-103 272h-91c-6 0 -9 3 -9 9v182c0 6 3 9 9 9h182z" />
+ <glyph glyph-name="2a" unicode="&#xe02a;"
+d="M9 800h682c6 0 9 -3 9 -9v-782c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v782c0 6 3 9 9 9zM100 700v-200h500v200h-500zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400v-300h100v300h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100z" />
+ <glyph glyph-name="2b" unicode="&#xe02b;"
+d="M0 800h700v-200h-700v200zM0 500h700v-491c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v491zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100z" />
+ <glyph glyph-name="2c" unicode="&#xe02c;"
+d="M409 800h182c6 0 10 -4 12 -9l94 -182c2 -5 6 -9 12 -9h82c6 0 9 -3 9 -9v-582c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v441c0 83 67 150 150 150h141c6 0 10 4 12 9l94 182c2 5 6 9 12 9zM150 500c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z
+M500 500c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200zM500 400c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100z" />
+ <glyph glyph-name="2d" unicode="&#xe02d;"
+d="M0 600h800l-400 -400z" />
+ <glyph glyph-name="2e" unicode="&#xe02e;" horiz-adv-x="400"
+d="M400 800v-800l-400 400z" />
+ <glyph glyph-name="2f" unicode="&#xe02f;" horiz-adv-x="400"
+d="M0 800l400 -400l-400 -400v800z" />
+ <glyph glyph-name="30" unicode="&#xe030;"
+d="M400 600l400 -400h-800z" />
+ <glyph glyph-name="31" unicode="&#xe031;"
+d="M0 550c0 23 20 50 46 50h3h5h4h200c17 0 37 -13 44 -28l38 -72h444c14 0 19 -10 15 -22l-81 -253c-4 -13 -21 -25 -35 -25h-350c-14 0 -30 12 -34 25c-27 83 -54 167 -81 250l-10 25h-150c-2 0 -5 -1 -7 -1c-28 0 -51 23 -51 51zM358 100c28 0 50 -22 50 -50
+s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM658 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
+ <glyph glyph-name="32" unicode="&#xe032;"
+d="M0 700h500v-100h-300v-300h-100l-100 -100v500zM300 500h500v-500l-100 100h-400v400z" />
+ <glyph glyph-name="33" unicode="&#xe033;"
+d="M641 700l140 -141c-137 -143 -279 -280 -418 -421l-72 -69c-76 71 -148 146 -222 219l-69 71l141 141c51 -48 101 -97 150 -147c117 116 231 234 350 347z" />
+ <glyph glyph-name="34" unicode="&#xe034;"
+d="M150 600l250 -250l250 250l150 -150l-400 -400l-400 400z" />
+ <glyph glyph-name="35" unicode="&#xe035;" horiz-adv-x="600"
+d="M400 800l150 -150l-250 -250l250 -250l-150 -150l-400 400z" />
+ <glyph glyph-name="36" unicode="&#xe036;" horiz-adv-x="600"
+d="M150 800l400 -400l-400 -400l-150 150l250 250l-250 250z" />
+ <glyph glyph-name="37" unicode="&#xe037;"
+d="M400 600l400 -400l-150 -150l-250 250l-250 -250l-150 150z" />
+ <glyph glyph-name="38" unicode="&#xe038;"
+d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM600 622l-250 -250l-100 100l-72 -72l172 -172l322 322z" />
+ <glyph glyph-name="39" unicode="&#xe039;"
+d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM250 622l-72 -72l150 -150l-150 -150l72 -72l150 150l150 -150l72 72l-150 150l150 150l-72 72l-150 -150z" />
+ <glyph glyph-name="3a" unicode="&#xe03a;"
+d="M350 800c28 0 50 -22 50 -50v-50h75c14 0 25 -11 25 -25v-75h-300v75c0 14 11 25 25 25h75v50c0 28 22 50 50 50zM25 700h75v-200h500v200h75c14 0 25 -11 25 -25v-650c0 -14 -11 -25 -25 -25h-650c-14 0 -25 11 -25 25v650c0 14 11 25 25 25z" />
+ <glyph glyph-name="3b" unicode="&#xe03b;"
+d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM350 600h100v-181c23 -24 47 -47 72 -69l-72 -72c-27 30 -55 59 -84 88l-16 12
+v222z" />
+ <glyph glyph-name="3c" unicode="&#xe03c;"
+d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -18 -4 -34 -9 -50h-191v50c0 83 -67 150 -150 150s-150 -67 -150 -150v-50h-272c-17 30 -28 63 -28 100c0 110 90 200 200 200c23 114 129 200 250 200zM434 400h3h4c3 0 6 1 9 1c28 0 50 -22 50 -50v-1
+v-150h150l-200 -200l-200 200h150v150v2c0 20 15 42 34 48z" />
+ <glyph glyph-name="3d" unicode="&#xe03d;"
+d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -18 -4 -34 -9 -50h-141l-200 200l-200 -200h-222c-17 30 -28 63 -28 100c0 110 90 200 200 200c23 114 129 200 250 200zM450 350l250 -250h-200v-50c0 -28 -22 -50 -50 -50s-50 22 -50 50v50h-200z" />
+ <glyph glyph-name="3e" unicode="&#xe03e;"
+d="M450 700c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -83 -67 -150 -150 -150h-450c-110 0 -200 90 -200 200s90 200 200 200c23 114 129 200 250 200z" />
+ <glyph glyph-name="3f" unicode="&#xe03f;"
+d="M250 800c82 0 154 -40 200 -100c-143 -1 -270 -84 -325 -209c-37 -9 -70 -26 -100 -47c-16 32 -25 67 -25 106c0 138 112 250 250 250zM450 600c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -83 -67 -150 -150 -150h-450c-110 0 -200 90 -200 200
+s90 200 200 200c23 114 129 200 250 200z" />
+ <glyph glyph-name="40" unicode="&#xe040;"
+d="M500 700h100l-300 -600h-100zM100 600h100l-100 -200l100 -200h-100l-100 200zM600 600h100l100 -200l-100 -200h-100l100 200z" />
+ <glyph glyph-name="41" unicode="&#xe041;"
+d="M350 800h100l50 -119l28 -12l119 50l69 -72l-47 -119l12 -28l119 -50v-100l-119 -50l-12 -28l50 -119l-72 -72l-119 50l-28 -12l-50 -119h-100l-50 119l-28 12l-119 -50l-72 72l50 116l-12 31l-119 50v100l119 50l12 28l-50 119l72 72l119 -50l28 12zM400 550
+c-83 0 -150 -67 -150 -150s67 -150 150 -150s150 67 150 150s-67 150 -150 150z" />
+ <glyph glyph-name="42" unicode="&#xe042;"
+d="M0 800h800v-200h-800v200zM200 500h400l-200 -200zM0 100h800v-100h-800v100z" />
+ <glyph glyph-name="43" unicode="&#xe043;"
+d="M0 800h100v-800h-100v800zM600 800h200v-800h-200v800zM500 600v-400l-200 200z" />
+ <glyph glyph-name="44" unicode="&#xe044;"
+d="M0 800h200v-800h-200v800zM700 800h100v-800h-100v800zM300 600l200 -200l-200 -200v400z" />
+ <glyph glyph-name="45" unicode="&#xe045;"
+d="M0 800h800v-100h-800v100zM400 500l200 -200h-400zM0 200h800v-200h-800v200z" />
+ <glyph glyph-name="46" unicode="&#xe046;"
+d="M150 700c83 0 150 -67 150 -150v-50h100v50c0 83 67 150 150 150s150 -67 150 -150s-67 -150 -150 -150h-50v-100h50c83 0 150 -67 150 -150s-67 -150 -150 -150s-150 67 -150 150v50h-100v-50c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150h50v100h-50
+c-83 0 -150 67 -150 150s67 150 150 150zM150 600c-28 0 -50 -22 -50 -50s22 -50 50 -50h50v50c0 28 -22 50 -50 50zM550 600c-28 0 -50 -22 -50 -50v-50h50c28 0 50 22 50 50s-22 50 -50 50zM300 400v-100h100v100h-100zM150 200c-28 0 -50 -22 -50 -50s22 -50 50 -50
+s50 22 50 50v50h-50zM500 200v-50c0 -28 22 -50 50 -50s50 22 50 50s-22 50 -50 50h-50z" />
+ <glyph glyph-name="47" unicode="&#xe047;"
+d="M0 791c0 6 3 9 9 9h782c6 0 9 -4 9 -10v-790l-200 200h-591c-6 0 -9 3 -9 9v582z" />
+ <glyph glyph-name="48" unicode="&#xe048;"
+d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM600 600l-100 -300l-300 -100l100 300zM400 450c-28 0 -50 -22 -50 -50
+s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
+ <glyph glyph-name="49" unicode="&#xe049;"
+d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700v-600c166 0 300 134 300 300s-134 300 -300 300z" />
+ <glyph glyph-name="4a" unicode="&#xe04a;"
+d="M0 800h800v-100h-800v100zM0 600h500v-100h-500v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100zM750 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
+ <glyph glyph-name="4b" unicode="&#xe04b;"
+d="M25 700h750c14 0 25 -11 25 -25v-75h-800v75c0 14 11 25 25 25zM0 500h800v-375c0 -14 -11 -25 -25 -25h-750c-14 0 -25 11 -25 25v375zM100 300v-100h100v100h-100zM300 300v-100h100v100h-100z" />
+ <glyph glyph-name="4c" unicode="&#xe04c;"
+d="M100 800h100v-100h450l100 100l50 -50l-100 -100v-450h100v-100h-100v-100h-100v100h-500v500h-100v100h100v100zM200 600v-350l350 350h-350zM600 550l-350 -350h350v350z" />
+ <glyph glyph-name="4d" unicode="&#xe04d;"
+d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM400 600c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z
+M200 452c0 20 15 42 34 48h3h3h8c12 0 28 -7 36 -16l91 -90l25 6c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100l6 25l-90 91c-9 8 -16 24 -16 36zM550 500c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
+ <glyph glyph-name="4e" unicode="&#xe04e;"
+d="M300 800h200v-300h200l-300 -300l-300 300h200v300zM0 100h800v-100h-800v100z" />
+ <glyph glyph-name="4f" unicode="&#xe04f;"
+d="M0 800h800v-100h-800v100zM400 600l300 -300h-200v-300h-200v300h-200z" />
+ <glyph glyph-name="50" unicode="&#xe050;"
+d="M200 700h600v-600h-600l-200 300zM350 622l-72 -72l150 -150l-150 -150l72 -72l150 150l150 -150l72 72l-150 150l150 150l-72 72l-150 -150z" />
+ <glyph glyph-name="51" unicode="&#xe051;"
+d="M400 700c220 0 400 -180 400 -400h-100c0 166 -134 300 -300 300s-300 -134 -300 -300h-100c0 220 180 400 400 400zM341 491l59 -88l59 88c82 -25 141 -101 141 -191c0 -110 -90 -200 -200 -200s-200 90 -200 200c0 90 59 166 141 191z" />
+ <glyph glyph-name="52" unicode="&#xe052;"
+d="M0 800h300v-400h400v-400h-700v800zM400 800l300 -300h-300v300zM100 600v-100h100v100h-100zM100 400v-100h100v100h-100zM100 200v-100h400v100h-400z" />
+ <glyph glyph-name="53" unicode="&#xe053;" horiz-adv-x="600"
+d="M200 700h100v-100h75c30 0 58 -6 81 -22c24 -15 44 -44 44 -78v-100h-100v94c-4 3 -13 6 -25 6h-250c-13 0 -25 -12 -25 -25v-50c0 -14 20 -40 34 -44l257 -65c66 -16 109 -73 109 -141v-50c0 -69 -56 -125 -125 -125h-75v-100h-100v100h-75c-30 0 -58 6 -81 22
+c-24 15 -44 44 -44 78v100h100v-94c4 -3 13 -6 25 -6h250c13 0 25 12 25 25v50c0 14 -20 40 -34 44l-257 65c-66 16 -109 73 -109 141v50c0 69 56 125 125 125h75v100z" />
+ <glyph glyph-name="54" unicode="&#xe054;"
+d="M0 700h300v-300l-300 -300v600zM500 700h300v-300l-300 -300v600z" />
+ <glyph glyph-name="55" unicode="&#xe055;"
+d="M300 700v-600h-300v300zM800 700v-600h-300v300z" />
+ <glyph glyph-name="56" unicode="&#xe056;"
+d="M300 700v-100c-111 0 -200 -89 -200 -200h200v-300h-300v300c0 165 135 300 300 300zM800 700v-100c-111 0 -200 -89 -200 -200h200v-300h-300v300c0 165 135 300 300 300z" />
+ <glyph glyph-name="57" unicode="&#xe057;"
+d="M0 700h300v-300c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-200v300zM500 700h300v-300c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-200v300z" />
+ <glyph glyph-name="58" unicode="&#xe058;" horiz-adv-x="600"
+d="M300 800l34 -34c11 -11 266 -269 266 -488c0 -165 -135 -300 -300 -300s-300 135 -300 300c0 219 255 477 266 488zM150 328c-28 0 -50 -22 -50 -50c0 -110 90 -200 200 -200c28 0 50 22 50 50s-22 50 -50 50c-55 0 -100 45 -100 100c0 28 -22 50 -50 50z" />
+ <glyph glyph-name="59" unicode="&#xe059;"
+d="M400 800l400 -500h-800zM0 200h800v-200h-800v200z" />
+ <glyph glyph-name="5a" unicode="&#xe05a;" horiz-adv-x="600"
+d="M300 800l300 -300h-600zM0 300h600l-300 -300z" />
+ <glyph glyph-name="5b" unicode="&#xe05b;"
+d="M0 500h200v-200h-200v200zM300 500h200v-200h-200v200zM600 500h200v-200h-200v200z" />
+ <glyph glyph-name="5c" unicode="&#xe05c;"
+d="M0 700h800v-100l-400 -200l-400 200v100zM0 500l400 -200l400 200v-400h-800v400z" />
+ <glyph glyph-name="5d" unicode="&#xe05d;"
+d="M400 800l400 -200v-600h-800v600zM400 688l-300 -150v-188l300 -150l300 150v188zM200 500h400v-100l-200 -100l-200 100v100z" />
+ <glyph glyph-name="5e" unicode="&#xe05e;"
+d="M600 700c69 0 134 -19 191 -50l-16 -106c-49 35 -109 56 -175 56c-131 0 -240 -84 -281 -200h331l-16 -100h-334c0 -36 8 -68 19 -100h297l-16 -100h-222c55 -61 133 -100 222 -100c78 0 147 30 200 78v-122c-59 -35 -127 -56 -200 -56c-147 0 -274 82 -344 200h-256
+l19 100h197c-8 32 -16 66 -16 100h-200l25 100h191c45 172 198 300 384 300z" />
+ <glyph glyph-name="5f" unicode="&#xe05f;"
+d="M0 700h700v-100h-700v100zM0 500h500v-100h-500v100zM0 300h800v-100h-800v100zM0 100h100v-100h-100v100zM200 100h100v-100h-100v100zM400 100h100v-100h-100v100z" />
+ <glyph glyph-name="60" unicode="&#xe060;"
+d="M0 800h800v-100h-800v100zM200 600h400l-200 -200zM0 200h800v-200h-800v200z" />
+ <glyph glyph-name="61" unicode="&#xe061;"
+d="M0 800h100v-800h-100v800zM600 800h200v-800h-200v800zM200 600l200 -200l-200 -200v400z" />
+ <glyph glyph-name="62" unicode="&#xe062;"
+d="M0 800h200v-800h-200v800zM700 800h100v-800h-100v800zM600 600v-400l-200 200z" />
+ <glyph glyph-name="63" unicode="&#xe063;"
+d="M0 800h800v-200h-800v200zM400 400l200 -200h-400zM0 100h800v-100h-800v100z" />
+ <glyph glyph-name="64" unicode="&#xe064;"
+d="M0 800h200v-100h-100v-600h600v100h100v-200h-800v800zM400 800h400v-400l-150 150l-250 -250l-100 100l250 250z" />
+ <glyph glyph-name="65" unicode="&#xe065;"
+d="M403 700c247 0 397 -300 397 -300s-150 -300 -397 -300c-253 0 -403 300 -403 300s150 300 403 300zM400 600c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200zM400 500c10 0 19 -3 28 -6c-16 -8 -28 -24 -28 -44c0 -28 22 -50 50 -50
+c20 0 36 12 44 28c3 -9 6 -18 6 -28c0 -55 -45 -100 -100 -100s-100 45 -100 100s45 100 100 100z" />
+ <glyph glyph-name="66" unicode="&#xe066;" horiz-adv-x="900"
+d="M331 700h3h3c3 1 7 1 10 1c12 0 29 -8 37 -17l94 -93l66 65c57 57 156 57 212 0c59 -58 59 -154 0 -212l-65 -66l93 -94c10 -8 18 -25 18 -38c0 -28 -22 -50 -50 -50c-13 0 -32 9 -40 20l-62 65l-366 -365l-12 -16h-272v272l375 381l-63 63c-9 8 -16 24 -16 36
+c0 20 16 42 35 48zM447 481l-313 -315l128 -132l316 316z" />
+ <glyph glyph-name="67" unicode="&#xe067;"
+d="M0 800h300v-400h400v-400h-700v800zM400 800l300 -300h-300v300z" />
+ <glyph glyph-name="68" unicode="&#xe068;"
+d="M200 800c0 0 200 -100 200 -300s-298 -302 -200 -500c0 0 -200 100 -200 300s300 300 200 500zM500 500c0 0 200 -100 200 -300c0 -150 -60 -200 -100 -200h-300c0 200 300 300 200 500z" />
+ <glyph glyph-name="69" unicode="&#xe069;"
+d="M0 800h100v-800h-100v800zM200 800h300v-100h300l-200 -203l200 -197h-400v100h-200v400z" />
+ <glyph glyph-name="6a" unicode="&#xe06a;" horiz-adv-x="400"
+d="M150 800h150l-100 -200h200l-150 -300h150l-300 -300l-100 300h134l66 200h-200z" />
+ <glyph glyph-name="6b" unicode="&#xe06b;"
+d="M0 800h300v-100h500v-100h-800v200zM0 500h800v-450c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v450z" />
+ <glyph glyph-name="6c" unicode="&#xe06c;"
+d="M150 800c83 0 150 -67 150 -150c0 -66 -41 -121 -100 -141v-118c15 5 33 9 50 9h200c28 0 50 22 50 50v59c-59 20 -100 75 -100 141c0 83 67 150 150 150s150 -67 150 -150c0 -66 -41 -121 -100 -141v-59c0 -82 -68 -150 -150 -150h-200c-14 0 -25 -7 -34 -16
+c50 -24 84 -74 84 -134c0 -83 -67 -150 -150 -150s-150 67 -150 150c0 66 41 121 100 141v218c-59 20 -100 75 -100 141c0 83 67 150 150 150z" />
+ <glyph glyph-name="6d" unicode="&#xe06d;"
+d="M0 800h400l-150 -150l150 -150l-100 -100l-150 150l-150 -150v400zM500 400l150 -150l150 150v-400h-400l150 150l-150 150z" />
+ <glyph glyph-name="6e" unicode="&#xe06e;"
+d="M100 800l150 -150l150 150v-400h-400l150 150l-150 150zM400 400h400l-150 -150l150 -150l-100 -100l-150 150l-150 -150v400z" />
+ <glyph glyph-name="6f" unicode="&#xe06f;"
+d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700c-56 0 -108 -17 -153 -44l22 -19c33 -18 13 -48 -13 -59c-29 -13 -77 10 -65 -41c14 -55 -27 -3 -47 -15c-43 -25 49 -152 31 -156c-14 -3 -40 34 -59 34
+c-8 0 -13 -5 -16 -10c1 -30 10 -57 19 -84c28 -10 74 1 97 -22c47 -28 100 -118 78 -162c33 -13 68 -22 106 -22c100 0 189 49 244 125c3 24 -5 44 -47 44c-69 0 -156 14 -153 97c2 46 101 108 66 143c-30 31 12 39 12 66c0 37 -65 32 -69 50s20 36 41 56
+c-30 10 -61 19 -94 19zM631 591c-38 -11 -95 -35 -87 -53c5 -15 55 -2 68 -13c11 -10 15 -58 44 -31l19 22c-12 27 -26 53 -44 75z" />
+ <glyph glyph-name="70" unicode="&#xe070;"
+d="M703 800l97 -100l-400 -400l-100 100l-200 -203l-100 100l300 303l100 -100zM0 100h800v-100h-800v100z" />
+ <glyph glyph-name="71" unicode="&#xe071;"
+d="M0 700h100v-100h-100v100zM200 700h100v-100h-100v100zM400 700h100v-100h-100v100zM600 700h100v-100h-100v100zM0 500h100v-100h-100v100zM200 500h100v-100h-100v100zM400 500h100v-100h-100v100zM600 500h100v-100h-100v100zM0 300h100v-100h-100v100zM200 300h100
+v-100h-100v100zM400 300h100v-100h-100v100zM600 300h100v-100h-100v100zM0 100h100v-100h-100v100zM200 100h100v-100h-100v100zM400 100h100v-100h-100v100zM600 100h100v-100h-100v100z" />
+ <glyph glyph-name="72" unicode="&#xe072;"
+d="M0 800h200v-200h-200v200zM300 800h200v-200h-200v200zM600 800h200v-200h-200v200zM0 500h200v-200h-200v200zM300 500h200v-200h-200v200zM600 500h200v-200h-200v200zM0 200h200v-200h-200v200zM300 200h200v-200h-200v200zM600 200h200v-200h-200v200z" />
+ <glyph glyph-name="73" unicode="&#xe073;"
+d="M0 800h300v-300h-300v300zM500 800h300v-300h-300v300zM0 300h300v-300h-300v300zM500 300h300v-300h-300v300z" />
+ <glyph glyph-name="74" unicode="&#xe074;"
+d="M19 800h662c11 0 19 -8 19 -19v-331c0 -28 -22 -50 -50 -50h-600c-28 0 -50 22 -50 50v331c0 11 8 19 19 19zM0 309c16 -5 32 -9 50 -9h600c18 0 34 4 50 9v-290c0 -11 -8 -19 -19 -19h-662c-11 0 -19 8 -19 19v290zM550 200c-28 0 -50 -22 -50 -50s22 -50 50 -50
+s50 22 50 50s-22 50 -50 50z" />
+ <glyph glyph-name="75" unicode="&#xe075;"
+d="M0 700h300v-100h-50c-28 0 -50 -22 -50 -50v-150h300v150c0 28 -22 50 -50 50h-50v100h300v-100h-50c-28 0 -50 -22 -50 -50v-400c0 -28 22 -50 50 -50h50v-100h-300v100h50c28 0 50 22 50 50v150h-300v-150c0 -28 22 -50 50 -50h50v-100h-300v100h50c28 0 50 22 50 50
+v400c0 28 -22 50 -50 50h-50v100z" />
+ <glyph glyph-name="76" unicode="&#xe076;"
+d="M400 700c165 0 300 -135 300 -300v-100h50c28 0 50 -22 50 -50v-200c0 -28 -22 -50 -50 -50h-100c-28 0 -50 22 -50 50v350c0 111 -89 200 -200 200s-200 -89 -200 -200v-350c0 -28 -22 -50 -50 -50h-100c-28 0 -50 22 -50 50v200c0 28 22 50 50 50h50v100
+c0 165 135 300 300 300z" />
+ <glyph glyph-name="77" unicode="&#xe077;"
+d="M0 500c0 109 91 200 200 200s200 -91 200 -200c0 109 91 200 200 200s200 -91 200 -200c0 -55 -22 -104 -59 -141l-341 -343l-341 343c-37 36 -59 86 -59 141z" />
+ <glyph glyph-name="78" unicode="&#xe078;"
+d="M400 700l400 -300l-100 3v-403h-200v200h-200v-200h-200v400h-100z" />
+ <glyph glyph-name="79" unicode="&#xe079;"
+d="M0 800h800v-800h-800v800zM100 700v-300l100 100l400 -400h100v100l-200 200l100 100l100 -100v300h-600z" />
+ <glyph glyph-name="7a" unicode="&#xe07a;"
+d="M19 800h762c11 0 19 -8 19 -19v-762c0 -11 -8 -19 -19 -19h-762c-11 0 -19 8 -19 19v762c0 11 8 19 19 19zM100 600v-300h100l100 -100h200l100 100h100v300h-600z" />
+ <glyph glyph-name="7b" unicode="&#xe07b;"
+d="M200 600c79 0 143 -56 200 -122c58 66 119 122 200 122c131 0 200 -101 200 -200s-69 -200 -200 -200c-81 0 -142 56 -200 122c-58 -66 -121 -122 -200 -122c-131 0 -200 101 -200 200s69 200 200 200zM200 500c-74 0 -100 -54 -100 -100s26 -100 100 -100
+c42 0 88 47 134 100c-46 53 -92 100 -134 100zM600 500c-43 0 -89 -47 -134 -100c45 -53 91 -100 134 -100c74 0 100 54 100 100s-26 100 -100 100z" />
+ <glyph glyph-name="7c" unicode="&#xe07c;" horiz-adv-x="400"
+d="M300 800c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100zM150 550c83 0 150 -69 150 -150c0 -66 -100 -214 -100 -250c0 -28 22 -50 50 -50s50 22 50 50h100c0 -83 -67 -150 -150 -150s-150 64 -150 150s100 222 100 250s-22 50 -50 50
+s-50 -22 -50 -50h-100c0 83 67 150 150 150z" />
+ <glyph glyph-name="7d" unicode="&#xe07d;"
+d="M200 800h500v-100h-122c-77 -197 -156 -392 -234 -588l-6 -12h162v-100h-500v100h122c77 197 156 392 234 588l7 12h-163v100z" />
+ <glyph glyph-name="7e" unicode="&#xe07e;"
+d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM100 100h600v-100h-600v100z" />
+ <glyph glyph-name="7f" unicode="&#xe07f;"
+d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100z" />
+ <glyph glyph-name="80" unicode="&#xe080;"
+d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM200 100h600v-100h-600v100z" />
+ <glyph glyph-name="81" unicode="&#xe081;"
+d="M550 800c138 0 250 -112 250 -250s-112 -250 -250 -250c-16 0 -30 3 -44 6l-6 -6v-100h-200v-200h-300v200l306 306c-3 14 -6 28 -6 44c0 138 112 250 250 250zM600 700c-55 0 -100 -45 -100 -100s45 -100 100 -100s100 45 100 100s-45 100 -100 100z" />
+ <glyph glyph-name="82" unicode="&#xe082;"
+d="M134 600h3h4h4h5h500c28 0 50 -22 50 -50v-350h100v-150c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v150h100v350v2c0 20 15 42 34 48zM200 500v-300h100v-100h200v100h100v300h-400z" />
+ <glyph glyph-name="83" unicode="&#xe083;"
+d="M0 800h400v-400h-400v400zM500 600h100v-400h-400v100h300v300zM700 400h100v-400h-400v100h300v300z" />
+ <glyph glyph-name="84" unicode="&#xe084;" horiz-adv-x="600"
+d="M337 694c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-300 -150c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50c0 21 16 44 37 49zM437 544c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-400 -200c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50
+c0 21 16 44 37 49zM437 344c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-106 -56c24 -4 43 -26 43 -50c0 -28 -23 -51 -51 -51c-2 0 -6 1 -8 1h-200c-26 1 -48 24 -48 50c0 16 12 36 26 44zM151 -50c0 23 20 50 46 50h3h4h5h100c28 0 50 -22 50 -50
+s-22 -50 -50 -50h-100c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51z" />
+ <glyph glyph-name="85" unicode="&#xe085;"
+d="M199 800h100v-200h-200v100h100v100zM587 797c18 1 38 1 56 -3c36 -8 69 -26 97 -54c78 -77 78 -203 0 -281l-150 -150c-8 -13 -27 -24 -42 -24c-28 0 -50 22 -50 50c0 15 10 35 23 43l150 150c40 40 40 105 0 144c-40 40 -110 34 -144 0l-43 -44c-8 -13 -28 -24 -43 -24
+c-28 0 -50 22 -50 50c0 15 11 35 24 43l44 44c33 33 72 53 128 56zM209 490c4 5 13 16 21 16h4c2 0 6 1 8 1c28 0 50 -22 50 -50c0 -11 -7 -27 -15 -35l-150 -150c-40 -40 -40 -105 0 -144c40 -40 110 -34 144 0l44 44c8 13 28 24 43 24c28 0 50 -22 50 -50
+c0 -15 -11 -35 -24 -43l-44 -44c-22 -22 -48 -37 -75 -47c-71 -25 -150 -9 -206 47c-78 77 -78 203 0 281zM499 200h200v-100h-100v-100h-100v200z" />
+ <glyph glyph-name="86" unicode="&#xe086;"
+d="M587 797c18 1 38 1 56 -3c36 -8 69 -26 97 -54c78 -77 78 -203 0 -281l-150 -150c-62 -62 -131 -81 -181 -78s-70 17 -85 25s-26 27 -26 44c0 28 22 51 50 51c8 0 19 -3 26 -7c0 0 15 -11 41 -13c26 -1 63 4 106 47l150 150c40 40 40 105 0 144c-40 40 -110 34 -144 0
+c-8 -13 -27 -24 -42 -24c-28 0 -50 22 -50 50c0 15 11 35 24 43c33 33 72 53 128 56zM387 566c50 -2 63 -17 84 -22s38 -28 38 -49c0 -28 -22 -50 -50 -50c-10 0 -24 5 -32 11c0 0 -19 9 -47 10s-63 -4 -103 -44l-150 -150c-40 -40 -40 -105 0 -144c40 -40 110 -34 144 0
+c8 13 28 24 43 24c28 0 50 -22 50 -50c0 -15 -11 -35 -24 -43c-22 -22 -48 -37 -75 -47c-71 -25 -150 -9 -206 47c-78 77 -78 203 0 281l150 150c60 60 128 78 178 76z" />
+ <glyph glyph-name="87" unicode="&#xe087;"
+d="M0 700h300v-300h-300v300zM400 700h400v-100h-400v100zM400 500h300v-100h-300v100zM0 300h300v-300h-300v300zM400 300h400v-100h-400v100zM400 100h300v-100h-300v100z" />
+ <glyph glyph-name="88" unicode="&#xe088;"
+d="M50 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 700h600v-100h-600v100zM50 500c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 500h600v-100h-600v100zM50 300c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50
+s22 50 50 50zM200 300h600v-100h-600v100zM50 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 100h600v-100h-600v100z" />
+ <glyph glyph-name="89" unicode="&#xe089;"
+d="M800 800l-400 -800l-100 300l-300 100z" />
+ <glyph glyph-name="8a" unicode="&#xe08a;" horiz-adv-x="600"
+d="M300 700c110 0 200 -90 200 -200v-100h100v-400h-600v400h100v100c0 110 90 200 200 200zM300 600c-56 0 -100 -44 -100 -100v-100h200v100c0 56 -44 100 -100 100z" />
+ <glyph glyph-name="8b" unicode="&#xe08b;" horiz-adv-x="600"
+d="M300 800c110 0 200 -90 200 -200v-200h100v-400h-600v400h400v200c0 56 -44 100 -100 100s-100 -44 -100 -100h-100c0 110 90 200 200 200z" />
+ <glyph glyph-name="8c" unicode="&#xe08c;"
+d="M400 700v-100c-111 0 -200 -89 -200 -200h100l-150 -200l-150 200h100c0 165 135 300 300 300zM650 600l150 -200h-100c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-100z" />
+ <glyph glyph-name="8d" unicode="&#xe08d;"
+d="M100 800h600v-300h100l-150 -250l-150 250h100v200h-400v-100h-100v200zM150 550l150 -250h-100v-200h400v100h100v-200h-600v300h-100z" />
+ <glyph glyph-name="8e" unicode="&#xe08e;"
+d="M600 700l200 -150l-200 -150v100h-500v-100h-100v100c0 55 45 100 100 100h500v100zM200 300v-100h500v100h100v-100c0 -54 -46 -100 -100 -100h-500v-100l-200 150z" />
+ <glyph glyph-name="8f" unicode="&#xe08f;" horiz-adv-x="900"
+d="M350 800c193 0 350 -157 350 -350c0 -60 -17 -117 -44 -166c5 -3 12 -8 16 -12l100 -100c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-100 100c-4 3 -9 9 -12 13c-49 -26 -107 -41 -166 -41c-193 0 -350 157 -350 350s157 350 350 350zM350 200
+c142 0 250 108 250 250c0 139 -111 250 -250 250s-250 -111 -250 -250s111 -250 250 -250z" />
+ <glyph glyph-name="90" unicode="&#xe090;" horiz-adv-x="600"
+d="M300 800c166 0 300 -134 300 -300c0 -200 -300 -500 -300 -500s-300 300 -300 500c0 166 134 300 300 300zM300 700c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200z" />
+ <glyph glyph-name="91" unicode="&#xe091;" horiz-adv-x="900"
+d="M0 800h800v-541c1 -3 1 -8 1 -11s0 -7 -1 -10v-238h-800v800zM495 250c0 26 22 50 50 50h5h150v400h-600v-600h600v100h-150h-5c-28 0 -50 22 -50 50zM350 600c83 0 150 -67 150 -150c0 -100 -150 -250 -150 -250s-150 150 -150 250c0 83 67 150 150 150zM350 500
+c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
+ <glyph glyph-name="92" unicode="&#xe092;" horiz-adv-x="600"
+d="M0 700h200v-600h-200v600zM400 700h200v-600h-200v600z" />
+ <glyph glyph-name="93" unicode="&#xe093;" horiz-adv-x="600"
+d="M0 700l600 -300l-600 -300v600z" />
+ <glyph glyph-name="94" unicode="&#xe094;" horiz-adv-x="600"
+d="M300 700c166 0 300 -134 300 -300s-134 -300 -300 -300s-300 134 -300 300s134 300 300 300z" />
+ <glyph glyph-name="95" unicode="&#xe095;"
+d="M400 700v-600l-400 300zM400 400l400 300v-600z" />
+ <glyph glyph-name="96" unicode="&#xe096;"
+d="M0 700l400 -300l-400 -300v600zM400 100v600l400 -300z" />
+ <glyph glyph-name="97" unicode="&#xe097;"
+d="M0 700h200v-600h-200v600zM200 400l500 300v-600z" />
+ <glyph glyph-name="98" unicode="&#xe098;"
+d="M0 700l500 -300l-500 -300v600zM500 100v600h200v-600h-200z" />
+ <glyph glyph-name="99" unicode="&#xe099;" horiz-adv-x="600"
+d="M0 700h600v-600h-600v600z" />
+ <glyph glyph-name="9a" unicode="&#xe09a;"
+d="M200 800h400v-200h200v-400h-200v-200h-400v200h-200v400h200v200z" />
+ <glyph glyph-name="9b" unicode="&#xe09b;"
+d="M0 700h800v-100h-800v100zM0 403h800v-100h-800v100zM0 103h800v-100h-800v100z" />
+ <glyph glyph-name="9c" unicode="&#xe09c;" horiz-adv-x="600"
+d="M278 700c7 2 13 4 22 4c55 0 100 -45 100 -100v-4v-200c0 -55 -45 -100 -100 -100s-100 45 -100 100v200v2c0 44 35 88 78 98zM34 500h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-50c0 -111 89 -200 200 -200s200 89 200 200v50c0 28 22 50 50 50s50 -22 50 -50v-50
+c0 -148 -109 -270 -250 -294v-106h50c55 0 100 -45 100 -100h-400c0 55 45 100 100 100h50v106c-141 24 -250 146 -250 294v50v2c0 20 15 42 34 48z" />
+ <glyph glyph-name="9d" unicode="&#xe09d;"
+d="M0 500h800v-200h-800v200z" />
+ <glyph glyph-name="9e" unicode="&#xe09e;"
+d="M34 700h4h3h4h5h700c28 0 50 -22 50 -50v-500c0 -28 -22 -50 -50 -50h-250v-100h100c55 0 100 -45 100 -100h-600c0 55 45 100 100 100h100v100h-250c-28 0 -50 22 -50 50v500v2c0 20 15 42 34 48zM100 600v-400h600v400h-600z" />
+ <glyph glyph-name="9f" unicode="&#xe09f;"
+d="M272 700c-14 -40 -22 -84 -22 -128c0 -221 179 -400 400 -400c45 0 88 11 128 25c-53 -158 -202 -275 -378 -275c-221 0 -400 179 -400 400c0 176 114 325 272 378z" />
+ <glyph glyph-name="a0" unicode="&#xe0a0;"
+d="M350 700l150 -150h-100v-150h150v100l150 -150l-150 -150v100h-150v-150h100l-150 -150l-150 150h100v150h-150v-100l-150 150l150 150v-100h150v150h-100z" />
+ <glyph glyph-name="a1" unicode="&#xe0a1;"
+d="M800 800v-550c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150c17 0 35 -4 50 -9v206c-201 -6 -327 -27 -400 -50v-397c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150c17 0 35 -4 50 -9v409s100 100 600 100z" />
+ <glyph glyph-name="a2" unicode="&#xe0a2;" horiz-adv-x="700"
+d="M499 700c51 0 102 -20 141 -59c78 -77 78 -203 0 -281l-250 -244c-48 -48 -127 -48 -175 0s-48 127 0 175c32 32 64 65 96 97l69 -69l-59 -63l-38 -34c-10 -10 -10 -28 0 -38s28 -10 38 0l250 247c38 40 39 103 0 141c-40 40 -105 40 -144 0v-3l-278 -272
+c-67 -69 -68 -179 0 -247c69 -69 181 -69 250 0c39 44 83 84 125 125l69 -69l-125 -125c-107 -107 -281 -107 -388 0s-107 281 0 388l278 272c38 39 90 59 141 59z" />
+ <glyph glyph-name="a3" unicode="&#xe0a3;"
+d="M600 800l200 -200l-100 -100l-200 200zM400 600l200 -200l-400 -400h-200v200z" />
+ <glyph glyph-name="a4" unicode="&#xe0a4;"
+d="M550 800c83 0 150 -90 150 -200s-67 -200 -150 -200c-22 0 -41 5 -59 16c6 27 9 55 9 84c0 85 -27 158 -72 212c27 52 71 88 122 88zM250 700c83 0 150 -90 150 -200s-67 -200 -150 -200s-150 90 -150 200s67 200 150 200zM725 384c44 -22 75 -66 75 -118v-166h-200v66
+c0 50 -17 96 -44 134c67 2 126 33 169 84zM75 284c44 -53 106 -84 175 -84s131 31 175 84c44 -22 75 -66 75 -118v-166h-500v166c0 52 31 96 75 118z" />
+ <glyph glyph-name="a5" unicode="&#xe0a5;"
+d="M400 800c110 0 200 -112 200 -250s-90 -250 -200 -250s-200 112 -200 250s90 250 200 250zM191 300c54 -61 128 -100 209 -100s155 39 209 100c107 -4 191 -92 191 -200v-100h-800v100c0 108 84 196 191 200z" />
+ <glyph glyph-name="a6" unicode="&#xe0a6;" horiz-adv-x="600"
+d="M19 800h462c11 0 19 -8 19 -19v-762c0 -11 -8 -19 -19 -19h-462c-11 0 -19 8 -19 19v762c0 11 8 19 19 19zM100 700v-500h300v500h-300zM250 150c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
+ <glyph glyph-name="a7" unicode="&#xe0a7;"
+d="M350 800c17 0 34 0 50 -3v-397l-297 297c63 64 150 103 247 103zM500 694c169 -24 300 -168 300 -344c0 -193 -157 -350 -350 -350c-85 0 -162 31 -222 81l272 272v341zM91 562l237 -234l-216 -212c-69 54 -112 139 -112 234c0 84 36 158 91 212z" />
+ <glyph glyph-name="a8" unicode="&#xe0a8;"
+d="M92 650c0 23 20 50 46 50h3h4h5h400c28 0 50 -22 50 -50s-22 -50 -50 -50h-50v-200h100c55 0 100 -45 100 -100h-300v-300l-56 -100l-44 100v300h-300c0 55 45 100 100 100h100v200h-50c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51z" />
+ <glyph glyph-name="a9" unicode="&#xe0a9;"
+d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM300 600v-400l300 200z" />
+ <glyph glyph-name="aa" unicode="&#xe0aa;"
+d="M300 800h200v-300h300v-200h-300v-300h-200v300h-300v200h300v300z" />
+ <glyph glyph-name="ab" unicode="&#xe0ab;"
+d="M300 800h100v-400h-100v400zM172 656l62 -78l-40 -31c-58 -46 -94 -117 -94 -197c0 -139 111 -250 250 -250s250 111 250 250c0 80 -39 151 -97 197l-37 31l62 78l38 -31c82 -64 134 -163 134 -275c0 -193 -157 -350 -350 -350s-350 157 -350 350c0 112 54 211 134 275z
+" />
+ <glyph glyph-name="ac" unicode="&#xe0ac;"
+d="M200 800h400v-200h-400v200zM9 500h782c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-91v200h-600v-200h-91c-6 0 -9 3 -9 9v282c0 6 3 9 9 9zM200 300h400v-300h-400v300z" />
+ <glyph glyph-name="ad" unicode="&#xe0ad;"
+d="M0 700h100v-700h-100v700zM700 700h100v-700h-100v700zM200 600h200v-100h-200v100zM300 400h200v-100h-200v100zM400 200h200v-100h-200v100z" />
+ <glyph glyph-name="ae" unicode="&#xe0ae;"
+d="M325 700c42 -141 87 -280 131 -419c29 74 59 148 88 222c29 -58 57 -116 87 -172h169v-100h-231l-13 28c-37 -92 -74 -184 -112 -275c-39 127 -79 255 -119 382c-41 -133 -83 -265 -125 -397c-28 88 -56 175 -84 262h-116v100h188l9 -34l3 -6c42 137 83 273 125 409z" />
+ <glyph glyph-name="af" unicode="&#xe0af;"
+d="M200 700c0 58 42 100 100 100s100 -42 100 -100c0 -28 -20 -48 -31 -72c-2 -6 -3 -16 -3 -28h234v-234c12 0 22 3 28 6c24 10 44 28 72 28c58 0 100 -42 100 -100s-42 -100 -100 -100c-28 0 -48 20 -72 31c-6 2 -16 3 -28 3v-234h-234c0 12 3 22 6 28c10 24 28 44 28 72
+c0 58 -42 100 -100 100s-100 -42 -100 -100c0 -28 20 -48 31 -72c2 -6 3 -16 3 -28h-234v600h234c0 12 -3 22 -6 28c-10 24 -28 44 -28 72z" />
+ <glyph glyph-name="b0" unicode="&#xe0b0;" horiz-adv-x="500"
+d="M247 700c83 0 147 -20 190 -59s60 -93 60 -141c0 -117 -66 -181 -116 -225s-84 -67 -84 -150v-25h-100v25c0 117 69 181 119 225s81 67 81 150c0 25 -8 48 -28 66s-56 34 -122 34s-97 -18 -116 -37s-27 -43 -31 -69l-100 12c5 38 19 88 59 128s103 66 188 66zM197 0h100
+v-100h-100v100z" />
+ <glyph glyph-name="b1" unicode="&#xe0b1;"
+d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -69 -48 -127 -112 -144c-22 55 -75 94 -138 94c-20 0 -39 -5 -56 -12c-17 64 -75 112 -144 112s-127 -48 -144 -112c-17 7 -36 12 -56 12c-37 0 -71 -16 -97 -38c-33 36 -53 86 -53 138
+c0 110 90 200 200 200c23 114 129 200 250 200zM334 300h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-200c0 -28 -22 -50 -50 -50s-50 22 -50 50v200v2c0 20 15 42 34 48zM134 200h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-100c0 -28 -22 -50 -50 -50s-50 22 -50 50v100v2
+c0 20 15 42 34 48zM534 200h3h4c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-100c0 -28 -22 -50 -50 -50s-50 22 -50 50v100v2c0 20 15 42 34 48z" />
+ <glyph glyph-name="b2" unicode="&#xe0b2;"
+d="M600 700l200 -150l-200 -150v100h-53v-3l-150 -187l175 -207v-3h28v100l200 -150l-200 -150v100h-25c-35 0 -57 10 -78 34v4l-163 190l-153 -190c-21 -26 -46 -38 -81 -38h-100v100h103v3l163 203l-163 191v3h-103v100h100c35 0 57 -10 78 -34v-4l150 -174l141 174
+c21 26 46 38 81 38h50v100z" />
+ <glyph glyph-name="b3" unicode="&#xe0b3;"
+d="M400 700c109 0 208 -47 281 -119l119 119v-300h-300l109 110c-55 55 -126 90 -209 90c-166 0 -300 -134 -300 -300s134 -300 300 -300c84 0 158 33 212 88l69 -69c-72 -73 -171 -119 -281 -119c-220 0 -400 180 -400 400s180 400 400 400z" />
+ <glyph glyph-name="b4" unicode="&#xe0b4;"
+d="M400 800h400v-400l-166 166l-400 -400l166 -166h-400v400l166 -166l400 400z" />
+ <glyph glyph-name="b5" unicode="&#xe0b5;" horiz-adv-x="600"
+d="M250 800l250 -300h-200v-200h200l-250 -300l-250 300h200v200h-200z" />
+ <glyph glyph-name="b6" unicode="&#xe0b6;"
+d="M300 600v-200h200v200l300 -250l-300 -250v200h-200v-200l-300 250z" />
+ <glyph glyph-name="b7" unicode="&#xe0b7;"
+d="M0 800c441 0 800 -359 800 -800h-200c0 333 -267 600 -600 600v200zM0 500c275 0 500 -225 500 -500h-200c0 167 -133 300 -300 300v200zM0 200c111 0 200 -89 200 -200h-200v200z" />
+ <glyph glyph-name="b8" unicode="&#xe0b8;"
+d="M100 800c386 0 700 -314 700 -700h-100c0 332 -268 600 -600 600v100zM100 600c276 0 500 -224 500 -500h-100c0 222 -178 400 -400 400v100zM100 400c165 0 300 -135 300 -300h-100c0 111 -89 200 -200 200v100zM100 200c55 0 100 -45 100 -100s-45 -100 -100 -100
+s-100 45 -100 100s45 100 100 100z" />
+ <glyph glyph-name="b9" unicode="&#xe0b9;"
+d="M300 800h400c55 0 100 -45 100 -100v-200h-400v150c0 28 -22 50 -50 50s-50 -22 -50 -50v-250h400v-300c0 -55 -45 -100 -100 -100h-500c-55 0 -100 45 -100 100v200h100v-150c0 -28 22 -50 50 -50s50 22 50 50v550c0 55 45 100 100 100z" />
+ <glyph glyph-name="ba" unicode="&#xe0ba;"
+d="M75 700h225v-100h-200v-500h400v100h100v-125c0 -40 -35 -75 -75 -75h-450c-40 0 -75 35 -75 75v550c0 40 35 75 75 75zM600 700l200 -200l-200 -200v100h-200c-94 0 -173 -65 -194 -153c23 199 189 353 394 353v100z" />
+ <glyph glyph-name="bb" unicode="&#xe0bb;"
+d="M500 700l300 -284l-300 -316v200h-100c-200 0 -348 -102 -400 -300c0 295 100 500 500 500v200z" />
+ <glyph glyph-name="bc" unicode="&#xe0bc;"
+d="M381 791l19 9l19 -9c127 -53 253 -108 381 -160v-31c0 -166 -67 -313 -147 -419c-40 -53 -83 -97 -125 -128s-82 -53 -128 -53s-86 22 -128 53s-85 75 -125 128c-80 107 -147 253 -147 419v31c128 52 254 107 381 160zM400 100v591l-294 -122c8 -126 58 -243 122 -328
+c35 -46 73 -86 106 -110s62 -31 66 -31z" />
+ <glyph glyph-name="bd" unicode="&#xe0bd;"
+d="M600 800h100v-800h-100v800zM400 700h100v-700h-100v700zM200 500h100v-500h-100v500zM0 300h100v-300h-100v300z" />
+ <glyph glyph-name="be" unicode="&#xe0be;"
+d="M300 800h100v-200h200l100 -100l-100 -100h-200v-400h-100v500h-200l-100 100l100 100h200v100z" />
+ <glyph glyph-name="bf" unicode="&#xe0bf;"
+d="M200 800h100v-600h200l-250 -200l-250 200h200v600zM400 800h200v-100h-200v100zM400 600h300v-100h-300v100zM400 400h400v-100h-400v100z" />
+ <glyph glyph-name="c0" unicode="&#xe0c0;"
+d="M200 800h100v-600h200l-250 -200l-250 200h200v600zM400 800h400v-100h-400v100zM400 600h300v-100h-300v100zM400 400h200v-100h-200v100z" />
+ <glyph glyph-name="c1" unicode="&#xe0c1;"
+d="M75 700h650c40 0 75 -35 75 -75v-550c0 -40 -35 -75 -75 -75h-650c-40 0 -75 35 -75 75v550c0 40 35 75 75 75zM100 600v-100h100v100h-100zM300 600v-100h400v100h-400zM100 400v-100h100v100h-100zM300 400v-100h400v100h-400zM100 200v-100h100v100h-100zM300 200
+v-100h400v100h-400z" />
+ <glyph glyph-name="c2" unicode="&#xe0c2;"
+d="M400 800l100 -300h300l-250 -200l100 -300l-250 200l-250 -200l100 300l-250 200h300z" />
+ <glyph glyph-name="c3" unicode="&#xe0c3;"
+d="M400 800c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM150 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM650 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM400 600c110 0 200 -90 200 -200
+s-90 -200 -200 -200s-200 90 -200 200s90 200 200 200zM50 450c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM750 450c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM150 200c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50
+s22 50 50 50zM650 200c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM400 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
+ <glyph glyph-name="c4" unicode="&#xe0c4;"
+d="M34 800h632c18 0 34 -16 34 -34v-732c0 -18 -16 -34 -34 -34h-632c-18 0 -34 16 -34 34v732c0 18 16 34 34 34zM100 700v-500h500v500h-500zM350 150c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
+ <glyph glyph-name="c5" unicode="&#xe0c5;"
+d="M0 800h300l500 -500l-300 -300l-500 500v300zM200 700c-55 0 -100 -45 -100 -100s45 -100 100 -100s100 45 100 100s-45 100 -100 100z" />
+ <glyph glyph-name="c6" unicode="&#xe0c6;"
+d="M0 600h200l300 -300l-200 -200l-300 300v200zM340 600h160l300 -300l-200 -200l-78 78l119 122zM150 500c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
+ <glyph glyph-name="c7" unicode="&#xe0c7;"
+d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM400 600c110 0 200 -90 200 -200s-90 -200 -200 -200s-200 90 -200 200
+s90 200 200 200zM400 500c-56 0 -100 -44 -100 -100s44 -100 100 -100s100 44 100 100s-44 100 -100 100z" />
+ <glyph glyph-name="c8" unicode="&#xe0c8;"
+d="M0 700h559l-100 -100h-359v-500h500v159l100 100v-359h-700v700zM700 700l100 -100l-400 -400l-200 200l100 100l100 -100z" />
+ <glyph glyph-name="c9" unicode="&#xe0c9;"
+d="M9 800h782c6 0 9 -3 9 -9v-782c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v782c0 6 3 9 9 9zM150 722l-72 -72l100 -100l-100 -100l72 -72l172 172zM400 500v-100h300v100h-300z" />
+ <glyph glyph-name="ca" unicode="&#xe0ca;"
+d="M0 800h800v-200h-50c0 55 -45 100 -100 100h-150v-550c0 -28 22 -50 50 -50h50v-100h-400v100h50c28 0 50 22 50 50v550h-150c-55 0 -100 -45 -100 -100h-50v200z" />
+ <glyph glyph-name="cb" unicode="&#xe0cb;"
+d="M0 700h100v-400h-100v400zM200 700h350c21 0 39 -13 47 -31c0 0 103 -291 103 -319s-22 -50 -50 -50h-150c-28 0 -50 -25 -50 -50s39 -158 47 -184c8 -27 -5 -55 -31 -63c-27 -8 -53 5 -66 31s-110 219 -128 238c-19 18 -44 28 -72 28v400z" />
+ <glyph glyph-name="cc" unicode="&#xe0cc;"
+d="M444 700l22 -3c26 -8 39 -36 31 -63s-47 -159 -47 -184s22 -50 50 -50h150c28 0 50 -22 50 -50s-103 -319 -103 -319c-8 -18 -26 -31 -47 -31h-350v400c28 0 53 10 72 28c18 19 115 212 128 238c10 20 25 32 44 34zM0 400h100v-400h-100v400z" />
+ <glyph glyph-name="cd" unicode="&#xe0cd;"
+d="M200 700h300v-100h-100v-6c25 -4 50 -8 72 -16l-35 -94c-29 10 -57 16 -87 16c-139 0 -250 -111 -250 -250s111 -250 250 -250s250 111 250 250c0 31 -5 61 -16 91l94 34c13 -38 22 -80 22 -125c0 -193 -157 -350 -350 -350s-350 157 -350 350c0 176 130 323 300 347v3
+h-100v100zM700 588c0 0 -296 -353 -316 -372c-20 -20 -52 -20 -72 0s-20 52 0 72s388 300 388 300z" />
+ <glyph glyph-name="ce" unicode="&#xe0ce;"
+d="M600 700l200 -150l-200 -150v100h-600v100h600v100zM200 300v-100h600v-100h-600v-100l-200 150z" />
+ <glyph glyph-name="cf" unicode="&#xe0cf;"
+d="M300 800h100c55 0 100 -45 100 -100h100c55 0 100 -45 100 -100h-700c0 55 45 100 100 100h100c0 55 45 100 100 100zM100 500h100v-350c0 -28 22 -50 50 -50s50 22 50 50v350h100v-350c0 -28 22 -50 50 -50s50 22 50 50v350h100v-481c0 -11 -8 -19 -19 -19h-462
+c-11 0 -19 8 -19 19v481z" />
+ <glyph glyph-name="d0" unicode="&#xe0d0;"
+d="M100 800h200v-400c0 -55 45 -100 100 -100s100 45 100 100v400h100v-400c0 -110 -90 -200 -200 -200h-50c-138 0 -250 90 -250 200v400zM0 100h700v-100h-700v100z" />
+ <glyph glyph-name="d1" unicode="&#xe0d1;"
+d="M9 700h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM609 700h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM309 500h182c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v282
+c0 6 3 9 9 9zM0 100h800v-100h-800v100z" />
+ <glyph glyph-name="d2" unicode="&#xe0d2;"
+d="M10 700h181c6 0 9 -3 9 -9v-191h-200v191c0 6 4 9 10 9zM610 700h181c6 0 9 -3 9 -9v-191h-200v191c0 6 4 9 10 9zM310 600h181c6 0 9 -3 9 -9v-91h-200v91c0 6 4 9 10 9zM0 400h800v-100h-800v100zM0 200h200v-191c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v191zM300 200
+h200v-91c0 -6 -3 -9 -9 -9h-181c-6 0 -10 3 -10 9v91zM600 200h200v-191c0 -6 -3 -9 -9 -9h-181c-6 0 -10 3 -10 9v191z" />
+ <glyph glyph-name="d3" unicode="&#xe0d3;"
+d="M0 700h800v-100h-800v100zM9 500h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM309 500h182c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v282c0 6 3 9 9 9zM609 500h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182
+c-6 0 -9 3 -9 9v482c0 6 3 9 9 9z" />
+ <glyph glyph-name="d4" unicode="&#xe0d4;"
+d="M50 600h500c28 0 50 -22 50 -50v-150l100 100h100v-300h-100l-100 100v-150c0 -28 -22 -50 -50 -50h-500c-28 0 -50 22 -50 50v400c0 28 22 50 50 50z" />
+ <glyph glyph-name="d5" unicode="&#xe0d5;"
+d="M334 800h66v-800h-66l-134 200h-200v400h200zM500 600v100c26 0 52 -4 75 -10c130 -32 225 -150 225 -290s-95 -259 -225 -291c-23 -6 -49 -9 -75 -9v100c16 0 33 2 50 6c86 22 150 100 150 194s-64 172 -150 194h-3c-16 4 -32 6 -47 6zM500 500l22 -3h3v-3
+c42 -12 75 -49 75 -94c0 -46 -32 -85 -75 -97l-25 -3v200z" />
+ <glyph glyph-name="d6" unicode="&#xe0d6;" horiz-adv-x="600"
+d="M334 800h66v-800h-66l-134 200h-200v400h200zM500 500l22 -3h3v-3c42 -12 75 -49 75 -94c0 -46 -32 -85 -75 -97l-25 -3v200z" />
+ <glyph glyph-name="d7" unicode="&#xe0d7;" horiz-adv-x="400"
+d="M334 800h66v-800h-66l-134 200h-200v400h200z" />
+ <glyph glyph-name="d8" unicode="&#xe0d8;"
+d="M309 800h82c6 0 10 -4 12 -9l294 -682l3 -19v-81c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v81l3 19l294 682c2 5 6 9 12 9zM300 500v-200h100v200h-100zM300 200v-100h100v100h-100z" />
+ <glyph glyph-name="d9" unicode="&#xe0d9;"
+d="M375 700c138 0 269 -39 378 -109l-53 -82c-93 60 -205 91 -325 91c-119 0 -229 -35 -322 -94l-53 88c109 69 238 106 375 106zM378 400c79 0 151 -23 213 -62l-53 -85c-46 29 -101 47 -160 47c-60 0 -114 -17 -162 -47l-54 85c62 40 136 62 216 62zM375 100
+c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100z" />
+ <glyph glyph-name="da" unicode="&#xe0da;" horiz-adv-x="900"
+d="M551 700c16 0 30 -3 44 -6l-94 -94v-200h200l94 94c3 -14 6 -28 6 -44c0 -138 -112 -250 -250 -250c-32 0 -62 6 -90 16l-288 -288c-20 -19 -46 -28 -72 -28s-52 11 -72 31c-39 39 -39 102 0 141l291 287c-11 28 -19 59 -19 91c0 138 112 250 250 250zM101 50
+c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
+ <glyph glyph-name="db" unicode="&#xe0db;"
+d="M141 700c85 -80 167 -164 250 -247c83 82 165 167 250 247l140 -141c-80 -85 -164 -167 -247 -250c82 -83 167 -165 247 -250l-140 -140c-85 80 -167 164 -250 247c-83 -82 -165 -167 -250 -247l-141 140c80 85 164 167 247 250c-82 83 -167 165 -247 250z" />
+ <glyph glyph-name="dc" unicode="&#xe0dc;"
+d="M0 800h100l231 -300h38l231 300h100l-225 -300h225v-100h-300v-100h300v-100h-300v-200h-100v200h-300v100h300v100h-300v100h225z" />
+ <glyph glyph-name="dd" unicode="&#xe0dd;" horiz-adv-x="900"
+d="M350 800c193 0 350 -157 350 -350c0 -60 -17 -117 -44 -166c4 -2 10 -6 13 -9l103 -103c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-103 103c-3 2 -7 7 -9 10c-49 -26 -107 -41 -166 -41c-193 0 -350 157 -350 350s157 350 350 350zM350 700
+c-139 0 -250 -111 -250 -250s111 -250 250 -250c60 0 120 22 160 59c8 12 21 26 34 32l3 3c34 43 53 97 53 156c0 139 -111 250 -250 250zM300 600h100v-100h100v-100h-100v-100h-100v100h-100v100h100v100z" />
+ <glyph glyph-name="de" unicode="&#xe0de;" horiz-adv-x="900"
+d="M350 800c193 0 350 -157 350 -350c0 -60 -17 -117 -44 -166c4 -2 10 -6 13 -9l103 -103c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-103 103c-3 2 -7 7 -9 10c-49 -26 -107 -41 -166 -41c-193 0 -350 157 -350 350s157 350 350 350zM350 700
+c-139 0 -250 -111 -250 -250s111 -250 250 -250c60 0 120 22 160 59c8 12 21 26 34 32l3 3c34 43 53 97 53 156c0 139 -111 250 -250 250zM200 500h300v-100h-300v100z" />
+ </font>
+</defs></svg>
diff --git a/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.ttf b/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.ttf
new file mode 100644
index 000000000..0f94acd1e
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.ttf
Binary files differ
diff --git a/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.woff b/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.woff
new file mode 100644
index 000000000..793176af4
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.woff
Binary files differ
diff --git a/vendor/sabre/dav/lib/DAV/Browser/assets/sabredav.css b/vendor/sabre/dav/lib/DAV/Browser/assets/sabredav.css
new file mode 100644
index 000000000..c9ab2c74f
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Browser/assets/sabredav.css
@@ -0,0 +1,228 @@
+/* Start of reset */
+
+* {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+body {
+ margin: 0;
+}
+
+
+/**
+ * Define consistent border, margin, and padding.
+ */
+fieldset {
+ border: 1px solid #c0c0c0;
+ margin: 0 2px;
+ padding: 0.35em 0.625em 0.75em;
+}
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+td,
+th {
+ padding: 0;
+}
+
+/** End of reset */
+
+
+body {
+ font-family: 'Roboto', sans-serif;
+ font-size: 14px;
+ line-height: 22px;
+ font-weight: 300;
+}
+h1 {
+ font-size: 42px;
+ line-height: 44px;
+ padding-bottom: 5px;
+ color: #b10610;
+ margin-top: 10px;
+ margin-bottom: 30px;
+}
+h2 {
+ color: #333333;
+ font-size: 28px;
+ line-height: 44px;
+ font-weight: 300;
+}
+h3 {
+ font-size: 21px;
+ margin-top: 20px;
+ margin-bottom: 10px;
+}
+a {
+ color: #31a1cd;
+}
+h1 a {
+ text-decoration: none;
+}
+h2 a {
+ color: #333333;
+}
+a:visited {
+ color: #6098a2;
+}
+h2 a:visited {
+ color: #333333;
+}
+a:hover {
+ color: #b10610;
+}
+hr {
+ border: none;
+ border-top: 1px dashed #c9ea75;
+ margin-top: 30px;
+ margin-bottom: 30px;
+}
+header {
+ background: #eeeeee;
+}
+header a {
+ font-size: 28px;
+ font-weight: 500;
+ color: #333;
+ text-decoration: none;
+}
+.logo {
+ padding: 5px 10px;
+}
+.logo img {
+ vertical-align: middle;
+ border: 0;
+}
+input, button {
+ font: inherit;
+ color: inherit;
+}
+
+input[type=text] {
+ border: 1px solid #bbbbbb;
+ line-height: 22px;
+ padding: 5px 10px;
+ border-radius: 3px;
+}
+
+nav {
+ padding: 5px;
+}
+
+.btn, button, input[type=submit] {
+ display: inline-block;
+ color: white;
+ background: #4fa3ac;
+ padding: 9px 15px;
+ border-radius: 2px;
+ border: 0;
+ text-decoration: none;
+}
+a.btn:visited {
+ color: white;
+}
+
+.btn.disabled {
+ background: #eeeeee;
+ color: #bbbbbb;
+}
+section {
+ margin: 40px 10px;
+}
+
+section table {
+ height: 40px;
+}
+
+.nodeTable tr {
+ border-bottom: 3px solid white;
+}
+
+.nodeTable td {
+ padding: 10px 10px 10px 10px;
+
+}
+
+.nodeTable a {
+ text-decoration: none;
+}
+
+.nodeTable .nameColumn {
+ font-weight: bold;
+ padding: 10px 20px;
+ background: #ebf5f6;
+ min-width: 200px;
+}
+.nodeTable .oi {
+ color: #b10610;
+}
+
+.propTable tr {
+ height: 40px;
+}
+
+.propTable th {
+ background: #f6f6f6;
+ padding: 0 10px;
+ text-align: left;
+}
+
+.propTable td {
+ padding: 0 10px;
+ background: #eeeeee;
+}
+
+.propTable pre {
+ font-size: 80%;
+ background: #f8f8f8;
+}
+
+.actions {
+ border: 1px dotted #76baa6;
+ padding: 20px;
+ margin-bottom: 20px;
+
+}
+
+.actions h3 {
+ margin-top: 10px;
+ margin-bottom: 30px;
+ padding-bottom: 20px;
+ border-bottom: 1px solid #eeeeee;
+}
+
+.actions label {
+ width: 150px;
+ display: inline-block;
+ line-height: 40px;
+}
+
+.actions input[type=text] {
+ width: 450px;
+}
+
+.actions input[type=submit] {
+ display: inline-block;
+ margin-left: 153px;
+}
+
+footer {
+ padding: 50px 0;
+ font-size: 80%;
+ text-align: center;
+}
+
+ul.tree {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+ul.tree ul {
+ list-style: none;
+ padding-left: 10px;
+ border-left: 4px solid #ccc;
+}
diff --git a/vendor/sabre/dav/lib/DAV/Browser/assets/sabredav.png b/vendor/sabre/dav/lib/DAV/Browser/assets/sabredav.png
new file mode 100644
index 000000000..48a97398a
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Browser/assets/sabredav.png
Binary files differ
diff --git a/vendor/sabre/dav/lib/DAV/Client.php b/vendor/sabre/dav/lib/DAV/Client.php
new file mode 100644
index 000000000..d46b397b6
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Client.php
@@ -0,0 +1,448 @@
+<?php
+
+namespace Sabre\DAV;
+
+use Sabre\HTTP;
+
+/**
+ * SabreDAV DAV client
+ *
+ * This client wraps around Curl to provide a convenient API to a WebDAV
+ * server.
+ *
+ * NOTE: This class is experimental, it's api will likely change in the future.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Client extends HTTP\Client {
+
+ /**
+ * The xml service.
+ *
+ * Uset this service to configure the property and namespace maps.
+ *
+ * @var mixed
+ */
+ public $xml;
+
+ /**
+ * The elementMap
+ *
+ * This property is linked via reference to $this->xml->elementMap.
+ * It's deprecated as of version 3.0.0, and should no longer be used.
+ *
+ * @deprecated
+ * @var array
+ */
+ public $propertyMap = [];
+
+ /**
+ * Base URI
+ *
+ * This URI will be used to resolve relative urls.
+ *
+ * @var string
+ */
+ protected $baseUri;
+
+ /**
+ * Basic authentication
+ */
+ const AUTH_BASIC = 1;
+
+ /**
+ * Digest authentication
+ */
+ const AUTH_DIGEST = 2;
+
+ /**
+ * NTLM authentication
+ */
+ const AUTH_NTLM = 4;
+
+ /**
+ * Identity encoding, which basically does not nothing.
+ */
+ const ENCODING_IDENTITY = 1;
+
+ /**
+ * Deflate encoding
+ */
+ const ENCODING_DEFLATE = 2;
+
+ /**
+ * Gzip encoding
+ */
+ const ENCODING_GZIP = 4;
+
+ /**
+ * Sends all encoding headers.
+ */
+ const ENCODING_ALL = 7;
+
+ /**
+ * Content-encoding
+ *
+ * @var int
+ */
+ protected $encoding = self::ENCODING_IDENTITY;
+
+ /**
+ * Constructor
+ *
+ * Settings are provided through the 'settings' argument. The following
+ * settings are supported:
+ *
+ * * baseUri
+ * * userName (optional)
+ * * password (optional)
+ * * proxy (optional)
+ * * authType (optional)
+ * * encoding (optional)
+ *
+ * authType must be a bitmap, using self::AUTH_BASIC, self::AUTH_DIGEST
+ * and self::AUTH_NTLM. If you know which authentication method will be
+ * used, it's recommended to set it, as it will save a great deal of
+ * requests to 'discover' this information.
+ *
+ * Encoding is a bitmap with one of the ENCODING constants.
+ *
+ * @param array $settings
+ */
+ function __construct(array $settings) {
+
+ if (!isset($settings['baseUri'])) {
+ throw new \InvalidArgumentException('A baseUri must be provided');
+ }
+
+ parent::__construct();
+
+ $this->baseUri = $settings['baseUri'];
+
+ if (isset($settings['proxy'])) {
+ $this->addCurlSetting(CURLOPT_PROXY, $settings['proxy']);
+ }
+
+ if (isset($settings['userName'])) {
+ $userName = $settings['userName'];
+ $password = isset($settings['password']) ? $settings['password'] : '';
+
+ if (isset($settings['authType'])) {
+ $curlType = 0;
+ if ($settings['authType'] & self::AUTH_BASIC) {
+ $curlType |= CURLAUTH_BASIC;
+ }
+ if ($settings['authType'] & self::AUTH_DIGEST) {
+ $curlType |= CURLAUTH_DIGEST;
+ }
+ if ($settings['authType'] & self::AUTH_NTLM) {
+ $curlType |= CURLAUTH_NTLM;
+ }
+ } else {
+ $curlType = CURLAUTH_BASIC | CURLAUTH_DIGEST;
+ }
+
+ $this->addCurlSetting(CURLOPT_HTTPAUTH, $curlType);
+ $this->addCurlSetting(CURLOPT_USERPWD, $userName . ':' . $password);
+
+ }
+
+ if (isset($settings['encoding'])) {
+ $encoding = $settings['encoding'];
+
+ $encodings = [];
+ if ($encoding & self::ENCODING_IDENTITY) {
+ $encodings[] = 'identity';
+ }
+ if ($encoding & self::ENCODING_DEFLATE) {
+ $encodings[] = 'deflate';
+ }
+ if ($encoding & self::ENCODING_GZIP) {
+ $encodings[] = 'gzip';
+ }
+ $this->addCurlSetting(CURLOPT_ENCODING, implode(',', $encodings));
+ }
+
+ $this->addCurlSetting(CURLOPT_USERAGENT, 'sabre-dav/' . Version::VERSION . ' (http://sabre.io/)');
+
+ $this->xml = new Xml\Service();
+ // BC
+ $this->propertyMap = & $this->xml->elementMap;
+
+ }
+
+ /**
+ * Does a PROPFIND request
+ *
+ * The list of requested properties must be specified as an array, in clark
+ * notation.
+ *
+ * The returned array will contain a list of filenames as keys, and
+ * properties as values.
+ *
+ * The properties array will contain the list of properties. Only properties
+ * that are actually returned from the server (without error) will be
+ * returned, anything else is discarded.
+ *
+ * Depth should be either 0 or 1. A depth of 1 will cause a request to be
+ * made to the server to also return all child resources.
+ *
+ * @param string $url
+ * @param array $properties
+ * @param int $depth
+ * @return array
+ */
+ function propFind($url, array $properties, $depth = 0) {
+
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $dom->formatOutput = true;
+ $root = $dom->createElementNS('DAV:', 'd:propfind');
+ $prop = $dom->createElement('d:prop');
+
+ foreach ($properties as $property) {
+
+ list(
+ $namespace,
+ $elementName
+ ) = \Sabre\Xml\Service::parseClarkNotation($property);
+
+ if ($namespace === 'DAV:') {
+ $element = $dom->createElement('d:' . $elementName);
+ } else {
+ $element = $dom->createElementNS($namespace, 'x:' . $elementName);
+ }
+
+ $prop->appendChild($element);
+ }
+
+ $dom->appendChild($root)->appendChild($prop);
+ $body = $dom->saveXML();
+
+ $url = $this->getAbsoluteUrl($url);
+
+ $request = new HTTP\Request('PROPFIND', $url, [
+ 'Depth' => $depth,
+ 'Content-Type' => 'application/xml'
+ ], $body);
+
+ $response = $this->send($request);
+
+ if ((int)$response->getStatus() >= 400) {
+ throw new \Sabre\HTTP\ClientHttpException($response);
+ }
+
+ $result = $this->parseMultiStatus($response->getBodyAsString());
+
+ // If depth was 0, we only return the top item
+ if ($depth === 0) {
+ reset($result);
+ $result = current($result);
+ return isset($result[200]) ? $result[200] : [];
+ }
+
+ $newResult = [];
+ foreach ($result as $href => $statusList) {
+
+ $newResult[$href] = isset($statusList[200]) ? $statusList[200] : [];
+
+ }
+
+ return $newResult;
+
+ }
+
+ /**
+ * Updates a list of properties on the server
+ *
+ * The list of properties must have clark-notation properties for the keys,
+ * and the actual (string) value for the value. If the value is null, an
+ * attempt is made to delete the property.
+ *
+ * @param string $url
+ * @param array $properties
+ * @return bool
+ */
+ function propPatch($url, array $properties) {
+
+ $propPatch = new Xml\Request\PropPatch();
+ $propPatch->properties = $properties;
+ $xml = $this->xml->write(
+ '{DAV:}propertyupdate',
+ $propPatch
+ );
+
+ $url = $this->getAbsoluteUrl($url);
+ $request = new HTTP\Request('PROPPATCH', $url, [
+ 'Content-Type' => 'application/xml',
+ ], $xml);
+ $response = $this->send($request);
+
+ if ($response->getStatus() >= 400) {
+ throw new \Sabre\HTTP\ClientHttpException($response);
+ }
+
+ if ($response->getStatus() === 207) {
+ // 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());
+
+ $errorProperties = [];
+ foreach ($result as $href => $statusList) {
+ foreach ($statusList as $status => $properties) {
+
+ if ($status >= 400) {
+ foreach ($properties as $propName => $propValue) {
+ $errorProperties[] = $propName . ' (' . $status . ')';
+ }
+ }
+
+ }
+ }
+ if ($errorProperties) {
+
+ throw new \Sabre\HTTP\ClientException('PROPPATCH failed. The following properties errored: ' . implode(', ', $errorProperties));
+ }
+ }
+ return true;
+
+ }
+
+ /**
+ * Performs an HTTP options request
+ *
+ * This method returns all the features from the 'DAV:' header as an array.
+ * If there was no DAV header, or no contents this method will return an
+ * empty array.
+ *
+ * @return array
+ */
+ function options() {
+
+ $request = new HTTP\Request('OPTIONS', $this->getAbsoluteUrl(''));
+ $response = $this->send($request);
+
+ $dav = $response->getHeader('Dav');
+ if (!$dav) {
+ return [];
+ }
+
+ $features = explode(',', $dav);
+ foreach ($features as &$v) {
+ $v = trim($v);
+ }
+ return $features;
+
+ }
+
+ /**
+ * Performs an actual HTTP request, and returns the result.
+ *
+ * If the specified url is relative, it will be expanded based on the base
+ * url.
+ *
+ * The returned array contains 3 keys:
+ * * body - the response body
+ * * httpCode - a HTTP code (200, 404, etc)
+ * * headers - a list of response http headers. The header names have
+ * been lowercased.
+ *
+ * For large uploads, it's highly recommended to specify body as a stream
+ * resource. You can easily do this by simply passing the result of
+ * fopen(..., 'r').
+ *
+ * This method will throw an exception if an HTTP error was received. Any
+ * HTTP status code above 399 is considered an error.
+ *
+ * Note that it is no longer recommended to use this method, use the send()
+ * method instead.
+ *
+ * @param string $method
+ * @param string $url
+ * @param string|resource|null $body
+ * @param array $headers
+ * @throws ClientException, in case a curl error occurred.
+ * @return array
+ */
+ function request($method, $url = '', $body = null, array $headers = []) {
+
+ $url = $this->getAbsoluteUrl($url);
+
+ $response = $this->send(new HTTP\Request($method, $url, $headers, $body));
+ return [
+ 'body' => $response->getBodyAsString(),
+ 'statusCode' => (int)$response->getStatus(),
+ 'headers' => array_change_key_case($response->getHeaders()),
+ ];
+
+ }
+
+ /**
+ * Returns the full url based on the given url (which may be relative). All
+ * urls are expanded based on the base url as given by the server.
+ *
+ * @param string $url
+ * @return string
+ */
+ function getAbsoluteUrl($url) {
+
+ // If the url starts with http:// or https://, the url is already absolute.
+ if (preg_match('/^http(s?):\/\//', $url)) {
+ return $url;
+ }
+
+ // If the url starts with a slash, we must calculate the url based off
+ // the root of the base url.
+ if (strpos($url, '/') === 0) {
+ $parts = parse_url($this->baseUri);
+ return $parts['scheme'] . '://' . $parts['host'] . (isset($parts['port']) ? ':' . $parts['port'] : '') . $url;
+ }
+
+ // Otherwise...
+ return $this->baseUri . $url;
+
+ }
+
+ /**
+ * Parses a WebDAV multistatus response body
+ *
+ * This method returns an array with the following structure
+ *
+ * [
+ * 'url/to/resource' => [
+ * '200' => [
+ * '{DAV:}property1' => 'value1',
+ * '{DAV:}property2' => 'value2',
+ * ],
+ * '404' => [
+ * '{DAV:}property1' => null,
+ * '{DAV:}property2' => null,
+ * ],
+ * ],
+ * 'url/to/resource2' => [
+ * .. etc ..
+ * ]
+ * ]
+ *
+ *
+ * @param string $body xml body
+ * @return array
+ */
+ function parseMultiStatus($body) {
+
+ $multistatus = $this->xml->expect('{DAV:}multistatus', $body);
+
+ $result = [];
+
+ foreach ($multistatus->getResponses() as $response) {
+
+ $result[$response->getHref()] = $response->getResponseProperties();
+
+ }
+
+ return $result;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Collection.php b/vendor/sabre/dav/lib/DAV/Collection.php
index 0090a4d6e..a46bcc342 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Collection.php
+++ b/vendor/sabre/dav/lib/DAV/Collection.php
@@ -8,7 +8,7 @@ namespace Sabre\DAV;
* This is a helper class, that should aid in getting collections classes setup.
* Most of its methods are implemented, and throw permission denied exceptions
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -28,11 +28,11 @@ abstract class Collection extends Node implements ICollection {
* @throws Exception\NotFound
* @return INode
*/
- public function getChild($name) {
+ function getChild($name) {
- foreach($this->getChildren() as $child) {
+ foreach ($this->getChildren() as $child) {
- if ($child->getName()==$name) return $child;
+ if ($child->getName() === $name) return $child;
}
throw new Exception\NotFound('File not found: ' . $name);
@@ -47,14 +47,14 @@ abstract class Collection extends Node implements ICollection {
* @param string $name
* @return bool
*/
- public function childExists($name) {
+ function childExists($name) {
try {
$this->getChild($name);
return true;
- } catch(Exception\NotFound $e) {
+ } catch (Exception\NotFound $e) {
return false;
@@ -86,7 +86,7 @@ abstract class Collection extends Node implements ICollection {
* @param resource|string $data Initial payload
* @return null|string
*/
- public function createFile($name, $data = null) {
+ function createFile($name, $data = null) {
throw new Exception\Forbidden('Permission denied to create file (filename ' . $name . ')');
@@ -99,7 +99,7 @@ abstract class Collection extends Node implements ICollection {
* @throws Exception\Forbidden
* @return void
*/
- public function createDirectory($name) {
+ function createDirectory($name) {
throw new Exception\Forbidden('Permission denied to create directory');
@@ -107,4 +107,3 @@ abstract class Collection extends Node implements ICollection {
}
-
diff --git a/vendor/sabre/dav/lib/DAV/CorePlugin.php b/vendor/sabre/dav/lib/DAV/CorePlugin.php
new file mode 100644
index 000000000..3a70b2a7e
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/CorePlugin.php
@@ -0,0 +1,927 @@
+<?php
+
+namespace Sabre\DAV;
+
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+use Sabre\Xml\ParseException;
+
+/**
+ * The core plugin provides all the basic features for a WebDAV server.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class CorePlugin extends ServerPlugin {
+
+ /**
+ * Reference to server object.
+ *
+ * @var Server
+ */
+ protected $server;
+
+ /**
+ * Sets up the plugin
+ *
+ * @param Server $server
+ * @return void
+ */
+ function initialize(Server $server) {
+
+ $this->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:PROPPATCH', [$this, 'httpPropPatch']);
+ $server->on('method:PUT', [$this, 'httpPut']);
+ $server->on('method:MKCOL', [$this, 'httpMkcol']);
+ $server->on('method:MOVE', [$this, 'httpMove']);
+ $server->on('method:COPY', [$this, 'httpCopy']);
+ $server->on('method:REPORT', [$this, 'httpReport']);
+
+ $server->on('propPatch', [$this, 'propPatchProtectedPropertyCheck'], 90);
+ $server->on('propPatch', [$this, 'propPatchNodeUpdate'], 200);
+ $server->on('propFind', [$this, 'propFind']);
+ $server->on('propFind', [$this, 'propFindNode'], 120);
+ $server->on('propFind', [$this, 'propFindLate'], 200);
+
+ }
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'core';
+
+ }
+
+ /**
+ * This is the default implementation for the GET method.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpGet(RequestInterface $request, ResponseInterface $response) {
+
+ $path = $request->getPath();
+ $node = $this->server->tree->getNodeForPath($path);
+
+ if (!$node instanceof IFile) return;
+
+ $body = $node->get();
+
+ // Converting string into stream, if needed.
+ if (is_string($body)) {
+ $stream = fopen('php://temp', 'r+');
+ fwrite($stream, $body);
+ rewind($stream);
+ $body = $stream;
+ }
+
+ /*
+ * TODO: getetag, getlastmodified, getsize should also be used using
+ * this method
+ */
+ $httpHeaders = $this->server->getHTTPHeaders($path);
+
+ /* ContentType needs to get a default, because many webservers will otherwise
+ * default to text/html, and we don't want this for security reasons.
+ */
+ if (!isset($httpHeaders['Content-Type'])) {
+ $httpHeaders['Content-Type'] = 'application/octet-stream';
+ }
+
+
+ if (isset($httpHeaders['Content-Length'])) {
+
+ $nodeSize = $httpHeaders['Content-Length'];
+
+ // Need to unset Content-Length, because we'll handle that during figuring out the range
+ unset($httpHeaders['Content-Length']);
+
+ } else {
+ $nodeSize = null;
+ }
+
+ $response->addHeaders($httpHeaders);
+
+ $range = $this->server->getHTTPRange();
+ $ifRange = $request->getHeader('If-Range');
+ $ignoreRangeHeader = false;
+
+ // If ifRange is set, and range is specified, we first need to check
+ // the precondition.
+ if ($nodeSize && $range && $ifRange) {
+
+ // if IfRange is parsable as a date we'll treat it as a DateTime
+ // otherwise, we must treat it as an etag.
+ try {
+ $ifRangeDate = new \DateTime($ifRange);
+
+ // It's a date. We must check if the entity is modified since
+ // the specified date.
+ if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true;
+ else {
+ $modified = new \DateTime($httpHeaders['Last-Modified']);
+ if ($modified > $ifRangeDate) $ignoreRangeHeader = true;
+ }
+
+ } catch (\Exception $e) {
+
+ // It's an entity. We can do a simple comparison.
+ if (!isset($httpHeaders['ETag'])) $ignoreRangeHeader = true;
+ elseif ($httpHeaders['ETag'] !== $ifRange) $ignoreRangeHeader = true;
+ }
+ }
+
+ // We're only going to support HTTP ranges if the backend provided a filesize
+ if (!$ignoreRangeHeader && $nodeSize && $range) {
+
+ // Determining the exact byte offsets
+ if (!is_null($range[0])) {
+
+ $start = $range[0];
+ $end = $range[1] ? $range[1] : $nodeSize - 1;
+ if ($start >= $nodeSize)
+ throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')');
+
+ if ($end < $start) throw new Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')');
+ if ($end >= $nodeSize) $end = $nodeSize - 1;
+
+ } else {
+
+ $start = $nodeSize - $range[1];
+ $end = $nodeSize - 1;
+
+ if ($start < 0) $start = 0;
+
+ }
+
+ // 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) {
+ $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 . ')');
+ $consumed += strlen(fread($body, min($start - $consumed, $consumeBlock)));
+ }
+ }
+
+ $response->setHeader('Content-Length', $end - $start + 1);
+ $response->setHeader('Content-Range', 'bytes ' . $start . '-' . $end . '/' . $nodeSize);
+ $response->setStatus(206);
+ $response->setBody($body);
+
+ } else {
+
+ if ($nodeSize) $response->setHeader('Content-Length', $nodeSize);
+ $response->setStatus(200);
+ $response->setBody($body);
+
+ }
+ // Sending back false will interupt the event chain and tell the server
+ // we've handled this method.
+ return false;
+
+ }
+
+ /**
+ * HTTP OPTIONS
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpOptions(RequestInterface $request, ResponseInterface $response) {
+
+ $methods = $this->server->getAllowedMethods($request->getPath());
+
+ $response->setHeader('Allow', strtoupper(implode(', ', $methods)));
+ $features = ['1', '3', 'extended-mkcol'];
+
+ foreach ($this->server->getPlugins() as $plugin) {
+ $features = array_merge($features, $plugin->getFeatures());
+ }
+
+ $response->setHeader('DAV', implode(', ', $features));
+ $response->setHeader('MS-Author-Via', 'DAV');
+ $response->setHeader('Accept-Ranges', 'bytes');
+ $response->setHeader('Content-Length', '0');
+ $response->setStatus(200);
+
+ // Sending back false will interupt the event chain and tell the server
+ // we've handled this method.
+ return false;
+
+ }
+
+ /**
+ * HTTP HEAD
+ *
+ * This method is normally used to take a peak at a url, and only get the
+ * HTTP response headers, without the body. This is used by clients to
+ * determine if a remote file was changed, so they can use a local cached
+ * version, instead of downloading it again
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpHead(RequestInterface $request, ResponseInterface $response) {
+
+ // This is implemented by changing the HEAD request to a GET request,
+ // and dropping the response body.
+ $subRequest = clone $request;
+ $subRequest->setMethod('GET');
+
+ try {
+ $this->server->invokeMethod($subRequest, $response, 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,
+ // which would trigger a 501.
+ // This breaks some clients though, so we're transforming these
+ // 501s into 200s.
+ $response->setStatus(200);
+ $response->setBody('');
+ $response->setHeader('Content-Type', 'text/plain');
+ $response->setHeader('X-Sabre-Real-Status', $e->getHTTPCode());
+ }
+
+ // Sending back false will interupt the event chain and tell the server
+ // we've handled this method.
+ return false;
+
+ }
+
+ /**
+ * HTTP Delete
+ *
+ * The HTTP delete method, deletes a given uri
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ function httpDelete(RequestInterface $request, ResponseInterface $response) {
+
+ $path = $request->getPath();
+
+ if (!$this->server->emit('beforeUnbind', [$path])) return false;
+ $this->server->tree->delete($path);
+ $this->server->emit('afterUnbind', [$path]);
+
+ $response->setStatus(204);
+ $response->setHeader('Content-Length', '0');
+
+ // Sending back false will interupt the event chain and tell the server
+ // we've handled this method.
+ return false;
+
+ }
+
+ /**
+ * WebDAV PROPFIND
+ *
+ * This WebDAV method requests information about an uri resource, or a list of resources
+ * If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value
+ * If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory)
+ *
+ * The request body contains an XML data structure that has a list of properties the client understands
+ * The response body is also an xml document, containing information about every uri resource and the requested properties
+ *
+ * It has to return a HTTP 207 Multi-status status code
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ function httpPropFind(RequestInterface $request, ResponseInterface $response) {
+
+ $path = $request->getPath();
+
+ $requestBody = $request->getBodyAsString();
+ if (strlen($requestBody)) {
+ try {
+ $propFindXml = $this->server->xml->expect('{DAV:}propfind', $requestBody);
+ } catch (ParseException $e) {
+ throw new BadRequest($e->getMessage(), null, $e);
+ }
+ } else {
+ $propFindXml = new Xml\Request\PropFind();
+ $propFindXml->allProp = true;
+ $propFindXml->properties = [];
+ }
+
+ $depth = $this->server->getHTTPDepth(1);
+ // The only two options for the depth of a propfind is 0 or 1 - as long as depth infinity is not enabled
+ if (!$this->server->enablePropfindDepthInfinity && $depth != 0) $depth = 1;
+
+ $newProperties = $this->server->getPropertiesForPath($path, $propFindXml->properties, $depth);
+
+ // This is a multi-status response
+ $response->setStatus(207);
+ $response->setHeader('Content-Type', 'application/xml; charset=utf-8');
+ $response->setHeader('Vary', 'Brief,Prefer');
+
+ // Normally this header is only needed for OPTIONS responses, however..
+ // iCal seems to also depend on these being set for PROPFIND. Since
+ // this is not harmful, we'll add it.
+ $features = ['1', '3', 'extended-mkcol'];
+ foreach ($this->server->getPlugins() as $plugin) {
+ $features = array_merge($features, $plugin->getFeatures());
+ }
+ $response->setHeader('DAV', implode(', ', $features));
+
+ $prefer = $this->server->getHTTPPrefer();
+ $minimal = $prefer['return'] === 'minimal';
+
+ $data = $this->server->generateMultiStatus($newProperties, $minimal);
+ $response->setBody($data);
+
+ // Sending back false will interupt the event chain and tell the server
+ // we've handled this method.
+ return false;
+
+ }
+
+ /**
+ * WebDAV PROPPATCH
+ *
+ * This method is called to update properties on a Node. The request is an XML body with all the mutations.
+ * In this XML body it is specified which properties should be set/updated and/or deleted
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpPropPatch(RequestInterface $request, ResponseInterface $response) {
+
+ $path = $request->getPath();
+
+ try {
+ $propPatch = $this->server->xml->expect('{DAV:}propertyupdate', $request->getBody());
+ } catch (ParseException $e) {
+ throw new BadRequest($e->getMessage(), null, $e);
+ }
+ $newProperties = $propPatch->properties;
+
+ $result = $this->server->updateProperties($path, $newProperties);
+
+ $prefer = $this->server->getHTTPPrefer();
+ $response->setHeader('Vary', 'Brief,Prefer');
+
+ if ($prefer['return'] === 'minimal') {
+
+ // If return-minimal is specified, we only have to check if the
+ // request was succesful, and don't need to return the
+ // multi-status.
+ $ok = true;
+ foreach ($result as $prop => $code) {
+ if ((int)$code > 299) {
+ $ok = false;
+ }
+ }
+
+ if ($ok) {
+
+ $response->setStatus(204);
+ return false;
+
+ }
+
+ }
+
+ $response->setStatus(207);
+ $response->setHeader('Content-Type', 'application/xml; charset=utf-8');
+
+
+ // Reorganizing the result for generateMultiStatus
+ $multiStatus = [];
+ foreach ($result as $propertyName => $code) {
+ if (isset($multiStatus[$code])) {
+ $multiStatus[$code][$propertyName] = null;
+ } else {
+ $multiStatus[$code] = [$propertyName => null];
+ }
+ }
+ $multiStatus['href'] = $path;
+
+ $response->setBody(
+ $this->server->generateMultiStatus([$multiStatus])
+ );
+
+ // Sending back false will interupt the event chain and tell the server
+ // we've handled this method.
+ return false;
+
+ }
+
+ /**
+ * HTTP PUT method
+ *
+ * This HTTP method updates a file, or creates a new one.
+ *
+ * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 204 No Content
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpPut(RequestInterface $request, ResponseInterface $response) {
+
+ $body = $request->getBodyAsStream();
+ $path = $request->getPath();
+
+ // Intercepting Content-Range
+ if ($request->getHeader('Content-Range')) {
+ /*
+ An origin server that allows PUT on a given target resource MUST send
+ a 400 (Bad Request) response to a PUT request that contains a
+ Content-Range header field.
+
+ Reference: http://tools.ietf.org/html/rfc7231#section-4.3.4
+ */
+ throw new Exception\BadRequest('Content-Range on PUT requests are forbidden.');
+ }
+
+ // Intercepting the Finder problem
+ if (($expected = $request->getHeader('X-Expected-Entity-Length')) && $expected > 0) {
+
+ /*
+ Many webservers will not cooperate well with Finder PUT requests,
+ because it uses 'Chunked' transfer encoding for the request body.
+
+ The symptom of this problem is that Finder sends files to the
+ server, but they arrive as 0-length files in PHP.
+
+ If we don't do anything, the user might think they are uploading
+ files successfully, but they end up empty on the server. Instead,
+ we throw back an error if we detect this.
+
+ The reason Finder uses Chunked, is because it thinks the files
+ might change as it's being uploaded, and therefore the
+ Content-Length can vary.
+
+ Instead it sends the X-Expected-Entity-Length header with the size
+ of the file at the very start of the request. If this header is set,
+ but we don't get a request body we will fail the request to
+ protect the end-user.
+ */
+
+ // Only reading first byte
+ $firstByte = fread($body, 1);
+ if (strlen($firstByte) !== 1) {
+ throw new Exception\Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.');
+ }
+
+ // The body needs to stay intact, so we copy everything to a
+ // temporary stream.
+
+ $newBody = fopen('php://temp', 'r+');
+ fwrite($newBody, $firstByte);
+ stream_copy_to_stream($body, $newBody);
+ rewind($newBody);
+
+ $body = $newBody;
+
+ }
+
+ if ($this->server->tree->nodeExists($path)) {
+
+ $node = $this->server->tree->getNodeForPath($path);
+
+ // If the node is a collection, we'll deny it
+ if (!($node instanceof IFile)) throw new Exception\Conflict('PUT is not allowed on non-files.');
+
+ if (!$this->server->updateFile($path, $body, $etag)) {
+ return false;
+ }
+
+ $response->setHeader('Content-Length', '0');
+ if ($etag) $response->setHeader('ETag', $etag);
+ $response->setStatus(204);
+
+ } else {
+
+ $etag = null;
+ // If we got here, the resource didn't exist yet.
+ if (!$this->server->createFile($path, $body, $etag)) {
+ // For one reason or another the file was not created.
+ return false;
+ }
+
+ $response->setHeader('Content-Length', '0');
+ if ($etag) $response->setHeader('ETag', $etag);
+ $response->setStatus(201);
+
+ }
+
+ // Sending back false will interupt the event chain and tell the server
+ // we've handled this method.
+ return false;
+
+ }
+
+
+ /**
+ * WebDAV MKCOL
+ *
+ * The MKCOL method is used to create a new collection (directory) on the server
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpMkcol(RequestInterface $request, ResponseInterface $response) {
+
+ $requestBody = $request->getBodyAsString();
+ $path = $request->getPath();
+
+ if ($requestBody) {
+
+ $contentType = $request->getHeader('Content-Type');
+ if (strpos($contentType, 'application/xml') !== 0 && strpos($contentType, 'text/xml') !== 0) {
+
+ // We must throw 415 for unsupported mkcol bodies
+ throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type');
+
+ }
+
+ try {
+ $mkcol = $this->server->xml->expect('{DAV:}mkcol', $requestBody);
+ } catch (\Sabre\Xml\ParseException $e) {
+ throw new Exception\BadRequest($e->getMessage(), null, $e);
+ }
+
+ $properties = $mkcol->getProperties();
+
+ 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);
+
+ $result = $this->server->createCollection($path, $mkcol);
+
+ if (is_array($result)) {
+ $response->setStatus(207);
+ $response->setHeader('Content-Type', 'application/xml; charset=utf-8');
+
+ $response->setBody(
+ $this->server->generateMultiStatus([$result])
+ );
+
+ } else {
+ $response->setHeader('Content-Length', '0');
+ $response->setStatus(201);
+ }
+
+ // Sending back false will interupt the event chain and tell the server
+ // we've handled this method.
+ return false;
+
+ }
+
+ /**
+ * WebDAV HTTP MOVE method
+ *
+ * This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpMove(RequestInterface $request, ResponseInterface $response) {
+
+ $path = $request->getPath();
+
+ $moveInfo = $this->server->getCopyAndMoveInfo($request);
+
+ if ($moveInfo['destinationExists']) {
+
+ if (!$this->server->emit('beforeUnbind', [$moveInfo['destination']])) return false;
+
+ }
+ if (!$this->server->emit('beforeUnbind', [$path])) return false;
+ if (!$this->server->emit('beforeBind', [$moveInfo['destination']])) return false;
+ if (!$this->server->emit('beforeMove', [$path, $moveInfo['destination']])) return false;
+
+ if ($moveInfo['destinationExists']) {
+
+ $this->server->tree->delete($moveInfo['destination']);
+ $this->server->emit('afterUnbind', [$moveInfo['destination']]);
+
+ }
+
+ $this->server->tree->move($path, $moveInfo['destination']);
+
+ // Its important afterMove is called before afterUnbind, because it
+ // allows systems to transfer data from one path to another.
+ // PropertyStorage uses this. If afterUnbind was first, it would clean
+ // up all the properties before it has a chance.
+ $this->server->emit('afterMove', [$path, $moveInfo['destination']]);
+ $this->server->emit('afterUnbind', [$path]);
+ $this->server->emit('afterBind', [$moveInfo['destination']]);
+
+ // If a resource was overwritten we should send a 204, otherwise a 201
+ $response->setHeader('Content-Length', '0');
+ $response->setStatus($moveInfo['destinationExists'] ? 204 : 201);
+
+ // Sending back false will interupt the event chain and tell the server
+ // we've handled this method.
+ return false;
+
+ }
+
+ /**
+ * WebDAV HTTP COPY method
+ *
+ * This method copies one uri to a different uri, and works much like the MOVE request
+ * A lot of the actual request processing is done in getCopyMoveInfo
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpCopy(RequestInterface $request, ResponseInterface $response) {
+
+ $path = $request->getPath();
+
+ $copyInfo = $this->server->getCopyAndMoveInfo($request);
+
+ if (!$this->server->emit('beforeBind', [$copyInfo['destination']])) return false;
+ if ($copyInfo['destinationExists']) {
+ if (!$this->server->emit('beforeUnbind', [$copyInfo['destination']])) return false;
+ $this->server->tree->delete($copyInfo['destination']);
+ }
+
+ $this->server->tree->copy($path, $copyInfo['destination']);
+ $this->server->emit('afterBind', [$copyInfo['destination']]);
+
+ // If a resource was overwritten we should send a 204, otherwise a 201
+ $response->setHeader('Content-Length', '0');
+ $response->setStatus($copyInfo['destinationExists'] ? 204 : 201);
+
+ // Sending back false will interupt the event chain and tell the server
+ // we've handled this method.
+ return false;
+
+
+ }
+
+ /**
+ * HTTP REPORT method implementation
+ *
+ * Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253)
+ * It's used in a lot of extensions, so it made sense to implement it into the core.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpReport(RequestInterface $request, ResponseInterface $response) {
+
+ $path = $request->getPath();
+
+ $result = $this->server->xml->parse(
+ $request->getBody(),
+ $request->getUrl(),
+ $rootElementName
+ );
+
+ 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 interupt the event chain and tell the server
+ // we've handled this method.
+ return false;
+
+ }
+
+ /**
+ * This method is called during property updates.
+ *
+ * Here we check if a user attempted to update a protected property and
+ * ensure that the process fails if this is the case.
+ *
+ * @param string $path
+ * @param PropPatch $propPatch
+ * @return void
+ */
+ function propPatchProtectedPropertyCheck($path, PropPatch $propPatch) {
+
+ // Comparing the mutation list to the list of propetected properties.
+ $mutations = $propPatch->getMutations();
+
+ $protected = array_intersect(
+ $this->server->protectedProperties,
+ array_keys($mutations)
+ );
+
+ if ($protected) {
+ $propPatch->setResultCode($protected, 403);
+ }
+
+ }
+
+ /**
+ * This method is called during property updates.
+ *
+ * Here we check if a node implements IProperties and let the node handle
+ * updating of (some) properties.
+ *
+ * @param string $path
+ * @param PropPatch $propPatch
+ * @return void
+ */
+ function propPatchNodeUpdate($path, PropPatch $propPatch) {
+
+ // This should trigger a 404 if the node doesn't exist.
+ $node = $this->server->tree->getNodeForPath($path);
+
+ if ($node instanceof IProperties) {
+ $node->propPatch($propPatch);
+ }
+
+ }
+
+ /**
+ * This method is called when properties are retrieved.
+ *
+ * Here we add all the default properties.
+ *
+ * @param PropFind $propFind
+ * @param INode $node
+ * @return void
+ */
+ function propFind(PropFind $propFind, INode $node) {
+
+ $propFind->handle('{DAV:}getlastmodified', function() use ($node) {
+ $lm = $node->getLastModified();
+ if ($lm) {
+ return new Xml\Property\GetLastModified($lm);
+ }
+ });
+
+ if ($node instanceof IFile) {
+ $propFind->handle('{DAV:}getcontentlength', [$node, 'getSize']);
+ $propFind->handle('{DAV:}getetag', [$node, 'getETag']);
+ $propFind->handle('{DAV:}getcontenttype', [$node, 'getContentType']);
+ }
+
+ if ($node instanceof IQuota) {
+ $quotaInfo = null;
+ $propFind->handle('{DAV:}quota-used-bytes', function() use (&$quotaInfo, $node) {
+ $quotaInfo = $node->getQuotaInfo();
+ return $quotaInfo[0];
+ });
+ $propFind->handle('{DAV:}quota-available-bytes', function() use (&$quotaInfo, $node) {
+ if (!$quotaInfo) {
+ $quotaInfo = $node->getQuotaInfo();
+ }
+ return $quotaInfo[1];
+ });
+ }
+
+ $propFind->handle('{DAV:}supported-report-set', function() use ($propFind) {
+ $reports = [];
+ foreach ($this->server->getPlugins() as $plugin) {
+ $reports = array_merge($reports, $plugin->getSupportedReportSet($propFind->getPath()));
+ }
+ return new Xml\Property\SupportedReportSet($reports);
+ });
+ $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) {
+ return new Xml\Property\SupportedMethodSet(
+ $this->server->getAllowedMethods($propFind->getPath())
+ );
+ });
+
+ }
+
+ /**
+ * Fetches properties for a node.
+ *
+ * This event is called a bit later, so plugins have a chance first to
+ * populate the result.
+ *
+ * @param PropFind $propFind
+ * @param INode $node
+ * @return void
+ */
+ function propFindNode(PropFind $propFind, INode $node) {
+
+ if ($node instanceof IProperties && $propertyNames = $propFind->get404Properties()) {
+
+ $nodeProperties = $node->getProperties($propertyNames);
+ foreach ($propertyNames as $propertyName) {
+ if (array_key_exists($propertyName, $nodeProperties)) {
+ $propFind->set($propertyName, $nodeProperties[$propertyName], 200);
+ }
+ }
+
+ }
+
+ }
+
+ /**
+ * This method is called when properties are retrieved.
+ *
+ * This specific handler is called very late in the process, because we
+ * want other systems to first have a chance to handle the properties.
+ *
+ * @param PropFind $propFind
+ * @param INode $node
+ * @return void
+ */
+ function propFindLate(PropFind $propFind, INode $node) {
+
+ $propFind->handle('{http://calendarserver.org/ns/}getctag', function() use ($propFind) {
+
+ // If we already have a sync-token from the current propFind
+ // request, we can re-use that.
+ $val = $propFind->get('{http://sabredav.org/ns}sync-token');
+ if ($val) return $val;
+
+ $val = $propFind->get('{DAV:}sync-token');
+ if ($val && is_scalar($val)) {
+ return $val;
+ }
+ if ($val && $val instanceof Xml\Property\Href) {
+ return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX));
+ }
+
+ // If we got here, the earlier two properties may simply not have
+ // been part of the earlier request. We're going to fetch them.
+ $result = $this->server->getProperties($propFind->getPath(), [
+ '{http://sabredav.org/ns}sync-token',
+ '{DAV:}sync-token',
+ ]);
+
+ if (isset($result['{http://sabredav.org/ns}sync-token'])) {
+ return $result['{http://sabredav.org/ns}sync-token'];
+ }
+ if (isset($result['{DAV:}sync-token'])) {
+ $val = $result['{DAV:}sync-token'];
+ if (is_scalar($val)) {
+ return $val;
+ } elseif ($val instanceof Xml\Property\Href) {
+ return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX));
+ }
+ }
+
+ });
+
+ }
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ '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,
+ ];
+
+ }
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception.php b/vendor/sabre/dav/lib/DAV/Exception.php
index 22a319e9f..14f5bab2a 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception.php
+++ b/vendor/sabre/dav/lib/DAV/Exception.php
@@ -1,15 +1,5 @@
<?php
-/**
- * SabreDAV base exception
- *
- * This is SabreDAV's base exception file, use this to implement your own exception.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-
namespace Sabre\DAV;
/**
@@ -20,6 +10,10 @@ namespace Sabre\DAV;
*
* This class also allows you to generate custom xml data for your exceptions. This will be displayed
* in the 'error' element in the failing response.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
*/
class Exception extends \Exception {
@@ -28,7 +22,7 @@ class Exception extends \Exception {
*
* @return int
*/
- public function getHTTPCode() {
+ function getHTTPCode() {
return 500;
@@ -41,7 +35,7 @@ class Exception extends \Exception {
* @param \DOMElement $errorNode
* @return void
*/
- public function serialize(Server $server,\DOMElement $errorNode) {
+ function serialize(Server $server, \DOMElement $errorNode) {
}
@@ -54,11 +48,10 @@ class Exception extends \Exception {
* @param Server $server
* @return array
*/
- public function getHTTPHeaders(Server $server) {
+ function getHTTPHeaders(Server $server) {
- return array();
+ return [];
}
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/BadRequest.php b/vendor/sabre/dav/lib/DAV/Exception/BadRequest.php
index d59727e3a..c21f493da 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/BadRequest.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/BadRequest.php
@@ -2,24 +2,26 @@
namespace Sabre\DAV\Exception;
+use Sabre\DAV;
+
/**
* BadRequest
*
* The BadRequest is thrown when the user submitted an invalid HTTP request
* BadRequest
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class BadRequest extends \Sabre\DAV\Exception {
+class BadRequest extends DAV\Exception {
/**
* Returns the HTTP statuscode for this exception
*
* @return int
*/
- public function getHTTPCode() {
+ function getHTTPCode() {
return 400;
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/Conflict.php b/vendor/sabre/dav/lib/DAV/Exception/Conflict.php
index cbb8fcf1a..4190e6082 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/Conflict.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/Conflict.php
@@ -2,24 +2,26 @@
namespace Sabre\DAV\Exception;
+use Sabre\DAV;
+
/**
* Conflict
*
* A 409 Conflict is thrown when a user tried to make a directory over an existing
* file or in a parent directory that doesn't exist.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class Conflict extends \Sabre\DAV\Exception {
+class Conflict extends DAV\Exception {
/**
* Returns the HTTP statuscode for this exception
*
* @return int
*/
- public function getHTTPCode() {
+ function getHTTPCode() {
return 409;
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/ConflictingLock.php b/vendor/sabre/dav/lib/DAV/Exception/ConflictingLock.php
index 715870f46..b2d2746c5 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/ConflictingLock.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/ConflictingLock.php
@@ -10,7 +10,7 @@ use Sabre\DAV;
* Similar to the Locked exception, this exception thrown when a LOCK request
* was made, on a resource which was already locked
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -23,13 +23,12 @@ class ConflictingLock extends Locked {
* @param \DOMElement $errorNode
* @return void
*/
- public function serialize(DAV\Server $server, \DOMElement $errorNode) {
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
if ($this->lock) {
- $error = $errorNode->ownerDocument->createElementNS('DAV:','d:no-conflicting-lock');
+ $error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:no-conflicting-lock');
$errorNode->appendChild($error);
- if (!is_object($this->lock)) var_dump($this->lock);
- $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri));
+ $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:', 'd:href', $this->lock->uri));
}
}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/Forbidden.php b/vendor/sabre/dav/lib/DAV/Exception/Forbidden.php
index 2dc620612..77df7ca9e 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/Forbidden.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/Forbidden.php
@@ -2,23 +2,25 @@
namespace Sabre\DAV\Exception;
+use Sabre\DAV;
+
/**
* Forbidden
*
* This exception is thrown whenever a user tries to do an operation he's not allowed to
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class Forbidden extends \Sabre\DAV\Exception {
+class Forbidden extends DAV\Exception {
/**
* Returns the HTTP statuscode for this exception
*
* @return int
*/
- public function getHTTPCode() {
+ function getHTTPCode() {
return 403;
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/InsufficientStorage.php b/vendor/sabre/dav/lib/DAV/Exception/InsufficientStorage.php
index f7e382c5a..96e1acc50 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/InsufficientStorage.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/InsufficientStorage.php
@@ -2,23 +2,25 @@
namespace Sabre\DAV\Exception;
+use Sabre\DAV;
+
/**
* InsufficientStorage
*
* This Exception can be thrown, when for example a harddisk is full or a quota is exceeded
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class InsufficientStorage extends \Sabre\DAV\Exception {
+class InsufficientStorage extends DAV\Exception {
/**
* Returns the HTTP statuscode for this exception
*
* @return int
*/
- public function getHTTPCode() {
+ function getHTTPCode() {
return 507;
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/InvalidResourceType.php b/vendor/sabre/dav/lib/DAV/Exception/InvalidResourceType.php
index 847ed4786..505fe5c10 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/InvalidResourceType.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/InvalidResourceType.php
@@ -10,7 +10,7 @@ namespace Sabre\DAV\Exception;
*
* See RFC5689 section 3.3
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -23,9 +23,9 @@ class InvalidResourceType extends Forbidden {
* @param \DOMElement $errorNode
* @return void
*/
- public function serialize(\Sabre\DAV\Server $server,\DOMElement $errorNode) {
+ function serialize(\Sabre\DAV\Server $server, \DOMElement $errorNode) {
- $error = $errorNode->ownerDocument->createElementNS('DAV:','d:valid-resourcetype');
+ $error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:valid-resourcetype');
$errorNode->appendChild($error);
}
diff --git a/vendor/sabre/dav/lib/DAV/Exception/InvalidSyncToken.php b/vendor/sabre/dav/lib/DAV/Exception/InvalidSyncToken.php
new file mode 100644
index 000000000..51a253b29
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Exception/InvalidSyncToken.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Sabre\DAV\Exception;
+
+use Sabre\DAV;
+
+/**
+ * InvalidSyncToken
+ *
+ * This exception is emited for the {DAV:}valid-sync-token pre-condition, as
+ * defined in rfc6578, section 3.2.
+ *
+ * http://tools.ietf.org/html/rfc6578#section-3.2
+ *
+ * This is emitted in cases where the the sync-token, supplied by a client is
+ * either completely unknown, or has expired.
+ *
+ * @author Evert Pot (http://evertpot.com/)
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class InvalidSyncToken extends Forbidden {
+
+ /**
+ * This method allows the exception to include additional information into the WebDAV error response
+ *
+ * @param DAV\Server $server
+ * @param \DOMElement $errorNode
+ * @return void
+ */
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
+
+ $error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:valid-sync-token');
+ $errorNode->appendChild($error);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/LengthRequired.php b/vendor/sabre/dav/lib/DAV/Exception/LengthRequired.php
index 9487686dc..989718558 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/LengthRequired.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/LengthRequired.php
@@ -10,7 +10,7 @@ use Sabre\DAV;
* This exception is thrown when a request was made that required a
* Content-Length header, but did not contain one.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -21,7 +21,7 @@ class LengthRequired extends DAV\Exception {
*
* @return int
*/
- public function getHTTPCode() {
+ function getHTTPCode() {
return 411;
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php b/vendor/sabre/dav/lib/DAV/Exception/LockTokenMatchesRequestUri.php
index 37fc7f8dc..5f8c31868 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/LockTokenMatchesRequestUri.php
@@ -9,7 +9,7 @@ use Sabre\DAV;
*
* This exception is thrown by UNLOCK if a supplied lock-token is invalid
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -18,7 +18,7 @@ class LockTokenMatchesRequestUri extends Conflict {
/**
* Creates the exception
*/
- public function __construct() {
+ function __construct() {
$this->message = 'The locktoken supplied does not match any locks on this entity';
@@ -31,9 +31,9 @@ class LockTokenMatchesRequestUri extends Conflict {
* @param \DOMElement $errorNode
* @return void
*/
- public function serialize(DAV\Server $server,\DOMElement $errorNode) {
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
- $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-matches-request-uri');
+ $error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:lock-token-matches-request-uri');
$errorNode->appendChild($error);
}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/Locked.php b/vendor/sabre/dav/lib/DAV/Exception/Locked.php
index 2bee1b02f..8176db46e 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/Locked.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/Locked.php
@@ -9,7 +9,7 @@ use Sabre\DAV;
*
* The 423 is thrown when a client tried to access a resource that was locked, without supplying a valid lock token
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -30,7 +30,7 @@ class Locked extends DAV\Exception {
*
* @param DAV\Locks\LockInfo $lock
*/
- public function __construct(DAV\Locks\LockInfo $lock = null) {
+ function __construct(DAV\Locks\LockInfo $lock = null) {
$this->lock = $lock;
@@ -41,7 +41,7 @@ class Locked extends DAV\Exception {
*
* @return int
*/
- public function getHTTPCode() {
+ function getHTTPCode() {
return 423;
@@ -54,13 +54,13 @@ class Locked extends DAV\Exception {
* @param \DOMElement $errorNode
* @return void
*/
- public function serialize(DAV\Server $server,\DOMElement $errorNode) {
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
if ($this->lock) {
- $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-submitted');
+ $error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:lock-token-submitted');
$errorNode->appendChild($error);
- $href = $errorNode->ownerDocument->createElementNS('DAV:','d:href');
+ $href = $errorNode->ownerDocument->createElementNS('DAV:', 'd:href');
$href->appendChild($errorNode->ownerDocument->createTextNode($this->lock->uri));
$error->appendChild(
$href
@@ -70,4 +70,3 @@ class Locked extends DAV\Exception {
}
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/MethodNotAllowed.php b/vendor/sabre/dav/lib/DAV/Exception/MethodNotAllowed.php
index 05970cfa8..30c1c2553 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/MethodNotAllowed.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/MethodNotAllowed.php
@@ -2,23 +2,25 @@
namespace Sabre\DAV\Exception;
+use Sabre\DAV;
+
/**
* MethodNotAllowed
*
* The 405 is thrown when a client tried to create a directory on an already existing directory
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class MethodNotAllowed extends \Sabre\DAV\Exception {
+class MethodNotAllowed extends DAV\Exception {
/**
* Returns the HTTP statuscode for this exception
*
* @return int
*/
- public function getHTTPCode() {
+ function getHTTPCode() {
return 405;
@@ -32,13 +34,13 @@ class MethodNotAllowed extends \Sabre\DAV\Exception {
* @param \Sabre\DAV\Server $server
* @return array
*/
- public function getHTTPHeaders(\Sabre\DAV\Server $server) {
+ function getHTTPHeaders(\Sabre\DAV\Server $server) {
$methods = $server->getAllowedMethods($server->getRequestUri());
- return array(
- 'Allow' => strtoupper(implode(', ',$methods)),
- );
+ return [
+ 'Allow' => strtoupper(implode(', ', $methods)),
+ ];
}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/NotAuthenticated.php b/vendor/sabre/dav/lib/DAV/Exception/NotAuthenticated.php
index c082d489b..e69a60c75 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/NotAuthenticated.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/NotAuthenticated.php
@@ -10,7 +10,7 @@ use Sabre\DAV;
* This exception is thrown when the client did not provide valid
* authentication credentials.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -21,7 +21,7 @@ class NotAuthenticated extends DAV\Exception {
*
* @return int
*/
- public function getHTTPCode() {
+ function getHTTPCode() {
return 401;
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/NotFound.php b/vendor/sabre/dav/lib/DAV/Exception/NotFound.php
index 83e699cb2..b0397446d 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/NotFound.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/NotFound.php
@@ -2,27 +2,28 @@
namespace Sabre\DAV\Exception;
+use Sabre\DAV;
+
/**
* NotFound
*
* This Exception is thrown when a Node couldn't be found. It returns HTTP error code 404
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class NotFound extends \Sabre\DAV\Exception {
+class NotFound extends DAV\Exception {
/**
* Returns the HTTP statuscode for this exception
*
* @return int
*/
- public function getHTTPCode() {
+ function getHTTPCode() {
return 404;
}
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/NotImplemented.php b/vendor/sabre/dav/lib/DAV/Exception/NotImplemented.php
index 5f031cb7f..68f309222 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/NotImplemented.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/NotImplemented.php
@@ -2,23 +2,25 @@
namespace Sabre\DAV\Exception;
+use Sabre\DAV;
+
/**
* NotImplemented
*
* This exception is thrown when the client tried to call an unsupported HTTP method or other feature
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class NotImplemented extends \Sabre\DAV\Exception {
+class NotImplemented extends DAV\Exception {
/**
* Returns the HTTP statuscode for this exception
*
* @return int
*/
- public function getHTTPCode() {
+ function getHTTPCode() {
return 501;
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/PaymentRequired.php b/vendor/sabre/dav/lib/DAV/Exception/PaymentRequired.php
index 3c256a064..43e17344a 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/PaymentRequired.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/PaymentRequired.php
@@ -10,7 +10,7 @@ use Sabre\DAV;
* The PaymentRequired exception may be thrown in a case where a user must pay
* to access a certain resource or operation.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -21,7 +21,7 @@ class PaymentRequired extends DAV\Exception {
*
* @return int
*/
- public function getHTTPCode() {
+ function getHTTPCode() {
return 402;
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/PreconditionFailed.php b/vendor/sabre/dav/lib/DAV/Exception/PreconditionFailed.php
index deb8a5bea..550360e5a 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/PreconditionFailed.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/PreconditionFailed.php
@@ -11,7 +11,7 @@ use Sabre\DAV;
* like for example an If, If-None-Match or If-Match header, which caused the HTTP
* request to not execute (the condition of the header failed)
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -33,7 +33,7 @@ class PreconditionFailed extends DAV\Exception {
* @param string $message
* @param string $header
*/
- public function __construct($message, $header=null) {
+ function __construct($message, $header = null) {
parent::__construct($message);
$this->header = $header;
@@ -45,7 +45,7 @@ class PreconditionFailed extends DAV\Exception {
*
* @return int
*/
- public function getHTTPCode() {
+ function getHTTPCode() {
return 412;
@@ -58,7 +58,7 @@ class PreconditionFailed extends DAV\Exception {
* @param \DOMElement $errorNode
* @return void
*/
- public function serialize(DAV\Server $server,\DOMElement $errorNode) {
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
if ($this->header) {
$prop = $errorNode->ownerDocument->createElement('s:header');
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/ReportNotSupported.php b/vendor/sabre/dav/lib/DAV/Exception/ReportNotSupported.php
index 8e32096e0..a83695627 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/ReportNotSupported.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/ReportNotSupported.php
@@ -9,11 +9,11 @@ use Sabre\DAV;
*
* This exception is thrown when the client requested an unknown report through the REPORT method
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class ReportNotSupported extends Forbidden {
+class ReportNotSupported extends UnsupportedMediaType {
/**
* This method allows the exception to include additional information into the WebDAV error response
@@ -22,9 +22,9 @@ class ReportNotSupported extends Forbidden {
* @param \DOMElement $errorNode
* @return void
*/
- public function serialize(DAV\Server $server,\DOMElement $errorNode) {
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
- $error = $errorNode->ownerDocument->createElementNS('DAV:','d:supported-report');
+ $error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:supported-report');
$errorNode->appendChild($error);
}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php b/vendor/sabre/dav/lib/DAV/Exception/RequestedRangeNotSatisfiable.php
index 25002be6a..c8ccfc062 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/RequestedRangeNotSatisfiable.php
@@ -10,7 +10,7 @@ use Sabre\DAV;
* This exception is normally thrown when the user
* request a range that is out of the entity bounds.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -21,11 +21,10 @@ class RequestedRangeNotSatisfiable extends DAV\Exception {
*
* @return int
*/
- public function getHTTPCode() {
+ function getHTTPCode() {
return 416;
}
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/ServiceUnavailable.php b/vendor/sabre/dav/lib/DAV/Exception/ServiceUnavailable.php
index 59e433954..31df606e2 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/ServiceUnavailable.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/ServiceUnavailable.php
@@ -11,20 +11,20 @@ use Sabre\DAV;
* is currently not available (e.g. down for maintenance).
*
* @author Thomas Müller <thomas.mueller@tmit.eu>
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
class ServiceUnavailable extends DAV\Exception {
- /**
- * Returns the HTTP statuscode for this exception
- *
- * @return int
- */
- public function getHTTPCode() {
+ /**
+ * Returns the HTTP statuscode for this exception
+ *
+ * @return int
+ */
+ function getHTTPCode() {
- return 503;
+ return 503;
- }
+ }
}
diff --git a/vendor/sabre/dav/lib/DAV/Exception/TooManyMatches.php b/vendor/sabre/dav/lib/DAV/Exception/TooManyMatches.php
new file mode 100644
index 000000000..d0f0f84e8
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Exception/TooManyMatches.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Sabre\DAV\Exception;
+
+use Sabre\DAV;
+
+/**
+ * TooManyMatches
+ *
+ * This exception is emited for the {DAV:}number-of-matches-within-limits
+ * post-condition, as defined in rfc6578, section 3.2.
+ *
+ * http://tools.ietf.org/html/rfc6578#section-3.2
+ *
+ * This is emitted in cases where the response to a {DAV:}sync-collection would
+ * generate more results than the implementation is willing to send back.
+ *
+ * @author Evert Pot (http://evertpot.com/)
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class TooManyMatches extends Forbidden {
+
+ /**
+ * This method allows the exception to include additional information into the WebDAV error response
+ *
+ * @param DAV\Server $server
+ * @param \DOMElement $errorNode
+ * @return void
+ */
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
+
+ $error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:number-of-matches-within-limits');
+ $errorNode->appendChild($error);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/UnsupportedMediaType.php b/vendor/sabre/dav/lib/DAV/Exception/UnsupportedMediaType.php
index 46eea60df..f3d92842d 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/UnsupportedMediaType.php
+++ b/vendor/sabre/dav/lib/DAV/Exception/UnsupportedMediaType.php
@@ -2,24 +2,26 @@
namespace Sabre\DAV\Exception;
+use Sabre\DAV;
+
/**
* UnSupportedMediaType
*
* The 415 Unsupported Media Type status code is generally sent back when the client
* tried to call an HTTP method, with a body the server didn't understand
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class UnsupportedMediaType extends \Sabre\DAV\Exception {
+class UnsupportedMediaType extends DAV\Exception {
/**
* returns the http statuscode for this exception
*
* @return int
*/
- public function getHTTPCode() {
+ function getHTTPCode() {
return 415;
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/FS/Directory.php b/vendor/sabre/dav/lib/DAV/FS/Directory.php
index 6fdd2aecf..963e5554c 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/FS/Directory.php
+++ b/vendor/sabre/dav/lib/DAV/FS/Directory.php
@@ -1,12 +1,13 @@
<?php
namespace Sabre\DAV\FS;
+
use Sabre\DAV;
/**
* Directory class
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -36,10 +37,11 @@ class Directory extends Node implements DAV\ICollection, DAV\IQuota {
* @param resource|string $data Initial payload
* @return null|string
*/
- public function createFile($name, $data = null) {
+ function createFile($name, $data = null) {
$newPath = $this->path . '/' . $name;
- file_put_contents($newPath,$data);
+ file_put_contents($newPath, $data);
+ clearstatcache(true, $newPath);
}
@@ -49,10 +51,11 @@ class Directory extends Node implements DAV\ICollection, DAV\IQuota {
* @param string $name
* @return void
*/
- public function createDirectory($name) {
+ function createDirectory($name) {
$newPath = $this->path . '/' . $name;
mkdir($newPath);
+ clearstatcache(true, $newPath);
}
@@ -66,7 +69,7 @@ class Directory extends Node implements DAV\ICollection, DAV\IQuota {
* @throws DAV\Exception\NotFound
* @return DAV\INode
*/
- public function getChild($name) {
+ function getChild($name) {
$path = $this->path . '/' . $name;
@@ -74,7 +77,7 @@ class Directory extends Node implements DAV\ICollection, DAV\IQuota {
if (is_dir($path)) {
- return new Directory($path);
+ return new self($path);
} else {
@@ -89,10 +92,19 @@ class Directory extends Node implements DAV\ICollection, DAV\IQuota {
*
* @return DAV\INode[]
*/
- public function getChildren() {
+ function getChildren() {
+
+ $nodes = [];
+ $iterator = new \FilesystemIterator(
+ $this->path,
+ \FilesystemIterator::CURRENT_AS_SELF
+ | \FilesystemIterator::SKIP_DOTS
+ );
+ foreach ($iterator as $entry) {
+
+ $nodes[] = $this->getChild($entry->getFilename());
- $nodes = array();
- foreach(scandir($this->path) as $node) if($node!='.' && $node!='..') $nodes[] = $this->getChild($node);
+ }
return $nodes;
}
@@ -103,7 +115,7 @@ class Directory extends Node implements DAV\ICollection, DAV\IQuota {
* @param string $name
* @return bool
*/
- public function childExists($name) {
+ function childExists($name) {
$path = $this->path . '/' . $name;
return file_exists($path);
@@ -115,9 +127,9 @@ class Directory extends Node implements DAV\ICollection, DAV\IQuota {
*
* @return void
*/
- public function delete() {
+ function delete() {
- foreach($this->getChildren() as $child) $child->delete();
+ foreach ($this->getChildren() as $child) $child->delete();
rmdir($this->path);
}
@@ -127,14 +139,13 @@ class Directory extends Node implements DAV\ICollection, DAV\IQuota {
*
* @return array
*/
- public function getQuotaInfo() {
+ function getQuotaInfo() {
- return array(
- disk_total_space($this->path)-disk_free_space($this->path),
+ return [
+ disk_total_space($this->path) - disk_free_space($this->path),
disk_free_space($this->path)
- );
+ ];
}
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/FS/File.php b/vendor/sabre/dav/lib/DAV/FS/File.php
index d10370fae..4fc5af057 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/FS/File.php
+++ b/vendor/sabre/dav/lib/DAV/FS/File.php
@@ -7,7 +7,7 @@ use Sabre\DAV;
/**
* File class
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -19,20 +19,21 @@ class File extends Node implements DAV\IFile {
* @param resource $data
* @return void
*/
- public function put($data) {
+ function put($data) {
- file_put_contents($this->path,$data);
+ file_put_contents($this->path, $data);
+ clearstatcache(true, $this->path);
}
/**
* Returns the data
*
- * @return string
+ * @return resource
*/
- public function get() {
+ function get() {
- return fopen($this->path,'r');
+ return fopen($this->path, 'r');
}
@@ -41,7 +42,7 @@ class File extends Node implements DAV\IFile {
*
* @return void
*/
- public function delete() {
+ function delete() {
unlink($this->path);
@@ -52,7 +53,7 @@ class File extends Node implements DAV\IFile {
*
* @return int
*/
- public function getSize() {
+ function getSize() {
return filesize($this->path);
@@ -68,9 +69,13 @@ class File extends Node implements DAV\IFile {
*
* @return mixed
*/
- public function getETag() {
+ function getETag() {
- return null;
+ return '"' . sha1(
+ fileinode($this->path) .
+ filesize($this->path) .
+ filemtime($this->path)
+ ) . '"';
}
@@ -81,11 +86,10 @@ class File extends Node implements DAV\IFile {
*
* @return mixed
*/
- public function getContentType() {
+ function getContentType() {
return null;
}
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/FS/Node.php b/vendor/sabre/dav/lib/DAV/FS/Node.php
index 605fa3c82..831c11911 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/FS/Node.php
+++ b/vendor/sabre/dav/lib/DAV/FS/Node.php
@@ -3,13 +3,14 @@
namespace Sabre\DAV\FS;
use Sabre\DAV;
+use Sabre\HTTP\URLUtil;
/**
* Base node-class
*
* The node class implements the method used by both the File and the Directory classes
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -27,7 +28,7 @@ abstract class Node implements DAV\INode {
*
* @param string $path
*/
- public function __construct($path) {
+ function __construct($path) {
$this->path = $path;
@@ -40,9 +41,9 @@ abstract class Node implements DAV\INode {
*
* @return string
*/
- public function getName() {
+ function getName() {
- list(, $name) = DAV\URLUtil::splitPath($this->path);
+ list(, $name) = URLUtil::splitPath($this->path);
return $name;
}
@@ -53,30 +54,27 @@ abstract class Node implements DAV\INode {
* @param string $name The new name
* @return void
*/
- public function setName($name) {
+ function setName($name) {
- list($parentPath, ) = DAV\URLUtil::splitPath($this->path);
- list(, $newName) = DAV\URLUtil::splitPath($name);
+ list($parentPath, ) = URLUtil::splitPath($this->path);
+ list(, $newName) = URLUtil::splitPath($name);
$newPath = $parentPath . '/' . $newName;
- rename($this->path,$newPath);
+ rename($this->path, $newPath);
$this->path = $newPath;
}
-
-
/**
* Returns the last modification time, as a unix timestamp
*
* @return int
*/
- public function getLastModified() {
+ function getLastModified() {
return filemtime($this->path);
}
}
-
diff --git a/vendor/sabre/dav/lib/DAV/FSExt/Directory.php b/vendor/sabre/dav/lib/DAV/FSExt/Directory.php
new file mode 100644
index 000000000..648079e26
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/FSExt/Directory.php
@@ -0,0 +1,219 @@
+<?php
+
+namespace Sabre\DAV\FSExt;
+
+use Sabre\DAV;
+use Sabre\DAV\FS\Node;
+
+/**
+ * Directory class
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Directory extends Node implements DAV\ICollection, DAV\IQuota, DAV\IMoveTarget {
+
+ /**
+ * Creates a new file in the directory
+ *
+ * Data will either be supplied as a stream resource, or in certain cases
+ * as a string. Keep in mind that you may have to support either.
+ *
+ * After successful creation of the file, you may choose to return the ETag
+ * of the new file here.
+ *
+ * The returned ETag must be surrounded by double-quotes (The quotes should
+ * be part of the actual string).
+ *
+ * If you cannot accurately determine the ETag, you should not return it.
+ * If you don't store the file exactly as-is (you're transforming it
+ * somehow) you should also not return an ETag.
+ *
+ * This means that if a subsequent GET to this new file does not exactly
+ * return the same contents of what was submitted here, you are strongly
+ * recommended to omit the ETag.
+ *
+ * @param string $name Name of the file
+ * @param resource|string $data Initial payload
+ * @return null|string
+ */
+ function createFile($name, $data = null) {
+
+ // We're not allowing dots
+ 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) .
+ filemtime($newPath)
+ ) . '"';
+
+ }
+
+ /**
+ * Creates a new subdirectory
+ *
+ * @param string $name
+ * @return void
+ */
+ function createDirectory($name) {
+
+ // We're not allowing dots
+ if ($name == '.' || $name == '..') throw new DAV\Exception\Forbidden('Permission denied to . and ..');
+ $newPath = $this->path . '/' . $name;
+ mkdir($newPath);
+ clearstatcache(true, $newPath);
+
+ }
+
+ /**
+ * Returns a specific child node, referenced by its name
+ *
+ * This method must throw Sabre\DAV\Exception\NotFound if the node does not
+ * exist.
+ *
+ * @param string $name
+ * @throws DAV\Exception\NotFound
+ * @return DAV\INode
+ */
+ function getChild($name) {
+
+ $path = $this->path . '/' . $name;
+
+ if (!file_exists($path)) throw new DAV\Exception\NotFound('File could not be located');
+ if ($name == '.' || $name == '..') throw new DAV\Exception\Forbidden('Permission denied to . and ..');
+
+ if (is_dir($path)) {
+
+ return new self($path);
+
+ } else {
+
+ return new File($path);
+
+ }
+
+ }
+
+ /**
+ * Checks if a child exists.
+ *
+ * @param string $name
+ * @return bool
+ */
+ function childExists($name) {
+
+ if ($name == '.' || $name == '..')
+ throw new DAV\Exception\Forbidden('Permission denied to . and ..');
+
+ $path = $this->path . '/' . $name;
+ return file_exists($path);
+
+ }
+
+ /**
+ * Returns an array with all the child nodes
+ *
+ * @return DAV\INode[]
+ */
+ function getChildren() {
+
+ $nodes = [];
+ $iterator = new \FilesystemIterator(
+ $this->path,
+ \FilesystemIterator::CURRENT_AS_SELF
+ | \FilesystemIterator::SKIP_DOTS
+ );
+
+ foreach ($iterator as $entry) {
+
+ $node = $entry->getFilename();
+
+ if ($node === '.sabredav')
+ continue;
+
+ $nodes[] = $this->getChild($node);
+
+ }
+ return $nodes;
+
+ }
+
+ /**
+ * Deletes all files in this directory, and then itself
+ *
+ * @return bool
+ */
+ function delete() {
+
+ // Deleting all children
+ foreach ($this->getChildren() as $child) $child->delete();
+
+ // Removing resource info, if its still around
+ if (file_exists($this->path . '/.sabredav')) unlink($this->path . '/.sabredav');
+
+ // Removing the directory itself
+ rmdir($this->path);
+
+ return true;
+
+ }
+
+ /**
+ * Returns available diskspace information
+ *
+ * @return array
+ */
+ function getQuotaInfo() {
+
+ $total = disk_total_space(realpath($this->path));
+ $free = disk_free_space(realpath($this->path));
+
+ return [
+ $total - $free,
+ $free
+ ];
+ }
+
+ /**
+ * Moves a node into this collection.
+ *
+ * It is up to the implementors to:
+ * 1. Create the new resource.
+ * 2. Remove the old resource.
+ * 3. Transfer any properties or other data.
+ *
+ * Generally you should make very sure that your collection can easily move
+ * the move.
+ *
+ * If you don't, just return false, which will trigger sabre/dav to handle
+ * the move itself. If you return true from this function, the assumption
+ * is that the move was successful.
+ *
+ * @param string $targetName New local file/collection name.
+ * @param string $sourcePath Full path to source node
+ * @param DAV\INode $sourceNode Source node itself
+ * @return bool
+ */
+ function moveInto($targetName, $sourcePath, DAV\INode $sourceNode) {
+
+ // We only support FSExt\Directory or FSExt\File objects, so
+ // anything else we want to quickly reject.
+ if (!$sourceNode instanceof self && !$sourceNode instanceof File) {
+ return false;
+ }
+
+ // PHP allows us to access protected properties from other objects, as
+ // long as they are defined in a class that has a shared inheritence
+ // with the current class.
+ rename($sourceNode->path, $this->path . '/' . $targetName);
+
+ return true;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/FSExt/File.php b/vendor/sabre/dav/lib/DAV/FSExt/File.php
index 6588fad7e..eb5ae19fe 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/FSExt/File.php
+++ b/vendor/sabre/dav/lib/DAV/FSExt/File.php
@@ -1,12 +1,14 @@
<?php
namespace Sabre\DAV\FSExt;
+
use Sabre\DAV;
+use Sabre\DAV\FS\Node;
/**
* File class
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -15,15 +17,16 @@ class File extends Node implements DAV\PartialUpdate\IPatchSupport {
/**
* Updates the data
*
- * data is a readable stream resource.
+ * Data is a readable stream resource.
*
* @param resource|string $data
* @return string
*/
- public function put($data) {
+ function put($data) {
- file_put_contents($this->path,$data);
- return '"' . md5_file($this->path) . '"';
+ file_put_contents($this->path, $data);
+ clearstatcache(true, $this->path);
+ return $this->getETag();
}
@@ -42,7 +45,7 @@ class File extends Node implements DAV\PartialUpdate\IPatchSupport {
* The third argument is the start or end byte.
*
* After a successful put operation, you may choose to return an ETag. The
- * etag must always be surrounded by double-quotes. These quotes must
+ * ETAG must always be surrounded by double-quotes. These quotes must
* appear in the actual string you're returning.
*
* Clients may use the ETag from a PUT request to later on make sure that
@@ -54,15 +57,15 @@ class File extends Node implements DAV\PartialUpdate\IPatchSupport {
* @param int $offset
* @return string|null
*/
- public function patch($data, $rangeType, $offset = null) {
+ function patch($data, $rangeType, $offset = null) {
- switch($rangeType) {
+ switch ($rangeType) {
case 1 :
$f = fopen($this->path, 'a');
break;
case 2 :
$f = fopen($this->path, 'c');
- fseek($f,$offset);
+ fseek($f, $offset);
break;
case 3 :
$f = fopen($this->path, 'c');
@@ -72,10 +75,11 @@ class File extends Node implements DAV\PartialUpdate\IPatchSupport {
if (is_string($data)) {
fwrite($f, $data);
} else {
- stream_copy_to_stream($data,$f);
+ stream_copy_to_stream($data, $f);
}
fclose($f);
- return '"' . md5_file($this->path) . '"';
+ clearstatcache(true, $this->path);
+ return $this->getETag();
}
@@ -84,9 +88,9 @@ class File extends Node implements DAV\PartialUpdate\IPatchSupport {
*
* @return resource
*/
- public function get() {
+ function get() {
- return fopen($this->path,'r');
+ return fopen($this->path, 'r');
}
@@ -95,10 +99,9 @@ class File extends Node implements DAV\PartialUpdate\IPatchSupport {
*
* @return bool
*/
- public function delete() {
+ function delete() {
- unlink($this->path);
- return parent::delete();
+ return unlink($this->path);
}
@@ -112,9 +115,13 @@ class File extends Node implements DAV\PartialUpdate\IPatchSupport {
*
* @return string|null
*/
- public function getETag() {
+ function getETag() {
- return '"' . md5_file($this->path). '"';
+ return '"' . sha1(
+ fileinode($this->path) .
+ filesize($this->path) .
+ filemtime($this->path)
+ ) . '"';
}
@@ -125,7 +132,7 @@ class File extends Node implements DAV\PartialUpdate\IPatchSupport {
*
* @return string|null
*/
- public function getContentType() {
+ function getContentType() {
return null;
@@ -136,11 +143,10 @@ class File extends Node implements DAV\PartialUpdate\IPatchSupport {
*
* @return int
*/
- public function getSize() {
+ function getSize() {
return filesize($this->path);
}
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/File.php b/vendor/sabre/dav/lib/DAV/File.php
index af8ce735f..e0a0391db 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/File.php
+++ b/vendor/sabre/dav/lib/DAV/File.php
@@ -8,7 +8,7 @@ namespace Sabre\DAV;
* This is a helper class, that should aid in getting file classes setup.
* Most of its methods are implemented, and throw permission denied exceptions
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -22,7 +22,7 @@ abstract class File extends Node implements IFile {
* @param resource $data
* @return void
*/
- public function put($data) {
+ function put($data) {
throw new Exception\Forbidden('Permission denied to change data');
@@ -35,7 +35,7 @@ abstract class File extends Node implements IFile {
*
* @return mixed
*/
- public function get() {
+ function get() {
throw new Exception\Forbidden('Permission denied to read this file');
@@ -46,7 +46,7 @@ abstract class File extends Node implements IFile {
*
* @return int
*/
- public function getSize() {
+ function getSize() {
return 0;
@@ -62,7 +62,7 @@ abstract class File extends Node implements IFile {
*
* @return string|null
*/
- public function getETag() {
+ function getETag() {
return null;
@@ -75,11 +75,10 @@ abstract class File extends Node implements IFile {
*
* @return string|null
*/
- public function getContentType() {
+ function getContentType() {
return null;
}
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/ICollection.php b/vendor/sabre/dav/lib/DAV/ICollection.php
index c38d5e553..390d9b741 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/ICollection.php
+++ b/vendor/sabre/dav/lib/DAV/ICollection.php
@@ -7,7 +7,7 @@ namespace Sabre\DAV;
*
* This interface should be implemented by each class that represents a collection
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -74,4 +74,3 @@ interface ICollection extends INode {
function childExists($name);
}
-
diff --git a/vendor/sabre/dav/lib/DAV/IExtendedCollection.php b/vendor/sabre/dav/lib/DAV/IExtendedCollection.php
new file mode 100644
index 000000000..c561d7072
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/IExtendedCollection.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Sabre\DAV;
+
+/**
+ * The IExtendedCollection interface.
+ *
+ * This interface can be used to create special-type of collection-resources
+ * as defined by RFC 5689.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface IExtendedCollection extends ICollection {
+
+ /**
+ * Creates a new collection.
+ *
+ * This method will receive a MkCol object with all the information about
+ * the new collection that's being created.
+ *
+ * The MkCol object contains information about the resourceType of the new
+ * collection. If you don't support the specified resourceType, you should
+ * throw Exception\InvalidResourceType.
+ *
+ * The object also contains a list of WebDAV properties for the new
+ * collection.
+ *
+ * You should call the handle() method on this object to specify exactly
+ * which properties you are storing. This allows the system to figure out
+ * exactly which properties you didn't store, which in turn allows other
+ * plugins (such as the propertystorage plugin) to handle storing the
+ * property for you.
+ *
+ * @param string $name
+ * @param MkCol $mkCol
+ * @throws Exception\InvalidResourceType
+ * @return void
+ */
+ function createExtendedCollection($name, MkCol $mkCol);
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/IFile.php b/vendor/sabre/dav/lib/DAV/IFile.php
index 6245d3fad..e16a3a58a 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/IFile.php
+++ b/vendor/sabre/dav/lib/DAV/IFile.php
@@ -9,14 +9,14 @@ namespace Sabre\DAV;
* this specific node a PUT or GET method may be performed, to either update,
* or retrieve the contents of the file.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
interface IFile extends INode {
/**
- * Updates the data
+ * Replaces the contents of the file.
*
* The data argument is a readable stream resource.
*
@@ -60,9 +60,14 @@ interface IFile extends INode {
*
* An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
*
- * Return null if the ETag can not effectively be determined
+ * Return null if the ETag can not effectively be determined.
*
- * @return void
+ * The ETag must be surrounded by double-quotes, so something like this
+ * would make a valid ETag:
+ *
+ * return '"someetag"';
+ *
+ * @return string|null
*/
function getETag();
@@ -74,4 +79,3 @@ interface IFile extends INode {
function getSize();
}
-
diff --git a/vendor/sabre/dav/lib/DAV/IMoveTarget.php b/vendor/sabre/dav/lib/DAV/IMoveTarget.php
new file mode 100644
index 000000000..f0f67bc26
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/IMoveTarget.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Sabre\DAV;
+
+/**
+ * By implementing this interface, a collection can effectively say "other
+ * nodes may be moved into this collection".
+ *
+ * The benefit of this, is that sabre/dav will by default perform a move, by
+ * tranfersing an entire directory tree, copying every collection, and deleting
+ * every item.
+ *
+ * If a backend supports a better optimized move operation, this can trigger
+ * some huge speed gains.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface IMoveTarget extends ICollection {
+
+ /**
+ * Moves a node into this collection.
+ *
+ * It is up to the implementors to:
+ * 1. Create the new resource.
+ * 2. Remove the old resource.
+ * 3. Transfer any properties or other data.
+ *
+ * Generally you should make very sure that your collection can easily move
+ * the move.
+ *
+ * If you don't, just return false, which will trigger sabre/dav to handle
+ * the move itself. If you return true from this function, the assumption
+ * is that the move was successful.
+ *
+ * @param string $targetName New local file/collection name.
+ * @param string $sourcePath Full path to source node
+ * @param INode $sourceNode Source node itself
+ * @return bool
+ */
+ function moveInto($targetName, $sourcePath, INode $sourceNode);
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/IMultiGet.php b/vendor/sabre/dav/lib/DAV/IMultiGet.php
new file mode 100644
index 000000000..e26b457ef
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/IMultiGet.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Sabre\DAV;
+
+/**
+ * IMultiGet
+ *
+ * This interface adds a tiny bit of functionality to collections.
+ *
+ * There a certain situations, in particular in relation to WebDAV-Sync, CalDAV
+ * and CardDAV, where information for a list of items will be requested.
+ *
+ * Because the getChild() call is the main abstraction method, this can in
+ * reality result in many database calls, which could potentially be
+ * optimized.
+ *
+ * The MultiGet interface is used by the server in these cases.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface IMultiGet extends ICollection {
+
+ /**
+ * This method receives a list of paths in it's first argument.
+ * It must return an array with Node objects.
+ *
+ * If any children are not found, you do not have to return them.
+ *
+ * @param string[] $paths
+ * @return array
+ */
+ function getMultipleChildren(array $paths);
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/INode.php b/vendor/sabre/dav/lib/DAV/INode.php
index e183c84be..b5e6cb9ef 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/INode.php
+++ b/vendor/sabre/dav/lib/DAV/INode.php
@@ -5,7 +5,7 @@ namespace Sabre\DAV;
/**
* The INode interface is the base interface, and the parent class of both ICollection and IFile
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -43,4 +43,3 @@ interface INode {
function getLastModified();
}
-
diff --git a/vendor/sabre/dav/lib/DAV/IProperties.php b/vendor/sabre/dav/lib/DAV/IProperties.php
new file mode 100644
index 000000000..00969c2c4
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/IProperties.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Sabre\DAV;
+
+/**
+ * IProperties interface
+ *
+ * Implement this interface to support custom WebDAV properties requested and sent from clients.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface IProperties extends INode {
+
+ /**
+ * Updates properties on this node.
+ *
+ * This method received a PropPatch object, which contains all the
+ * information about the update.
+ *
+ * To update specific properties, call the 'handle' method on this object.
+ * Read the PropPatch documentation for more information.
+ *
+ * @param PropPatch $propPatch
+ * @return void
+ */
+ function propPatch(PropPatch $propPatch);
+
+ /**
+ * Returns a list of properties for this nodes.
+ *
+ * The properties list is a list of propertynames the client requested,
+ * encoded in clark-notation {xmlnamespace}tagname
+ *
+ * If the array is empty, it means 'all properties' were requested.
+ *
+ * Note that it's fine to liberally give properties back, instead of
+ * conforming to the list of requested properties.
+ * The Server class will filter out the extra.
+ *
+ * @param array $properties
+ * @return array
+ */
+ function getProperties($properties);
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/IQuota.php b/vendor/sabre/dav/lib/DAV/IQuota.php
index 988df3d06..e16f386b9 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/IQuota.php
+++ b/vendor/sabre/dav/lib/DAV/IQuota.php
@@ -9,7 +9,7 @@ namespace Sabre\DAV;
* will check for quota information on any given node. If the information is not available it will
* attempt to fetch the information from the root node.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -24,4 +24,3 @@ interface IQuota extends ICollection {
function getQuotaInfo();
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/AbstractBackend.php b/vendor/sabre/dav/lib/DAV/Locks/Backend/AbstractBackend.php
index b2c7b983a..044316cdb 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/AbstractBackend.php
+++ b/vendor/sabre/dav/lib/DAV/Locks/Backend/AbstractBackend.php
@@ -2,8 +2,6 @@
namespace Sabre\DAV\Locks\Backend;
-use Sabre\DAV\Locks;
-
/**
* This is an Abstract clas for lock backends.
*
@@ -11,11 +9,10 @@ use Sabre\DAV\Locks;
* to ensure that if default code is required in the backend, there will be a
* non-bc-breaking way to do so.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
abstract class AbstractBackend implements BackendInterface {
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/BackendInterface.php b/vendor/sabre/dav/lib/DAV/Locks/Backend/BackendInterface.php
index bae666b2f..a2d2fe89c 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/BackendInterface.php
+++ b/vendor/sabre/dav/lib/DAV/Locks/Backend/BackendInterface.php
@@ -8,7 +8,7 @@ use Sabre\DAV\Locks;
* If you are defining your own Locks backend, you must implement this
* interface.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -27,7 +27,7 @@ interface BackendInterface {
* @param bool $returnChildLocks
* @return array
*/
- public function getLocks($uri, $returnChildLocks);
+ function getLocks($uri, $returnChildLocks);
/**
* Locks a uri
@@ -36,7 +36,7 @@ interface BackendInterface {
* @param Locks\LockInfo $lockInfo
* @return bool
*/
- public function lock($uri,Locks\LockInfo $lockInfo);
+ function lock($uri, Locks\LockInfo $lockInfo);
/**
* Removes a lock from a uri
@@ -45,7 +45,6 @@ interface BackendInterface {
* @param Locks\LockInfo $lockInfo
* @return bool
*/
- public function unlock($uri,Locks\LockInfo $lockInfo);
+ function unlock($uri, Locks\LockInfo $lockInfo);
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/File.php b/vendor/sabre/dav/lib/DAV/Locks/Backend/File.php
index 9ac7e06b2..849539bee 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/File.php
+++ b/vendor/sabre/dav/lib/DAV/Locks/Backend/File.php
@@ -5,14 +5,15 @@ namespace Sabre\DAV\Locks\Backend;
use Sabre\DAV\Locks\LockInfo;
/**
- * The Lock manager allows you to handle all file-locks centrally.
+ * This Locks backend stores all locking information in a single file.
*
- * This Lock Manager stores all its data in a single file.
+ * Note that this is not nearly as robust as a database. If you are considering
+ * using this backend, keep in mind that the PDO backend can work with SqLite,
+ * which is designed to be a good file-based database.
*
- * Note that this is not nearly as robust as a database, you are encouraged
- * to use the PDO backend instead.
+ * It literally solves the problem this class solves as well, but much better.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -30,7 +31,7 @@ class File extends AbstractBackend {
*
* @param string $locksFile path to file
*/
- public function __construct($locksFile) {
+ function __construct($locksFile) {
$this->locksFile = $locksFile;
@@ -49,20 +50,20 @@ class File extends AbstractBackend {
* @param bool $returnChildLocks
* @return array
*/
- public function getLocks($uri, $returnChildLocks) {
+ function getLocks($uri, $returnChildLocks) {
- $newLocks = array();
+ $newLocks = [];
$locks = $this->getData();
- foreach($locks as $lock) {
+ foreach ($locks as $lock) {
if ($lock->uri === $uri ||
//deep locks on parents
- ($lock->depth!=0 && strpos($uri, $lock->uri . '/')===0) ||
+ ($lock->depth != 0 && strpos($uri, $lock->uri . '/') === 0) ||
// locks on children
- ($returnChildLocks && (strpos($lock->uri, $uri . '/')===0)) ) {
+ ($returnChildLocks && (strpos($lock->uri, $uri . '/') === 0))) {
$newLocks[] = $lock;
@@ -71,7 +72,7 @@ class File extends AbstractBackend {
}
// Checking if we can remove any of these locks
- foreach($newLocks as $k=>$lock) {
+ foreach ($newLocks as $k => $lock) {
if (time() > $lock->timeout + $lock->created) unset($newLocks[$k]);
}
return $newLocks;
@@ -85,7 +86,7 @@ class File extends AbstractBackend {
* @param LockInfo $lockInfo
* @return bool
*/
- public function lock($uri, LockInfo $lockInfo) {
+ function lock($uri, LockInfo $lockInfo) {
// We're making the lock timeout 30 minutes
$lockInfo->timeout = 1800;
@@ -94,7 +95,7 @@ class File extends AbstractBackend {
$locks = $this->getData();
- foreach($locks as $k=>$lock) {
+ foreach ($locks as $k => $lock) {
if (
($lock->token == $lockInfo->token) ||
(time() > $lock->timeout + $lock->created)
@@ -115,10 +116,10 @@ class File extends AbstractBackend {
* @param LockInfo $lockInfo
* @return bool
*/
- public function unlock($uri, LockInfo $lockInfo) {
+ function unlock($uri, LockInfo $lockInfo) {
$locks = $this->getData();
- foreach($locks as $k=>$lock) {
+ foreach ($locks as $k => $lock) {
if ($lock->token == $lockInfo->token) {
@@ -139,21 +140,22 @@ class File extends AbstractBackend {
*/
protected function getData() {
- if (!file_exists($this->locksFile)) return array();
+ if (!file_exists($this->locksFile)) return [];
// opening up the file, and creating a shared lock
- $handle = fopen($this->locksFile,'r');
- flock($handle,LOCK_SH);
+ $handle = fopen($this->locksFile, 'r');
+ flock($handle, LOCK_SH);
// Reading data until the eof
$data = stream_get_contents($handle);
// We're all good
+ flock($handle, LOCK_UN);
fclose($handle);
// Unserializing and checking if the resource file contains data for this file
$data = unserialize($data);
- if (!$data) return array();
+ if (!$data) return [];
return $data;
}
@@ -167,17 +169,17 @@ class File extends AbstractBackend {
protected function putData(array $newData) {
// opening up the file, and creating an exclusive lock
- $handle = fopen($this->locksFile,'a+');
- flock($handle,LOCK_EX);
+ $handle = fopen($this->locksFile, 'a+');
+ flock($handle, LOCK_EX);
// We can only truncate and rewind once the lock is acquired.
- ftruncate($handle,0);
+ ftruncate($handle, 0);
rewind($handle);
- fwrite($handle,serialize($newData));
+ fwrite($handle, serialize($newData));
+ flock($handle, LOCK_UN);
fclose($handle);
}
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/PDO.php b/vendor/sabre/dav/lib/DAV/Locks/Backend/PDO.php
index ebaeef860..a01d9bae4 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/PDO.php
+++ b/vendor/sabre/dav/lib/DAV/Locks/Backend/PDO.php
@@ -10,36 +10,34 @@ use Sabre\DAV\Locks\LockInfo;
* This Lock Manager stores all its data in a database. You must pass a PDO
* connection object in the constructor.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
class PDO extends AbstractBackend {
/**
- * The PDO connection object
+ * The PDO tablename this backend uses.
*
- * @var pdo
+ * @var string
*/
- private $pdo;
+ public $tableName = 'locks';
/**
- * The PDO tablename this backend uses.
+ * The PDO connection object
*
- * @var string
+ * @var pdo
*/
- protected $tableName;
+ protected $pdo;
/**
* Constructor
*
* @param PDO $pdo
- * @param string $tableName
*/
- public function __construct(\PDO $pdo, $tableName = 'locks') {
+ function __construct(\PDO $pdo) {
$this->pdo = $pdo;
- $this->tableName = $tableName;
}
@@ -56,46 +54,46 @@ class PDO extends AbstractBackend {
* @param bool $returnChildLocks
* @return array
*/
- public function getLocks($uri, $returnChildLocks) {
+ function getLocks($uri, $returnChildLocks) {
// NOTE: the following 10 lines or so could be easily replaced by
// pure sql. MySQL's non-standard string concatenation prevents us
// from doing this though.
- $query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM '.$this->tableName.' WHERE ((created + timeout) > CAST(? AS UNSIGNED INTEGER)) AND ((uri = ?)';
- $params = array(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);
+ $uriParts = explode('/', $uri);
// We already covered the last part of the uri
array_pop($uriParts);
- $currentPath='';
+ $currentPath = '';
- foreach($uriParts as $part) {
+ foreach ($uriParts as $part) {
- if ($currentPath) $currentPath.='/';
- $currentPath.=$part;
+ if ($currentPath) $currentPath .= '/';
+ $currentPath .= $part;
- $query.=' OR (depth!=0 AND uri = ?)';
+ $query .= ' OR (depth!=0 AND uri = ?)';
$params[] = $currentPath;
}
if ($returnChildLocks) {
- $query.=' OR (uri LIKE ?)';
+ $query .= ' OR (uri LIKE ?)';
$params[] = $uri . '/%';
}
- $query.=')';
+ $query .= ')';
$stmt = $this->pdo->prepare($query);
$stmt->execute($params);
$result = $stmt->fetchAll();
- $lockList = array();
- foreach($result as $row) {
+ $lockList = [];
+ foreach ($result as $row) {
$lockInfo = new LockInfo();
$lockInfo->owner = $row['owner'];
@@ -120,25 +118,41 @@ class PDO extends AbstractBackend {
* @param LockInfo $lockInfo
* @return bool
*/
- public function lock($uri, LockInfo $lockInfo) {
+ function lock($uri, LockInfo $lockInfo) {
// We're making the lock timeout 30 minutes
- $lockInfo->timeout = 30*60;
+ $lockInfo->timeout = 30 * 60;
$lockInfo->created = time();
$lockInfo->uri = $uri;
- $locks = $this->getLocks($uri,false);
+ $locks = $this->getLocks($uri, false);
$exists = false;
- foreach($locks as $lock) {
+ foreach ($locks as $lock) {
if ($lock->token == $lockInfo->token) $exists = true;
}
if ($exists) {
- $stmt = $this->pdo->prepare('UPDATE '.$this->tableName.' SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?');
- $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token));
+ $stmt = $this->pdo->prepare('UPDATE ' . $this->tableName . ' SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?');
+ $stmt->execute([
+ $lockInfo->owner,
+ $lockInfo->timeout,
+ $lockInfo->scope,
+ $lockInfo->depth,
+ $uri,
+ $lockInfo->created,
+ $lockInfo->token
+ ]);
} else {
- $stmt = $this->pdo->prepare('INSERT INTO '.$this->tableName.' (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)');
- $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token));
+ $stmt = $this->pdo->prepare('INSERT INTO ' . $this->tableName . ' (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)');
+ $stmt->execute([
+ $lockInfo->owner,
+ $lockInfo->timeout,
+ $lockInfo->scope,
+ $lockInfo->depth,
+ $uri,
+ $lockInfo->created,
+ $lockInfo->token
+ ]);
}
return true;
@@ -154,14 +168,13 @@ class PDO extends AbstractBackend {
* @param LockInfo $lockInfo
* @return bool
*/
- public function unlock($uri, LockInfo $lockInfo) {
+ function unlock($uri, LockInfo $lockInfo) {
- $stmt = $this->pdo->prepare('DELETE FROM '.$this->tableName.' WHERE uri = ? AND token = ?');
- $stmt->execute(array($uri,$lockInfo->token));
+ $stmt = $this->pdo->prepare('DELETE FROM ' . $this->tableName . ' WHERE uri = ? AND token = ?');
+ $stmt->execute([$uri, $lockInfo->token]);
- return $stmt->rowCount()===1;
+ return $stmt->rowCount() === 1;
}
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Locks/LockInfo.php b/vendor/sabre/dav/lib/DAV/Locks/LockInfo.php
index 74bdb0f9c..2c8cca0fe 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Locks/LockInfo.php
+++ b/vendor/sabre/dav/lib/DAV/Locks/LockInfo.php
@@ -8,7 +8,7 @@ namespace Sabre\DAV\Locks;
* An object of the LockInfo class holds all the information relevant to a
* single lock.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -78,4 +78,3 @@ class LockInfo {
public $uri;
}
-
diff --git a/vendor/sabre/dav/lib/DAV/Locks/Plugin.php b/vendor/sabre/dav/lib/DAV/Locks/Plugin.php
new file mode 100644
index 000000000..4855b7076
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Locks/Plugin.php
@@ -0,0 +1,589 @@
+<?php
+
+namespace Sabre\DAV\Locks;
+
+use Sabre\DAV;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * Locking plugin
+ *
+ * This plugin provides locking support to a WebDAV server.
+ * The easiest way to get started, is by hooking it up as such:
+ *
+ * $lockBackend = new Sabre\DAV\Locks\Backend\File('./mylockdb');
+ * $lockPlugin = new Sabre\DAV\Locks\Plugin($lockBackend);
+ * $server->addPlugin($lockPlugin);
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Plugin extends DAV\ServerPlugin {
+
+ /**
+ * locksBackend
+ *
+ * @var Backend\Backend\Interface
+ */
+ protected $locksBackend;
+
+ /**
+ * server
+ *
+ * @var Sabre\DAV\Server
+ */
+ protected $server;
+
+ /**
+ * __construct
+ *
+ * @param Backend\BackendInterface $locksBackend
+ */
+ function __construct(Backend\BackendInterface $locksBackend) {
+
+ $this->locksBackend = $locksBackend;
+
+ }
+
+ /**
+ * Initializes the plugin
+ *
+ * This method is automatically called by the Server class after addPlugin.
+ *
+ * @param DAV\Server $server
+ * @return void
+ */
+ function initialize(DAV\Server $server) {
+
+ $this->server = $server;
+
+ $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('validateTokens', [$this, 'validateTokens']);
+ $server->on('propFind', [$this, 'propFind']);
+ $server->on('afterUnbind', [$this, 'afterUnbind']);
+
+ }
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using Sabre\DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'locks';
+
+ }
+
+ /**
+ * This method is called after most properties have been found
+ * it allows us to add in any Lock-related properties
+ *
+ * @param DAV\PropFind $propFind
+ * @param DAV\INode $node
+ * @return void
+ */
+ function propFind(DAV\PropFind $propFind, DAV\INode $node) {
+
+ $propFind->handle('{DAV:}supportedlock', function() {
+ return new DAV\Xml\Property\SupportedLock();
+ });
+ $propFind->handle('{DAV:}lockdiscovery', function() use ($propFind) {
+ return new DAV\Xml\Property\LockDiscovery(
+ $this->getLocks($propFind->getPath())
+ );
+ });
+
+ }
+
+ /**
+ * Use this method to tell the server this plugin defines additional
+ * HTTP methods.
+ *
+ * This method is passed a uri. It should only return HTTP methods that are
+ * available for the specified uri.
+ *
+ * @param string $uri
+ * @return array
+ */
+ function getHTTPMethods($uri) {
+
+ return ['LOCK','UNLOCK'];
+
+ }
+
+ /**
+ * Returns a list of features for the HTTP OPTIONS Dav: header.
+ *
+ * In this case this is only the number 2. The 2 in the Dav: header
+ * indicates the server supports locks.
+ *
+ * @return array
+ */
+ function getFeatures() {
+
+ return [2];
+
+ }
+
+ /**
+ * Returns all lock information on a particular uri
+ *
+ * This function should return an array with Sabre\DAV\Locks\LockInfo objects. If there are no locks on a file, return an empty array.
+ *
+ * Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree
+ * If the $returnChildLocks argument is set to true, we'll also traverse all the children of the object
+ * for any possible locks and return those as well.
+ *
+ * @param string $uri
+ * @param bool $returnChildLocks
+ * @return array
+ */
+ function getLocks($uri, $returnChildLocks = false) {
+
+ return $this->locksBackend->getLocks($uri, $returnChildLocks);
+
+ }
+
+ /**
+ * Locks an uri
+ *
+ * The WebDAV lock request can be operated to either create a new lock on a file, or to refresh an existing lock
+ * If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type
+ * of lock (shared or exclusive) and the owner of the lock
+ *
+ * If a lock is to be refreshed, no body should be supplied and there should be a valid If header containing the lock
+ *
+ * Additionally, a lock can be requested for a non-existent file. In these case we're obligated to create an empty file as per RFC4918:S7.3
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpLock(RequestInterface $request, ResponseInterface $response) {
+
+ $uri = $request->getPath();
+
+ $existingLocks = $this->getLocks($uri);
+
+ if ($body = $request->getBodyAsString()) {
+ // This is a new lock request
+
+ $existingLock = null;
+ // Checking if there's already non-shared locks on the uri.
+ foreach ($existingLocks as $existingLock) {
+ if ($existingLock->scope === LockInfo::EXCLUSIVE) {
+ throw new DAV\Exception\ConflictingLock($existingLock);
+ }
+ }
+
+ $lockInfo = $this->parseLockRequest($body);
+ $lockInfo->depth = $this->server->getHTTPDepth();
+ $lockInfo->uri = $uri;
+ if ($existingLock && $lockInfo->scope != LockInfo::SHARED)
+ throw new DAV\Exception\ConflictingLock($existingLock);
+
+ } else {
+
+ // Gonna check if this was a lock refresh.
+ $existingLocks = $this->getLocks($uri);
+ $conditions = $this->server->getIfConditions($request);
+ $found = null;
+
+ foreach ($existingLocks as $existingLock) {
+ foreach ($conditions as $condition) {
+ foreach ($condition['tokens'] as $token) {
+ if ($token['token'] === 'opaquelocktoken:' . $existingLock->token) {
+ $found = $existingLock;
+ break 3;
+ }
+ }
+ }
+ }
+
+ // If none were found, this request is in error.
+ if (is_null($found)) {
+ if ($existingLocks) {
+ throw new DAV\Exception\Locked(reset($existingLocks));
+ } else {
+ throw new DAV\Exception\BadRequest('An xml body is required for lock requests');
+ }
+
+ }
+
+ // This must have been a lock refresh
+ $lockInfo = $found;
+
+ // The resource could have been locked through another uri.
+ if ($uri != $lockInfo->uri) $uri = $lockInfo->uri;
+
+ }
+
+ if ($timeout = $this->getTimeoutHeader()) $lockInfo->timeout = $timeout;
+
+ $newFile = false;
+
+ // If we got this far.. we should go check if this node actually exists. If this is not the case, we need to create it first
+ try {
+ $this->server->tree->getNodeForPath($uri);
+
+ // We need to call the beforeWriteContent event for RFC3744
+ // Edit: looks like this is not used, and causing problems now.
+ //
+ // See Issue 222
+ // $this->server->emit('beforeWriteContent',array($uri));
+
+ } catch (DAV\Exception\NotFound $e) {
+
+ // It didn't, lets create it
+ $this->server->createFile($uri, fopen('php://memory', 'r'));
+ $newFile = true;
+
+ }
+
+ $this->lockNode($uri, $lockInfo);
+
+ $response->setHeader('Content-Type', 'application/xml; charset=utf-8');
+ $response->setHeader('Lock-Token', '<opaquelocktoken:' . $lockInfo->token . '>');
+ $response->setStatus($newFile ? 201 : 200);
+ $response->setBody($this->generateLockResponse($lockInfo));
+
+ // Returning false will interupt the event chain and mark this method
+ // as 'handled'.
+ return false;
+
+ }
+
+ /**
+ * Unlocks a uri
+ *
+ * This WebDAV method allows you to remove a lock from a node. The client should provide a valid locktoken through the Lock-token http header
+ * The server should return 204 (No content) on success
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ function httpUnlock(RequestInterface $request, ResponseInterface $response) {
+
+ $lockToken = $request->getHeader('Lock-Token');
+
+ // If the locktoken header is not supplied, we need to throw a bad request exception
+ if (!$lockToken) throw new DAV\Exception\BadRequest('No lock token was supplied');
+
+ $path = $request->getPath();
+ $locks = $this->getLocks($path);
+
+ // Windows sometimes forgets to include < and > in the Lock-Token
+ // header
+ if ($lockToken[0] !== '<') $lockToken = '<' . $lockToken . '>';
+
+ foreach ($locks as $lock) {
+
+ if ('<opaquelocktoken:' . $lock->token . '>' == $lockToken) {
+
+ $this->unlockNode($path, $lock);
+ $response->setHeader('Content-Length', '0');
+ $response->setStatus(204);
+
+ // Returning false will break the method chain, and mark the
+ // method as 'handled'.
+ return false;
+
+ }
+
+ }
+
+ // If we got here, it means the locktoken was invalid
+ throw new DAV\Exception\LockTokenMatchesRequestUri();
+
+ }
+
+ /**
+ * This method is called after a node is deleted.
+ *
+ * We use this event to clean up any locks that still exist on the node.
+ *
+ * @param string $path
+ * @return void
+ */
+ function afterUnbind($path) {
+
+ $locks = $this->getLocks($path, $includeChildren = true);
+ foreach ($locks as $lock) {
+ $this->unlockNode($path, $lock);
+ }
+
+ }
+
+ /**
+ * Locks a uri
+ *
+ * All the locking information is supplied in the lockInfo object. The object has a suggested timeout, but this can be safely ignored
+ * It is important that if the existing timeout is ignored, the property is overwritten, as this needs to be sent back to the client
+ *
+ * @param string $uri
+ * @param LockInfo $lockInfo
+ * @return bool
+ */
+ function lockNode($uri, LockInfo $lockInfo) {
+
+ if (!$this->server->emit('beforeLock', [$uri, $lockInfo])) return;
+ return $this->locksBackend->lock($uri, $lockInfo);
+
+ }
+
+ /**
+ * Unlocks a uri
+ *
+ * This method removes a lock from a uri. It is assumed all the supplied information is correct and verified
+ *
+ * @param string $uri
+ * @param LockInfo $lockInfo
+ * @return bool
+ */
+ function unlockNode($uri, LockInfo $lockInfo) {
+
+ if (!$this->server->emit('beforeUnlock', [$uri, $lockInfo])) return;
+ return $this->locksBackend->unlock($uri, $lockInfo);
+
+ }
+
+
+ /**
+ * Returns the contents of the HTTP Timeout header.
+ *
+ * The method formats the header into an integer.
+ *
+ * @return int
+ */
+ function getTimeoutHeader() {
+
+ $header = $this->server->httpRequest->getHeader('Timeout');
+
+ if ($header) {
+
+ if (stripos($header, 'second-') === 0) $header = (int)(substr($header, 7));
+ elseif (stripos($header, 'infinite') === 0) $header = LockInfo::TIMEOUT_INFINITE;
+ else throw new DAV\Exception\BadRequest('Invalid HTTP timeout header');
+
+ } else {
+
+ $header = 0;
+
+ }
+
+ return $header;
+
+ }
+
+ /**
+ * Generates the response for successful LOCK requests
+ *
+ * @param LockInfo $lockInfo
+ * @return string
+ */
+ protected function generateLockResponse(LockInfo $lockInfo) {
+
+ return $this->server->xml->write('{DAV:}prop', [
+ '{DAV:}lockdiscovery' =>
+ new DAV\Xml\Property\LockDiscovery([$lockInfo])
+ ]);
+ }
+
+ /**
+ * The validateTokens event is triggered before every request.
+ *
+ * It's a moment where this plugin can check all the supplied lock tokens
+ * in the If: header, and check if they are valid.
+ *
+ * In addition, it will also ensure that it checks any missing lokens that
+ * must be present in the request, and reject requests without the proper
+ * tokens.
+ *
+ * @param RequestInterface $request
+ * @param mixed $conditions
+ * @return void
+ */
+ function validateTokens(RequestInterface $request, &$conditions) {
+
+ // First we need to gather a list of locks that must be satisfied.
+ $mustLocks = [];
+ $method = $request->getMethod();
+
+ // Methods not in that list are operations that doesn't alter any
+ // resources, and we don't need to check the lock-states for.
+ switch ($method) {
+
+ case 'DELETE' :
+ $mustLocks = array_merge($mustLocks, $this->getLocks(
+ $request->getPath(),
+ true
+ ));
+ break;
+ case 'MKCOL' :
+ case 'MKCALENDAR' :
+ case 'PROPPATCH' :
+ case 'PUT' :
+ case 'PATCH' :
+ $mustLocks = array_merge($mustLocks, $this->getLocks(
+ $request->getPath(),
+ false
+ ));
+ break;
+ case 'MOVE' :
+ $mustLocks = array_merge($mustLocks, $this->getLocks(
+ $request->getPath(),
+ true
+ ));
+ $mustLocks = array_merge($mustLocks, $this->getLocks(
+ $this->server->calculateUri($request->getHeader('Destination')),
+ false
+ ));
+ break;
+ case 'COPY' :
+ $mustLocks = array_merge($mustLocks, $this->getLocks(
+ $this->server->calculateUri($request->getHeader('Destination')),
+ false
+ ));
+ break;
+ case 'LOCK' :
+ //Temporary measure.. figure out later why this is needed
+ // Here we basically ignore all incoming tokens...
+ foreach ($conditions as $ii => $condition) {
+ foreach ($condition['tokens'] as $jj => $token) {
+ $conditions[$ii]['tokens'][$jj]['validToken'] = true;
+ }
+ }
+ return;
+
+ }
+
+ // It's possible that there's identical locks, because of shared
+ // parents. We're removing the duplicates here.
+ $tmp = [];
+ foreach ($mustLocks as $lock) $tmp[$lock->token] = $lock;
+ $mustLocks = array_values($tmp);
+
+ foreach ($conditions as $kk => $condition) {
+
+ foreach ($condition['tokens'] as $ii => $token) {
+
+ // Lock tokens always start with opaquelocktoken:
+ if (substr($token['token'], 0, 16) !== 'opaquelocktoken:') {
+ continue;
+ }
+
+ $checkToken = substr($token['token'], 16);
+ // Looping through our list with locks.
+ foreach ($mustLocks as $jj => $mustLock) {
+
+ if ($mustLock->token == $checkToken) {
+
+ // We have a match!
+ // Removing this one from mustlocks
+ unset($mustLocks[$jj]);
+
+ // Marking the condition as valid.
+ $conditions[$kk]['tokens'][$ii]['validToken'] = true;
+
+ // Advancing to the next token
+ continue 2;
+
+ }
+
+ }
+
+ // If we got here, it means that there was a
+ // lock-token, but it was not in 'mustLocks'.
+ //
+ // This is an edge-case, as it could mean that token
+ // was specified with a url that was not 'required' to
+ // check. So we're doing one extra lookup to make sure
+ // we really don't know this token.
+ //
+ // This also gets triggered when the user specified a
+ // lock-token that was expired.
+ $oddLocks = $this->getLocks($condition['uri']);
+ foreach ($oddLocks as $oddLock) {
+
+ if ($oddLock->token === $checkToken) {
+
+ // We have a hit!
+ $conditions[$kk]['tokens'][$ii]['validToken'] = true;
+ continue 2;
+
+ }
+ }
+
+ // If we get all the way here, the lock-token was
+ // really unknown.
+
+
+ }
+
+ }
+
+ // If there's any locks left in the 'mustLocks' array, it means that
+ // the resource was locked and we must block it.
+ if ($mustLocks) {
+
+ throw new DAV\Exception\Locked(reset($mustLocks));
+
+ }
+
+ }
+
+ /**
+ * Parses a webdav lock xml body, and returns a new Sabre\DAV\Locks\LockInfo object
+ *
+ * @param string $body
+ * @return LockInfo
+ */
+ protected function parseLockRequest($body) {
+
+ $result = $this->server->xml->expect(
+ '{DAV:}lockinfo',
+ $body
+ );
+
+ $lockInfo = new LockInfo();
+
+ $lockInfo->owner = $result->owner;
+ $lockInfo->token = DAV\UUIDUtil::getUUID();
+ $lockInfo->scope = $result->scope;
+
+ return $lockInfo;
+
+ }
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ '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/',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/MkCol.php b/vendor/sabre/dav/lib/DAV/MkCol.php
new file mode 100644
index 000000000..c79055418
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/MkCol.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace Sabre\DAV;
+
+/**
+ * This class represents a MKCOL operation.
+ *
+ * MKCOL creates a new collection. MKCOL comes in two flavours:
+ *
+ * 1. MKCOL with no body, signifies the creation of a simple collection.
+ * 2. MKCOL with a request body. This can create a collection with a specific
+ * resource type, and a set of properties that should be set on the new
+ * collection. This can be used to create caldav calendars, carddav address
+ * books, etc.
+ *
+ * Property updates must always be atomic. This means that a property update
+ * must either completely succeed, or completely fail.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class MkCol extends PropPatch {
+
+ /**
+ * A list of resource-types in clark-notation.
+ *
+ * @var array
+ */
+ protected $resourceType;
+
+ /**
+ * Creates the MKCOL object.
+ *
+ * @param string[] $resourceType List of resourcetype values.
+ * @param array $mutations List of new properties values.
+ */
+ function __construct(array $resourceType, array $mutations) {
+
+ $this->resourceType = $resourceType;
+ parent::__construct($mutations);
+
+ }
+
+ /**
+ * Returns the resourcetype of the new collection.
+ *
+ * @return string[]
+ */
+ function getResourceType() {
+
+ return $this->resourceType;
+
+ }
+
+ /**
+ * Returns true or false if the MKCOL operation has at least the specified
+ * resource type.
+ *
+ * If the resourcetype is specified as an array, all resourcetypes are
+ * checked.
+ *
+ * @param string|string[] $resourceType
+ */
+ function hasResourceType($resourceType) {
+
+ return count(array_diff((array)$resourceType, $this->resourceType)) === 0;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Mount/Plugin.php b/vendor/sabre/dav/lib/DAV/Mount/Plugin.php
index 8376b03b0..8e06acb9f 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Mount/Plugin.php
+++ b/vendor/sabre/dav/lib/DAV/Mount/Plugin.php
@@ -3,13 +3,15 @@
namespace Sabre\DAV\Mount;
use Sabre\DAV;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
/**
* This plugin provides support for RFC4709: Mounting WebDAV servers
*
* Simply append ?mount to any collection to generate the davmount response.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -28,10 +30,10 @@ class Plugin extends DAV\ServerPlugin {
* @param DAV\Server $server
* @return void
*/
- public function initialize(DAV\Server $server) {
+ function initialize(DAV\Server $server) {
$this->server = $server;
- $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90);
+ $this->server->on('method:GET', [$this, 'httpGet'], 90);
}
@@ -39,21 +41,21 @@ class Plugin extends DAV\ServerPlugin {
* 'beforeMethod' event handles. This event handles intercepts GET requests ending
* with ?mount
*
- * @param string $method
- * @param string $uri
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
* @return bool
*/
- public function beforeMethod($method, $uri) {
+ function httpGet(RequestInterface $request, ResponseInterface $response) {
- if ($method!='GET') return;
- if ($this->server->httpRequest->getQueryString()!='mount') return;
+ $queryParams = $request->getQueryParameters();
+ if (!array_key_exists('mount', $queryParams)) return;
- $currentUri = $this->server->httpRequest->getAbsoluteUri();
+ $currentUri = $request->getAbsoluteUrl();
// Stripping off everything after the ?
- list($currentUri) = explode('?',$currentUri);
+ list($currentUri) = explode('?', $currentUri);
- $this->davMount($currentUri);
+ $this->davMount($response, $currentUri);
// Returning false to break the event chain
return false;
@@ -63,19 +65,20 @@ class Plugin extends DAV\ServerPlugin {
/**
* Generates the davmount response
*
+ * @param ResponseInterface $response
* @param string $uri absolute uri
* @return void
*/
- public function davMount($uri) {
+ function davMount(ResponseInterface $response, $uri) {
- $this->server->httpResponse->sendStatus(200);
- $this->server->httpResponse->setHeader('Content-Type','application/davmount+xml');
+ $response->setStatus(200);
+ $response->setHeader('Content-Type', 'application/davmount+xml');
ob_start();
echo '<?xml version="1.0"?>', "\n";
echo "<dm:mount xmlns:dm=\"http://purl.org/NET/webdav/mount\">\n";
echo " <dm:url>", htmlspecialchars($uri, ENT_NOQUOTES, 'UTF-8'), "</dm:url>\n";
echo "</dm:mount>";
- $this->server->httpResponse->sendBody(ob_get_clean());
+ $response->setBody(ob_get_clean());
}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Node.php b/vendor/sabre/dav/lib/DAV/Node.php
index 44e47be68..ba270e8f9 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Node.php
+++ b/vendor/sabre/dav/lib/DAV/Node.php
@@ -7,22 +7,22 @@ namespace Sabre\DAV;
*
* This is a helper class, that should aid in getting nodes setup.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
abstract class Node implements INode {
/**
- * Returns the last modification time
+ * Returns the last modification time as a unix timestamp.
*
- * In this case, it will simply return the current time
+ * If the information is not available, return null.
*
* @return int
*/
- public function getLastModified() {
+ function getLastModified() {
- return time();
+ return null;
}
@@ -32,7 +32,7 @@ abstract class Node implements INode {
* @throws Sabre\DAV\Exception\Forbidden
* @return void
*/
- public function delete() {
+ function delete() {
throw new Exception\Forbidden('Permission denied to delete node');
@@ -45,11 +45,10 @@ abstract class Node implements INode {
* @param string $name The new name
* @return void
*/
- public function setName($name) {
+ function setName($name) {
throw new Exception\Forbidden('Permission denied to rename file');
}
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/PartialUpdate/IPatchSupport.php b/vendor/sabre/dav/lib/DAV/PartialUpdate/IPatchSupport.php
index aff1d320f..97d24f9cb 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/PartialUpdate/IPatchSupport.php
+++ b/vendor/sabre/dav/lib/DAV/PartialUpdate/IPatchSupport.php
@@ -9,7 +9,7 @@ use Sabre\DAV;
* It may be used to update a file chunk, upload big a file into smaller
* chunks or resume an upload
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -45,4 +45,3 @@ interface IPatchSupport extends DAV\IFile {
function patch($data, $rangeType, $offset = null);
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/PartialUpdate/Plugin.php b/vendor/sabre/dav/lib/DAV/PartialUpdate/Plugin.php
index 2c402dc8f..24ba970b1 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/PartialUpdate/Plugin.php
+++ b/vendor/sabre/dav/lib/DAV/PartialUpdate/Plugin.php
@@ -3,6 +3,8 @@
namespace Sabre\DAV\PartialUpdate;
use Sabre\DAV;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
/**
* Partial update plugin (Patch method)
@@ -14,7 +16,7 @@ use Sabre\DAV;
* $patchPlugin = new \Sabre\DAV\PartialUpdate\Plugin();
* $server->addPlugin($patchPlugin);
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -39,10 +41,10 @@ class Plugin extends DAV\ServerPlugin {
* @param DAV\Server $server
* @return void
*/
- public function initialize(DAV\Server $server) {
+ function initialize(DAV\Server $server) {
$this->server = $server;
- $server->subscribeEvent('unknownMethod',array($this,'unknownMethod'));
+ $server->on('method:PATCH', [$this, 'httpPatch']);
}
@@ -54,57 +56,37 @@ class Plugin extends DAV\ServerPlugin {
*
* @return string
*/
- public function getPluginName() {
+ function getPluginName() {
return 'partialupdate';
}
/**
- * This method is called by the Server if the user used an HTTP method
- * the server didn't recognize.
- *
- * This plugin intercepts the PATCH methods.
- *
- * @param string $method
- * @param string $uri
- * @return bool|null
- */
- public function unknownMethod($method, $uri) {
-
- switch($method) {
-
- case 'PATCH':
- return $this->httpPatch($uri);
-
- }
-
- }
-
- /**
* Use this method to tell the server this plugin defines additional
* HTTP methods.
*
* This method is passed a uri. It should only return HTTP methods that are
* available for the specified uri.
*
- * We claim to support PATCH method (partial update) if and only if
+ * We claim to support PATCH method (partirl update) if and only if
* - the node exist
* - the node implements our partial update interface
*
* @param string $uri
* @return array
*/
- public function getHTTPMethods($uri) {
+ function getHTTPMethods($uri) {
$tree = $this->server->tree;
+
if ($tree->nodeExists($uri)) {
$node = $tree->getNodeForPath($uri);
- if ($node instanceof IFile || $node instanceof IPatchSupport) {
- return array('PATCH');
+ if ($node instanceof IPatchSupport) {
+ return ['PATCH'];
}
}
- return array();
+ return [];
}
@@ -113,9 +95,9 @@ class Plugin extends DAV\ServerPlugin {
*
* @return array
*/
- public function getFeatures() {
+ function getFeatures() {
- return array('sabredav-partialupdate');
+ return ['sabredav-partialupdate'];
}
@@ -126,25 +108,28 @@ class Plugin extends DAV\ServerPlugin {
* existing resource. If the resource does not exist yet and the first
* offset is not 0, the request fails
*
- * @param string $uri
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
* @return void
*/
- protected function httpPatch($uri) {
+ function httpPatch(RequestInterface $request, ResponseInterface $response) {
+
+ $path = $request->getPath();
// Get the node. Will throw a 404 if not found
- $node = $this->server->tree->getNodeForPath($uri);
- if (!$node instanceof IFile && !$node instanceof IPatchSupport) {
+ $node = $this->server->tree->getNodeForPath($path);
+ if (!$node instanceof IPatchSupport) {
throw new DAV\Exception\MethodNotAllowed('The target resource does not support the PATCH method.');
}
- $range = $this->getHTTPUpdateRange();
+ $range = $this->getHTTPUpdateRange($request);
if (!$range) {
throw new DAV\Exception\BadRequest('No valid "X-Update-Range" found in the headers');
}
$contentType = strtolower(
- $this->server->httpRequest->getHeader('Content-Type')
+ $request->getHeader('Content-Type')
);
if ($contentType != 'application/x-sabredav-partialupdate') {
@@ -154,7 +139,7 @@ class Plugin extends DAV\ServerPlugin {
$len = $this->server->httpRequest->getHeader('Content-Length');
if (!$len) throw new DAV\Exception\LengthRequired('A Content-Length header is required');
- switch($range[0]) {
+ switch ($range[0]) {
case self::RANGE_START :
// Calculate the end-range if it doesn't exist.
if (!$range[2]) {
@@ -163,48 +148,33 @@ class Plugin extends DAV\ServerPlugin {
if ($range[2] < $range[1]) {
throw new DAV\Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[2] . ') is lower than the start offset (' . $range[1] . ')');
}
- if($range[2] - $range[1] + 1 != $len) {
+ if ($range[2] - $range[1] + 1 != $len) {
throw new DAV\Exception\RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[1] . ') and end (' . $range[2] . ') offsets');
}
}
break;
}
- // Checking If-None-Match and related headers.
- if (!$this->server->checkPreconditions()) return;
- if (!$this->server->broadcastEvent('beforeWriteContent',array($uri, $node, null)))
+ if (!$this->server->emit('beforeWriteContent', [$path, $node, null]))
return;
$body = $this->server->httpRequest->getBody();
- if ($node instanceof IPatchSupport) {
- $etag = $node->patch($body, $range[0], isset($range[1])?$range[1]:null);
- } else {
- // The old interface
- switch($range[0]) {
- case self::RANGE_APPEND :
- throw new DAV\Exception\NotImplemented('This node does not support the append syntax. Please upgrade it to IPatchSupport');
- case self::RANGE_START :
- $etag = $node->putRange($body, $range[1]);
- break;
- case self::RANGE_END :
- throw new DAV\Exception\NotImplemented('This node does not support the end-range syntax. Please upgrade it to IPatchSupport');
- break;
- }
- }
+ $etag = $node->patch($body, $range[0], isset($range[1]) ? $range[1] : null);
- $this->server->broadcastEvent('afterWriteContent',array($uri, $node));
+ $this->server->emit('afterWriteContent', [$path, $node]);
- $this->server->httpResponse->setHeader('Content-Length','0');
- if ($etag) $this->server->httpResponse->setHeader('ETag',$etag);
- $this->server->httpResponse->sendStatus(204);
+ $response->setHeader('Content-Length', '0');
+ if ($etag) $response->setHeader('ETag', $etag);
+ $response->setStatus(204);
+ // Breaks the event chain
return false;
}
- /**
+ /**
* Returns the HTTP custom range update header
*
* This method returns null if there is no well-formed HTTP range request
@@ -216,30 +186,29 @@ class Plugin extends DAV\ServerPlugin {
* Examples:
*
* null - invalid
- * array(1) - append
- * array(2,10,15) - update bytes 10, 11, 12, 13, 14, 15
- * array(2,10,null) - update bytes 10 until the end of the patch body
- * array(3,-5) - update from 5 bytes from the end of the file.
+ * [1] - append
+ * [2,10,15] - update bytes 10, 11, 12, 13, 14, 15
+ * [2,10,null] - update bytes 10 until the end of the patch body
+ * [3,-5] - update from 5 bytes from the end of the file.
*
+ * @param RequestInterface $request
* @return array|null
*/
- public function getHTTPUpdateRange() {
+ function getHTTPUpdateRange(RequestInterface $request) {
- $range = $this->server->httpRequest->getHeader('X-Update-Range');
+ $range = $request->getHeader('X-Update-Range');
if (is_null($range)) return null;
// Matching "Range: bytes=1234-5678: both numbers are optional
- if (!preg_match('/^(append)|(?:bytes=([0-9]+)-([0-9]*))|(?:bytes=(-[0-9]+))$/i',$range,$matches)) return null;
+ if (!preg_match('/^(append)|(?:bytes=([0-9]+)-([0-9]*))|(?:bytes=(-[0-9]+))$/i', $range, $matches)) return null;
- if ($matches[1]==='append') {
- return array(self::RANGE_APPEND);
- } elseif (strlen($matches[2])>0) {
- return array(self::RANGE_START, $matches[2], $matches[3]?:null);
- } elseif ($matches[4]) {
- return array(self::RANGE_END, $matches[4]);
+ if ($matches[1] === 'append') {
+ return [self::RANGE_APPEND];
+ } elseif (strlen($matches[2]) > 0) {
+ return [self::RANGE_START, $matches[2], $matches[3] ?: null];
} else {
- return null;
+ return [self::RANGE_END, $matches[4]];
}
}
diff --git a/vendor/sabre/dav/lib/DAV/PropFind.php b/vendor/sabre/dav/lib/DAV/PropFind.php
new file mode 100644
index 000000000..8ae6b6cfd
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/PropFind.php
@@ -0,0 +1,347 @@
+<?php
+
+namespace Sabre\DAV;
+
+/**
+ * This class holds all the information about a PROPFIND request.
+ *
+ * It contains the type of PROPFIND request, which properties were requested
+ * and also the returned items.
+ */
+class PropFind {
+
+ /**
+ * A normal propfind
+ */
+ const NORMAL = 0;
+
+ /**
+ * An allprops request.
+ *
+ * While this was originally intended for instructing the server to really
+ * fetch every property, because it was used so often and it's so heavy
+ * this turned into a small list of default properties after a while.
+ *
+ * So 'all properties' now means a hardcoded list.
+ */
+ const ALLPROPS = 1;
+
+ /**
+ * A propname request. This just returns a list of properties that are
+ * defined on a node, without their values.
+ */
+ const PROPNAME = 2;
+
+ /**
+ * Creates the PROPFIND object
+ *
+ * @param string $path
+ * @param array $properties
+ * @param int $depth
+ * @param int $requestType
+ */
+ function __construct($path, array $properties, $depth = 0, $requestType = self::NORMAL) {
+
+ $this->path = $path;
+ $this->properties = $properties;
+ $this->depth = $depth;
+ $this->requestType = $requestType;
+
+ if ($requestType === self::ALLPROPS) {
+ $this->properties = [
+ '{DAV:}getlastmodified',
+ '{DAV:}getcontentlength',
+ '{DAV:}resourcetype',
+ '{DAV:}quota-used-bytes',
+ '{DAV:}quota-available-bytes',
+ '{DAV:}getetag',
+ '{DAV:}getcontenttype',
+ ];
+ }
+
+ foreach ($this->properties as $propertyName) {
+
+ // Seeding properties with 404's.
+ $this->result[$propertyName] = [404, null];
+
+ }
+ $this->itemsLeft = count($this->result);
+
+ }
+
+ /**
+ * Handles a specific property.
+ *
+ * This method checks wether the specified property was requested in this
+ * PROPFIND request, and if so, it will call the callback and use the
+ * return value for it's value.
+ *
+ * Example:
+ *
+ * $propFind->handle('{DAV:}displayname', function() {
+ * return 'hello';
+ * });
+ *
+ * Note that handle will only work the first time. If null is returned, the
+ * value is ignored.
+ *
+ * It's also possible to not pass a callback, but immediately pass a value
+ *
+ * @param string $propertyName
+ * @param mixed $valueOrCallBack
+ * @return void
+ */
+ function handle($propertyName, $valueOrCallBack) {
+
+ if ($this->itemsLeft && isset($this->result[$propertyName]) && $this->result[$propertyName][0] === 404) {
+ if (is_callable($valueOrCallBack)) {
+ $value = $valueOrCallBack();
+ } else {
+ $value = $valueOrCallBack;
+ }
+ if (!is_null($value)) {
+ $this->itemsLeft--;
+ $this->result[$propertyName] = [200, $value];
+ }
+ }
+
+ }
+
+ /**
+ * Sets the value of the property
+ *
+ * If status is not supplied, the status will default to 200 for non-null
+ * properties, and 404 for null properties.
+ *
+ * @param string $propertyName
+ * @param mixed $value
+ * @param int $status
+ * @return void
+ */
+ function set($propertyName, $value, $status = null) {
+
+ if (is_null($status)) {
+ $status = is_null($value) ? 404 : 200;
+ }
+ // If this is an ALLPROPS request and the property is
+ // unknown, add it to the result; else ignore it:
+ if (!isset($this->result[$propertyName])) {
+ if ($this->requestType === self::ALLPROPS) {
+ $this->result[$propertyName] = [$status, $value];
+ }
+ return;
+ }
+ if ($status !== 404 && $this->result[$propertyName][0] === 404) {
+ $this->itemsLeft--;
+ } elseif ($status === 404 && $this->result[$propertyName][0] !== 404) {
+ $this->itemsLeft++;
+ }
+ $this->result[$propertyName] = [$status, $value];
+
+ }
+
+ /**
+ * Returns the current value for a property.
+ *
+ * @param string $propertyName
+ * @return mixed
+ */
+ function get($propertyName) {
+
+ return isset($this->result[$propertyName]) ? $this->result[$propertyName][1] : null;
+
+ }
+
+ /**
+ * Returns the current status code for a property name.
+ *
+ * If the property does not appear in the list of requested properties,
+ * null will be returned.
+ *
+ * @param string $propertyName
+ * @return int|null
+ */
+ function getStatus($propertyName) {
+
+ return isset($this->result[$propertyName]) ? $this->result[$propertyName][0] : null;
+
+ }
+
+ /**
+ * Updates the path for this PROPFIND.
+ *
+ * @param string $path
+ * @return void
+ */
+ function setPath($path) {
+
+ $this->path = $path;
+
+ }
+
+ /**
+ * Returns the path this PROPFIND request is for.
+ *
+ * @return string
+ */
+ function getPath() {
+
+ return $this->path;
+
+ }
+
+ /**
+ * Returns the depth of this propfind request.
+ *
+ * @return int
+ */
+ function getDepth() {
+
+ return $this->depth;
+
+ }
+
+ /**
+ * Updates the depth of this propfind request.
+ *
+ * @param int $depth
+ * @return void
+ */
+ function setDepth($depth) {
+
+ $this->depth = $depth;
+
+ }
+
+ /**
+ * Returns all propertynames that have a 404 status, and thus don't have a
+ * value yet.
+ *
+ * @return array
+ */
+ function get404Properties() {
+
+ if ($this->itemsLeft === 0) {
+ return [];
+ }
+ $result = [];
+ foreach ($this->result as $propertyName => $stuff) {
+ if ($stuff[0] === 404) {
+ $result[] = $propertyName;
+ }
+ }
+ return $result;
+
+ }
+
+ /**
+ * Returns the full list of requested properties.
+ *
+ * This returns just their names, not a status or value.
+ *
+ * @return array
+ */
+ function getRequestedProperties() {
+
+ return $this->properties;
+
+ }
+
+ /**
+ * Returns true if this was an '{DAV:}allprops' request.
+ *
+ * @return bool
+ */
+ function isAllProps() {
+
+ return $this->requestType === self::ALLPROPS;
+
+ }
+
+ /**
+ * Returns a result array that's often used in multistatus responses.
+ *
+ * The array uses status codes as keys, and property names and value pairs
+ * as the value of the top array.. such as :
+ *
+ * [
+ * 200 => [ '{DAV:}displayname' => 'foo' ],
+ * ]
+ *
+ * @return array
+ */
+ function getResultForMultiStatus() {
+
+ $r = [
+ 200 => [],
+ 404 => [],
+ ];
+ foreach ($this->result as $propertyName => $info) {
+ if (!isset($r[$info[0]])) {
+ $r[$info[0]] = [$propertyName => $info[1]];
+ } else {
+ $r[$info[0]][$propertyName] = $info[1];
+ }
+ }
+ // Removing the 404's for multi-status requests.
+ if ($this->requestType === self::ALLPROPS) unset($r[404]);
+ return $r;
+
+ }
+
+ /**
+ * The path that we're fetching properties for.
+ *
+ * @var string
+ */
+ protected $path;
+
+ /**
+ * The Depth of the request.
+ *
+ * 0 means only the current item. 1 means the current item + its children.
+ * It can also be DEPTH_INFINITY if this is enabled in the server.
+ *
+ * @var int
+ */
+ protected $depth = 0;
+
+ /**
+ * The type of request. See the TYPE constants
+ */
+ protected $requestType;
+
+ /**
+ * A list of requested properties
+ *
+ * @var array
+ */
+ protected $properties = [];
+
+ /**
+ * The result of the operation.
+ *
+ * The keys in this array are property names.
+ * The values are an array with two elements: the http status code and then
+ * optionally a value.
+ *
+ * Example:
+ *
+ * [
+ * "{DAV:}owner" : [404],
+ * "{DAV:}displayname" : [200, "Admin"]
+ * ]
+ *
+ * @var array
+ */
+ protected $result = [];
+
+ /**
+ * This is used as an internal counter for the number of properties that do
+ * not yet have a value.
+ *
+ * @var int
+ */
+ protected $itemsLeft;
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/PropPatch.php b/vendor/sabre/dav/lib/DAV/PropPatch.php
new file mode 100644
index 000000000..6d599dacc
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/PropPatch.php
@@ -0,0 +1,373 @@
+<?php
+
+namespace Sabre\DAV;
+
+use UnexpectedValueException;
+
+/**
+ * This class represents a set of properties that are going to be updated.
+ *
+ * Usually this is simply a PROPPATCH request, but it can also be used for
+ * internal updates.
+ *
+ * Property updates must always be atomic. This means that a property update
+ * must either completely succeed, or completely fail.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PropPatch {
+
+ /**
+ * Properties that are being updated.
+ *
+ * This is a key-value list. If the value is null, the property is supposed
+ * to be deleted.
+ *
+ * @var array
+ */
+ protected $mutations;
+
+ /**
+ * A list of properties and the result of the update. The result is in the
+ * form of a HTTP status code.
+ *
+ * @var array
+ */
+ protected $result = [];
+
+ /**
+ * This is the list of callbacks when we're performing the actual update.
+ *
+ * @var array
+ */
+ protected $propertyUpdateCallbacks = [];
+
+ /**
+ * This property will be set to true if the operation failed.
+ *
+ * @var bool
+ */
+ protected $failed = false;
+
+ /**
+ * Constructor
+ *
+ * @param array $mutations A list of updates
+ */
+ function __construct(array $mutations) {
+
+ $this->mutations = $mutations;
+
+ }
+
+ /**
+ * Call this function if you wish to handle updating certain properties.
+ * For instance, your class may be responsible for handling updates for the
+ * {DAV:}displayname property.
+ *
+ * In that case, call this method with the first argument
+ * "{DAV:}displayname" and a second argument that's a method that does the
+ * actual updating.
+ *
+ * It's possible to specify more than one property as an array.
+ *
+ * The callback must return a boolean or an it. If the result is true, the
+ * operation was considered successful. If it's false, it's consided
+ * failed.
+ *
+ * If the result is an integer, we'll use that integer as the http status
+ * code associated with the operation.
+ *
+ * @param string|string[] $properties
+ * @param callable $callback
+ * @return void
+ */
+ function handle($properties, callable $callback) {
+
+ $usedProperties = [];
+ foreach ((array)$properties as $propertyName) {
+
+ if (array_key_exists($propertyName, $this->mutations) && !isset($this->result[$propertyName])) {
+
+ $usedProperties[] = $propertyName;
+ // HTTP Accepted
+ $this->result[$propertyName] = 202;
+ }
+
+ }
+
+ // Only registering if there's any unhandled properties.
+ if (!$usedProperties) {
+ return;
+ }
+ $this->propertyUpdateCallbacks[] = [
+ // If the original argument to this method was a string, we need
+ // to also make sure that it stays that way, so the commit function
+ // knows how to format the arguments to the callback.
+ is_string($properties) ? $properties : $usedProperties,
+ $callback
+ ];
+
+ }
+
+ /**
+ * Call this function if you wish to handle _all_ properties that haven't
+ * been handled by anything else yet. Note that you effectively claim with
+ * this that you promise to process _all_ properties that are coming in.
+ *
+ * @param callable $callback
+ * @return void
+ */
+ function handleRemaining(callable $callback) {
+
+ $properties = $this->getRemainingMutations();
+ if (!$properties) {
+ // Nothing to do, don't register callback
+ return;
+ }
+
+ foreach ($properties as $propertyName) {
+ // HTTP Accepted
+ $this->result[$propertyName] = 202;
+
+ $this->propertyUpdateCallbacks[] = [
+ $properties,
+ $callback
+ ];
+ }
+
+ }
+
+ /**
+ * Sets the result code for one or more properties.
+ *
+ * @param string|string[] $properties
+ * @param int $resultCode
+ * @return void
+ */
+ function setResultCode($properties, $resultCode) {
+
+ foreach ((array)$properties as $propertyName) {
+ $this->result[$propertyName] = $resultCode;
+ }
+
+ if ($resultCode >= 400) {
+ $this->failed = true;
+ }
+
+ }
+
+ /**
+ * Sets the result code for all properties that did not have a result yet.
+ *
+ * @param int $resultCode
+ * @return void
+ */
+ function setRemainingResultCode($resultCode) {
+
+ $this->setResultCode(
+ $this->getRemainingMutations(),
+ $resultCode
+ );
+
+ }
+
+ /**
+ * Returns the list of properties that don't have a result code yet.
+ *
+ * This method returns a list of property names, but not its values.
+ *
+ * @return string[]
+ */
+ function getRemainingMutations() {
+
+ $remaining = [];
+ foreach ($this->mutations as $propertyName => $propValue) {
+ if (!isset($this->result[$propertyName])) {
+ $remaining[] = $propertyName;
+ }
+ }
+
+ return $remaining;
+
+ }
+
+ /**
+ * Returns the list of properties that don't have a result code yet.
+ *
+ * This method returns list of properties and their values.
+ *
+ * @return array
+ */
+ function getRemainingValues() {
+
+ $remaining = [];
+ foreach ($this->mutations as $propertyName => $propValue) {
+ if (!isset($this->result[$propertyName])) {
+ $remaining[$propertyName] = $propValue;
+ }
+ }
+
+ return $remaining;
+
+ }
+
+ /**
+ * Performs the actual update, and calls all callbacks.
+ *
+ * This method returns true or false depending on if the operation was
+ * successful.
+ *
+ * @return bool
+ */
+ function commit() {
+
+ // First we validate if every property has a handler
+ foreach ($this->mutations as $propertyName => $value) {
+
+ if (!isset($this->result[$propertyName])) {
+ $this->failed = true;
+ $this->result[$propertyName] = 403;
+ }
+
+ }
+
+ foreach ($this->propertyUpdateCallbacks as $callbackInfo) {
+
+ if ($this->failed) {
+ break;
+ }
+ if (is_string($callbackInfo[0])) {
+ $this->doCallbackSingleProp($callbackInfo[0], $callbackInfo[1]);
+ } else {
+ $this->doCallbackMultiProp($callbackInfo[0], $callbackInfo[1]);
+ }
+
+ }
+
+ /**
+ * If anywhere in this operation updating a property failed, we must
+ * update all other properties accordingly.
+ */
+ if ($this->failed) {
+
+ foreach ($this->result as $propertyName => $status) {
+ if ($status === 202) {
+ // Failed dependency
+ $this->result[$propertyName] = 424;
+ }
+ }
+
+ }
+
+ return !$this->failed;
+
+ }
+
+ /**
+ * Executes a property callback with the single-property syntax.
+ *
+ * @param string $propertyName
+ * @param callable $callback
+ * @return void
+ */
+ private function doCallBackSingleProp($propertyName, callable $callback) {
+
+ $result = $callback($this->mutations[$propertyName]);
+ if (is_bool($result)) {
+ if ($result) {
+ if (is_null($this->mutations[$propertyName])) {
+ // Delete
+ $result = 204;
+ } else {
+ // Update
+ $result = 200;
+ }
+ } else {
+ // Fail
+ $result = 403;
+ }
+ }
+ if (!is_int($result)) {
+ throw new UnexpectedValueException('A callback sent to handle() did not return an int or a bool');
+ }
+ $this->result[$propertyName] = $result;
+ if ($result >= 400) {
+ $this->failed = true;
+ }
+
+ }
+
+ /**
+ * Executes a property callback with the multi-property syntax.
+ *
+ * @param array $propertyList
+ * @param callable $callback
+ * @return void
+ */
+ private function doCallBackMultiProp(array $propertyList, callable $callback) {
+
+ $argument = [];
+ foreach ($propertyList as $propertyName) {
+ $argument[$propertyName] = $this->mutations[$propertyName];
+ }
+
+ $result = $callback($argument);
+
+ if (is_array($result)) {
+ foreach ($propertyList as $propertyName) {
+ if (!isset($result[$propertyName])) {
+ $resultCode = 500;
+ } else {
+ $resultCode = $result[$propertyName];
+ }
+ if ($resultCode >= 400) {
+ $this->failed = true;
+ }
+ $this->result[$propertyName] = $resultCode;
+
+ }
+ } elseif ($result === true) {
+
+ // Success
+ foreach ($argument as $propertyName => $propertyValue) {
+ $this->result[$propertyName] = is_null($propertyValue) ? 204 : 200;
+ }
+
+ } elseif ($result === false) {
+ // Fail :(
+ $this->failed = true;
+ foreach ($propertyList as $propertyName) {
+ $this->result[$propertyName] = 403;
+ }
+ } else {
+ throw new UnexpectedValueException('A callback sent to handle() did not return an array or a bool');
+ }
+
+ }
+
+ /**
+ * Returns the result of the operation.
+ *
+ * @return array
+ */
+ function getResult() {
+
+ return $this->result;
+
+ }
+
+ /**
+ * Returns the full list of mutations
+ *
+ * @return array
+ */
+ function getMutations() {
+
+ return $this->mutations;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php b/vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php
new file mode 100644
index 000000000..31ecafdb2
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Sabre\DAV\PropertyStorage\Backend;
+
+use Sabre\DAV\PropFind;
+use Sabre\DAV\PropPatch;
+
+/**
+ * Propertystorage backend interface.
+ *
+ * Propertystorage backends must implement this interface to be used by the
+ * propertystorage plugin.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface BackendInterface {
+
+ /**
+ * Fetches properties for a path.
+ *
+ * This method received a PropFind object, which contains all the
+ * information about the properties that need to be fetched.
+ *
+ * Ususually you would just want to call 'get404Properties' on this object,
+ * as this will give you the _exact_ list of properties that need to be
+ * fetched, and haven't yet.
+ *
+ * However, you can also support the 'allprops' property here. In that
+ * case, you should check for $propFind->isAllProps().
+ *
+ * @param string $path
+ * @param PropFind $propFind
+ * @return void
+ */
+ function propFind($path, PropFind $propFind);
+
+ /**
+ * Updates properties for a path
+ *
+ * This method received a PropPatch object, which contains all the
+ * information about the update.
+ *
+ * Usually you would want to call 'handleRemaining' on this object, to get;
+ * a list of all properties that need to be stored.
+ *
+ * @param string $path
+ * @param PropPatch $propPatch
+ * @return void
+ */
+ function propPatch($path, PropPatch $propPatch);
+
+ /**
+ * This method is called after a node is deleted.
+ *
+ * This allows a backend to clean up all associated properties.
+ *
+ * The delete method will get called once for the deletion of an entire
+ * tree.
+ *
+ * @param string $path
+ * @return void
+ */
+ function delete($path);
+
+ /**
+ * This method is called after a successful MOVE
+ *
+ * This should be used to migrate all properties from one path to another.
+ * Note that entire collections may be moved, so ensure that all properties
+ * for children are also moved along.
+ *
+ * @param string $source
+ * @param string $destination
+ * @return void
+ */
+ function move($source, $destination);
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php b/vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php
new file mode 100644
index 000000000..910e4979d
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php
@@ -0,0 +1,217 @@
+<?php
+
+namespace Sabre\DAV\PropertyStorage\Backend;
+
+use Sabre\DAV\PropFind;
+use Sabre\DAV\PropPatch;
+use Sabre\DAV\Xml\Property\Complex;
+
+/**
+ * PropertyStorage PDO backend.
+ *
+ * This backend class uses a PDO-enabled database to store webdav properties.
+ * Both sqlite and mysql have been tested.
+ *
+ * The database structure can be found in the examples/sql/ directory.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PDO implements BackendInterface {
+
+ /**
+ * Value is stored as string.
+ */
+ const VT_STRING = 1;
+
+ /**
+ * Value is stored as XML fragment.
+ */
+ const VT_XML = 2;
+
+ /**
+ * Value is stored as a property object.
+ */
+ const VT_OBJECT = 3;
+
+ /**
+ * PDO
+ *
+ * @var \PDO
+ */
+ protected $pdo;
+
+ /**
+ * PDO table name we'll be using
+ *
+ * @var string
+ */
+ public $tableName = 'propertystorage';
+
+ /**
+ * Creates the PDO property storage engine
+ *
+ * @param \PDO $pdo
+ */
+ function __construct(\PDO $pdo) {
+
+ $this->pdo = $pdo;
+
+ }
+
+ /**
+ * Fetches properties for a path.
+ *
+ * This method received a PropFind object, which contains all the
+ * information about the properties that need to be fetched.
+ *
+ * Ususually you would just want to call 'get404Properties' on this object,
+ * as this will give you the _exact_ list of properties that need to be
+ * fetched, and haven't yet.
+ *
+ * However, you can also support the 'allprops' property here. In that
+ * case, you should check for $propFind->isAllProps().
+ *
+ * @param string $path
+ * @param PropFind $propFind
+ * @return void
+ */
+ function propFind($path, PropFind $propFind) {
+
+ if (!$propFind->isAllProps() && count($propFind->get404Properties()) === 0) {
+ return;
+ }
+
+ $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)) {
+ switch ($row['valuetype']) {
+ case null :
+ case self::VT_STRING :
+ $propFind->set($row['name'], $row['value']);
+ break;
+ case self::VT_XML :
+ $propFind->set($row['name'], new Complex($row['value']));
+ break;
+ case self::VT_OBJECT :
+ $propFind->set($row['name'], unserialize($row['value']));
+ break;
+ }
+ }
+
+ }
+
+ /**
+ * Updates properties for a path
+ *
+ * This method received a PropPatch object, which contains all the
+ * information about the update.
+ *
+ * Usually you would want to call 'handleRemaining' on this object, to get;
+ * a list of all properties that need to be stored.
+ *
+ * @param string $path
+ * @param PropPatch $propPatch
+ * @return void
+ */
+ function propPatch($path, PropPatch $propPatch) {
+
+ $propPatch->handleRemaining(function($properties) use ($path) {
+
+ $updateStmt = $this->pdo->prepare("REPLACE INTO " . $this->tableName . " (path, name, valuetype, value) VALUES (?, ?, ?, ?)");
+ $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;
+ } elseif ($value instanceof Complex) {
+ $valueType = self::VT_XML;
+ $value = $value->getXml();
+ } else {
+ $valueType = self::VT_OBJECT;
+ $value = serialize($value);
+ }
+ $updateStmt->execute([$path, $name, $valueType, $value]);
+ } else {
+ $deleteStmt->execute([$path, $name]);
+ }
+
+ }
+
+ return true;
+
+ });
+
+ }
+
+ /**
+ * This method is called after a node is deleted.
+ *
+ * This allows a backend to clean up all associated properties.
+ *
+ * The delete method will get called once for the deletion of an entire
+ * tree.
+ *
+ * @param string $path
+ * @return void
+ */
+ function delete($path) {
+
+ $stmt = $this->pdo->prepare("DELETE FROM " . $this->tableName . " WHERE path = ? OR path LIKE ? ESCAPE '='");
+ $childPath = strtr(
+ $path,
+ [
+ '=' => '==',
+ '%' => '=%',
+ '_' => '=_'
+ ]
+ ) . '/%';
+
+ $stmt->execute([$path, $childPath]);
+
+ }
+
+ /**
+ * This method is called after a successful MOVE
+ *
+ * This should be used to migrate all properties from one path to another.
+ * Note that entire collections may be moved, so ensure that all properties
+ * for children are also moved along.
+ *
+ * @param string $source
+ * @param string $destination
+ * @return void
+ */
+ function move($source, $destination) {
+
+ // I don't know a way to write this all in a single sql query that's
+ // also compatible across db engines, so we're letting PHP do all the
+ // updates. Much slower, but it should still be pretty fast in most
+ // cases.
+ $select = $this->pdo->prepare('SELECT id, path FROM ' . $this->tableName . ' WHERE path = ? OR path LIKE ?');
+ $select->execute([$source, $source . '/%']);
+
+ $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;
+
+ $trailingPart = substr($row['path'], strlen($source) + 1);
+ $newPath = $destination;
+ if ($trailingPart) {
+ $newPath .= '/' . $trailingPart;
+ }
+ $update->execute([$newPath, $row['id']]);
+
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/PropertyStorage/Plugin.php b/vendor/sabre/dav/lib/DAV/PropertyStorage/Plugin.php
new file mode 100644
index 000000000..0c28b7882
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/PropertyStorage/Plugin.php
@@ -0,0 +1,180 @@
+<?php
+
+namespace Sabre\DAV\PropertyStorage;
+
+use Sabre\DAV\Server;
+use Sabre\DAV\ServerPlugin;
+use Sabre\DAV\PropPatch;
+use Sabre\DAV\PropFind;
+use Sabre\DAV\INode;
+
+/**
+ * PropertyStorage Plugin.
+ *
+ * Adding this plugin to your server allows clients to store any arbitrary
+ * WebDAV property.
+ *
+ * See:
+ * http://sabre.io/dav/property-storage/
+ *
+ * for more information.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Plugin extends ServerPlugin {
+
+ /**
+ * If you only want this plugin to store properties for a limited set of
+ * paths, you can use a pathFilter to do this.
+ *
+ * The pathFilter should be a callable. The callable retrieves a path as
+ * its argument, and should return true or false wether it allows
+ * properties to be stored.
+ *
+ * @var callable
+ */
+ public $pathFilter;
+
+ /**
+ * Creates the plugin
+ *
+ * @param Backend\BackendInterface $backend
+ */
+ function __construct(Backend\BackendInterface $backend) {
+
+ $this->backend = $backend;
+
+ }
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by Sabre\DAV\Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ *
+ * @param Server $server
+ * @return void
+ */
+ function initialize(Server $server) {
+
+ $server->on('propFind', [$this, 'propFind'], 130);
+ $server->on('propPatch', [$this, 'propPatch'], 300);
+ $server->on('afterMove', [$this, 'afterMove']);
+ $server->on('afterUnbind', [$this, 'afterUnbind']);
+
+ }
+
+ /**
+ * Called during PROPFIND operations.
+ *
+ * If there's any requested properties that don't have a value yet, this
+ * plugin will look in the property storage backend to find them.
+ *
+ * @param PropFind $propFind
+ * @param INode $node
+ * @return void
+ */
+ function propFind(PropFind $propFind, INode $node) {
+
+ $path = $propFind->getPath();
+ $pathFilter = $this->pathFilter;
+ if ($pathFilter && !$pathFilter($path)) return;
+ $this->backend->propFind($propFind->getPath(), $propFind);
+
+ }
+
+ /**
+ * Called during PROPPATCH operations
+ *
+ * If there's any updated properties that haven't been stored, the
+ * propertystorage backend can handle it.
+ *
+ * @param string $path
+ * @param PropPatch $propPatch
+ * @return void
+ */
+ function propPatch($path, PropPatch $propPatch) {
+
+ $pathFilter = $this->pathFilter;
+ if ($pathFilter && !$pathFilter($path)) return;
+ $this->backend->propPatch($path, $propPatch);
+
+ }
+
+ /**
+ * Called after a node is deleted.
+ *
+ * This allows the backend to clean up any properties still in the
+ * database.
+ *
+ * @param string $path
+ * @return void
+ */
+ function afterUnbind($path) {
+
+ $pathFilter = $this->pathFilter;
+ if ($pathFilter && !$pathFilter($path)) return;
+ $this->backend->delete($path);
+
+ }
+
+ /**
+ * Called after a node is moved.
+ *
+ * This allows the backend to move all the associated properties.
+ *
+ * @param string $source
+ * @param string $destination
+ * @return void
+ */
+ function afterMove($source, $destination) {
+
+ $pathFilter = $this->pathFilter;
+ if ($pathFilter && !$pathFilter($source)) return;
+ // If the destination is filtered, afterUnbind will handle cleaning up
+ // the properties.
+ if ($pathFilter && !$pathFilter($destination)) return;
+
+ $this->backend->move($source, $destination);
+
+ }
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using \Sabre\DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'property-storage';
+
+ }
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => 'This plugin allows any arbitrary WebDAV property to be set on any resource.',
+ 'link' => 'http://sabre.io/dav/property-storage/',
+ ];
+
+ }
+}
diff --git a/vendor/sabre/dav/lib/DAV/Server.php b/vendor/sabre/dav/lib/DAV/Server.php
new file mode 100644
index 000000000..b37652812
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Server.php
@@ -0,0 +1,1627 @@
+<?php
+
+namespace Sabre\DAV;
+
+use Sabre\Event\EventEmitter;
+use Sabre\HTTP;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+use Sabre\HTTP\URLUtil;
+use Sabre\Uri;
+
+/**
+ * Main DAV server class
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Server extends EventEmitter {
+
+ /**
+ * Infinity is used for some request supporting the HTTP Depth header and indicates that the operation should traverse the entire tree
+ */
+ const DEPTH_INFINITY = -1;
+
+ /**
+ * XML namespace for all SabreDAV related elements
+ */
+ const NS_SABREDAV = 'http://sabredav.org/ns';
+
+ /**
+ * The tree object
+ *
+ * @var Sabre\DAV\Tree
+ */
+ public $tree;
+
+ /**
+ * The base uri
+ *
+ * @var string
+ */
+ protected $baseUri = null;
+
+ /**
+ * httpResponse
+ *
+ * @var Sabre\HTTP\Response
+ */
+ public $httpResponse;
+
+ /**
+ * httpRequest
+ *
+ * @var Sabre\HTTP\Request
+ */
+ public $httpRequest;
+
+ /**
+ * PHP HTTP Sapi
+ *
+ * @var Sabre\HTTP\Sapi
+ */
+ public $sapi;
+
+ /**
+ * The list of plugins
+ *
+ * @var array
+ */
+ protected $plugins = [];
+
+ /**
+ * This property will be filled with a unique string that describes the
+ * transaction. This is useful for performance measuring and logging
+ * purposes.
+ *
+ * By default it will just fill it with a lowercased HTTP method name, but
+ * plugins override this. For example, the WebDAV-Sync sync-collection
+ * report will set this to 'report-sync-collection'.
+ *
+ * @var string
+ */
+ public $transactionType;
+
+ /**
+ * This is a list of properties that are always server-controlled, and
+ * must not get modified with PROPPATCH.
+ *
+ * Plugins may add to this list.
+ *
+ * @var string[]
+ */
+ public $protectedProperties = [
+
+ // RFC4918
+ '{DAV:}getcontentlength',
+ '{DAV:}getetag',
+ '{DAV:}getlastmodified',
+ '{DAV:}lockdiscovery',
+ '{DAV:}supportedlock',
+
+ // RFC4331
+ '{DAV:}quota-available-bytes',
+ '{DAV:}quota-used-bytes',
+
+ // RFC3744
+ '{DAV:}supported-privilege-set',
+ '{DAV:}current-user-privilege-set',
+ '{DAV:}acl',
+ '{DAV:}acl-restrictions',
+ '{DAV:}inherited-acl-set',
+
+ // RFC3253
+ '{DAV:}supported-method-set',
+ '{DAV:}supported-report-set',
+
+ // RFC6578
+ '{DAV:}sync-token',
+
+ // calendarserver.org extensions
+ '{http://calendarserver.org/ns/}ctag',
+
+ // sabredav extensions
+ '{http://sabredav.org/ns}sync-token',
+
+ ];
+
+ /**
+ * This is a flag that allow or not showing file, line and code
+ * of the exception in the returned XML
+ *
+ * @var bool
+ */
+ public $debugExceptions = false;
+
+ /**
+ * This property allows you to automatically add the 'resourcetype' value
+ * based on a node's classname or interface.
+ *
+ * The preset ensures that {DAV:}collection is automatically added for nodes
+ * implementing Sabre\DAV\ICollection.
+ *
+ * @var array
+ */
+ public $resourceTypeMapping = [
+ 'Sabre\\DAV\\ICollection' => '{DAV:}collection',
+ ];
+
+ /**
+ * This property allows the usage of Depth: infinity on PROPFIND requests.
+ *
+ * By default Depth: infinity is treated as Depth: 1. Allowing Depth:
+ * infinity is potentially risky, as it allows a single client to do a full
+ * index of the webdav server, which is an easy DoS attack vector.
+ *
+ * Only turn this on if you know what you're doing.
+ *
+ * @var bool
+ */
+ public $enablePropfindDepthInfinity = false;
+
+ /**
+ * Reference to the XML utility object.
+ *
+ * @var Xml\Service
+ */
+ public $xml;
+
+ /**
+ * If this setting is turned off, SabreDAV's version number will be hidden
+ * from various places.
+ *
+ * Some people feel this is a good security measure.
+ *
+ * @var bool
+ */
+ static $exposeVersion = true;
+
+ /**
+ * Sets up the server
+ *
+ * If a Sabre\DAV\Tree object is passed as an argument, it will
+ * use it as the directory tree. If a Sabre\DAV\INode is passed, it
+ * will create a Sabre\DAV\Tree and use the node as the root.
+ *
+ * If nothing is passed, a Sabre\DAV\SimpleCollection is created in
+ * a Sabre\DAV\Tree.
+ *
+ * If an array is passed, we automatically create a root node, and use
+ * the nodes in the array as top-level children.
+ *
+ * @param Tree|INode|array|null $treeOrNode The tree object
+ */
+ function __construct($treeOrNode = null) {
+
+ if ($treeOrNode instanceof Tree) {
+ $this->tree = $treeOrNode;
+ } elseif ($treeOrNode instanceof INode) {
+ $this->tree = new Tree($treeOrNode);
+ } elseif (is_array($treeOrNode)) {
+
+ // If it's an array, a list of nodes was passed, and we need to
+ // create the root node.
+ foreach ($treeOrNode as $node) {
+ if (!($node instanceof INode)) {
+ throw new Exception('Invalid argument passed to constructor. If you\'re passing an array, all the values must implement Sabre\\DAV\\INode');
+ }
+ }
+
+ $root = new SimpleCollection('root', $treeOrNode);
+ $this->tree = new Tree($root);
+
+ } elseif (is_null($treeOrNode)) {
+ $root = new SimpleCollection('root');
+ $this->tree = new Tree($root);
+ } else {
+ throw new Exception('Invalid argument passed to constructor. Argument must either be an instance of Sabre\\DAV\\Tree, Sabre\\DAV\\INode, an array or null');
+ }
+
+ $this->xml = new Xml\Service();
+ $this->sapi = new HTTP\Sapi();
+ $this->httpResponse = new HTTP\Response();
+ $this->httpRequest = $this->sapi->getRequest();
+ $this->addPlugin(new CorePlugin());
+
+ }
+
+ /**
+ * Starts the DAV Server
+ *
+ * @return void
+ */
+ function exec() {
+
+ try {
+
+ // If nginx (pre-1.2) is used as a proxy server, and SabreDAV as an
+ // origin, we must make sure we send back HTTP/1.0 if this was
+ // requested.
+ // This is mainly because nginx doesn't support Chunked Transfer
+ // Encoding, and this forces the webserver SabreDAV is running on,
+ // to buffer entire responses to calculate Content-Length.
+ $this->httpResponse->setHTTPVersion($this->httpRequest->getHTTPVersion());
+
+ // Setting the base url
+ $this->httpRequest->setBaseUrl($this->getBaseUri());
+ $this->invokeMethod($this->httpRequest, $this->httpResponse);
+
+ } catch (\Exception $e) {
+
+ try {
+ $this->emit('exception', [$e]);
+ } catch (\Exception $ignore) {
+ }
+ $DOM = new \DOMDocument('1.0', 'utf-8');
+ $DOM->formatOutput = true;
+
+ $error = $DOM->createElementNS('DAV:', 'd:error');
+ $error->setAttribute('xmlns:s', self::NS_SABREDAV);
+ $DOM->appendChild($error);
+
+ $h = function($v) {
+
+ return htmlspecialchars($v, ENT_NOQUOTES, 'UTF-8');
+
+ };
+
+ if (self::$exposeVersion) {
+ $error->appendChild($DOM->createElement('s:sabredav-version', $h(Version::VERSION)));
+ }
+
+ $error->appendChild($DOM->createElement('s:exception', $h(get_class($e))));
+ $error->appendChild($DOM->createElement('s:message', $h($e->getMessage())));
+ if ($this->debugExceptions) {
+ $error->appendChild($DOM->createElement('s:file', $h($e->getFile())));
+ $error->appendChild($DOM->createElement('s:line', $h($e->getLine())));
+ $error->appendChild($DOM->createElement('s:code', $h($e->getCode())));
+ $error->appendChild($DOM->createElement('s:stacktrace', $h($e->getTraceAsString())));
+ }
+
+ if ($this->debugExceptions) {
+ $previous = $e;
+ while ($previous = $previous->getPrevious()) {
+ $xPrevious = $DOM->createElement('s:previous-exception');
+ $xPrevious->appendChild($DOM->createElement('s:exception', $h(get_class($previous))));
+ $xPrevious->appendChild($DOM->createElement('s:message', $h($previous->getMessage())));
+ $xPrevious->appendChild($DOM->createElement('s:file', $h($previous->getFile())));
+ $xPrevious->appendChild($DOM->createElement('s:line', $h($previous->getLine())));
+ $xPrevious->appendChild($DOM->createElement('s:code', $h($previous->getCode())));
+ $xPrevious->appendChild($DOM->createElement('s:stacktrace', $h($previous->getTraceAsString())));
+ $error->appendChild($xPrevious);
+ }
+ }
+
+
+ if ($e instanceof Exception) {
+
+ $httpCode = $e->getHTTPCode();
+ $e->serialize($this, $error);
+ $headers = $e->getHTTPHeaders($this);
+
+ } else {
+
+ $httpCode = 500;
+ $headers = [];
+
+ }
+ $headers['Content-Type'] = 'application/xml; charset=utf-8';
+
+ $this->httpResponse->setStatus($httpCode);
+ $this->httpResponse->setHeaders($headers);
+ $this->httpResponse->setBody($DOM->saveXML());
+ $this->sapi->sendResponse($this->httpResponse);
+
+ }
+
+ }
+
+ /**
+ * Sets the base server uri
+ *
+ * @param string $uri
+ * @return void
+ */
+ function setBaseUri($uri) {
+
+ // If the baseUri does not end with a slash, we must add it
+ if ($uri[strlen($uri) - 1] !== '/')
+ $uri .= '/';
+
+ $this->baseUri = $uri;
+
+ }
+
+ /**
+ * Returns the base responding uri
+ *
+ * @return string
+ */
+ function getBaseUri() {
+
+ if (is_null($this->baseUri)) $this->baseUri = $this->guessBaseUri();
+ return $this->baseUri;
+
+ }
+
+ /**
+ * This method attempts to detect the base uri.
+ * Only the PATH_INFO variable is considered.
+ *
+ * If this variable is not set, the root (/) is assumed.
+ *
+ * @return string
+ */
+ function guessBaseUri() {
+
+ $pathInfo = $this->httpRequest->getRawServerValue('PATH_INFO');
+ $uri = $this->httpRequest->getRawServerValue('REQUEST_URI');
+
+ // If PATH_INFO is found, we can assume it's accurate.
+ if (!empty($pathInfo)) {
+
+ // We need to make sure we ignore the QUERY_STRING part
+ if ($pos = strpos($uri, '?'))
+ $uri = substr($uri, 0, $pos);
+
+ // PATH_INFO is only set for urls, such as: /example.php/path
+ // in that case PATH_INFO contains '/path'.
+ // Note that REQUEST_URI is percent encoded, while PATH_INFO is
+ // not, Therefore they are only comparable if we first decode
+ // REQUEST_INFO as well.
+ $decodedUri = URLUtil::decodePath($uri);
+
+ // A simple sanity check:
+ if (substr($decodedUri, strlen($decodedUri) - strlen($pathInfo)) === $pathInfo) {
+ $baseUri = substr($decodedUri, 0, strlen($decodedUri) - strlen($pathInfo));
+ return rtrim($baseUri, '/') . '/';
+ }
+
+ throw new Exception('The REQUEST_URI (' . $uri . ') did not end with the contents of PATH_INFO (' . $pathInfo . '). This server might be misconfigured.');
+
+ }
+
+ // The last fallback is that we're just going to assume the server root.
+ return '/';
+
+ }
+
+ /**
+ * Adds a plugin to the server
+ *
+ * For more information, console the documentation of Sabre\DAV\ServerPlugin
+ *
+ * @param ServerPlugin $plugin
+ * @return void
+ */
+ function addPlugin(ServerPlugin $plugin) {
+
+ $this->plugins[$plugin->getPluginName()] = $plugin;
+ $plugin->initialize($this);
+
+ }
+
+ /**
+ * Returns an initialized plugin by it's name.
+ *
+ * This function returns null if the plugin was not found.
+ *
+ * @param string $name
+ * @return ServerPlugin
+ */
+ function getPlugin($name) {
+
+ if (isset($this->plugins[$name]))
+ return $this->plugins[$name];
+
+ return null;
+
+ }
+
+ /**
+ * Returns all plugins
+ *
+ * @return array
+ */
+ function getPlugins() {
+
+ return $this->plugins;
+
+ }
+
+ /**
+ * Handles a http request, and execute a method based on its name
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @param $sendResponse Whether to send the HTTP response to the DAV client.
+ * @return void
+ */
+ 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 (self::$exposeVersion) {
+ $response->setHeader('X-Sabre-Version', Version::VERSION);
+ }
+
+ $this->transactionType = strtolower($method);
+
+ if (!$this->checkPreconditions($request, $response)) {
+ $this->sapi->sendResponse($response);
+ return;
+ }
+
+ if ($this->emit('method:' . $method, [$request, $response])) {
+ if ($this->emit('method', [$request, $response])) {
+ $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('afterMethod:' . $method, [$request, $response])) return;
+ if (!$this->emit('afterMethod', [$request, $response])) return;
+
+ if ($response->getStatus() === null) {
+ 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
+
+ /**
+ * Returns an array with all the supported HTTP methods for a specific uri.
+ *
+ * @param string $path
+ * @return array
+ */
+ function getAllowedMethods($path) {
+
+ $methods = [
+ 'OPTIONS',
+ 'GET',
+ 'HEAD',
+ 'DELETE',
+ 'PROPFIND',
+ 'PUT',
+ 'PROPPATCH',
+ 'COPY',
+ 'MOVE',
+ 'REPORT'
+ ];
+
+ // The MKCOL is only allowed on an unmapped uri
+ try {
+ $this->tree->getNodeForPath($path);
+ } catch (Exception\NotFound $e) {
+ $methods[] = 'MKCOL';
+ }
+
+ // We're also checking if any of the plugins register any new methods
+ foreach ($this->plugins as $plugin) $methods = array_merge($methods, $plugin->getHTTPMethods($path));
+ array_unique($methods);
+
+ return $methods;
+
+ }
+
+ /**
+ * Gets the uri for the request, keeping the base uri into consideration
+ *
+ * @return string
+ */
+ function getRequestUri() {
+
+ return $this->calculateUri($this->httpRequest->getUrl());
+
+ }
+
+ /**
+ * Turns a URI such as the REQUEST_URI into a local path.
+ *
+ * This method:
+ * * strips off the base path
+ * * normalizes the path
+ * * 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, '://')) {
+
+ $uri = parse_url($uri, PHP_URL_PATH);
+
+ }
+
+ $uri = Uri\normalize(str_replace('//', '/', $uri));
+ $baseUri = Uri\normalize($this->getBaseUri());
+
+ if (strpos($uri, $baseUri) === 0) {
+
+ return trim(URLUtil::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) {
+
+ return '';
+
+ } else {
+
+ throw new Exception\Forbidden('Requested uri (' . $uri . ') is out of base uri (' . $this->getBaseUri() . ')');
+
+ }
+
+ }
+
+ /**
+ * Returns the HTTP depth header
+ *
+ * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre\DAV\Server::DEPTH_INFINITY object
+ * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existent
+ *
+ * @param mixed $default
+ * @return int
+ */
+ function getHTTPDepth($default = self::DEPTH_INFINITY) {
+
+ // If its not set, we'll grab the default
+ $depth = $this->httpRequest->getHeader('Depth');
+
+ if (is_null($depth)) return $default;
+
+ if ($depth == 'infinity') return self::DEPTH_INFINITY;
+
+
+ // If its an unknown value. we'll grab the default
+ if (!ctype_digit($depth)) return $default;
+
+ return (int)$depth;
+
+ }
+
+ /**
+ * Returns the HTTP range header
+ *
+ * This method returns null if there is no well-formed HTTP range request
+ * header or array($start, $end).
+ *
+ * The first number is the offset of the first byte in the range.
+ * The second number is the offset of the last byte in the range.
+ *
+ * If the second offset is null, it should be treated as the offset of the last byte of the entity
+ * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity
+ *
+ * @return array|null
+ */
+ function getHTTPRange() {
+
+ $range = $this->httpRequest->getHeader('range');
+ if (is_null($range)) return null;
+
+ // Matching "Range: bytes=1234-5678: both numbers are optional
+
+ if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i', $range, $matches)) return null;
+
+ if ($matches[1] === '' && $matches[2] === '') return null;
+
+ return [
+ $matches[1] !== '' ? $matches[1] : null,
+ $matches[2] !== '' ? $matches[2] : null,
+ ];
+
+ }
+
+ /**
+ * Returns the HTTP Prefer header information.
+ *
+ * The prefer header is defined in:
+ * http://tools.ietf.org/html/draft-snell-http-prefer-14
+ *
+ * This method will return an array with options.
+ *
+ * Currently, the following options may be returned:
+ * [
+ * 'return-asynch' => true,
+ * 'return-minimal' => true,
+ * 'return-representation' => true,
+ * 'wait' => 30,
+ * 'strict' => true,
+ * 'lenient' => true,
+ * ]
+ *
+ * This method also supports the Brief header, and will also return
+ * 'return-minimal' if the brief header was set to 't'.
+ *
+ * For the boolean options, false will be returned if the headers are not
+ * specified. For the integer options it will be 'null'.
+ *
+ * @return array
+ */
+ function getHTTPPrefer() {
+
+ $result = [
+ // can be true or false
+ 'respond-async' => false,
+ // Could be set to 'representation' or 'minimal'.
+ 'return' => null,
+ // Used as a timeout, is usually a number.
+ 'wait' => null,
+ // can be 'strict' or 'lenient'.
+ 'handling' => false,
+ ];
+
+ if ($prefer = $this->httpRequest->getHeader('Prefer')) {
+
+ $result = array_merge(
+ $result,
+ \Sabre\HTTP\parsePrefer($prefer)
+ );
+
+ } elseif ($this->httpRequest->getHeader('Brief') == 't') {
+ $result['return'] = 'minimal';
+ }
+
+ return $result;
+
+ }
+
+
+ /**
+ * Returns information about Copy and Move requests
+ *
+ * This function is created to help getting information about the source and the destination for the
+ * WebDAV MOVE and COPY HTTP request. It also validates a lot of information and throws proper exceptions
+ *
+ * The returned value is an array with the following keys:
+ * * destination - Destination path
+ * * destinationExists - Whether or not the destination is an existing url (and should therefore be overwritten)
+ *
+ * @param RequestInterface $request
+ * @throws Exception\BadRequest upon missing or broken request headers
+ * @throws Exception\UnsupportedMediaType when trying to copy into a
+ * non-collection.
+ * @throws Exception\PreconditionFailed If overwrite is set to false, but
+ * the destination exists.
+ * @throws Exception\Forbidden when source and destination paths are
+ * identical.
+ * @throws Exception\Conflict When trying to copy a node into its own
+ * subtree.
+ * @return array
+ */
+ function getCopyAndMoveInfo(RequestInterface $request) {
+
+ // Collecting the relevant HTTP headers
+ if (!$request->getHeader('Destination')) throw new Exception\BadRequest('The destination header was not supplied');
+ $destination = $this->calculateUri($request->getHeader('Destination'));
+ $overwrite = $request->getHeader('Overwrite');
+ if (!$overwrite) $overwrite = 'T';
+ if (strtoupper($overwrite) == 'T') $overwrite = true;
+ elseif (strtoupper($overwrite) == 'F') $overwrite = false;
+ // We need to throw a bad request exception, if the header was invalid
+ else throw new Exception\BadRequest('The HTTP Overwrite header should be either T or F');
+
+ list($destinationDir) = URLUtil::splitPath($destination);
+
+ try {
+ $destinationParent = $this->tree->getNodeForPath($destinationDir);
+ if (!($destinationParent instanceof ICollection)) throw new Exception\UnsupportedMediaType('The destination node is not a collection');
+ } catch (Exception\NotFound $e) {
+
+ // If the destination parent node is not found, we throw a 409
+ throw new Exception\Conflict('The destination node is not found');
+ }
+
+ try {
+
+ $destinationNode = $this->tree->getNodeForPath($destination);
+
+ // If this succeeded, it means the destination already exists
+ // we'll need to throw precondition failed in case overwrite is false
+ if (!$overwrite) throw new Exception\PreconditionFailed('The destination node already exists, and the overwrite header is set to false', 'Overwrite');
+
+ } catch (Exception\NotFound $e) {
+
+ // Destination didn't exist, we're all good
+ $destinationNode = false;
+
+ }
+
+ $requestPath = $request->getPath();
+ if ($destination === $requestPath) {
+ throw new Exception\Forbidden('Source and destination uri are identical.');
+ }
+ if (substr($destination, 0, strlen($requestPath) + 1) === $requestPath . '/') {
+ throw new Exception\Conflict('The destination may not be part of the same subtree as the source path.');
+ }
+
+ // These are the three relevant properties we need to return
+ return [
+ 'destination' => $destination,
+ 'destinationExists' => !!$destinationNode,
+ 'destinationNode' => $destinationNode,
+ ];
+
+ }
+
+ /**
+ * Returns a list of properties for a path
+ *
+ * This is a simplified version getPropertiesForPath. If you aren't
+ * interested in status codes, but you just want to have a flat list of
+ * properties, use this method.
+ *
+ * Please note though that any problems related to retrieving properties,
+ * such as permission issues will just result in an empty array being
+ * returned.
+ *
+ * @param string $path
+ * @param array $propertyNames
+ */
+ function getProperties($path, $propertyNames) {
+
+ $result = $this->getPropertiesForPath($path, $propertyNames, 0);
+ if (isset($result[0][200])) {
+ return $result[0][200];
+ } else {
+ return [];
+ }
+
+ }
+
+ /**
+ * A kid-friendly way to fetch properties for a node's children.
+ *
+ * The returned array will be indexed by the path of the of child node.
+ * Only properties that are actually found will be returned.
+ *
+ * The parent node will not be returned.
+ *
+ * @param string $path
+ * @param array $propertyNames
+ * @return array
+ */
+ function getPropertiesForChildren($path, $propertyNames) {
+
+ $result = [];
+ foreach ($this->getPropertiesForPath($path, $propertyNames, 1) as $k => $row) {
+
+ // Skipping the parent path
+ if ($k === 0) continue;
+
+ $result[$row['href']] = $row[200];
+
+ }
+ return $result;
+
+ }
+
+ /**
+ * Returns a list of HTTP headers for a particular resource
+ *
+ * The generated http headers are based on properties provided by the
+ * resource. The method basically provides a simple mapping between
+ * DAV property and HTTP header.
+ *
+ * The headers are intended to be used for HEAD and GET requests.
+ *
+ * @param string $path
+ * @return array
+ */
+ function getHTTPHeaders($path) {
+
+ $propertyMap = [
+ '{DAV:}getcontenttype' => 'Content-Type',
+ '{DAV:}getcontentlength' => 'Content-Length',
+ '{DAV:}getlastmodified' => 'Last-Modified',
+ '{DAV:}getetag' => 'ETag',
+ ];
+
+ $properties = $this->getProperties($path, array_keys($propertyMap));
+
+ $headers = [];
+ foreach ($propertyMap as $property => $header) {
+ if (!isset($properties[$property])) continue;
+
+ if (is_scalar($properties[$property])) {
+ $headers[$header] = $properties[$property];
+
+ // GetLastModified gets special cased
+ } elseif ($properties[$property] instanceof Xml\Property\GetLastModified) {
+ $headers[$header] = HTTP\Util::toHTTPDate($properties[$property]->getTime());
+ }
+
+ }
+
+ return $headers;
+
+ }
+
+ /**
+ * Small helper to support PROPFIND with DEPTH_INFINITY.
+ *
+ * @param array[] $propFindRequests
+ * @param PropFind $propFind
+ * @return void
+ */
+ private function addPathNodesRecursively(&$propFindRequests, PropFind $propFind) {
+
+ $newDepth = $propFind->getDepth();
+ $path = $propFind->getPath();
+
+ if ($newDepth !== self::DEPTH_INFINITY) {
+ $newDepth--;
+ }
+
+ foreach ($this->tree->getChildren($path) as $childNode) {
+ $subPropFind = clone $propFind;
+ $subPropFind->setDepth($newDepth);
+ if ($path !== '') {
+ $subPath = $path . '/' . $childNode->getName();
+ } else {
+ $subPath = $childNode->getName();
+ }
+ $subPropFind->setPath($subPath);
+
+ $propFindRequests[] = [
+ $subPropFind,
+ $childNode
+ ];
+
+ if (($newDepth === self::DEPTH_INFINITY || $newDepth >= 1) && $childNode instanceof ICollection) {
+ $this->addPathNodesRecursively($propFindRequests, $subPropFind);
+ }
+
+ }
+ }
+
+ /**
+ * Returns a list of properties for a given path
+ *
+ * The path that should be supplied should have the baseUrl stripped out
+ * The list of properties should be supplied in Clark notation. If the list is empty
+ * 'allprops' is assumed.
+ *
+ * If a depth of 1 is requested child elements will also be returned.
+ *
+ * @param string $path
+ * @param array $propertyNames
+ * @param int $depth
+ * @return array
+ */
+ function getPropertiesForPath($path, $propertyNames = [], $depth = 0) {
+
+ // The only two options for the depth of a propfind is 0 or 1 - as long as depth infinity is not enabled
+ if (!$this->enablePropfindDepthInfinity && $depth != 0) $depth = 1;
+
+ $path = trim($path, '/');
+
+ $propFindType = $propertyNames ? PropFind::NORMAL : PropFind::ALLPROPS;
+ $propFind = new PropFind($path, (array)$propertyNames, $depth, $propFindType);
+
+ $parentNode = $this->tree->getNodeForPath($path);
+
+ $propFindRequests = [[
+ $propFind,
+ $parentNode
+ ]];
+
+ if (($depth > 0 || $depth === self::DEPTH_INFINITY) && $parentNode instanceof ICollection) {
+ $this->addPathNodesRecursively($propFindRequests, $propFind);
+ }
+
+ $returnPropertyList = [];
+
+ foreach ($propFindRequests as $propFindRequest) {
+
+ list($propFind, $node) = $propFindRequest;
+ $r = $this->getPropertiesByNode($propFind, $node);
+ if ($r) {
+ $result = $propFind->getResultForMultiStatus();
+ $result['href'] = $propFind->getPath();
+
+ // WebDAV recommends adding a slash to the path, if the path is
+ // a collection.
+ // Furthermore, iCal also demands this to be the case for
+ // principals. This is non-standard, but we support it.
+ $resourceType = $this->getResourceTypeForNode($node);
+ if (in_array('{DAV:}collection', $resourceType) || in_array('{DAV:}principal', $resourceType)) {
+ $result['href'] .= '/';
+ }
+ $returnPropertyList[] = $result;
+ }
+
+ }
+
+ return $returnPropertyList;
+
+ }
+
+ /**
+ * Returns a list of properties for a list of paths.
+ *
+ * The path that should be supplied should have the baseUrl stripped out
+ * The list of properties should be supplied in Clark notation. If the list is empty
+ * 'allprops' is assumed.
+ *
+ * The result is returned as an array, with paths for it's keys.
+ * The result may be returned out of order.
+ *
+ * @param array $paths
+ * @param array $propertyNames
+ * @return array
+ */
+ function getPropertiesForMultiplePaths(array $paths, array $propertyNames = []) {
+
+ $result = [
+ ];
+
+ $nodes = $this->tree->getMultipleNodes($paths);
+
+ foreach ($nodes as $path => $node) {
+
+ $propFind = new PropFind($path, $propertyNames);
+ $r = $this->getPropertiesByNode($propFind, $node);
+ if ($r) {
+ $result[$path] = $propFind->getResultForMultiStatus();
+ $result[$path]['href'] = $path;
+
+ $resourceType = $this->getResourceTypeForNode($node);
+ if (in_array('{DAV:}collection', $resourceType) || in_array('{DAV:}principal', $resourceType)) {
+ $result[$path]['href'] .= '/';
+ }
+ }
+
+ }
+
+ return $result;
+
+ }
+
+
+ /**
+ * Determines all properties for a node.
+ *
+ * This method tries to grab all properties for a node. This method is used
+ * internally getPropertiesForPath and a few others.
+ *
+ * It could be useful to call this, if you already have an instance of your
+ * target node and simply want to run through the system to get a correct
+ * list of properties.
+ *
+ * @param PropFind $propFind
+ * @param INode $node
+ * @return bool
+ */
+ function getPropertiesByNode(PropFind $propFind, INode $node) {
+
+ return $this->emit('propFind', [$propFind, $node]);
+
+ }
+
+ /**
+ * This method is invoked by sub-systems creating a new file.
+ *
+ * Currently this is done by HTTP PUT and HTTP LOCK (in the Locks_Plugin).
+ * It was important to get this done through a centralized function,
+ * allowing plugins to intercept this using the beforeCreateFile event.
+ *
+ * This method will return true if the file was actually created
+ *
+ * @param string $uri
+ * @param resource $data
+ * @param string $etag
+ * @return bool
+ */
+ function createFile($uri, $data, &$etag = null) {
+
+ list($dir, $name) = URLUtil::splitPath($uri);
+
+ if (!$this->emit('beforeBind', [$uri])) return false;
+
+ $parent = $this->tree->getNodeForPath($dir);
+ if (!$parent instanceof ICollection) {
+ throw new Exception\Conflict('Files can only be created as children of collections');
+ }
+
+ // It is possible for an event handler to modify the content of the
+ // body, before it gets written. If this is the case, $modified
+ // should be set to true.
+ //
+ // If $modified is true, we must not send back an ETag.
+ $modified = false;
+ if (!$this->emit('beforeCreateFile', [$uri, &$data, $parent, &$modified])) return false;
+
+ $etag = $parent->createFile($name, $data);
+
+ if ($modified) $etag = null;
+
+ $this->tree->markDirty($dir . '/' . $name);
+
+ $this->emit('afterBind', [$uri]);
+ $this->emit('afterCreateFile', [$uri, $parent]);
+
+ return true;
+ }
+
+ /**
+ * This method is invoked by sub-systems updating a file.
+ *
+ * This method will return true if the file was actually updated
+ *
+ * @param string $uri
+ * @param resource $data
+ * @param string $etag
+ * @return bool
+ */
+ function updateFile($uri, $data, &$etag = null) {
+
+ $node = $this->tree->getNodeForPath($uri);
+
+ // It is possible for an event handler to modify the content of the
+ // body, before it gets written. If this is the case, $modified
+ // should be set to true.
+ //
+ // If $modified is true, we must not send back an ETag.
+ $modified = false;
+ if (!$this->emit('beforeWriteContent', [$uri, $node, &$data, &$modified])) return false;
+
+ $etag = $node->put($data);
+ if ($modified) $etag = null;
+ $this->emit('afterWriteContent', [$uri, $node]);
+
+ return true;
+ }
+
+
+
+ /**
+ * This method is invoked by sub-systems creating a new directory.
+ *
+ * @param string $uri
+ * @return void
+ */
+ function createDirectory($uri) {
+
+ $this->createCollection($uri, new MkCol(['{DAV:}collection'], []));
+
+ }
+
+ /**
+ * Use this method to create a new collection
+ *
+ * @param string $uri The new uri
+ * @param MkCol $mkCol
+ * @return array|null
+ */
+ function createCollection($uri, MkCol $mkCol) {
+
+ list($parentUri, $newName) = URLUtil::splitPath($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
+ if (!$parent instanceof ICollection) {
+ throw new Exception\Conflict('Parent node is not a collection');
+ }
+
+ // Making sure the child does not already exist
+ try {
+ $parent->getChild($newName);
+
+ // If we got here.. it means there's already a node on that url, and we need to throw a 405
+ throw new Exception\MethodNotAllowed('The resource you tried to create already exists');
+
+ } catch (Exception\NotFound $e) {
+ // NotFound is the expected behavior.
+ }
+
+
+ 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.
+ */
+ if (count($mkCol->getResourceType()) > 1) {
+ throw new Exception\InvalidResourceType('The {DAV:}resourcetype you specified is not supported here.');
+ }
+
+ $parent->createDirectory($newName);
+
+ }
+
+ // If there are any properties that have not been handled/stored,
+ // we ask the 'propPatch' event to handle them. This will allow for
+ // example the propertyStorage system to store properties upon MKCOL.
+ if ($mkCol->getRemainingMutations()) {
+ $this->emit('propPatch', [$uri, $mkCol]);
+ }
+ $success = $mkCol->commit();
+
+ if (!$success) {
+ $result = $mkCol->getResult();
+ // generateMkCol needs the href key to exist.
+ $result['href'] = $uri;
+ return $result;
+ }
+
+ $this->tree->markDirty($parentUri);
+ $this->emit('afterBind', [$uri]);
+
+ }
+
+ /**
+ * This method updates a resource's properties
+ *
+ * The properties array must be a list of properties. Array-keys are
+ * property names in clarknotation, array-values are it's values.
+ * If a property must be deleted, the value should be null.
+ *
+ * Note that this request should either completely succeed, or
+ * completely fail.
+ *
+ * The response is an array with properties for keys, and http status codes
+ * as their values.
+ *
+ * @param string $path
+ * @param array $properties
+ * @return array
+ */
+ function updateProperties($path, array $properties) {
+
+ $propPatch = new PropPatch($properties);
+ $this->emit('propPatch', [$path, $propPatch]);
+ $propPatch->commit();
+
+ return $propPatch->getResult();
+
+ }
+
+ /**
+ * This method checks the main HTTP preconditions.
+ *
+ * Currently these are:
+ * * If-Match
+ * * If-None-Match
+ * * If-Modified-Since
+ * * If-Unmodified-Since
+ *
+ * The method will return true if all preconditions are met
+ * The method will return false, or throw an exception if preconditions
+ * failed. If false is returned the operation should be aborted, and
+ * the appropriate HTTP response headers are already set.
+ *
+ * Normally this method will throw 412 Precondition Failed for failures
+ * related to If-None-Match, If-Match and If-Unmodified Since. It will
+ * set the status to 304 Not Modified for If-Modified_since.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function checkPreconditions(RequestInterface $request, ResponseInterface $response) {
+
+ $path = $request->getPath();
+ $node = null;
+ $lastMod = null;
+ $etag = null;
+
+ if ($ifMatch = $request->getHeader('If-Match')) {
+
+ // If-Match contains an entity tag. Only if the entity-tag
+ // matches we are allowed to make the request succeed.
+ // If the entity-tag is '*' we are only allowed to make the
+ // request succeed if a resource exists at that url.
+ try {
+ $node = $this->tree->getNodeForPath($path);
+ } catch (Exception\NotFound $e) {
+ throw new Exception\PreconditionFailed('An If-Match header was specified and the resource did not exist', 'If-Match');
+ }
+
+ // Only need to check entity tags if they are not *
+ if ($ifMatch !== '*') {
+
+ // There can be multiple ETags
+ $ifMatch = explode(',', $ifMatch);
+ $haveMatch = false;
+ foreach ($ifMatch as $ifMatchItem) {
+
+ // Stripping any extra spaces
+ $ifMatchItem = trim($ifMatchItem, ' ');
+
+ $etag = $node instanceof IFile ? $node->getETag() : null;
+ if ($etag === $ifMatchItem) {
+ $haveMatch = true;
+ } else {
+ // Evolution has a bug where it sometimes prepends the "
+ // with a \. This is our workaround.
+ if (str_replace('\\"', '"', $ifMatchItem) === $etag) {
+ $haveMatch = true;
+ }
+ }
+
+ }
+ if (!$haveMatch) {
+ if ($etag) $response->setHeader('ETag', $etag);
+ throw new Exception\PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.', 'If-Match');
+ }
+ }
+ }
+
+ if ($ifNoneMatch = $request->getHeader('If-None-Match')) {
+
+ // The If-None-Match header contains an ETag.
+ // Only if the ETag does not match the current ETag, the request will succeed
+ // The header can also contain *, in which case the request
+ // will only succeed if the entity does not exist at all.
+ $nodeExists = true;
+ if (!$node) {
+ try {
+ $node = $this->tree->getNodeForPath($path);
+ } catch (Exception\NotFound $e) {
+ $nodeExists = false;
+ }
+ }
+ if ($nodeExists) {
+ $haveMatch = false;
+ if ($ifNoneMatch === '*') $haveMatch = true;
+ else {
+
+ // There might be multiple ETags
+ $ifNoneMatch = explode(',', $ifNoneMatch);
+ $etag = $node instanceof IFile ? $node->getETag() : null;
+
+ foreach ($ifNoneMatch as $ifNoneMatchItem) {
+
+ // Stripping any extra spaces
+ $ifNoneMatchItem = trim($ifNoneMatchItem, ' ');
+
+ if ($etag === $ifNoneMatchItem) $haveMatch = true;
+
+ }
+
+ }
+
+ if ($haveMatch) {
+ if ($etag) $response->setHeader('ETag', $etag);
+ if ($request->getMethod() === 'GET') {
+ $response->setStatus(304);
+ return false;
+ } else {
+ throw new Exception\PreconditionFailed('An If-None-Match header was specified, but the ETag matched (or * was specified).', 'If-None-Match');
+ }
+ }
+ }
+
+ }
+
+ if (!$ifNoneMatch && ($ifModifiedSince = $request->getHeader('If-Modified-Since'))) {
+
+ // The If-Modified-Since header contains a date. We
+ // will only return the entity if it has been changed since
+ // that date. If it hasn't been changed, we return a 304
+ // header
+ // Note that this header only has to be checked if there was no If-None-Match header
+ // as per the HTTP spec.
+ $date = HTTP\Util::parseHTTPDate($ifModifiedSince);
+
+ if ($date) {
+ if (is_null($node)) {
+ $node = $this->tree->getNodeForPath($path);
+ }
+ $lastMod = $node->getLastModified();
+ if ($lastMod) {
+ $lastMod = new \DateTime('@' . $lastMod);
+ if ($lastMod <= $date) {
+ $response->setStatus(304);
+ $response->setHeader('Last-Modified', HTTP\Util::toHTTPDate($lastMod));
+ return false;
+ }
+ }
+ }
+ }
+
+ if ($ifUnmodifiedSince = $request->getHeader('If-Unmodified-Since')) {
+
+ // The If-Unmodified-Since will allow allow the request if the
+ // entity has not changed since the specified date.
+ $date = HTTP\Util::parseHTTPDate($ifUnmodifiedSince);
+
+ // We must only check the date if it's valid
+ if ($date) {
+ if (is_null($node)) {
+ $node = $this->tree->getNodeForPath($path);
+ }
+ $lastMod = $node->getLastModified();
+ if ($lastMod) {
+ $lastMod = new \DateTime('@' . $lastMod);
+ if ($lastMod > $date) {
+ throw new Exception\PreconditionFailed('An If-Unmodified-Since header was specified, but the entity has been changed since the specified date.', 'If-Unmodified-Since');
+ }
+ }
+ }
+
+ }
+
+ // Now the hardest, the If: header. The If: header can contain multiple
+ // urls, ETags and so-called 'state tokens'.
+ //
+ // Examples of state tokens include lock-tokens (as defined in rfc4918)
+ // and sync-tokens (as defined in rfc6578).
+ //
+ // The only proper way to deal with these, is to emit events, that a
+ // Sync and Lock plugin can pick up.
+ $ifConditions = $this->getIfConditions($request);
+
+ foreach ($ifConditions as $kk => $ifCondition) {
+ foreach ($ifCondition['tokens'] as $ii => $token) {
+ $ifConditions[$kk]['tokens'][$ii]['validToken'] = false;
+ }
+ }
+
+ // Plugins are responsible for validating all the tokens.
+ // If a plugin deemed a token 'valid', it will set 'validToken' to
+ // true.
+ $this->emit('validateTokens', [ $request, &$ifConditions ]);
+
+ // Now we're going to analyze the result.
+
+ // Every ifCondition needs to validate to true, so we exit as soon as
+ // we have an invalid condition.
+ foreach ($ifConditions as $ifCondition) {
+
+ $uri = $ifCondition['uri'];
+ $tokens = $ifCondition['tokens'];
+
+ // We only need 1 valid token for the condition to succeed.
+ foreach ($tokens as $token) {
+
+ $tokenValid = $token['validToken'] || !$token['token'];
+
+ $etagValid = false;
+ if (!$token['etag']) {
+ $etagValid = true;
+ }
+ // Checking the ETag, only if the token was already deamed
+ // valid and there is one.
+ if ($token['etag'] && $tokenValid) {
+
+ // The token was valid, and there was an ETag. We must
+ // grab the current ETag and check it.
+ $node = $this->tree->getNodeForPath($uri);
+ $etagValid = $node instanceof IFile && $node->getETag() == $token['etag'];
+
+ }
+
+
+ if (($tokenValid && $etagValid) ^ $token['negate']) {
+ // Both were valid, so we can go to the next condition.
+ continue 2;
+ }
+
+
+ }
+
+ // If we ended here, it means there was no valid ETag + token
+ // combination found for the current condition. This means we fail!
+ throw new Exception\PreconditionFailed('Failed to find a valid token/etag combination for ' . $uri, 'If');
+
+ }
+
+ return true;
+
+ }
+
+ /**
+ * This method is created to extract information from the WebDAV HTTP 'If:' header
+ *
+ * The If header can be quite complex, and has a bunch of features. We're using a regex to extract all relevant information
+ * The function will return an array, containing structs with the following keys
+ *
+ * * uri - the uri the condition applies to.
+ * * tokens - The lock token. another 2 dimensional array containing 3 elements
+ *
+ * Example 1:
+ *
+ * If: (<opaquelocktoken:181d4fae-7d8c-11d0-a765-00a0c91e6bf2>)
+ *
+ * Would result in:
+ *
+ * [
+ * [
+ * 'uri' => '/request/uri',
+ * 'tokens' => [
+ * [
+ * [
+ * 'negate' => false,
+ * 'token' => 'opaquelocktoken:181d4fae-7d8c-11d0-a765-00a0c91e6bf2',
+ * 'etag' => ""
+ * ]
+ * ]
+ * ],
+ * ]
+ * ]
+ *
+ * Example 2:
+ *
+ * If: </path/> (Not <opaquelocktoken:181d4fae-7d8c-11d0-a765-00a0c91e6bf2> ["Im An ETag"]) (["Another ETag"]) </path2/> (Not ["Path2 ETag"])
+ *
+ * Would result in:
+ *
+ * [
+ * [
+ * 'uri' => 'path',
+ * 'tokens' => [
+ * [
+ * [
+ * 'negate' => true,
+ * 'token' => 'opaquelocktoken:181d4fae-7d8c-11d0-a765-00a0c91e6bf2',
+ * 'etag' => '"Im An ETag"'
+ * ],
+ * [
+ * 'negate' => false,
+ * 'token' => '',
+ * 'etag' => '"Another ETag"'
+ * ]
+ * ]
+ * ],
+ * ],
+ * [
+ * 'uri' => 'path2',
+ * 'tokens' => [
+ * [
+ * [
+ * 'negate' => true,
+ * 'token' => '',
+ * 'etag' => '"Path2 ETag"'
+ * ]
+ * ]
+ * ],
+ * ],
+ * ]
+ *
+ * @param RequestInterface $request
+ * @return array
+ */
+ function getIfConditions(RequestInterface $request) {
+
+ $header = $request->getHeader('If');
+ if (!$header) return [];
+
+ $matches = [];
+
+ $regex = '/(?:\<(?P<uri>.*?)\>\s)?\((?P<not>Not\s)?(?:\<(?P<token>[^\>]*)\>)?(?:\s?)(?:\[(?P<etag>[^\]]*)\])?\)/im';
+ preg_match_all($regex, $header, $matches, PREG_SET_ORDER);
+
+ $conditions = [];
+
+ foreach ($matches as $match) {
+
+ // If there was no uri specified in this match, and there were
+ // already conditions parsed, we add the condition to the list of
+ // conditions for the previous uri.
+ if (!$match['uri'] && count($conditions)) {
+ $conditions[count($conditions) - 1]['tokens'][] = [
+ 'negate' => $match['not'] ? true : false,
+ 'token' => $match['token'],
+ 'etag' => isset($match['etag']) ? $match['etag'] : ''
+ ];
+ } else {
+
+ if (!$match['uri']) {
+ $realUri = $request->getPath();
+ } else {
+ $realUri = $this->calculateUri($match['uri']);
+ }
+
+ $conditions[] = [
+ 'uri' => $realUri,
+ 'tokens' => [
+ [
+ 'negate' => $match['not'] ? true : false,
+ 'token' => $match['token'],
+ 'etag' => isset($match['etag']) ? $match['etag'] : ''
+ ]
+ ],
+
+ ];
+ }
+
+ }
+
+ return $conditions;
+
+ }
+
+ /**
+ * Returns an array with resourcetypes for a node.
+ *
+ * @param INode $node
+ * @return array
+ */
+ function getResourceTypeForNode(INode $node) {
+
+ $result = [];
+ foreach ($this->resourceTypeMapping as $className => $resourceType) {
+ if ($node instanceof $className) $result[] = $resourceType;
+ }
+ return $result;
+
+ }
+
+ // }}}
+ // {{{ XML Readers & Writers
+
+
+ /**
+ * Generates a WebDAV propfind response body based on a list of nodes.
+ *
+ * If 'strip404s' is set to true, all 404 responses will be removed.
+ *
+ * @param array $fileProperties The list with nodes
+ * @param bool strip404s
+ * @return string
+ */
+ function generateMultiStatus(array $fileProperties, $strip404s = false) {
+
+ $xml = [];
+
+ foreach ($fileProperties as $entry) {
+
+ $href = $entry['href'];
+ unset($entry['href']);
+ if ($strip404s) {
+ unset($entry[404]);
+ }
+ $response = new Xml\Element\Response(
+ ltrim($href, '/'),
+ $entry
+ );
+ $xml[] = [
+ 'name' => '{DAV:}response',
+ 'value' => $response
+ ];
+
+ }
+ return $this->xml->write('{DAV:}multistatus', $xml, $this->baseUri);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/ServerPlugin.php b/vendor/sabre/dav/lib/DAV/ServerPlugin.php
index c393f43fb..b2c468ab3 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/ServerPlugin.php
+++ b/vendor/sabre/dav/lib/DAV/ServerPlugin.php
@@ -7,7 +7,7 @@ namespace Sabre\DAV;
*
* Plugins can modify or extend the servers behaviour.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -24,7 +24,7 @@ abstract class ServerPlugin {
* @param Server $server
* @return void
*/
- abstract public function initialize(Server $server);
+ abstract function initialize(Server $server);
/**
* This method should return a list of server-features.
@@ -34,9 +34,9 @@ abstract class ServerPlugin {
*
* @return array
*/
- public function getFeatures() {
+ function getFeatures() {
- return array();
+ return [];
}
@@ -47,12 +47,12 @@ abstract class ServerPlugin {
* This method is passed a uri. It should only return HTTP methods that are
* available for the specified uri.
*
- * @param string $uri
+ * @param string $path
* @return array
*/
- public function getHTTPMethods($uri) {
+ function getHTTPMethods($path) {
- return array();
+ return [];
}
@@ -64,7 +64,7 @@ abstract class ServerPlugin {
*
* @return string
*/
- public function getPluginName() {
+ function getPluginName() {
return get_class($this);
@@ -80,11 +80,31 @@ abstract class ServerPlugin {
* @param string $uri
* @return array
*/
- public function getSupportedReportSet($uri) {
+ function getSupportedReportSet($uri) {
- return array();
+ return [];
}
-}
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => null,
+ 'link' => null,
+ ];
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/SimpleCollection.php b/vendor/sabre/dav/lib/DAV/SimpleCollection.php
index 1bdb166c7..998cfcbff 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/SimpleCollection.php
+++ b/vendor/sabre/dav/lib/DAV/SimpleCollection.php
@@ -8,7 +8,7 @@ namespace Sabre\DAV;
* The SimpleCollection is used to quickly setup static directory structures.
* Just create the object with a proper name, and add children to use it.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -17,9 +17,9 @@ class SimpleCollection extends Collection {
/**
* List of childnodes
*
- * @var array
+ * @var INode[]
*/
- protected $children = array();
+ protected $children = [];
/**
* Name of this resource
@@ -35,12 +35,12 @@ class SimpleCollection extends Collection {
* This nodes must be instances of INode
*
* @param string $name
- * @param array $children
+ * @param INode[] $children
*/
- public function __construct($name,array $children = array()) {
+ function __construct($name, array $children = []) {
$this->name = $name;
- foreach($children as $child) {
+ foreach ($children as $child) {
if (!($child instanceof INode)) throw new Exception('Only instances of Sabre\DAV\INode are allowed to be passed in the children argument');
$this->addChild($child);
@@ -55,7 +55,7 @@ class SimpleCollection extends Collection {
* @param INode $child
* @return void
*/
- public function addChild(INode $child) {
+ function addChild(INode $child) {
$this->children[$child->getName()] = $child;
@@ -66,7 +66,7 @@ class SimpleCollection extends Collection {
*
* @return string
*/
- public function getName() {
+ function getName() {
return $this->name;
@@ -85,7 +85,7 @@ class SimpleCollection extends Collection {
* @throws Exception\NotFound
* @return INode
*/
- public function getChild($name) {
+ function getChild($name) {
if (isset($this->children[$name])) return $this->children[$name];
throw new Exception\NotFound('File not found: ' . $name . ' in \'' . $this->getName() . '\'');
@@ -95,9 +95,9 @@ class SimpleCollection extends Collection {
/**
* Returns a list of children for this collection
*
- * @return array
+ * @return INode[]
*/
- public function getChildren() {
+ function getChildren() {
return array_values($this->children);
@@ -105,4 +105,3 @@ class SimpleCollection extends Collection {
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/SimpleFile.php b/vendor/sabre/dav/lib/DAV/SimpleFile.php
index b7413fdde..bcad786f3 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/SimpleFile.php
+++ b/vendor/sabre/dav/lib/DAV/SimpleFile.php
@@ -9,7 +9,7 @@ namespace Sabre\DAV;
* the directory structure. One usecase would be to add a 'readme.txt' to a
* root of a webserver with some standard content.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -20,7 +20,7 @@ class SimpleFile extends File {
*
* @var string
*/
- protected $contents = array();
+ protected $contents = [];
/**
* Name of this resource
@@ -46,7 +46,7 @@ class SimpleFile extends File {
* @param string $contents
* @param string|null $mimeType
*/
- public function __construct($name, $contents, $mimeType = null) {
+ function __construct($name, $contents, $mimeType = null) {
$this->name = $name;
$this->contents = $contents;
@@ -61,7 +61,7 @@ class SimpleFile extends File {
*
* @return string
*/
- public function getName() {
+ function getName() {
return $this->name;
@@ -74,7 +74,7 @@ class SimpleFile extends File {
*
* @return mixed
*/
- public function get() {
+ function get() {
return $this->contents;
@@ -85,7 +85,7 @@ class SimpleFile extends File {
*
* @return int
*/
- public function getSize() {
+ function getSize() {
return strlen($this->contents);
@@ -100,9 +100,9 @@ class SimpleFile extends File {
* Return null if the ETag can not effectively be determined
* @return string
*/
- public function getETag() {
+ function getETag() {
- return '"' . md5($this->contents) . '"';
+ return '"' . sha1($this->contents) . '"';
}
@@ -112,7 +112,7 @@ class SimpleFile extends File {
* If null is returned, we'll assume application/octet-stream
* @return string
*/
- public function getContentType() {
+ function getContentType() {
return $this->mimeType;
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/StringUtil.php b/vendor/sabre/dav/lib/DAV/StringUtil.php
index c71575f49..10eecebfd 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/StringUtil.php
+++ b/vendor/sabre/dav/lib/DAV/StringUtil.php
@@ -9,7 +9,7 @@ namespace Sabre\DAV;
* the CalDAV calendar-query REPORT, and CardDAV addressbook-query REPORT.
* Because they both need it, it was decided to put it in Sabre\DAV instead.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -24,15 +24,15 @@ class StringUtil {
* @param string $matchType
* @return bool
*/
- static public function textMatch($haystack, $needle, $collation, $matchType = 'contains') {
+ static function textMatch($haystack, $needle, $collation, $matchType = 'contains') {
- switch($collation) {
+ switch ($collation) {
case 'i;ascii-casemap' :
// default strtolower takes locale into consideration
// we don't want this.
- $haystack = str_replace(range('a','z'), range('A','Z'), $haystack);
- $needle = str_replace(range('a','z'), range('A','Z'), $needle);
+ $haystack = str_replace(range('a', 'z'), range('A', 'Z'), $haystack);
+ $needle = str_replace(range('a', 'z'), range('A', 'Z'), $needle);
break;
case 'i;octet' :
@@ -49,16 +49,16 @@ class StringUtil {
}
- switch($matchType) {
+ switch ($matchType) {
case 'contains' :
- return strpos($haystack, $needle)!==false;
+ return strpos($haystack, $needle) !== false;
case 'equals' :
return $haystack === $needle;
case 'starts-with' :
- return strpos($haystack, $needle)===0;
+ return strpos($haystack, $needle) === 0;
case 'ends-with' :
- return strrpos($haystack, $needle)===strlen($haystack)-strlen($needle);
+ return strrpos($haystack, $needle) === strlen($haystack) - strlen($needle);
default :
throw new Exception\BadRequest('Match-type: ' . $matchType . ' is not supported');
@@ -76,9 +76,9 @@ class StringUtil {
* @param string $input
* @return string
*/
- static public function ensureUTF8($input) {
+ static function ensureUTF8($input) {
- $encoding = mb_detect_encoding($input , array('UTF-8','ISO-8859-1'), true);
+ $encoding = mb_detect_encoding($input, ['UTF-8', 'ISO-8859-1'], true);
if ($encoding === 'ISO-8859-1') {
return utf8_encode($input);
diff --git a/vendor/sabre/dav/lib/DAV/Sync/ISyncCollection.php b/vendor/sabre/dav/lib/DAV/Sync/ISyncCollection.php
new file mode 100644
index 000000000..d3dc28a80
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Sync/ISyncCollection.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace Sabre\DAV\Sync;
+
+use Sabre\DAV;
+
+/**
+ * If a class extends ISyncCollection, it supports WebDAV-sync.
+ *
+ * You are responsible for maintaining a changelist for this collection. This
+ * means that if any child nodes in this collection was created, modified or
+ * deleted in any way, you should maintain an updated changelist.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface ISyncCollection extends DAV\ICollection {
+
+ /**
+ * This method returns the current sync-token for this collection.
+ * This can be any string.
+ *
+ * If null is returned from this function, the plugin assumes there's no
+ * sync information available.
+ *
+ * @return string|null
+ */
+ function getSyncToken();
+
+ /**
+ * The getChanges method returns all the changes that have happened, since
+ * the specified syncToken and the current collection.
+ *
+ * This function should return an array, such as the following:
+ *
+ * [
+ * 'syncToken' => 'The current synctoken',
+ * 'added' => [
+ * 'new.txt',
+ * ],
+ * 'modified' => [
+ * 'modified.txt',
+ * ],
+ * 'deleted' => array(
+ * 'foo.php.bak',
+ * 'old.txt'
+ * )
+ * ];
+ *
+ * The syncToken property should reflect the *current* syncToken of the
+ * collection, as reported getSyncToken(). This is needed here too, to
+ * ensure the operation is atomic.
+ *
+ * If the syncToken is specified as null, this is an initial sync, and all
+ * members should be reported.
+ *
+ * The modified property is an array of nodenames that have changed since
+ * the last token.
+ *
+ * The deleted property is an array with nodenames, that have been deleted
+ * from collection.
+ *
+ * The second argument is basically the 'depth' of the report. If it's 1,
+ * you only have to report changes that happened only directly in immediate
+ * descendants. If it's 2, it should also include changes from the nodes
+ * below the child collections. (grandchildren)
+ *
+ * The third (optional) argument allows a client to specify how many
+ * results should be returned at most. If the limit is not specified, it
+ * should be treated as infinite.
+ *
+ * If the limit (infinite or not) is higher than you're willing to return,
+ * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
+ *
+ * If the syncToken is expired (due to data cleanup) or unknown, you must
+ * return null.
+ *
+ * The limit is 'suggestive'. You are free to ignore it.
+ *
+ * @param string $syncToken
+ * @param int $syncLevel
+ * @param int $limit
+ * @return array
+ */
+ function getChanges($syncToken, $syncLevel, $limit = null);
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Sync/Plugin.php b/vendor/sabre/dav/lib/DAV/Sync/Plugin.php
new file mode 100644
index 000000000..4a141c72b
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Sync/Plugin.php
@@ -0,0 +1,277 @@
+<?php
+
+namespace Sabre\DAV\Sync;
+
+use Sabre\DAV;
+use Sabre\HTTP\RequestInterface;
+use Sabre\DAV\Xml\Request\SyncCollectionReport;
+
+/**
+ * This plugin all WebDAV-sync capabilities to the Server.
+ *
+ * WebDAV-sync is defined by rfc6578
+ *
+ * The sync capabilities only work with collections that implement
+ * Sabre\DAV\Sync\ISyncCollection.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Plugin extends DAV\ServerPlugin {
+
+ /**
+ * Reference to server object
+ *
+ * @var DAV\Server
+ */
+ protected $server;
+
+ const SYNCTOKEN_PREFIX = 'http://sabre.io/ns/sync/';
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using \Sabre\DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'sync';
+
+ }
+
+ /**
+ * Initializes the plugin.
+ *
+ * This is when the plugin registers it's hooks.
+ *
+ * @param DAV\Server $server
+ * @return void
+ */
+ function initialize(DAV\Server $server) {
+
+ $this->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') {
+ $this->server->transactionType = 'report-sync-collection';
+ $self->syncCollection($uri, $dom);
+ return false;
+ }
+
+ });
+
+ $server->on('propFind', [$this, 'propFind']);
+ $server->on('validateTokens', [$this, 'validateTokens']);
+
+ }
+
+ /**
+ * Returns a list of reports this plugin supports.
+ *
+ * This will be used in the {DAV:}supported-report-set property.
+ * Note that you still need to subscribe to the 'report' event to actually
+ * implement them
+ *
+ * @param string $uri
+ * @return array
+ */
+ function getSupportedReportSet($uri) {
+
+ $node = $this->server->tree->getNodeForPath($uri);
+ if ($node instanceof ISyncCollection && $node->getSyncToken()) {
+ return [
+ '{DAV:}sync-collection',
+ ];
+ }
+
+ return [];
+
+ }
+
+
+ /**
+ * This method handles the {DAV:}sync-collection HTTP REPORT.
+ *
+ * @param string $uri
+ * @param SyncCollectionReport $report
+ * @return void
+ */
+ function syncCollection($uri, SyncCollectionReport $report) {
+
+ // Getting the data
+ $node = $this->server->tree->getNodeForPath($uri);
+ if (!$node instanceof ISyncCollection) {
+ throw new DAV\Exception\ReportNotSupported('The {DAV:}sync-collection REPORT is not supported on this url.');
+ }
+ $token = $node->getSyncToken();
+ if (!$token) {
+ throw new DAV\Exception\ReportNotSupported('No sync information is available at this node');
+ }
+
+ $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) {
+ 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
+ $this->sendSyncCollectionResponse(
+ $changeInfo['syncToken'],
+ $uri,
+ $changeInfo['added'],
+ $changeInfo['modified'],
+ $changeInfo['deleted'],
+ $report->properties
+ );
+
+ }
+
+ /**
+ * Sends the response to a sync-collection request.
+ *
+ * @param string $syncToken
+ * @param string $collectionUrl
+ * @param array $added
+ * @param array $modified
+ * @param array $deleted
+ * @param array $properties
+ * @return void
+ */
+ protected function sendSyncCollectionResponse($syncToken, $collectionUrl, array $added, array $modified, array $deleted, array $properties) {
+
+
+ $fullPaths = [];
+
+ // Pre-fetching children, if this is possible.
+ foreach (array_merge($added, $modified) as $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;
+ $responses[] = new DAV\Xml\Element\Response($fullPath, [], 404);
+
+ }
+ $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())
+ );
+
+ }
+
+ /**
+ * This method is triggered whenever properties are requested for a node.
+ * We intercept this to see if we must return a {DAV:}sync-token.
+ *
+ * @param DAV\PropFind $propFind
+ * @param DAV\INode $node
+ * @return void
+ */
+ function propFind(DAV\PropFind $propFind, DAV\INode $node) {
+
+ $propFind->handle('{DAV:}sync-token', function() use ($node) {
+ if (!$node instanceof ISyncCollection || !$token = $node->getSyncToken()) {
+ return;
+ }
+ return self::SYNCTOKEN_PREFIX . $token;
+ });
+
+ }
+
+ /**
+ * The validateTokens event is triggered before every request.
+ *
+ * It's a moment where this plugin can check all the supplied lock tokens
+ * in the If: header, and check if they are valid.
+ *
+ * @param RequestInterface $request
+ * @param array $conditions
+ * @return void
+ */
+ function validateTokens(RequestInterface $request, &$conditions) {
+
+ foreach ($conditions as $kk => $condition) {
+
+ foreach ($condition['tokens'] as $ii => $token) {
+
+ // Sync-tokens must always start with our designated prefix.
+ if (substr($token['token'], 0, strlen(self::SYNCTOKEN_PREFIX)) !== self::SYNCTOKEN_PREFIX) {
+ continue;
+ }
+
+ // Checking if the token is a match.
+ $node = $this->server->tree->getNodeForPath($condition['uri']);
+
+ if (
+ $node instanceof ISyncCollection &&
+ $node->getSyncToken() == substr($token['token'], strlen(self::SYNCTOKEN_PREFIX))
+ ) {
+ $conditions[$kk]['tokens'][$ii]['validToken'] = true;
+ }
+
+ }
+
+ }
+
+ }
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => 'Adds support for WebDAV Collection Sync (rfc6578)',
+ 'link' => 'http://sabre.io/dav/sync/',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/TemporaryFileFilterPlugin.php b/vendor/sabre/dav/lib/DAV/TemporaryFileFilterPlugin.php
index 37f976b7b..c5b8aa1ca 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/TemporaryFileFilterPlugin.php
+++ b/vendor/sabre/dav/lib/DAV/TemporaryFileFilterPlugin.php
@@ -2,6 +2,10 @@
namespace Sabre\DAV;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+use Sabre\HTTP\URLUtil;
+
/**
* Temporary File Filter Plugin
*
@@ -22,7 +26,7 @@ namespace Sabre\DAV;
* Additional patterns can be added, by adding on to the
* temporaryFilePatterns property.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -34,7 +38,7 @@ class TemporaryFileFilterPlugin extends ServerPlugin {
*
* @var array
*/
- public $temporaryFilePatterns = array(
+ public $temporaryFilePatterns = [
'/^\._(.*)$/', // OS/X resource forks
'/^.DS_Store$/', // OS/X custom folder settings
'/^desktop.ini$/', // Windows custom folder settings
@@ -42,7 +46,7 @@ class TemporaryFileFilterPlugin extends ServerPlugin {
'/^.(.*).swp$/', // ViM temporary files
'/^\.dat(.*)$/', // Smultron seems to create these
'/^~lock.(.*)#$/', // Windows 7 lockfiles
- );
+ ];
/**
* A reference to the main Server class
@@ -68,9 +72,9 @@ class TemporaryFileFilterPlugin extends ServerPlugin {
*
* @param string|null $dataDir
*/
- public function __construct($dataDir = null) {
+ function __construct($dataDir = null) {
- if (!$dataDir) $dataDir = ini_get('session.save_path').'/sabredav/';
+ if (!$dataDir) $dataDir = ini_get('session.save_path') . '/sabredav/';
if (!is_dir($dataDir)) mkdir($dataDir);
$this->dataDir = $dataDir;
@@ -85,11 +89,11 @@ class TemporaryFileFilterPlugin extends ServerPlugin {
* @param Server $server
* @return void
*/
- public function initialize(Server $server) {
+ function initialize(Server $server) {
$this->server = $server;
- $server->subscribeEvent('beforeMethod',array($this,'beforeMethod'));
- $server->subscribeEvent('beforeCreateFile',array($this,'beforeCreateFile'));
+ $server->on('beforeMethod', [$this, 'beforeMethod']);
+ $server->on('beforeCreateFile', [$this, 'beforeCreateFile']);
}
@@ -99,26 +103,26 @@ class TemporaryFileFilterPlugin extends ServerPlugin {
* This method intercepts any GET, DELETE, PUT and PROPFIND calls to
* filenames that are known to match the 'temporary file' regex.
*
- * @param string $method
- * @param string $uri
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
* @return bool
*/
- public function beforeMethod($method, $uri) {
+ function beforeMethod(RequestInterface $request, ResponseInterface $response) {
- if (!$tempLocation = $this->isTempFile($uri))
- return true;
+ if (!$tempLocation = $this->isTempFile($request->getPath()))
+ return;
- switch($method) {
+ switch ($request->getMethod()) {
case 'GET' :
- return $this->httpGet($tempLocation);
+ return $this->httpGet($request, $response, $tempLocation);
case 'PUT' :
- return $this->httpPut($tempLocation);
+ return $this->httpPut($request, $response, $tempLocation);
case 'PROPFIND' :
- return $this->httpPropfind($tempLocation, $uri);
+ return $this->httpPropfind($request, $response, $tempLocation);
case 'DELETE' :
- return $this->httpDelete($tempLocation);
- }
- return true;
+ return $this->httpDelete($request, $response, $tempLocation);
+ }
+ return;
}
@@ -130,18 +134,21 @@ class TemporaryFileFilterPlugin extends ServerPlugin {
*
* @param string $uri
* @param resource $data
+ * @param DAV\ICollection $parentNode
+ * @param bool $modified Should be set to true, if this event handler
+ * changed &$data.
* @return bool
*/
- public function beforeCreateFile($uri,$data) {
+ function beforeCreateFile($uri, $data, $parent, $modified) {
if ($tempPath = $this->isTempFile($uri)) {
$hR = $this->server->httpResponse;
- $hR->setHeader('X-Sabre-Temp','true');
- file_put_contents($tempPath,$data);
+ $hR->setHeader('X-Sabre-Temp', 'true');
+ file_put_contents($tempPath, $data);
return false;
}
- return true;
+ return;
}
@@ -151,16 +158,16 @@ class TemporaryFileFilterPlugin extends ServerPlugin {
* temporary file storage.
*
* @param string $path
- * @return boolean|string
+ * @return bool|string
*/
protected function isTempFile($path) {
// We're only interested in the basename.
list(, $tempPath) = URLUtil::splitPath($path);
- foreach($this->temporaryFilePatterns as $tempFile) {
+ foreach ($this->temporaryFilePatterns as $tempFile) {
- if (preg_match($tempFile,$tempPath)) {
+ if (preg_match($tempFile, $tempPath)) {
return $this->getDataDir() . '/sabredav_' . md5($path) . '.tempfile';
}
@@ -176,19 +183,20 @@ class TemporaryFileFilterPlugin extends ServerPlugin {
* If the file doesn't exist, it will return false which will kick in
* the regular system for the GET method.
*
+ * @param RequestInterface $request
+ * @param ResponseInterface $hR
* @param string $tempLocation
* @return bool
*/
- public function httpGet($tempLocation) {
+ function httpGet(RequestInterface $request, ResponseInterface $hR, $tempLocation) {
- if (!file_exists($tempLocation)) return true;
+ if (!file_exists($tempLocation)) return;
- $hR = $this->server->httpResponse;
- $hR->setHeader('Content-Type','application/octet-stream');
- $hR->setHeader('Content-Length',filesize($tempLocation));
- $hR->setHeader('X-Sabre-Temp','true');
- $hR->sendStatus(200);
- $hR->sendBody(fopen($tempLocation,'r'));
+ $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;
}
@@ -196,13 +204,14 @@ class TemporaryFileFilterPlugin extends ServerPlugin {
/**
* This method handles the PUT method.
*
+ * @param RequestInterface $request
+ * @param ResponseInterface $hR
* @param string $tempLocation
* @return bool
*/
- public function httpPut($tempLocation) {
+ function httpPut(RequestInterface $request, ResponseInterface $hR, $tempLocation) {
- $hR = $this->server->httpResponse;
- $hR->setHeader('X-Sabre-Temp','true');
+ $hR->setHeader('X-Sabre-Temp', 'true');
$newFile = !file_exists($tempLocation);
@@ -210,8 +219,8 @@ class TemporaryFileFilterPlugin extends ServerPlugin {
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->sendStatus($newFile?201:200);
+ file_put_contents($tempLocation, $this->server->httpRequest->getBody());
+ $hR->setStatus($newFile ? 201 : 200);
return false;
}
@@ -222,17 +231,18 @@ 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 ResponseInterface $hR
* @param string $tempLocation
* @return bool
*/
- public function httpDelete($tempLocation) {
+ function httpDelete(RequestInterface $request, ResponseInterface $hR, $tempLocation) {
- if (!file_exists($tempLocation)) return true;
+ if (!file_exists($tempLocation)) return;
unlink($tempLocation);
- $hR = $this->server->httpResponse;
- $hR->setHeader('X-Sabre-Temp','true');
- $hR->sendStatus(204);
+ $hR->setHeader('X-Sabre-Temp', 'true');
+ $hR->setStatus(204);
return false;
}
@@ -244,34 +254,32 @@ class TemporaryFileFilterPlugin extends ServerPlugin {
* for which properties were requested, and just sends back a default
* set of properties.
*
+ * @param RequestInterface $request
+ * @param ResponseInterface $hR
* @param string $tempLocation
- * @param string $uri
* @return bool
*/
- public function httpPropfind($tempLocation, $uri) {
-
- if (!file_exists($tempLocation)) return true;
+ function httpPropfind(RequestInterface $request, ResponseInterface $hR, $tempLocation) {
- $hR = $this->server->httpResponse;
- $hR->setHeader('X-Sabre-Temp','true');
- $hR->sendStatus(207);
- $hR->setHeader('Content-Type','application/xml; charset=utf-8');
+ if (!file_exists($tempLocation)) return;
- $this->server->parsePropFindRequest($this->server->httpRequest->getBody(true));
+ $hR->setHeader('X-Sabre-Temp', 'true');
+ $hR->setStatus(207);
+ $hR->setHeader('Content-Type', 'application/xml; charset=utf-8');
- $properties = array(
- 'href' => $uri,
- 200 => array(
- '{DAV:}getlastmodified' => new Property\GetLastModified(filemtime($tempLocation)),
- '{DAV:}getcontentlength' => filesize($tempLocation),
- '{DAV:}resourcetype' => new Property\ResourceType(null),
- '{'.Server::NS_SABREDAV.'}tempFile' => true,
+ $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,
- ),
- );
+ ],
+ ];
- $data = $this->server->generateMultiStatus(array($properties));
- $hR->sendBody($data);
+ $data = $this->server->generateMultiStatus([$properties]);
+ $hR->setBody($data);
return false;
}
diff --git a/vendor/sabre/dav/lib/DAV/Tree.php b/vendor/sabre/dav/lib/DAV/Tree.php
new file mode 100644
index 000000000..4563f7c72
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Tree.php
@@ -0,0 +1,340 @@
+<?php
+
+namespace Sabre\DAV;
+
+use Sabre\HTTP\URLUtil;
+
+/**
+ * The tree object is responsible for basic tree operations.
+ *
+ * It allows for fetching nodes by path, facilitates deleting, copying and
+ * moving.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Tree {
+
+ /**
+ * The root node
+ *
+ * @var ICollection
+ */
+ protected $rootNode;
+
+ /**
+ * This is the node cache. Accessed nodes are stored here.
+ * Arrays keys are path names, values are the actual nodes.
+ *
+ * @var array
+ */
+ protected $cache = [];
+
+ /**
+ * Creates the object
+ *
+ * This method expects the rootObject to be passed as a parameter
+ *
+ * @param ICollection $rootNode
+ */
+ function __construct(ICollection $rootNode) {
+
+ $this->rootNode = $rootNode;
+
+ }
+
+ /**
+ * Returns the INode object for the requested path
+ *
+ * @param string $path
+ * @return INode
+ */
+ function getNodeForPath($path) {
+
+ $path = trim($path, '/');
+ if (isset($this->cache[$path])) return $this->cache[$path];
+
+ // Is it the root node?
+ if (!strlen($path)) {
+ return $this->rootNode;
+ }
+
+ // Attempting to fetch its parent
+ list($parentName, $baseName) = URLUtil::splitPath($path);
+
+ // If there was no parent, we must simply ask it from the root node.
+ if ($parentName === "") {
+ $node = $this->rootNode->getChild($baseName);
+ } else {
+ // Otherwise, we recursively grab the parent and ask him/her.
+ $parent = $this->getNodeForPath($parentName);
+
+ if (!($parent instanceof ICollection))
+ throw new Exception\NotFound('Could not find node at path: ' . $path);
+
+ $node = $parent->getChild($baseName);
+
+ }
+
+ $this->cache[$path] = $node;
+ return $node;
+
+ }
+
+ /**
+ * This function allows you to check if a node exists.
+ *
+ * Implementors of this class should override this method to make
+ * it cheaper.
+ *
+ * @param string $path
+ * @return bool
+ */
+ function nodeExists($path) {
+
+ try {
+
+ // The root always exists
+ if ($path === '') return true;
+
+ list($parent, $base) = URLUtil::splitPath($path);
+
+ $parentNode = $this->getNodeForPath($parent);
+ if (!$parentNode instanceof ICollection) return false;
+ return $parentNode->childExists($base);
+
+ } catch (Exception\NotFound $e) {
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * Copies a file from path to another
+ *
+ * @param string $sourcePath The source location
+ * @param string $destinationPath The full destination path
+ * @return void
+ */
+ function copy($sourcePath, $destinationPath) {
+
+ $sourceNode = $this->getNodeForPath($sourcePath);
+
+ // grab the dirname and basename components
+ list($destinationDir, $destinationName) = URLUtil::splitPath($destinationPath);
+
+ $destinationParent = $this->getNodeForPath($destinationDir);
+ $this->copyNode($sourceNode, $destinationParent, $destinationName);
+
+ $this->markDirty($destinationDir);
+
+ }
+
+ /**
+ * Moves a file from one location to another
+ *
+ * @param string $sourcePath The path to the file which should be moved
+ * @param string $destinationPath The full destination path, so not just the destination parent node
+ * @return int
+ */
+ function move($sourcePath, $destinationPath) {
+
+ list($sourceDir) = URLUtil::splitPath($sourcePath);
+ list($destinationDir, $destinationName) = URLUtil::splitPath($destinationPath);
+
+ if ($sourceDir === $destinationDir) {
+ // If this is a 'local' rename, it means we can just trigger a rename.
+ $sourceNode = $this->getNodeForPath($sourcePath);
+ $sourceNode->setName($destinationName);
+ } else {
+ $newParentNode = $this->getNodeForPath($destinationDir);
+ $moveSuccess = false;
+ if ($newParentNode instanceof IMoveTarget) {
+ // The target collection may be able to handle the move
+ $sourceNode = $this->getNodeForPath($sourcePath);
+ $moveSuccess = $newParentNode->moveInto($destinationName, $sourcePath, $sourceNode);
+ }
+ if (!$moveSuccess) {
+ $this->copy($sourcePath, $destinationPath);
+ $this->getNodeForPath($sourcePath)->delete();
+ }
+ }
+ $this->markDirty($sourceDir);
+ $this->markDirty($destinationDir);
+
+ }
+
+ /**
+ * Deletes a node from the tree
+ *
+ * @param string $path
+ * @return void
+ */
+ function delete($path) {
+
+ $node = $this->getNodeForPath($path);
+ $node->delete();
+
+ list($parent) = URLUtil::splitPath($path);
+ $this->markDirty($parent);
+
+ }
+
+ /**
+ * Returns a list of childnodes for a given path.
+ *
+ * @param string $path
+ * @return array
+ */
+ function getChildren($path) {
+
+ $node = $this->getNodeForPath($path);
+ $children = $node->getChildren();
+ $basePath = trim($path, '/');
+ if ($basePath !== '') $basePath .= '/';
+
+ foreach ($children as $child) {
+
+ $this->cache[$basePath . $child->getName()] = $child;
+
+ }
+ return $children;
+
+ }
+
+ /**
+ * This method is called with every tree update
+ *
+ * Examples of tree updates are:
+ * * node deletions
+ * * node creations
+ * * copy
+ * * move
+ * * renaming nodes
+ *
+ * If Tree classes implement a form of caching, this will allow
+ * them to make sure caches will be expired.
+ *
+ * If a path is passed, it is assumed that the entire subtree is dirty
+ *
+ * @param string $path
+ * @return void
+ */
+ function markDirty($path) {
+
+ // We don't care enough about sub-paths
+ // flushing the entire cache
+ $path = trim($path, '/');
+ foreach ($this->cache as $nodePath => $node) {
+ if ($nodePath == $path || strpos($nodePath, $path . '/') === 0)
+ unset($this->cache[$nodePath]);
+
+ }
+
+ }
+
+ /**
+ * This method tells the tree system to pre-fetch and cache a list of
+ * children of a single parent.
+ *
+ * There are a bunch of operations in the WebDAV stack that request many
+ * children (based on uris), and sometimes fetching many at once can
+ * optimize this.
+ *
+ * This method returns an array with the found nodes. It's keys are the
+ * original paths. The result may be out of order.
+ *
+ * @param array $paths List of nodes that must be fetched.
+ * @return array
+ */
+ function getMultipleNodes($paths) {
+
+ // Finding common parents
+ $parents = [];
+ foreach ($paths as $path) {
+ list($parent, $node) = URLUtil::splitPath($path);
+ if (!isset($parents[$parent])) {
+ $parents[$parent] = [$node];
+ } else {
+ $parents[$parent][] = $node;
+ }
+ }
+
+ $result = [];
+
+ foreach ($parents as $parent => $children) {
+
+ $parentNode = $this->getNodeForPath($parent);
+ if ($parentNode instanceof IMultiGet) {
+ foreach ($parentNode->getMultipleChildren($children) as $childNode) {
+ $fullPath = $parent . '/' . $childNode->getName();
+ $result[$fullPath] = $childNode;
+ $this->cache[$fullPath] = $childNode;
+ }
+ } else {
+ foreach ($children as $child) {
+ $fullPath = $parent . '/' . $child;
+ $result[$fullPath] = $this->getNodeForPath($fullPath);
+ }
+ }
+
+ }
+
+ return $result;
+
+ }
+
+
+ /**
+ * copyNode
+ *
+ * @param INode $source
+ * @param ICollection $destinationParent
+ * @param string $destinationName
+ * @return void
+ */
+ protected function copyNode(INode $source, ICollection $destinationParent, $destinationName = null) {
+
+ if (!$destinationName) $destinationName = $source->getName();
+
+ if ($source instanceof IFile) {
+
+ $data = $source->get();
+
+ // If the body was a string, we need to convert it to a stream
+ if (is_string($data)) {
+ $stream = fopen('php://temp', 'r+');
+ fwrite($stream, $data);
+ rewind($stream);
+ $data = $stream;
+ }
+ $destinationParent->createFile($destinationName, $data);
+ $destination = $destinationParent->getChild($destinationName);
+
+ } elseif ($source instanceof ICollection) {
+
+ $destinationParent->createDirectory($destinationName);
+
+ $destination = $destinationParent->getChild($destinationName);
+ foreach ($source->getChildren() as $child) {
+
+ $this->copyNode($child, $destination);
+
+ }
+
+ }
+ if ($source instanceof IProperties && $destination instanceof IProperties) {
+
+ $props = $source->getProperties([]);
+ $propPatch = new PropPatch($props);
+ $destination->propPatch($propPatch);
+ $propPatch->commit();
+
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/UUIDUtil.php b/vendor/sabre/dav/lib/DAV/UUIDUtil.php
index 6a904a7bc..177adafd3 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/UUIDUtil.php
+++ b/vendor/sabre/dav/lib/DAV/UUIDUtil.php
@@ -9,7 +9,7 @@ namespace Sabre\DAV;
* UUIDs are used a decent amount within various *DAV standards, so it made
* sense to include it.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -25,24 +25,24 @@ class UUIDUtil {
*/
static function getUUID() {
- return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
+ return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 bits for "time_low"
- mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff),
// 16 bits for "time_mid"
- mt_rand( 0, 0xffff ),
+ mt_rand(0, 0xffff),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 4
- mt_rand( 0, 0x0fff ) | 0x4000,
+ mt_rand(0, 0x0fff) | 0x4000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
- mt_rand( 0, 0x3fff ) | 0x8000,
+ mt_rand(0, 0x3fff) | 0x8000,
// 48 bits for "node"
- mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
}
@@ -57,7 +57,7 @@ class UUIDUtil {
return preg_match(
'/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i',
$uuid
- ) == true;
+ ) !== 0;
}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Version.php b/vendor/sabre/dav/lib/DAV/Version.php
index afe603c3c..f9331943a 100644
--- a/vendor/sabre/dav/lib/Sabre/DAV/Version.php
+++ b/vendor/sabre/dav/lib/DAV/Version.php
@@ -5,7 +5,7 @@ namespace Sabre\DAV;
/**
* This class contains the SabreDAV version constants.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -14,11 +14,6 @@ class Version {
/**
* Full version number
*/
- const VERSION = '1.8.10';
-
- /**
- * Stability : alpha, beta, stable
- */
- const STABILITY = 'stable';
+ const VERSION = '3.1.3';
}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Element/Prop.php b/vendor/sabre/dav/lib/DAV/Xml/Element/Prop.php
new file mode 100644
index 000000000..db5332c50
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Element/Prop.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace Sabre\DAV\Xml\Element;
+
+use Sabre\DAV\Xml\Property\Complex;
+use Sabre\Xml\XmlDeserializable;
+use Sabre\Xml\Reader;
+
+/**
+ * This class is responsible for decoding the {DAV:}prop element as it appears
+ * in {DAV:}property-update.
+ *
+ * This class doesn't return an instance of itself. It just returns a
+ * key->value array.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Prop implements XmlDeserializable {
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ // If there's no children, we don't do anything.
+ if ($reader->isEmptyElement) {
+ $reader->next();
+ return [];
+ }
+
+ $values = [];
+
+ $reader->read();
+ do {
+
+ if ($reader->nodeType === Reader::ELEMENT) {
+
+ $clark = $reader->getClark();
+ $values[$clark] = self::parseCurrentElement($reader)['value'];
+
+ } else {
+ $reader->read();
+ }
+
+ } while ($reader->nodeType !== Reader::END_ELEMENT);
+
+ $reader->read();
+
+ return $values;
+
+ }
+
+ /**
+ * This function behaves similar to Sabre\Xml\Reader::parseCurrentElement,
+ * but instead of creating deep xml array structures, it will turn any
+ * top-level element it doesn't recognize into either a string, or an
+ * XmlFragment class.
+ *
+ * This method returns arn array with 2 properties:
+ * * name - A clark-notation XML element name.
+ * * value - The parsed value.
+ *
+ * @param Reader $reader
+ * @return array
+ */
+ private static function parseCurrentElement(Reader $reader) {
+
+ $name = $reader->getClark();
+
+ if (array_key_exists($name, $reader->elementMap)) {
+ $deserializer = $reader->elementMap[$name];
+ if (is_subclass_of($deserializer, 'Sabre\\Xml\\XmlDeserializable')) {
+ $value = call_user_func([ $deserializer, 'xmlDeserialize' ], $reader);
+ } elseif (is_callable($deserializer)) {
+ $value = call_user_func($deserializer, $reader);
+ } else {
+ $type = gettype($deserializer);
+ if ($type === 'string') {
+ $type .= ' (' . $deserializer . ')';
+ } elseif ($type === 'object') {
+ $type .= ' (' . get_class($deserializer) . ')';
+ }
+ throw new \LogicException('Could not use this type as a deserializer: ' . $type);
+ }
+ } else {
+ $value = Complex::xmlDeserialize($reader);
+ }
+
+ return [
+ 'name' => $name,
+ 'value' => $value,
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Element/Response.php b/vendor/sabre/dav/lib/DAV/Xml/Element/Response.php
new file mode 100644
index 000000000..97a2bb59f
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Element/Response.php
@@ -0,0 +1,253 @@
+<?php
+
+namespace Sabre\DAV\Xml\Element;
+
+use Sabre\Xml\Element;
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+
+/**
+ * WebDAV {DAV:}response parser
+ *
+ * This class parses the {DAV:}response element, as defined in:
+ *
+ * https://tools.ietf.org/html/rfc4918#section-14.24
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Response implements Element {
+
+ /**
+ * Url for the response
+ *
+ * @var string
+ */
+ protected $href;
+
+ /**
+ * Propertylist, ordered by HTTP status code
+ *
+ * @var array
+ */
+ protected $responseProperties;
+
+ /**
+ * The HTTP status for an entire response.
+ *
+ * This is currently only used in WebDAV-Sync
+ *
+ * @var string
+ */
+ protected $httpStatus;
+
+ /**
+ * The href argument is a url relative to the root of the server. This
+ * class will calculate the full path.
+ *
+ * The responseProperties argument is a list of properties
+ * within an array with keys representing HTTP status codes
+ *
+ * Besides specific properties, the entire {DAV:}response element may also
+ * have a http status code.
+ * In most cases you don't need it.
+ *
+ * This is currently used by the Sync extension to indicate that a node is
+ * deleted.
+ *
+ * @param string $href
+ * @param array $responseProperties
+ * @param string $httpStatus
+ */
+ function __construct($href, array $responseProperties, $httpStatus = null) {
+
+ $this->href = $href;
+ $this->responseProperties = $responseProperties;
+ $this->httpStatus = $httpStatus;
+
+ }
+
+ /**
+ * Returns the url
+ *
+ * @return string
+ */
+ function getHref() {
+
+ return $this->href;
+
+ }
+
+ /**
+ * Returns the httpStatus value
+ *
+ * @return string
+ */
+ function getHttpStatus() {
+
+ return $this->httpStatus;
+
+ }
+
+ /**
+ * Returns the property list
+ *
+ * @return array
+ */
+ function getResponseProperties() {
+
+ return $this->responseProperties;
+
+ }
+
+
+ /**
+ * The serialize method is called during xml writing.
+ *
+ * It should use the $writer argument to encode this object into XML.
+ *
+ * Important note: it is not needed to create the parent element. The
+ * parent element is already created, and we only have to worry about
+ * attributes, child elements and text (if any).
+ *
+ * Important note 2: If you are writing any new elements, you are also
+ * responsible for closing them.
+ *
+ * @param Writer $writer
+ * @return void
+ */
+ function xmlSerialize(Writer $writer) {
+
+ if ($status = $this->getHTTPStatus()) {
+ $writer->writeElement('{DAV:}status', 'HTTP/1.1 ' . $status . ' ' . \Sabre\HTTP\Response::$statusCodes[$status]);
+ }
+ $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;
+ }
+ $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->endElement(); // {DAV:}propstat
+
+ }
+ if ($empty) {
+ /*
+ * The WebDAV spec _requires_ at least one DAV:propstat to appear for
+ * every DAV:response. In some circumstances however, there are no
+ * properties to encode.
+ *
+ * In those cases we MUST specify at least one DAV:propstat anyway, with
+ * no properties.
+ */
+ $writer->writeElement('{DAV:}propstat', [
+ '{DAV:}prop' => [],
+ '{DAV:}status' => 'HTTP/1.1 418 ' . \Sabre\HTTP\Response::$statusCodes[418]
+ ]);
+
+ }
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $reader->pushContext();
+
+ $reader->elementMap['{DAV:}propstat'] = 'Sabre\\Xml\\Element\\KeyValue';
+
+ // We are overriding the parser for {DAV:}prop. This deserializer is
+ // almost identical to the one for Sabre\Xml\Element\KeyValue.
+ //
+ // The difference is that if there are any child-elements inside of
+ // {DAV:}prop, that have no value, normally any deserializers are
+ // 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) {
+
+ if ($reader->isEmptyElement) {
+ $reader->next();
+ return [];
+ }
+ $values = [];
+ $reader->read();
+ do {
+ if ($reader->nodeType === Reader::ELEMENT) {
+ $clark = $reader->getClark();
+
+ if ($reader->isEmptyElement) {
+ $values[$clark] = null;
+ $reader->next();
+ } else {
+ $values[$clark] = $reader->parseCurrentElement()['value'];
+ }
+ } else {
+ $reader->read();
+ }
+ } while ($reader->nodeType !== Reader::END_ELEMENT);
+ $reader->read();
+ return $values;
+
+ };
+ $elems = $reader->parseInnerTree();
+ $reader->popContext();
+
+ $href = null;
+ $propertyLists = [];
+ $statusCode = null;
+
+ foreach ($elems as $elem) {
+
+ switch ($elem['name']) {
+
+ case '{DAV:}href' :
+ $href = $elem['value'];
+ break;
+ case '{DAV:}propstat' :
+ $status = $elem['value']['{DAV:}status'];
+ list(, $status, ) = explode(' ', $status, 3);
+ $properties = isset($elem['value']['{DAV:}prop']) ? $elem['value']['{DAV:}prop'] : [];
+ if ($properties) $propertyLists[$status] = $properties;
+ break;
+ case '{DAV:}status' :
+ list(, $statusCode, ) = explode(' ', $elem['value'], 3);
+ break;
+
+ }
+
+ }
+
+ return new self($href, $propertyLists, $statusCode);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Property/Complex.php b/vendor/sabre/dav/lib/DAV/Xml/Property/Complex.php
new file mode 100644
index 000000000..1d9202082
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Property/Complex.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Sabre\DAV\Xml\Property;
+
+use Sabre\Xml\Element\XmlFragment;
+use Sabre\Xml\Reader;
+
+/**
+ * This class represents a 'complex' property that didn't have a default
+ * decoder.
+ *
+ * It's basically a container for an xml snippet.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Complex extends XmlFragment {
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $xml = $reader->readInnerXml();
+
+ if ($reader->nodeType === Reader::ELEMENT && $reader->isEmptyElement) {
+ // Easy!
+ $reader->next();
+ return null;
+ }
+ // Now we have a copy of the inner xml, we need to traverse it to get
+ // all the strings. If there's no non-string data, we just return the
+ // string, otherwise we return an instance of this class.
+ $reader->read();
+
+ $nonText = false;
+ $text = '';
+
+ while (true) {
+
+ switch ($reader->nodeType) {
+ case Reader::ELEMENT :
+ $nonText = true;
+ $reader->next();
+ continue 2;
+ case Reader::TEXT :
+ case Reader::CDATA :
+ $text .= $reader->value;
+ break;
+ case Reader::END_ELEMENT :
+ break 2;
+ }
+ $reader->read();
+
+ }
+
+ // Make sure we advance the cursor one step further.
+ $reader->read();
+
+ if ($nonText) {
+ $new = new self($xml);
+ return $new;
+ } else {
+ return $text;
+ }
+
+ }
+
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Property/GetLastModified.php b/vendor/sabre/dav/lib/DAV/Xml/Property/GetLastModified.php
new file mode 100644
index 000000000..2db47269f
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Property/GetLastModified.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace Sabre\DAV\Xml\Property;
+
+use Sabre\Xml\Element;
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+use Sabre\HTTP;
+use DateTime;
+use DateTimeZone;
+
+/**
+ * This property represents the {DAV:}getlastmodified property.
+ *
+ * Defined in:
+ * http://tools.ietf.org/html/rfc4918#section-15.7
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class GetLastModified implements Element {
+
+ /**
+ * time
+ *
+ * @var DateTime
+ */
+ public $time;
+
+ /**
+ * Constructor
+ *
+ * @param int|DateTime $time
+ */
+ function __construct($time) {
+
+ if ($time instanceof DateTime) {
+ $this->time = clone $time;
+ } else {
+ $this->time = new DateTime('@' . $time);
+ }
+
+ // Setting timezone to UTC
+ $this->time->setTimezone(new DateTimeZone('UTC'));
+
+ }
+
+ /**
+ * getTime
+ *
+ * @return DateTime
+ */
+ function getTime() {
+
+ return $this->time;
+
+ }
+
+ /**
+ * The serialize method is called during xml writing.
+ *
+ * It should use the $writer argument to encode this object into XML.
+ *
+ * Important note: it is not needed to create the parent element. The
+ * parent element is already created, and we only have to worry about
+ * attributes, child elements and text (if any).
+ *
+ * Important note 2: If you are writing any new elements, you are also
+ * responsible for closing them.
+ *
+ * @param Writer $writer
+ * @return void
+ */
+ function xmlSerialize(Writer $writer) {
+
+ $writer->write(
+ HTTP\Util::toHTTPDate($this->time)
+ );
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * Important note 2: You are responsible for advancing the reader to the
+ * next element. Not doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ return
+ new self(new DateTime($reader->parseInnerTree()));
+
+ }
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Property/Href.php b/vendor/sabre/dav/lib/DAV/Xml/Property/Href.php
new file mode 100644
index 000000000..538e98d0f
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Property/Href.php
@@ -0,0 +1,176 @@
+<?php
+
+namespace Sabre\DAV\Xml\Property;
+
+use Sabre\DAV\Browser\HtmlOutput;
+use Sabre\DAV\Browser\HtmlOutputHelper;
+use Sabre\Xml\Element;
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+
+/**
+ * Href property
+ *
+ * This class represents any WebDAV property that contains a {DAV:}href
+ * element, and there are many.
+ *
+ * It can support either 1 or more hrefs. If while unserializing no valid
+ * {DAV:}href elements were found, this property will unserialize itself as
+ * null.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Href implements Element, HtmlOutput {
+
+ /**
+ * List of uris
+ *
+ * @var array
+ */
+ protected $hrefs;
+
+ /**
+ * Automatically prefix the url with the server base directory
+ *
+ * @var bool
+ */
+ protected $autoPrefix = true;
+
+ /**
+ * Constructor
+ *
+ * You must either pass a string for a single href, or an array of hrefs.
+ *
+ * If auto-prefix is set to false, the hrefs will be treated as absolute
+ * and not relative to the servers base uri.
+ *
+ * @param string|string[] $href
+ * @param bool $autoPrefix
+ */
+ function __construct($hrefs, $autoPrefix = true) {
+
+ if (is_string($hrefs)) {
+ $hrefs = [$hrefs];
+ }
+ $this->hrefs = $hrefs;
+ $this->autoPrefix = $autoPrefix;
+
+
+ }
+
+ /**
+ * Returns the first Href.
+ *
+ * @return string
+ */
+ function getHref() {
+
+ return $this->hrefs[0];
+
+ }
+
+ /**
+ * Returns the hrefs as an array
+ *
+ * @return array
+ */
+ function getHrefs() {
+
+ return $this->hrefs;
+
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ foreach ($this->getHrefs() as $href) {
+ if ($this->autoPrefix) {
+ $href = $writer->contextUri . \Sabre\HTTP\encodePath($href);
+ }
+ $writer->writeElement('{DAV:}href', $href);
+ }
+
+ }
+
+ /**
+ * Generate html representation for this value.
+ *
+ * The html output is 100% trusted, and no effort is being made to sanitize
+ * it. It's up to the implementor to sanitize user provided values.
+ *
+ * The output must be in UTF-8.
+ *
+ * The baseUri parameter is a url to the root of the application, and can
+ * be used to construct local links.
+ *
+ * @param HtmlOutputHelper $html
+ * @return string
+ */
+ function toHtml(HtmlOutputHelper $html) {
+
+ $links = [];
+ foreach ($this->getHrefs() as $href) {
+ $links[] = $html->link($href);
+ }
+ return implode('<br />', $links);
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $hrefs = [];
+ foreach ((array)$reader->parseInnerTree() as $elem) {
+ if ($elem['name'] !== '{DAV:}href')
+ continue;
+
+ $hrefs[] = $elem['value'];
+
+ }
+ if ($hrefs) {
+ return new self($hrefs, false);
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Property/LockDiscovery.php b/vendor/sabre/dav/lib/DAV/Xml/Property/LockDiscovery.php
new file mode 100644
index 000000000..f4b692219
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Property/LockDiscovery.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace Sabre\DAV\Xml\Property;
+
+use Sabre\DAV;
+use Sabre\DAV\Locks\LockInfo;
+use Sabre\Xml\Element\XmlFragment;
+use Sabre\Xml\Writer;
+use Sabre\Xml\XmlSerializable;
+
+/**
+ * Represents {DAV:}lockdiscovery property.
+ *
+ * This property is defined here:
+ * http://tools.ietf.org/html/rfc4918#section-15.8
+ *
+ * This property contains all the open locks on a given resource
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class LockDiscovery implements XmlSerializable {
+
+ /**
+ * locks
+ *
+ * @var LockInfo[]
+ */
+ public $locks;
+
+ /**
+ * Hides the {DAV:}lockroot element from the response.
+ *
+ * It was reported that showing the lockroot in the response can break
+ * Office 2000 compatibility.
+ *
+ * @var bool
+ */
+ static $hideLockRoot = false;
+
+ /**
+ * __construct
+ *
+ * @param LockInfo[] $locks
+ */
+ function __construct($locks) {
+
+ $this->locks = $locks;
+
+ }
+
+ /**
+ * The serialize method is called during xml writing.
+ *
+ * It should use the $writer argument to encode this object into XML.
+ *
+ * Important note: it is not needed to create the parent element. The
+ * parent element is already created, and we only have to worry about
+ * attributes, child elements and text (if any).
+ *
+ * Important note 2: If you are writing any new elements, you are also
+ * responsible for closing them.
+ *
+ * @param Writer $writer
+ * @return void
+ */
+ function xmlSerialize(Writer $writer) {
+
+ foreach ($this->locks as $lock) {
+
+ $writer->startElement('{DAV:}activelock');
+
+ $writer->startElement('{DAV:}lockscope');
+ if ($lock->scope === LockInfo::SHARED) {
+ $writer->writeElement('{DAV:}shared');
+ } else {
+ $writer->writeElement('{DAV:}exclusive');
+ }
+
+ $writer->endElement(); // {DAV:}lockscope
+
+ $writer->startElement('{DAV:}locktype');
+ $writer->writeElement('{DAV:}write');
+ $writer->endElement(); // {DAV:}locktype
+
+ if (!self::$hideLockRoot) {
+ $writer->startElement('{DAV:}lockroot');
+ $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->startElement('{DAV:}locktoken');
+ $writer->writeElement('{DAV:}href', 'opaquelocktoken:' . $lock->token);
+ $writer->endElement(); // {DAV:}locktoken
+
+ $writer->writeElement('{DAV:}owner', new XmlFragment($lock->owner));
+ $writer->endElement(); // {DAV:}activelock
+
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Property/ResourceType.php b/vendor/sabre/dav/lib/DAV/Xml/Property/ResourceType.php
new file mode 100644
index 000000000..302888321
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Property/ResourceType.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace Sabre\DAV\Xml\Property;
+
+use Sabre\DAV\Browser\HtmlOutput;
+use Sabre\DAV\Browser\HtmlOutputHelper;
+use Sabre\Xml\Element;
+use Sabre\Xml\Reader;
+
+/**
+ * {DAV:}resourcetype property
+ *
+ * This class represents the {DAV:}resourcetype property, as defined in:
+ *
+ * https://tools.ietf.org/html/rfc4918#section-15.9
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ResourceType extends Element\Elements implements HtmlOutput {
+
+ /**
+ * Constructor
+ *
+ * You can either pass null (for no resourcetype), a string (for a single
+ * resourcetype) or an array (for multiple).
+ *
+ * The resourcetype must be specified in clark-notation
+ *
+ * @param array|string|null $resourceType
+ */
+ function __construct($resourceTypes = null) {
+
+ parent::__construct((array)$resourceTypes);
+
+ }
+
+ /**
+ * Returns the values in clark-notation
+ *
+ * For example array('{DAV:}collection')
+ *
+ * @return array
+ */
+ function getValue() {
+
+ return $this->value;
+
+ }
+
+ /**
+ * Checks if the principal contains a certain value
+ *
+ * @param string $type
+ * @return bool
+ */
+ function is($type) {
+
+ return in_array($type, $this->value);
+
+ }
+
+ /**
+ * Adds a resourcetype value to this property
+ *
+ * @param string $type
+ * @return void
+ */
+ function add($type) {
+
+ $this->value[] = $type;
+ $this->value = array_unique($this->value);
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * Important note 2: You are responsible for advancing the reader to the
+ * next element. Not doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ return
+ new self(parent::xmlDeserialize($reader));
+
+ }
+
+ /**
+ * Generate html representation for this value.
+ *
+ * The html output is 100% trusted, and no effort is being made to sanitize
+ * it. It's up to the implementor to sanitize user provided values.
+ *
+ * The output must be in UTF-8.
+ *
+ * The baseUri parameter is a url to the root of the application, and can
+ * be used to construct local links.
+ *
+ * @param HtmlOutputHelper $html
+ * @return string
+ */
+ function toHtml(HtmlOutputHelper $html) {
+
+ return implode(
+ ', ',
+ array_map([$html, 'xmlName'], $this->getValue())
+ );
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Property/SupportedLock.php b/vendor/sabre/dav/lib/DAV/Xml/Property/SupportedLock.php
new file mode 100644
index 000000000..f6d01aa37
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Property/SupportedLock.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Sabre\DAV\Xml\Property;
+
+use Sabre\Xml\Writer;
+use Sabre\Xml\XmlSerializable;
+
+/**
+ * This class represents the {DAV:}supportedlock property.
+ *
+ * This property is defined here:
+ * http://tools.ietf.org/html/rfc4918#section-15.10
+ *
+ * This property contains information about what kind of locks
+ * this server supports.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class SupportedLock implements XmlSerializable {
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ $writer->writeElement('{DAV:}lockentry', [
+ '{DAV:}lockscope' => ['{DAV:}exclusive' => null],
+ '{DAV:}locktype' => ['{DAV:}write' => null],
+ ]);
+ $writer->writeElement('{DAV:}lockentry', [
+ '{DAV:}lockscope' => ['{DAV:}shared' => null],
+ '{DAV:}locktype' => ['{DAV:}write' => null],
+ ]);
+
+ }
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Property/SupportedMethodSet.php b/vendor/sabre/dav/lib/DAV/Xml/Property/SupportedMethodSet.php
new file mode 100644
index 000000000..56b418db6
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Property/SupportedMethodSet.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace Sabre\DAV\Xml\Property;
+
+use Sabre\DAV\Browser\HtmlOutput;
+use Sabre\DAV\Browser\HtmlOutputHelper;
+use Sabre\Xml\Writer;
+use Sabre\Xml\XmlSerializable;
+
+/**
+ * supported-method-set property.
+ *
+ * This property is defined in RFC3253, but since it's
+ * so common in other webdav-related specs, it is part of the core server.
+ *
+ * This property is defined here:
+ * http://tools.ietf.org/html/rfc3253#section-3.1.3
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class SupportedMethodSet implements XmlSerializable, HtmlOutput {
+
+ /**
+ * List of methods
+ *
+ * @var string[]
+ */
+ protected $methods = [];
+
+ /**
+ * Creates the property
+ *
+ * Any reports passed in the constructor
+ * should be valid report-types in clark-notation.
+ *
+ * Either a string or an array of strings must be passed.
+ *
+ * @param string|string[] $methods
+ */
+ function __construct($methods = null) {
+
+ $this->methods = (array)$methods;
+
+ }
+
+ /**
+ * Returns the list of supported http methods.
+ *
+ * @return string[]
+ */
+ function getValue() {
+
+ return $this->methods;
+
+ }
+
+ /**
+ * Returns true or false if the property contains a specific method.
+ *
+ * @param string $methodName
+ * @return bool
+ */
+ function has($methodName) {
+
+ return in_array(
+ $methodName,
+ $this->methods
+ );
+
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ foreach ($this->getValue() as $val) {
+ $writer->startElement('{DAV:}supported-method');
+ $writer->writeAttribute('name', $val);
+ $writer->endElement();
+ }
+
+ }
+
+ /**
+ * Generate html representation for this value.
+ *
+ * The html output is 100% trusted, and no effort is being made to sanitize
+ * it. It's up to the implementor to sanitize user provided values.
+ *
+ * The output must be in UTF-8.
+ *
+ * The baseUri parameter is a url to the root of the application, and can
+ * be used to construct local links.
+ *
+ * @param HtmlOutputHelper $html
+ * @return string
+ */
+ function toHtml(HtmlOutputHelper $html) {
+
+ return implode(
+ ', ',
+ array_map([$html, 'h'], $this->getValue())
+ );
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Property/SupportedReportSet.php b/vendor/sabre/dav/lib/DAV/Xml/Property/SupportedReportSet.php
new file mode 100644
index 000000000..ebf27300d
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Property/SupportedReportSet.php
@@ -0,0 +1,154 @@
+<?php
+
+namespace Sabre\DAV\Xml\Property;
+
+use Sabre\DAV;
+use Sabre\DAV\Browser\HtmlOutput;
+use Sabre\DAV\Browser\HtmlOutputHelper;
+use Sabre\Xml\Writer;
+use Sabre\Xml\XmlSerializable;
+
+/**
+ * supported-report-set property.
+ *
+ * This property is defined in RFC3253, but since it's
+ * so common in other webdav-related specs, it is part of the core server.
+ *
+ * This property is defined here:
+ * http://tools.ietf.org/html/rfc3253#section-3.1.5
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class SupportedReportSet implements XmlSerializable, HtmlOutput {
+
+ /**
+ * List of reports
+ *
+ * @var array
+ */
+ protected $reports = [];
+
+ /**
+ * Creates the property
+ *
+ * Any reports passed in the constructor
+ * should be valid report-types in clark-notation.
+ *
+ * Either a string or an array of strings must be passed.
+ *
+ * @param string|string[] $reports
+ */
+ function __construct($reports = null) {
+
+ if (!is_null($reports))
+ $this->addReport($reports);
+
+ }
+
+ /**
+ * Adds a report to this property
+ *
+ * The report must be a string in clark-notation.
+ * Multiple reports can be specified as an array.
+ *
+ * @param mixed $report
+ * @return void
+ */
+ function addReport($report) {
+
+ $report = (array)$report;
+
+ foreach ($report as $r) {
+
+ if (!preg_match('/^{([^}]*)}(.*)$/', $r))
+ throw new DAV\Exception('Reportname must be in clark-notation');
+
+ $this->reports[] = $r;
+
+ }
+
+ }
+
+ /**
+ * Returns the list of supported reports
+ *
+ * @return string[]
+ */
+ function getValue() {
+
+ return $this->reports;
+
+ }
+
+ /**
+ * Returns true or false if the property contains a specific report.
+ *
+ * @param string $reportName
+ * @return bool
+ */
+ function has($reportName) {
+
+ return in_array(
+ $reportName,
+ $this->reports
+ );
+
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ foreach ($this->getValue() as $val) {
+ $writer->startElement('{DAV:}supported-report');
+ $writer->startElement('{DAV:}report');
+ $writer->writeElement($val);
+ $writer->endElement();
+ $writer->endElement();
+ }
+
+ }
+
+ /**
+ * Generate html representation for this value.
+ *
+ * The html output is 100% trusted, and no effort is being made to sanitize
+ * it. It's up to the implementor to sanitize user provided values.
+ *
+ * The output must be in UTF-8.
+ *
+ * The baseUri parameter is a url to the root of the application, and can
+ * be used to construct local links.
+ *
+ * @param HtmlOutputHelper $html
+ * @return string
+ */
+ function toHtml(HtmlOutputHelper $html) {
+
+ return implode(
+ ', ',
+ array_map([$html, 'xmlName'], $this->getValue())
+ );
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Request/Lock.php b/vendor/sabre/dav/lib/DAV/Xml/Request/Lock.php
new file mode 100644
index 000000000..76df98d13
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Request/Lock.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Sabre\DAV\Xml\Request;
+
+use Sabre\DAV\Locks\LockInfo;
+use Sabre\Xml\Element\KeyValue;
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+
+/**
+ * WebDAV LOCK request parser.
+ *
+ * This class parses the {DAV:}lockinfo request, as defined in:
+ *
+ * http://tools.ietf.org/html/rfc4918#section-9.10
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Lock implements XmlDeserializable {
+
+ /**
+ * Owner of the lock
+ *
+ * @var string
+ */
+ public $owner;
+
+ /**
+ * Scope of the lock.
+ *
+ * Either LockInfo::SHARED or LockInfo::EXCLUSIVE
+ * @var int
+ */
+ public $scope;
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $reader->pushContext();
+ $reader->elementMap['{DAV:}owner'] = 'Sabre\\Xml\\Element\\XmlFragment';
+
+ $values = KeyValue::xmlDeserialize($reader);
+
+ $reader->popContext();
+
+ $new = new self();
+ $new->owner = !empty($values['{DAV:}owner']) ? $values['{DAV:}owner']->getXml() : null;
+ $new->scope = LockInfo::SHARED;
+
+ if (isset($values['{DAV:}lockscope'])) {
+ foreach ($values['{DAV:}lockscope'] as $elem) {
+ if ($elem['name'] === '{DAV:}exclusive') $new->scope = LockInfo::EXCLUSIVE;
+ }
+ }
+ return $new;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Request/MkCol.php b/vendor/sabre/dav/lib/DAV/Xml/Request/MkCol.php
new file mode 100644
index 000000000..5db239061
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Request/MkCol.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Sabre\DAV\Xml\Request;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+
+/**
+ * WebDAV Extended MKCOL request parser.
+ *
+ * This class parses the {DAV:}mkol request, as defined in:
+ *
+ * https://tools.ietf.org/html/rfc5689#section-5.1
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class MkCol implements XmlDeserializable {
+
+ /**
+ * The list of properties that will be set.
+ *
+ * @var array
+ */
+ protected $properties = [];
+
+ /**
+ * Returns a key=>value array with properties that are supposed to get set
+ * during creation of the new collection.
+ *
+ * @return array
+ */
+ function getProperties() {
+
+ return $this->properties;
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $self = new self();
+
+ $elementMap = $reader->elementMap;
+ $elementMap['{DAV:}prop'] = 'Sabre\DAV\Xml\Element\Prop';
+ $elementMap['{DAV:}set'] = 'Sabre\Xml\Element\KeyValue';
+ $elementMap['{DAV:}remove'] = 'Sabre\Xml\Element\KeyValue';
+
+ $elems = $reader->parseInnerTree($elementMap);
+
+ foreach ($elems as $elem) {
+ if ($elem['name'] === '{DAV:}set') {
+ $self->properties = array_merge($self->properties, $elem['value']['{DAV:}prop']);
+ }
+ }
+
+ return $self;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Request/PropFind.php b/vendor/sabre/dav/lib/DAV/Xml/Request/PropFind.php
new file mode 100644
index 000000000..ad3ad7c43
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Request/PropFind.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Sabre\DAV\Xml\Request;
+
+use Sabre\Xml\Element\KeyValue;
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+
+/**
+ * WebDAV PROPFIND request parser.
+ *
+ * This class parses the {DAV:}propfind request, as defined in:
+ *
+ * https://tools.ietf.org/html/rfc4918#section-14.20
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PropFind implements XmlDeserializable {
+
+ /**
+ * If this is set to true, this was an 'allprop' request.
+ *
+ * @var bool
+ */
+ public $allProp = false;
+
+ /**
+ * The property list
+ *
+ * @var null|array
+ */
+ public $properties;
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $self = new self();
+
+ $reader->pushContext();
+ $reader->elementMap['{DAV:}prop'] = 'Sabre\Xml\Element\Elements';
+
+ foreach (KeyValue::xmlDeserialize($reader) as $k => $v) {
+
+ switch ($k) {
+ case '{DAV:}prop' :
+ $self->properties = $v;
+ break;
+ case '{DAV:}allprop' :
+ $self->allProp = true;
+
+ }
+
+ }
+
+ $reader->popContext();
+
+ return $self;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Request/PropPatch.php b/vendor/sabre/dav/lib/DAV/Xml/Request/PropPatch.php
new file mode 100644
index 000000000..07a05f887
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Request/PropPatch.php
@@ -0,0 +1,118 @@
+<?php
+
+namespace Sabre\DAV\Xml\Request;
+
+use Sabre\Xml\Element;
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+
+/**
+ * WebDAV PROPPATCH request parser.
+ *
+ * This class parses the {DAV:}propertyupdate request, as defined in:
+ *
+ * https://tools.ietf.org/html/rfc4918#section-14.20
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PropPatch implements Element {
+
+ /**
+ * The list of properties that will be updated and removed.
+ *
+ * If a property will be removed, it's value will be set to null.
+ *
+ * @var array
+ */
+ public $properties = [];
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ foreach ($this->properties as $propertyName => $propertyValue) {
+
+ if (is_null($propertyValue)) {
+ $writer->startElement("{DAV:}remove");
+ $writer->write(['{DAV:}prop' => [$propertyName => $propertyValue]]);
+ $writer->endElement();
+ } else {
+ $writer->startElement("{DAV:}set");
+ $writer->write(['{DAV:}prop' => [$propertyName => $propertyValue]]);
+ $writer->endElement();
+ }
+
+ }
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $self = new self();
+
+ $elementMap = $reader->elementMap;
+ $elementMap['{DAV:}prop'] = 'Sabre\DAV\Xml\Element\Prop';
+ $elementMap['{DAV:}set'] = 'Sabre\Xml\Element\KeyValue';
+ $elementMap['{DAV:}remove'] = 'Sabre\Xml\Element\KeyValue';
+
+ $elems = $reader->parseInnerTree($elementMap);
+
+ foreach ($elems as $elem) {
+ if ($elem['name'] === '{DAV:}set') {
+ $self->properties = array_merge($self->properties, $elem['value']['{DAV:}prop']);
+ }
+ if ($elem['name'] === '{DAV:}remove') {
+
+ // Ensuring there are no values.
+ foreach ($elem['value']['{DAV:}prop'] as $remove => $value) {
+ $self->properties[$remove] = null;
+ }
+
+ }
+ }
+
+ return $self;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php b/vendor/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php
new file mode 100644
index 000000000..3092ada47
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace Sabre\DAV\Xml\Request;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+use Sabre\Xml\Element\KeyValue;
+use Sabre\DAV\Exception\BadRequest;
+
+/**
+ * SyncCollection request parser.
+ *
+ * This class parses the {DAV:}sync-collection reprot, as defined in:
+ *
+ * http://tools.ietf.org/html/rfc6578#section-3.2
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class SyncCollectionReport implements XmlDeserializable {
+
+ /**
+ * The sync-token the client supplied for the report.
+ *
+ * @var string|null
+ */
+ public $syncToken;
+
+ /**
+ * The 'depth' of the sync the client is interested in.
+ *
+ * @var int
+ */
+ public $syncLevel;
+
+ /**
+ * Maximum amount of items returned.
+ *
+ * @var int|null
+ */
+ public $limit;
+
+ /**
+ * The list of properties that are being requested for every change.
+ *
+ * @var null|array
+ */
+ public $properties;
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $self = new self();
+
+ $reader->pushContext();
+
+ $reader->elementMap['{DAV:}prop'] = 'Sabre\Xml\Element\Elements';
+ $elems = KeyValue::xmlDeserialize($reader);
+
+ $reader->popContext();
+
+ $required = [
+ '{DAV:}sync-token',
+ '{DAV:}prop',
+ ];
+
+ foreach ($required as $elem) {
+ if (!array_key_exists($elem, $elems)) {
+ 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'];
+ }
+ }
+ $self->limit = $nresults;
+ }
+
+ if (isset($elems['{DAV:}sync-level'])) {
+
+ $value = $elems['{DAV:}sync-level'];
+ if ($value === 'infinity') {
+ $value = \Sabre\DAV\Server::DEPTH_INFINITY;
+ }
+ $self->syncLevel = $value;
+
+ }
+
+ return $self;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php b/vendor/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php
new file mode 100644
index 000000000..16a3d4a68
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace Sabre\DAV\Xml\Response;
+
+use Sabre\Xml\Element;
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+
+/**
+ * WebDAV MultiStatus parser
+ *
+ * This class parses the {DAV:}multistatus response, as defined in:
+ * https://tools.ietf.org/html/rfc4918#section-14.16
+ *
+ * And it also adds the {DAV:}synctoken change from:
+ * http://tools.ietf.org/html/rfc6578#section-6.4
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class MultiStatus implements Element {
+
+ /**
+ * The responses
+ *
+ * @var \Sabre\DAV\Xml\Element\Response[]
+ */
+ protected $responses;
+
+ /**
+ * A sync token (from RFC6578).
+ *
+ * @var string
+ */
+ protected $syncToken;
+
+ /**
+ * Constructor
+ *
+ * @param \Sabre\DAV\Xml\Element\Response[] $responses
+ * @param string $syncToken
+ */
+ function __construct(array $responses, $syncToken = null) {
+
+ $this->responses = $responses;
+ $this->syncToken = $syncToken;
+
+ }
+
+ /**
+ * Returns the response list.
+ *
+ * @return \Sabre\DAV\Xml\Element\Response[]
+ */
+ function getResponses() {
+
+ return $this->responses;
+
+ }
+
+ /**
+ * Returns the sync-token, if available.
+ *
+ * @return string|null
+ */
+ function getSyncToken() {
+
+ return $this->syncToken;
+
+ }
+
+ /**
+ * The serialize method is called during xml writing.
+ *
+ * It should use the $writer argument to encode this object into XML.
+ *
+ * Important note: it is not needed to create the parent element. The
+ * parent element is already created, and we only have to worry about
+ * attributes, child elements and text (if any).
+ *
+ * Important note 2: If you are writing any new elements, you are also
+ * responsible for closing them.
+ *
+ * @param Writer $writer
+ * @return void
+ */
+ function xmlSerialize(Writer $writer) {
+
+ foreach ($this->getResponses() as $response) {
+ $writer->writeElement('{DAV:}response', $response);
+ }
+ if ($syncToken = $this->getSyncToken()) {
+ $writer->writeElement('{DAV:}sync-token', $syncToken);
+ }
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $elementMap = $reader->elementMap;
+ $elementMap['{DAV:}prop'] = 'Sabre\\DAV\\Xml\\Element\\Prop';
+ $elements = $reader->parseInnerTree($elementMap);
+
+ $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'];
+ }
+ }
+
+ return new self($responses, $syncToken);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAV/Xml/Service.php b/vendor/sabre/dav/lib/DAV/Xml/Service.php
new file mode 100644
index 000000000..f41ed984a
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/Xml/Service.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Sabre\DAV\Xml;
+
+/**
+ * XML service for WebDAV
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Service extends \Sabre\Xml\Service {
+
+ /**
+ * This is a list of XML elements that we automatically map to PHP classes.
+ *
+ * For instance, this list may contain an entry `{DAV:}propfind` that would
+ * be mapped to Sabre\DAV\Xml\Request\PropFind
+ */
+ public $elementMap = [
+ '{DAV:}multistatus' => 'Sabre\\DAV\\Xml\\Response\\MultiStatus',
+ '{DAV:}response' => 'Sabre\\DAV\\Xml\\Element\\Response',
+
+ // Requests
+ '{DAV:}propfind' => 'Sabre\\DAV\\Xml\\Request\\PropFind',
+ '{DAV:}propertyupdate' => 'Sabre\\DAV\\Xml\\Request\\PropPatch',
+ '{DAV:}mkcol' => 'Sabre\\DAV\\Xml\\Request\\MkCol',
+
+ // Properties
+ '{DAV:}resourcetype' => 'Sabre\\DAV\\Xml\\Property\\ResourceType',
+
+ ];
+
+ /**
+ * This is a default list of namespaces.
+ *
+ * If you are defining your own custom namespace, add it here to reduce
+ * bandwidth and improve legibility of xml bodies.
+ *
+ * @var array
+ */
+ public $namespaceMap = [
+ 'DAV:' => 'd',
+ 'http://sabredav.org/ns' => 's',
+ ];
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/AbstractPrincipalCollection.php b/vendor/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php
index a116236f3..460f78981 100644
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/AbstractPrincipalCollection.php
+++ b/vendor/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php
@@ -1,7 +1,9 @@
<?php
namespace Sabre\DAVACL;
+
use Sabre\DAV;
+use Sabre\HTTP\URLUtil;
/**
* Principals Collection
@@ -11,25 +13,25 @@ use Sabre\DAV;
*
* To use this class, simply implement the getChildForPrincipal method.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
abstract class AbstractPrincipalCollection extends DAV\Collection implements IPrincipalCollection {
/**
- * Node or 'directory' name.
+ * Principal backend
*
- * @var string
+ * @var PrincipalBackend\BackendInterface
*/
- protected $path;
+ protected $principalBackend;
/**
- * Principal backend
+ * The path to the principals we're listing from.
*
- * @var PrincipalBackend\BackendInterface
+ * @var string
*/
- protected $principalBackend;
+ protected $principalPrefix;
/**
* If this value is set to true, it effectively disables listing of users
@@ -51,7 +53,7 @@ abstract class AbstractPrincipalCollection extends DAV\Collection implements IPr
* @param PrincipalBackend\BackendInterface $principalBackend
* @param string $principalPrefix
*/
- public function __construct(PrincipalBackend\BackendInterface $principalBackend, $principalPrefix = 'principals') {
+ function __construct(PrincipalBackend\BackendInterface $principalBackend, $principalPrefix = 'principals') {
$this->principalPrefix = $principalPrefix;
$this->principalBackend = $principalBackend;
@@ -75,9 +77,9 @@ abstract class AbstractPrincipalCollection extends DAV\Collection implements IPr
*
* @return string
*/
- public function getName() {
+ function getName() {
- list(,$name) = DAV\URLUtil::splitPath($this->principalPrefix);
+ list(, $name) = URLUtil::splitPath($this->principalPrefix);
return $name;
}
@@ -87,13 +89,13 @@ abstract class AbstractPrincipalCollection extends DAV\Collection implements IPr
*
* @return array
*/
- public function getChildren() {
+ function getChildren() {
if ($this->disableListing)
throw new DAV\Exception\MethodNotAllowed('Listing members of this collection is disabled');
- $children = array();
- foreach($this->principalBackend->getPrincipalsByPrefix($this->principalPrefix) as $principalInfo) {
+ $children = [];
+ foreach ($this->principalBackend->getPrincipalsByPrefix($this->principalPrefix) as $principalInfo) {
$children[] = $this->getChildForPrincipal($principalInfo);
@@ -110,7 +112,7 @@ abstract class AbstractPrincipalCollection extends DAV\Collection implements IPr
* @throws DAV\Exception\NotFound
* @return IPrincipal
*/
- public function getChild($name) {
+ function getChild($name) {
$principalInfo = $this->principalBackend->getPrincipalByPath($this->principalPrefix . '/' . $name);
if (!$principalInfo) throw new DAV\Exception\NotFound('Principal with name ' . $name . ' not found');
@@ -130,26 +132,50 @@ abstract class AbstractPrincipalCollection extends DAV\Collection implements IPr
* keys in searchProperties are the WebDAV property names, while the values
* are the property values to search on.
*
- * If multiple properties are being searched on, the search should be
- * AND'ed.
+ * By default, if multiple properties are submitted to this method, the
+ * various properties should be combined with 'AND'. If $test is set to
+ * 'anyof', it should be combined using 'OR'.
*
* This method should simply return a list of 'child names', which may be
* used to call $this->getChild in the future.
*
* @param array $searchProperties
+ * @param string $test
* @return array
*/
- public function searchPrincipals(array $searchProperties) {
+ function searchPrincipals(array $searchProperties, $test = 'allof') {
- $result = $this->principalBackend->searchPrincipals($this->principalPrefix, $searchProperties);
- $r = array();
+ $result = $this->principalBackend->searchPrincipals($this->principalPrefix, $searchProperties, $test);
+ $r = [];
- foreach($result as $row) {
- list(, $r[]) = DAV\URLUtil::splitPath($row);
+ foreach ($result as $row) {
+ list(, $r[]) = URLUtil::splitPath($row);
}
return $r;
}
+ /**
+ * Finds a principal by its URI.
+ *
+ * This method may receive any type of uri, but mailto: addresses will be
+ * the most common.
+ *
+ * Implementation of this API is optional. It is currently used by the
+ * CalDAV system to find principals based on their email addresses. If this
+ * API is not implemented, some features may not work correctly.
+ *
+ * This method must return a relative principal path, or null, if the
+ * principal was not found or you refuse to find it.
+ *
+ * @param string $uri
+ * @return string
+ */
+ function findByUri($uri) {
+
+ return $this->principalBackend->findByUri($uri, $this->principalPrefix);
+
+ }
+
}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/Exception/AceConflict.php b/vendor/sabre/dav/lib/DAVACL/Exception/AceConflict.php
index 6ee9afd73..22450b4a6 100644
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/Exception/AceConflict.php
+++ b/vendor/sabre/dav/lib/DAVACL/Exception/AceConflict.php
@@ -8,7 +8,7 @@ use Sabre\DAV;
* This exception is thrown when a client attempts to set conflicting
* permissions.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -23,11 +23,11 @@ class AceConflict extends DAV\Exception\Conflict {
* @param \DOMElement $errorNode
* @return void
*/
- public function serialize(DAV\Server $server,\DOMElement $errorNode) {
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
$doc = $errorNode->ownerDocument;
- $np = $doc->createElementNS('DAV:','d:no-ace-conflict');
+ $np = $doc->createElementNS('DAV:', 'd:no-ace-conflict');
$errorNode->appendChild($np);
}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/Exception/NeedPrivileges.php b/vendor/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php
index f7e435883..5624fd22f 100644
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/Exception/NeedPrivileges.php
+++ b/vendor/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php
@@ -10,7 +10,7 @@ use Sabre\DAV;
* The 403-need privileges is thrown when a user didn't have the appropriate
* permissions to perform an operation
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -36,7 +36,7 @@ class NeedPrivileges extends DAV\Exception\Forbidden {
* @param string $uri
* @param array $privileges
*/
- public function __construct($uri,array $privileges) {
+ function __construct($uri, array $privileges) {
$this->uri = $uri;
$this->privileges = $privileges;
@@ -54,25 +54,25 @@ class NeedPrivileges extends DAV\Exception\Forbidden {
* @param \DOMElement $errorNode
* @return void
*/
- public function serialize(DAV\Server $server,\DOMElement $errorNode) {
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
$doc = $errorNode->ownerDocument;
- $np = $doc->createElementNS('DAV:','d:need-privileges');
+ $np = $doc->createElementNS('DAV:', 'd:need-privileges');
$errorNode->appendChild($np);
- foreach($this->privileges as $privilege) {
+ foreach ($this->privileges as $privilege) {
- $resource = $doc->createElementNS('DAV:','d:resource');
+ $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');
+ $priv = $doc->createElementNS('DAV:', 'd:privilege');
$resource->appendChild($priv);
- preg_match('/^{([^}]*)}(.*)$/',$privilege,$privilegeParts);
- $priv->appendChild($doc->createElementNS($privilegeParts[1],'d:' . $privilegeParts[2]));
+ preg_match('/^{([^}]*)}(.*)$/', $privilege, $privilegeParts);
+ $priv->appendChild($doc->createElementNS($privilegeParts[1], 'd:' . $privilegeParts[2]));
}
@@ -80,4 +80,3 @@ class NeedPrivileges extends DAV\Exception\Forbidden {
}
}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/Exception/NoAbstract.php b/vendor/sabre/dav/lib/DAVACL/Exception/NoAbstract.php
index ba6f76cdb..a2363b174 100644
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/Exception/NoAbstract.php
+++ b/vendor/sabre/dav/lib/DAVACL/Exception/NoAbstract.php
@@ -8,7 +8,7 @@ use Sabre\DAV;
* This exception is thrown when a user tries to set a privilege that's marked
* as abstract.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -23,11 +23,11 @@ class NoAbstract extends DAV\Exception\PreconditionFailed {
* @param \DOMElement $errorNode
* @return void
*/
- public function serialize(DAV\Server $server,\DOMElement $errorNode) {
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
$doc = $errorNode->ownerDocument;
- $np = $doc->createElementNS('DAV:','d:no-abstract');
+ $np = $doc->createElementNS('DAV:', 'd:no-abstract');
$errorNode->appendChild($np);
}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php b/vendor/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php
index f61edef07..4349bf101 100644
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php
+++ b/vendor/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php
@@ -8,7 +8,7 @@ use Sabre\DAV;
* If a client tried to set a privilege assigned to a non-existant principal,
* this exception will be thrown.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -23,11 +23,11 @@ class NotRecognizedPrincipal extends DAV\Exception\PreconditionFailed {
* @param \DOMElement $errorNode
* @return void
*/
- public function serialize(DAV\Server $server,\DOMElement $errorNode) {
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
$doc = $errorNode->ownerDocument;
- $np = $doc->createElementNS('DAV:','d:recognized-principal');
+ $np = $doc->createElementNS('DAV:', 'd:recognized-principal');
$errorNode->appendChild($np);
}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/Exception/NotSupportedPrivilege.php b/vendor/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php
index 6d30698cd..73b81190d 100644
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/Exception/NotSupportedPrivilege.php
+++ b/vendor/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php
@@ -8,7 +8,7 @@ use Sabre\DAV;
* If a client tried to set a privilege that doesn't exist, this exception will
* be thrown.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -23,11 +23,11 @@ class NotSupportedPrivilege extends DAV\Exception\PreconditionFailed {
* @param \DOMElement $errorNode
* @return void
*/
- public function serialize(DAV\Server $server,\DOMElement $errorNode) {
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
$doc = $errorNode->ownerDocument;
- $np = $doc->createElementNS('DAV:','d:not-supported-privilege');
+ $np = $doc->createElementNS('DAV:', 'd:not-supported-privilege');
$errorNode->appendChild($np);
}
diff --git a/vendor/sabre/dav/lib/DAVACL/FS/Collection.php b/vendor/sabre/dav/lib/DAVACL/FS/Collection.php
new file mode 100644
index 000000000..5fab4768c
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/FS/Collection.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace Sabre\DAVACL\FS;
+
+use Sabre\DAV\FSExt\Directory as BaseCollection;
+use Sabre\DAVACL\IACL;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\Exception\NotFound;
+
+/**
+ * This is an ACL-enabled collection.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Collection extends BaseCollection implements IACL {
+
+ /**
+ * A list of ACL rules.
+ *
+ * @var array
+ */
+ protected $acl;
+
+ /**
+ * Owner uri, or null for no owner.
+ *
+ * @var string|null
+ */
+ protected $owner;
+
+ /**
+ * Constructor
+ *
+ * @param string $path on-disk path.
+ * @param array $acl ACL rules.
+ * @param string|null $owner principal owner string.
+ */
+ function __construct($path, array $acl, $owner = null) {
+
+ parent::__construct($path);
+ $this->acl = $acl;
+ $this->owner = $owner;
+
+ }
+
+ /**
+ * 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 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
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getOwner() {
+
+ return $this->owner;
+
+ }
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getGroup() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+
+ return $this->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) {
+
+ throw new Forbidden('Setting ACL is not allowed here');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/FS/File.php b/vendor/sabre/dav/lib/DAVACL/FS/File.php
new file mode 100644
index 000000000..0d549528b
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/FS/File.php
@@ -0,0 +1,123 @@
+<?php
+
+namespace Sabre\DAVACL\FS;
+
+use Sabre\DAV\FSExt\File as BaseFile;
+use Sabre\DAVACL\IACL;
+use Sabre\DAV\Exception\Forbidden;
+
+/**
+ * This is an ACL-enabled file node.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class File extends BaseFile implements IACL {
+
+ /**
+ * A list of ACL rules.
+ *
+ * @var array
+ */
+ protected $acl;
+
+ /**
+ * Owner uri, or null for no owner.
+ *
+ * @var string|null
+ */
+ protected $owner;
+
+ /**
+ * Constructor
+ *
+ * @param string $path on-disk path.
+ * @param array $acl ACL rules.
+ * @param string|null $owner principal owner string.
+ */
+ function __construct($path, array $acl, $owner = null) {
+
+ parent::__construct($path);
+ $this->acl = $acl;
+ $this->owner = $owner;
+
+ }
+
+ /**
+ * Returns the owner principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getOwner() {
+
+ return $this->owner;
+
+ }
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getGroup() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+
+ return $this->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) {
+
+ throw new Forbidden('Setting ACL is not allowed here');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/FS/HomeCollection.php b/vendor/sabre/dav/lib/DAVACL/FS/HomeCollection.php
new file mode 100644
index 000000000..c27616770
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/FS/HomeCollection.php
@@ -0,0 +1,188 @@
+<?php
+
+namespace Sabre\DAVACL\FS;
+
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAVACL\AbstractPrincipalCollection;
+use Sabre\DAVACL\IACL;
+use Sabre\DAVACL\PrincipalBackend\BackendInterface;
+use Sabre\Uri;
+
+/**
+ * This collection contains a collection for every principal.
+ * It is similar to /home on many unix systems.
+ *
+ * The per-user collections can only be accessed by the user who owns the
+ * collection.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class HomeCollection extends AbstractPrincipalCollection implements IACL {
+
+ /**
+ * Name of this collection.
+ *
+ * @var string
+ */
+ public $collectionName = 'home';
+
+ /**
+ * Path to where the users' files are actually stored.
+ *
+ * @var string
+ */
+ protected $storagePath;
+
+ /**
+ * Creates the home collection.
+ *
+ * @param BackendInterface $principalBackend
+ * @param string $storagePath Where the actual files are stored.
+ * @param string $principalPrefix list of principals to iterate.
+ */
+ function __construct(BackendInterface $principalBackend, $storagePath, $principalPrefix = 'principals') {
+
+ parent::__construct($principalBackend, $principalPrefix);
+ $this->storagePath = $storagePath;
+
+ }
+
+ /**
+ * Returns the name of the node.
+ *
+ * This is used to generate the url.
+ *
+ * @return string
+ */
+ function getName() {
+
+ return $this->collectionName;
+
+ }
+
+ /**
+ * Returns a principals' collection of files.
+ *
+ * The passed array contains principal information, and is guaranteed to
+ * at least contain a uri item. Other properties may or may not be
+ * supplied by the authentication backend.
+ *
+ * @param array $principalInfo
+ * @return void
+ */
+ function getChildForPrincipal(array $principalInfo) {
+
+ $owner = $principalInfo['uri'];
+ $acl = [
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $owner,
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}write',
+ 'principal' => $owner,
+ 'protected' => true,
+ ],
+ ];
+
+ list(, $principalBaseName) = Uri\split($owner);
+
+ $path = $this->storagePath . '/' . $principalBaseName;
+
+ if (!is_dir($path)) {
+ mkdir($path, 0777, true);
+ }
+ return new Collection(
+ $path,
+ $acl,
+ $owner
+ );
+
+ }
+
+ /**
+ * Returns the owner principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getOwner() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getGroup() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+
+ return [
+ [
+ 'principal' => '{DAV:}authenticated',
+ 'privilege' => '{DAV:}read',
+ 'protected' => true,
+ ]
+ ];
+
+ }
+
+ /**
+ * Updates the ACL
+ *
+ * This method will receive a list of new ACE's as an array argument.
+ *
+ * @param array $acl
+ * @return void
+ */
+ function setACL(array $acl) {
+
+ throw new Forbidden('Setting ACL is not allowed here');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/IACL.php b/vendor/sabre/dav/lib/DAVACL/IACL.php
index 088ca3eec..81908d08f 100644
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/IACL.php
+++ b/vendor/sabre/dav/lib/DAVACL/IACL.php
@@ -1,6 +1,7 @@
<?php
namespace Sabre\DAVACL;
+
use Sabre\DAV;
/**
@@ -8,7 +9,7 @@ use Sabre\DAV;
*
* If you want to add WebDAV ACL to a node, you must implement this class
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/IPrincipal.php b/vendor/sabre/dav/lib/DAVACL/IPrincipal.php
index d88a0289b..13728471e 100644
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/IPrincipal.php
+++ b/vendor/sabre/dav/lib/DAVACL/IPrincipal.php
@@ -9,7 +9,7 @@ use Sabre\DAV;
*
* Implement this interface to define your own principals
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/IPrincipalCollection.php b/vendor/sabre/dav/lib/DAVACL/IPrincipalCollection.php
index 2c097f9d7..3ab8382fa 100644
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/IPrincipalCollection.php
+++ b/vendor/sabre/dav/lib/DAVACL/IPrincipalCollection.php
@@ -10,11 +10,11 @@ use Sabre\DAV;
* Implement this interface to ensure that your principal collection can be
* searched using the principal-property-search REPORT.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-interface IPrincipalCollection extends DAV\INode {
+interface IPrincipalCollection extends DAV\ICollection {
/**
* This method is used to search for principals matching a set of
@@ -28,15 +28,35 @@ interface IPrincipalCollection extends DAV\INode {
* keys in searchProperties are the WebDAV property names, while the values
* are the property values to search on.
*
- * If multiple properties are being searched on, the search should be
- * AND'ed.
+ * By default, if multiple properties are submitted to this method, the
+ * various properties should be combined with 'AND'. If $test is set to
+ * 'anyof', it should be combined using 'OR'.
*
* This method should simply return a list of 'child names', which may be
* used to call $this->getChild in the future.
*
* @param array $searchProperties
+ * @param string $test
* @return array
*/
- function searchPrincipals(array $searchProperties);
+ function searchPrincipals(array $searchProperties, $test = 'allof');
+
+ /**
+ * Finds a principal by its URI.
+ *
+ * This method may receive any type of uri, but mailto: addresses will be
+ * the most common.
+ *
+ * Implementation of this API is optional. It is currently used by the
+ * CalDAV system to find principals based on their email addresses. If this
+ * API is not implemented, some features may not work correctly.
+ *
+ * This method must return a relative principal path, or null, if the
+ * principal was not found or you refuse to find it.
+ *
+ * @param string $uri
+ * @return string
+ */
+ function findByUri($uri);
}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/Plugin.php b/vendor/sabre/dav/lib/DAVACL/Plugin.php
index f9bf4bb44..601dffecc 100644
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/Plugin.php
+++ b/vendor/sabre/dav/lib/DAVACL/Plugin.php
@@ -1,7 +1,13 @@
<?php
namespace Sabre\DAVACL;
+
use Sabre\DAV;
+use Sabre\DAV\INode;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+use Sabre\Uri;
/**
* SabreDAV ACL Plugin
@@ -13,7 +19,7 @@ use Sabre\DAV;
* property, defined in RFC5397 and the {DAV:}expand-property report, as
* defined in RFC3253.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -53,9 +59,9 @@ class Plugin extends DAV\ServerPlugin {
*
* @var array
*/
- public $principalCollectionSet = array(
+ public $principalCollectionSet = [
'principals',
- );
+ ];
/**
* By default ACL is only enforced for nodes that have ACL support (the
@@ -81,15 +87,6 @@ class Plugin extends DAV\ServerPlugin {
public $hideNodesFromListings = false;
/**
- * This string is prepended to the username of the currently logged in
- * user. This allows the plugin to determine the principal path based on
- * the username.
- *
- * @var string
- */
- public $defaultUsernamePath = 'principals';
-
- /**
* This list of properties are the properties a client can search on using
* the {DAV:}principal-property-search report.
*
@@ -97,10 +94,10 @@ class Plugin extends DAV\ServerPlugin {
*
* @var array
*/
- public $principalSearchPropertySet = array(
- '{DAV:}displayname' => 'Display name',
+ public $principalSearchPropertySet = [
+ '{DAV:}displayname' => 'Display name',
'{http://sabredav.org/ns}email-address' => 'Email address',
- );
+ ];
/**
* Any principal uri's added here, will automatically be added to the list
@@ -109,7 +106,7 @@ class Plugin extends DAV\ServerPlugin {
*
* @var array
*/
- public $adminPrincipals = array();
+ public $adminPrincipals = [];
/**
* Returns a list of features added by this plugin.
@@ -118,9 +115,9 @@ class Plugin extends DAV\ServerPlugin {
*
* @return array
*/
- public function getFeatures() {
+ function getFeatures() {
- return array('access-control', 'calendarserver-principal-property-search');
+ return ['access-control', 'calendarserver-principal-property-search'];
}
@@ -130,9 +127,9 @@ class Plugin extends DAV\ServerPlugin {
* @param string $uri
* @return array
*/
- public function getMethods($uri) {
+ function getMethods($uri) {
- return array('ACL');
+ return ['ACL'];
}
@@ -144,7 +141,7 @@ class Plugin extends DAV\ServerPlugin {
*
* @return string
*/
- public function getPluginName() {
+ function getPluginName() {
return 'acl';
@@ -160,13 +157,13 @@ class Plugin extends DAV\ServerPlugin {
* @param string $uri
* @return array
*/
- public function getSupportedReportSet($uri) {
+ function getSupportedReportSet($uri) {
- return array(
+ return [
'{DAV:}expand-property',
'{DAV:}principal-property-search',
'{DAV:}principal-search-property-set',
- );
+ ];
}
@@ -185,9 +182,9 @@ class Plugin extends DAV\ServerPlugin {
* @throws Sabre\DAVACL\Exception\NeedPrivileges
* @return bool
*/
- public function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) {
+ function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) {
- if (!is_array($privileges)) $privileges = array($privileges);
+ if (!is_array($privileges)) $privileges = [$privileges];
$acl = $this->getCurrentUserPrivilegeSet($uri);
@@ -196,15 +193,15 @@ class Plugin extends DAV\ServerPlugin {
return true;
} else {
if ($throwExceptions)
- throw new Exception\NeedPrivileges($uri,$privileges);
+ throw new Exception\NeedPrivileges($uri, $privileges);
else
return false;
}
}
- $failed = array();
- foreach($privileges as $priv) {
+ $failed = [];
+ foreach ($privileges as $priv) {
if (!in_array($priv, $acl)) {
$failed[] = $priv;
@@ -214,7 +211,7 @@ class Plugin extends DAV\ServerPlugin {
if ($failed) {
if ($throwExceptions)
- throw new Exception\NeedPrivileges($uri,$failed);
+ throw new Exception\NeedPrivileges($uri, $failed);
else
return false;
}
@@ -230,16 +227,13 @@ class Plugin extends DAV\ServerPlugin {
*
* @return string|null
*/
- public function getCurrentUserPrincipal() {
+ function getCurrentUserPrincipal() {
$authPlugin = $this->server->getPlugin('auth');
if (is_null($authPlugin)) return null;
/** @var $authPlugin Sabre\DAV\Auth\Plugin */
- $userName = $authPlugin->getCurrentUser();
- if (!$userName) return null;
-
- return $this->defaultUsernamePath . '/' . $userName;
+ return $authPlugin->getCurrentPrincipal();
}
@@ -250,14 +244,14 @@ class Plugin extends DAV\ServerPlugin {
*
* @return array
*/
- public function getCurrentUserPrincipals() {
+ function getCurrentUserPrincipals() {
$currentUser = $this->getCurrentUserPrincipal();
- if (is_null($currentUser)) return array();
+ if (is_null($currentUser)) return [];
return array_merge(
- array($currentUser),
+ [$currentUser],
$this->getPrincipalMembership($currentUser)
);
@@ -269,7 +263,7 @@ class Plugin extends DAV\ServerPlugin {
*
* @var array
*/
- protected $principalMembershipCache = array();
+ protected $principalMembershipCache = [];
/**
@@ -278,23 +272,23 @@ class Plugin extends DAV\ServerPlugin {
* @param string $principal
* @return array
*/
- public function getPrincipalMembership($mainPrincipal) {
+ function getPrincipalMembership($mainPrincipal) {
// First check our cache
if (isset($this->principalMembershipCache[$mainPrincipal])) {
return $this->principalMembershipCache[$mainPrincipal];
}
- $check = array($mainPrincipal);
- $principals = array();
+ $check = [$mainPrincipal];
+ $principals = [];
- while(count($check)) {
+ while (count($check)) {
$principal = array_shift($check);
$node = $this->server->tree->getNodeForPath($principal);
if ($node instanceof IPrincipal) {
- foreach($node->getGroupMembership() as $groupMember) {
+ foreach ($node->getGroupMembership() as $groupMember) {
if (!in_array($groupMember, $principals)) {
@@ -325,10 +319,10 @@ class Plugin extends DAV\ServerPlugin {
* You can either get the list of privileges by a uri (path) or by
* specifying a Node.
*
- * @param string|DAV\INode $node
+ * @param string|INode $node
* @return array
*/
- public function getSupportedPrivilegeSet($node) {
+ function getSupportedPrivilegeSet($node) {
if (is_string($node)) {
$node = $this->server->tree->getNodeForPath($node);
@@ -353,54 +347,54 @@ class Plugin extends DAV\ServerPlugin {
*/
static function getDefaultSupportedPrivilegeSet() {
- return array(
+ return [
'privilege' => '{DAV:}all',
'abstract' => true,
- 'aggregates' => array(
- array(
+ 'aggregates' => [
+ [
'privilege' => '{DAV:}read',
- 'aggregates' => array(
- array(
+ 'aggregates' => [
+ [
'privilege' => '{DAV:}read-acl',
- 'abstract' => true,
- ),
- array(
+ 'abstract' => false,
+ ],
+ [
'privilege' => '{DAV:}read-current-user-privilege-set',
- 'abstract' => true,
- ),
- ),
- ), // {DAV:}read
- array(
+ 'abstract' => false,
+ ],
+ ],
+ ], // {DAV:}read
+ [
'privilege' => '{DAV:}write',
- 'aggregates' => array(
- array(
+ 'aggregates' => [
+ [
'privilege' => '{DAV:}write-acl',
- 'abstract' => true,
- ),
- array(
+ 'abstract' => false,
+ ],
+ [
'privilege' => '{DAV:}write-properties',
- 'abstract' => true,
- ),
- array(
+ 'abstract' => false,
+ ],
+ [
'privilege' => '{DAV:}write-content',
- 'abstract' => true,
- ),
- array(
+ 'abstract' => false,
+ ],
+ [
'privilege' => '{DAV:}bind',
- 'abstract' => true,
- ),
- array(
+ 'abstract' => false,
+ ],
+ [
'privilege' => '{DAV:}unbind',
- 'abstract' => true,
- ),
- array(
+ 'abstract' => false,
+ ],
+ [
'privilege' => '{DAV:}unlock',
- 'abstract' => true,
- ),
- ),
- ), // {DAV:}write
- ),
- ); // {DAV:}all
+ 'abstract' => false,
+ ],
+ ],
+ ], // {DAV:}write
+ ],
+ ]; // {DAV:}all
}
@@ -415,68 +409,65 @@ class Plugin extends DAV\ServerPlugin {
* - abstract
* - concrete
*
- * @param string|DAV\INode $node
+ * @param string|INode $node
* @return array
*/
- final public function getFlatPrivilegeSet($node) {
+ final function getFlatPrivilegeSet($node) {
$privs = $this->getSupportedPrivilegeSet($node);
- $flat = array();
- $this->getFPSTraverse($privs, null, $flat);
+ $fpsTraverse = null;
+ $fpsTraverse = function($priv, $concrete, &$flat) use (&$fpsTraverse) {
- return $flat;
+ $myPriv = [
+ 'privilege' => $priv['privilege'],
+ 'abstract' => isset($priv['abstract']) && $priv['abstract'],
+ 'aggregates' => [],
+ 'concrete' => isset($priv['abstract']) && $priv['abstract'] ? $concrete : $priv['privilege'],
+ ];
- }
+ if (isset($priv['aggregates'])) {
- /**
- * Traverses the privilege set tree for reordering
- *
- * This function is solely used by getFlatPrivilegeSet, and would have been
- * a closure if it wasn't for the fact I need to support PHP 5.2.
- *
- * @param array $priv
- * @param $concrete
- * @param array $flat
- * @return void
- */
- final private function getFPSTraverse($priv, $concrete, &$flat) {
+ foreach ($priv['aggregates'] as $subPriv) {
- $myPriv = array(
- 'privilege' => $priv['privilege'],
- 'abstract' => isset($priv['abstract']) && $priv['abstract'],
- 'aggregates' => array(),
- 'concrete' => isset($priv['abstract']) && $priv['abstract']?$concrete:$priv['privilege'],
- );
+ $myPriv['aggregates'][] = $subPriv['privilege'];
- if (isset($priv['aggregates']))
- foreach($priv['aggregates'] as $subPriv) $myPriv['aggregates'][] = $subPriv['privilege'];
+ }
- $flat[$priv['privilege']] = $myPriv;
+ }
+
+ $flat[$priv['privilege']] = $myPriv;
- if (isset($priv['aggregates'])) {
+ if (isset($priv['aggregates'])) {
- foreach($priv['aggregates'] as $subPriv) {
+ foreach ($priv['aggregates'] as $subPriv) {
- $this->getFPSTraverse($subPriv, $myPriv['concrete'], $flat);
+ $fpsTraverse($subPriv, $myPriv['concrete'], $flat);
+
+ }
}
- }
+ };
+
+ $flat = [];
+ $fpsTraverse($privs, null, $flat);
+
+ return $flat;
}
/**
* Returns the full ACL list.
*
- * Either a uri or a DAV\INode may be passed.
+ * Either a uri or a INode may be passed.
*
* null will be returned if the node doesn't support ACLs.
*
* @param string|DAV\INode $node
* @return array
*/
- public function getACL($node) {
+ function getACL($node) {
if (is_string($node)) {
$node = $this->server->tree->getNodeForPath($node);
@@ -485,12 +476,12 @@ class Plugin extends DAV\ServerPlugin {
return null;
}
$acl = $node->getACL();
- foreach($this->adminPrincipals as $adminPrincipal) {
- $acl[] = array(
+ foreach ($this->adminPrincipals as $adminPrincipal) {
+ $acl[] = [
'principal' => $adminPrincipal,
'privilege' => '{DAV:}all',
'protected' => true,
- );
+ ];
}
return $acl;
@@ -507,7 +498,7 @@ class Plugin extends DAV\ServerPlugin {
* @param string|DAV\INode $node
* @return array
*/
- public function getCurrentUserPrivilegeSet($node) {
+ function getCurrentUserPrivilegeSet($node) {
if (is_string($node)) {
$node = $this->server->tree->getNodeForPath($node);
@@ -519,13 +510,13 @@ class Plugin extends DAV\ServerPlugin {
$principals = $this->getCurrentUserPrincipals();
- $collected = array();
+ $collected = [];
- foreach($acl as $ace) {
+ foreach ($acl as $ace) {
$principal = $ace['principal'];
- switch($principal) {
+ switch ($principal) {
case '{DAV:}owner' :
$owner = $node->getOwner();
@@ -564,13 +555,13 @@ class Plugin extends DAV\ServerPlugin {
// Now we deduct all aggregated privileges.
$flat = $this->getFlatPrivilegeSet($node);
- $collected2 = array();
- while(count($collected)) {
+ $collected2 = [];
+ while (count($collected)) {
$current = array_pop($collected);
$collected2[] = $current['privilege'];
- foreach($flat[$current['privilege']]['aggregates'] as $subPriv) {
+ foreach ($flat[$current['privilege']]['aggregates'] as $subPriv) {
$collected2[] = $subPriv;
$collected[] = $flat[$subPriv];
}
@@ -581,6 +572,37 @@ class Plugin extends DAV\ServerPlugin {
}
+
+ /**
+ * Returns a principal based on its uri.
+ *
+ * Returns null if the principal could not be found.
+ *
+ * @param string $uri
+ * @return null|string
+ */
+ function getPrincipalByUri($uri) {
+
+ $result = null;
+ $collections = $this->principalCollectionSet;
+ foreach ($collections as $collection) {
+
+ $principalCollection = $this->server->tree->getNodeForPath($collection);
+ if (!$principalCollection instanceof IPrincipalCollection) {
+ // Not a principal collection, we're simply going to ignore
+ // this.
+ continue;
+ }
+
+ $result = $principalCollection->findByUri($uri);
+ if ($result) {
+ return $result;
+ }
+
+ }
+
+ }
+
/**
* Principal property search
*
@@ -598,21 +620,22 @@ class Plugin extends DAV\ServerPlugin {
* @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.
- *
*/
- public function principalSearch(array $searchProperties, array $requestedProperties, $collectionUri = null) {
+ function principalSearch(array $searchProperties, array $requestedProperties, $collectionUri = null, $test = 'allof') {
if (!is_null($collectionUri)) {
- $uris = array($collectionUri);
+ $uris = [$collectionUri];
} else {
$uris = $this->principalCollectionSet;
}
- $lookupResults = array();
- foreach($uris as $uri) {
+ $lookupResults = [];
+ foreach ($uris as $uri) {
$principalCollection = $this->server->tree->getNodeForPath($uri);
if (!$principalCollection instanceof IPrincipalCollection) {
@@ -621,16 +644,16 @@ class Plugin extends DAV\ServerPlugin {
continue;
}
- $results = $principalCollection->searchPrincipals($searchProperties);
- foreach($results as $result) {
- $lookupResults[] = rtrim($uri,'/') . '/' . $result;
+ $results = $principalCollection->searchPrincipals($searchProperties, $test);
+ foreach ($results as $result) {
+ $lookupResults[] = rtrim($uri, '/') . '/' . $result;
}
}
- $matches = array();
+ $matches = [];
- foreach($lookupResults as $lookupResult) {
+ foreach ($lookupResults as $lookupResult) {
list($matches[]) = $this->server->getPropertiesForPath($lookupResult, $requestedProperties, 0);
@@ -648,18 +671,18 @@ class Plugin extends DAV\ServerPlugin {
* @param DAV\Server $server
* @return void
*/
- public function initialize(DAV\Server $server) {
+ function initialize(DAV\Server $server) {
$this->server = $server;
- $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties'));
-
- $server->subscribeEvent('beforeMethod', array($this,'beforeMethod'),20);
- $server->subscribeEvent('beforeBind', array($this,'beforeBind'),20);
- $server->subscribeEvent('beforeUnbind', array($this,'beforeUnbind'),20);
- $server->subscribeEvent('updateProperties',array($this,'updateProperties'));
- $server->subscribeEvent('beforeUnlock', array($this,'beforeUnlock'),20);
- $server->subscribeEvent('report',array($this,'report'));
- $server->subscribeEvent('unknownMethod', array($this, 'unknownMethod'));
+ $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']);
array_push($server->protectedProperties,
'{DAV:}alternate-URI-set',
@@ -682,34 +705,40 @@ class Plugin extends DAV\ServerPlugin {
// Mapping the group-member-set property to the HrefList property
// class.
- $server->propertyMap['{DAV:}group-member-set'] = 'Sabre\\DAV\\Property\\HrefList';
+ $server->xml->elementMap['{DAV:}group-member-set'] = 'Sabre\\DAV\\Xml\\Property\\Href';
+ $server->xml->elementMap['{DAV:}acl'] = 'Sabre\\DAVACL\\Xml\\Property\\Acl';
+ $server->xml->elementMap['{DAV:}expand-property'] = 'Sabre\\DAVACL\\Xml\\Request\\ExpandPropertyReport';
+ $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';
}
-
/* {{{ Event handlers */
/**
* Triggered before any method is handled
*
- * @param string $method
- * @param string $uri
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
* @return void
*/
- public function beforeMethod($method, $uri) {
+ function beforeMethod(RequestInterface $request, ResponseInterface $response) {
+
+ $method = $request->getMethod();
+ $path = $request->getPath();
- $exists = $this->server->tree->nodeExists($uri);
+ $exists = $this->server->tree->nodeExists($path);
// If the node doesn't exists, none of these checks apply
if (!$exists) return;
- switch($method) {
+ switch ($method) {
case 'GET' :
case 'HEAD' :
case 'OPTIONS' :
// For these 3 we only need to know if the node is readable.
- $this->checkPrivileges($uri,'{DAV:}read');
+ $this->checkPrivileges($path, '{DAV:}read');
break;
case 'PUT' :
@@ -719,16 +748,16 @@ class Plugin extends DAV\ServerPlugin {
// already exists, and bind on the parent if the node is being
// created.
// The bind privilege is handled in the beforeBind event.
- $this->checkPrivileges($uri,'{DAV:}write-content');
+ $this->checkPrivileges($path, '{DAV:}write-content');
break;
case 'PROPPATCH' :
- $this->checkPrivileges($uri,'{DAV:}write-properties');
+ $this->checkPrivileges($path, '{DAV:}write-properties');
break;
case 'ACL' :
- $this->checkPrivileges($uri,'{DAV:}write-acl');
+ $this->checkPrivileges($path, '{DAV:}write-acl');
break;
case 'COPY' :
@@ -744,7 +773,7 @@ class Plugin extends DAV\ServerPlugin {
//
// If MOVE is used beforeUnbind will also be used to check if
// the sourcenode can be deleted.
- $this->checkPrivileges($uri,'{DAV:}read',self::R_RECURSIVE);
+ $this->checkPrivileges($path, '{DAV:}read', self::R_RECURSIVE);
break;
@@ -761,10 +790,10 @@ class Plugin extends DAV\ServerPlugin {
* @param string $uri
* @return void
*/
- public function beforeBind($uri) {
+ function beforeBind($uri) {
- list($parentUri,$nodeName) = DAV\URLUtil::splitPath($uri);
- $this->checkPrivileges($parentUri,'{DAV:}bind');
+ list($parentUri) = Uri\split($uri);
+ $this->checkPrivileges($parentUri, '{DAV:}bind');
}
@@ -777,10 +806,10 @@ class Plugin extends DAV\ServerPlugin {
* @param string $uri
* @return void
*/
- public function beforeUnbind($uri) {
+ function beforeUnbind($uri) {
- list($parentUri,$nodeName) = DAV\URLUtil::splitPath($uri);
- $this->checkPrivileges($parentUri,'{DAV:}unbind',self::R_RECURSIVEPARENTS);
+ list($parentUri) = Uri\split($uri);
+ $this->checkPrivileges($parentUri, '{DAV:}unbind', self::R_RECURSIVEPARENTS);
}
@@ -792,7 +821,7 @@ class Plugin extends DAV\ServerPlugin {
* @TODO: not yet implemented
* @return void
*/
- public function beforeUnlock($uri, DAV\Locks\LockInfo $lock) {
+ function beforeUnlock($uri, DAV\Locks\LockInfo $lock) {
}
@@ -800,28 +829,33 @@ class Plugin extends DAV\ServerPlugin {
/**
* Triggered before properties are looked up in specific nodes.
*
- * @param string $uri
+ * @param DAV\PropFind $propFind
* @param DAV\INode $node
* @param array $requestedProperties
* @param array $returnedProperties
* @TODO really should be broken into multiple methods, or even a class.
* @return bool
*/
- public function beforeGetProperties($uri, DAV\INode $node, &$requestedProperties, &$returnedProperties) {
+ function propFind(DAV\PropFind $propFind, DAV\INode $node) {
- // Checking the read permission
- if (!$this->checkPrivileges($uri,'{DAV:}read',self::R_PARENT,false)) {
+ $path = $propFind->getPath();
+ // Checking the read permission
+ if (!$this->checkPrivileges($path, '{DAV:}read', self::R_PARENT, false)) {
// User is not allowed to read properties
+
+ // Returning false causes the property-fetching system to pretend
+ // that the node does not exist, and will cause it to be hidden
+ // from listings such as PROPFIND or the browser plugin.
if ($this->hideNodesFromListings) {
return false;
}
- // Marking all requested properties as '403'.
- foreach($requestedProperties as $key=>$requestedProperty) {
- unset($requestedProperties[$key]);
- $returnedProperties[403][$requestedProperty] = null;
+ // Otherwise we simply mark every property as 403.
+ foreach ($propFind->getRequestedProperties() as $requestedProperty) {
+ $propFind->set($requestedProperty, null, 403);
}
+
return;
}
@@ -829,116 +863,78 @@ class Plugin extends DAV\ServerPlugin {
/* Adding principal properties */
if ($node instanceof IPrincipal) {
- if (false !== ($index = array_search('{DAV:}alternate-URI-set', $requestedProperties))) {
-
- unset($requestedProperties[$index]);
- $returnedProperties[200]['{DAV:}alternate-URI-set'] = new DAV\Property\HrefList($node->getAlternateUriSet());
-
- }
- if (false !== ($index = array_search('{DAV:}principal-URL', $requestedProperties))) {
-
- unset($requestedProperties[$index]);
- $returnedProperties[200]['{DAV:}principal-URL'] = new DAV\Property\Href($node->getPrincipalUrl() . '/');
-
- }
- if (false !== ($index = array_search('{DAV:}group-member-set', $requestedProperties))) {
-
- unset($requestedProperties[$index]);
- $returnedProperties[200]['{DAV:}group-member-set'] = new DAV\Property\HrefList($node->getGroupMemberSet());
-
- }
- if (false !== ($index = array_search('{DAV:}group-membership', $requestedProperties))) {
-
- unset($requestedProperties[$index]);
- $returnedProperties[200]['{DAV:}group-membership'] = new DAV\Property\HrefList($node->getGroupMembership());
-
- }
-
- if (false !== ($index = array_search('{DAV:}displayname', $requestedProperties))) {
-
- $returnedProperties[200]['{DAV:}displayname'] = $node->getDisplayName();
-
- }
+ $propFind->handle('{DAV:}alternate-URI-set', function() use ($node) {
+ return new DAV\Xml\Property\Href($node->getAlternateUriSet());
+ });
+ $propFind->handle('{DAV:}principal-URL', function() use ($node) {
+ return new DAV\Xml\Property\Href($node->getPrincipalUrl() . '/');
+ });
+ $propFind->handle('{DAV:}group-member-set', function() use ($node) {
+ $members = $node->getGroupMemberSet();
+ foreach ($members as $k => $member) {
+ $members[$k] = rtrim($member, '/') . '/';
+ }
+ return new DAV\Xml\Property\Href($members);
+ });
+ $propFind->handle('{DAV:}group-membership', function() use ($node) {
+ $members = $node->getGroupMembership();
+ foreach ($members as $k => $member) {
+ $members[$k] = rtrim($member, '/') . '/';
+ }
+ return new DAV\Xml\Property\Href($members);
+ });
+ $propFind->handle('{DAV:}displayname', [$node, 'getDisplayName']);
}
- if (false !== ($index = array_search('{DAV:}principal-collection-set', $requestedProperties))) {
- unset($requestedProperties[$index]);
+ $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 . '/';
- $returnedProperties[200]['{DAV:}principal-collection-set'] = new DAV\Property\HrefList($val);
-
- }
- if (false !== ($index = array_search('{DAV:}current-user-principal', $requestedProperties))) {
+ foreach ($val as $k => $v) $val[$k] = $v . '/';
+ return new DAV\Xml\Property\Href($val);
- unset($requestedProperties[$index]);
+ });
+ $propFind->handle('{DAV:}current-user-principal', function() {
if ($url = $this->getCurrentUserPrincipal()) {
- $returnedProperties[200]['{DAV:}current-user-principal'] = new Property\Principal(Property\Principal::HREF, $url . '/');
+ return new Xml\Property\Principal(Xml\Property\Principal::HREF, $url . '/');
} else {
- $returnedProperties[200]['{DAV:}current-user-principal'] = new Property\Principal(Property\Principal::UNAUTHENTICATED);
+ return new Xml\Property\Principal(Xml\Property\Principal::UNAUTHENTICATED);
}
-
- }
- if (false !== ($index = array_search('{DAV:}supported-privilege-set', $requestedProperties))) {
-
- unset($requestedProperties[$index]);
- $returnedProperties[200]['{DAV:}supported-privilege-set'] = new Property\SupportedPrivilegeSet($this->getSupportedPrivilegeSet($node));
-
- }
- if (false !== ($index = array_search('{DAV:}current-user-privilege-set', $requestedProperties))) {
-
- if (!$this->checkPrivileges($uri, '{DAV:}read-current-user-privilege-set', self::R_PARENT, false)) {
- $returnedProperties[403]['{DAV:}current-user-privilege-set'] = null;
- unset($requestedProperties[$index]);
+ });
+ $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) {
+ if (!$this->checkPrivileges($path, '{DAV:}read-current-user-privilege-set', self::R_PARENT, false)) {
+ $propFind->set('{DAV:}current-user-privilege-set', null, 403);
} else {
$val = $this->getCurrentUserPrivilegeSet($node);
if (!is_null($val)) {
- unset($requestedProperties[$index]);
- $returnedProperties[200]['{DAV:}current-user-privilege-set'] = new Property\CurrentUserPrivilegeSet($val);
+ return new Xml\Property\CurrentUserPrivilegeSet($val);
}
}
-
- }
-
- /* The ACL property contains all the permissions */
- if (false !== ($index = array_search('{DAV:}acl', $requestedProperties))) {
-
- if (!$this->checkPrivileges($uri, '{DAV:}read-acl', self::R_PARENT, false)) {
-
- unset($requestedProperties[$index]);
- $returnedProperties[403]['{DAV:}acl'] = null;
-
+ });
+ $propFind->handle('{DAV:}acl', function() use ($node, $propFind, $path) {
+ /* The ACL property contains all the permissions */
+ if (!$this->checkPrivileges($path, '{DAV:}read-acl', self::R_PARENT, false)) {
+ $propFind->set('{DAV:}acl', null, 403);
} else {
-
$acl = $this->getACL($node);
if (!is_null($acl)) {
- unset($requestedProperties[$index]);
- $returnedProperties[200]['{DAV:}acl'] = new Property\Acl($this->getACL($node));
+ return new Xml\Property\Acl($this->getACL($node));
}
-
}
-
- }
-
- /* The acl-restrictions property contains information on how privileges
- * must behave.
- */
- if (false !== ($index = array_search('{DAV:}acl-restrictions', $requestedProperties))) {
- unset($requestedProperties[$index]);
- $returnedProperties[200]['{DAV:}acl-restrictions'] = new Property\AclRestrictions();
- }
+ });
+ $propFind->handle('{DAV:}acl-restrictions', function() {
+ return new Xml\Property\AclRestrictions();
+ });
/* Adding ACL properties */
if ($node instanceof IACL) {
-
- if (false !== ($index = array_search('{DAV:}owner', $requestedProperties))) {
-
- unset($requestedProperties[$index]);
- $returnedProperties[200]['{DAV:}owner'] = new DAV\Property\Href($node->getOwner() . '/');
-
- }
-
+ $propFind->handle('{DAV:}owner', function() use ($node) {
+ return new DAV\Xml\Property\Href($node->getOwner() . '/');
+ });
}
}
@@ -947,42 +943,36 @@ class Plugin extends DAV\ServerPlugin {
* This method intercepts PROPPATCH methods and make sure the
* group-member-set is updated correctly.
*
- * @param array $propertyDelta
- * @param array $result
- * @param DAV\INode $node
- * @return bool
+ * @param string $path
+ * @param DAV\PropPatch $propPatch
+ * @return void
*/
- public function updateProperties(&$propertyDelta, &$result, DAV\INode $node) {
-
- if (!array_key_exists('{DAV:}group-member-set', $propertyDelta))
- return;
-
- if (is_null($propertyDelta['{DAV:}group-member-set'])) {
- $memberSet = array();
- } elseif ($propertyDelta['{DAV:}group-member-set'] instanceof DAV\Property\HrefList) {
- $memberSet = array_map(
- array($this->server,'calculateUri'),
- $propertyDelta['{DAV:}group-member-set']->getHrefs()
- );
- } else {
- throw new DAV\Exception('The group-member-set property MUST be an instance of Sabre\DAV\Property\HrefList or null');
- }
-
- if (!($node instanceof IPrincipal)) {
- $result[403]['{DAV:}group-member-set'] = null;
- unset($propertyDelta['{DAV:}group-member-set']);
-
- // Returning false will stop the updateProperties process
- return false;
- }
+ function propPatch($path, DAV\PropPatch $propPatch) {
+
+ $propPatch->handle('{DAV:}group-member-set', function($value) use ($path) {
+ if (is_null($value)) {
+ $memberSet = [];
+ } elseif ($value instanceof DAV\Xml\Property\Href) {
+ $memberSet = array_map(
+ [$this->server, 'calculateUri'],
+ $value->getHrefs()
+ );
+ } else {
+ throw new DAV\Exception('The group-member-set property MUST be an instance of Sabre\DAV\Property\HrefList or null');
+ }
+ $node = $this->server->tree->getNodeForPath($path);
+ if (!($node instanceof IPrincipal)) {
+ // Fail
+ return false;
+ }
- $node->setGroupMemberSet($memberSet);
- // We must also clear our cache, just in case
+ $node->setGroupMemberSet($memberSet);
+ // We must also clear our cache, just in case
- $this->principalMembershipCache = array();
+ $this->principalMembershipCache = [];
- $result[200]['{DAV:}group-member-set'] = null;
- unset($propertyDelta['{DAV:}group-member-set']);
+ return true;
+ });
}
@@ -990,21 +980,25 @@ class Plugin extends DAV\ServerPlugin {
* This method handles HTTP REPORT requests
*
* @param string $reportName
- * @param \DOMNode $dom
+ * @param mixed $report
+ * @param mixed $path
* @return bool
*/
- public function report($reportName, $dom) {
+ function report($reportName, $report, $path) {
- switch($reportName) {
+ switch ($reportName) {
case '{DAV:}principal-property-search' :
- $this->principalPropertySearchReport($dom);
+ $this->server->transactionType = 'report-principal-property-search';
+ $this->principalPropertySearchReport($report);
return false;
case '{DAV:}principal-search-property-set' :
- $this->principalSearchPropertySetReport($dom);
+ $this->server->transactionType = 'report-principal-search-property-set';
+ $this->principalSearchPropertySetReport($report);
return false;
case '{DAV:}expand-property' :
- $this->expandPropertyReport($dom);
+ $this->server->transactionType = 'report-expand-property';
+ $this->expandPropertyReport($report);
return false;
}
@@ -1012,45 +1006,31 @@ class Plugin extends DAV\ServerPlugin {
}
/**
- * This event is triggered for any HTTP method that is not known by the
- * webserver.
+ * This method is responsible for handling the 'ACL' event.
*
- * @param string $method
- * @param string $uri
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
* @return bool
*/
- public function unknownMethod($method, $uri) {
+ function httpAcl(RequestInterface $request, ResponseInterface $response) {
- if ($method!=='ACL') return;
+ $path = $request->getPath();
+ $body = $request->getBodyAsString();
- $this->httpACL($uri);
- return false;
-
- }
-
- /**
- * This method is responsible for handling the 'ACL' event.
- *
- * @param string $uri
- * @return void
- */
- public function httpACL($uri) {
-
- $body = $this->server->httpRequest->getBody(true);
- $dom = DAV\XMLUtil::loadDOMDocument($body);
+ if (!$body) {
+ throw new DAV\Exception\BadRequest('XML body expected in ACL request');
+ }
- $newAcl =
- Property\Acl::unserialize($dom->firstChild)
- ->getPrivileges();
+ $acl = $this->server->xml->expect('{DAV:}acl', $body);
+ $newAcl = $acl->getPrivileges();
// Normalizing urls
- foreach($newAcl as $k=>$newAce) {
+ foreach ($newAcl as $k => $newAce) {
$newAcl[$k]['principal'] = $this->server->calculateUri($newAce['principal']);
}
+ $node = $this->server->tree->getNodeForPath($path);
- $node = $this->server->tree->getNodeForPath($uri);
-
- if (!($node instanceof IACL)) {
+ if (!$node instanceof IACL) {
throw new DAV\Exception\MethodNotAllowed('This node does not support the ACL method');
}
@@ -1060,12 +1040,12 @@ class Plugin extends DAV\ServerPlugin {
/* Checking if protected principals from the existing principal set are
not overwritten. */
- foreach($oldAcl as $oldAce) {
+ foreach ($oldAcl as $oldAce) {
if (!isset($oldAce['protected']) || !$oldAce['protected']) continue;
$found = false;
- foreach($newAcl as $newAce) {
+ foreach ($newAcl as $newAce) {
if (
$newAce['privilege'] === $oldAce['privilege'] &&
$newAce['principal'] === $oldAce['principal'] &&
@@ -1079,7 +1059,7 @@ class Plugin extends DAV\ServerPlugin {
}
- foreach($newAcl as $newAce) {
+ foreach ($newAcl as $newAce) {
// Do we recognize the privilege
if (!isset($supportedPrivileges[$newAce['privilege']])) {
@@ -1103,6 +1083,11 @@ class Plugin extends DAV\ServerPlugin {
}
$node->setACL($newAcl);
+ $response->setStatus(200);
+
+ // Breaking the event chain, because we handled this method.
+ return false;
+
}
/* }}} */
@@ -1120,73 +1105,24 @@ 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 \DOMElement $dom
+ * @param Xml\Request\ExpandPropertyReport $report
* @return void
*/
- protected function expandPropertyReport($dom) {
+ protected function expandPropertyReport($report) {
- $requestedProperties = $this->parseExpandPropertyReportRequest($dom->firstChild->firstChild);
$depth = $this->server->getHTTPDepth(0);
$requestUri = $this->server->getRequestUri();
- $result = $this->expandProperties($requestUri,$requestedProperties,$depth);
-
- $dom = new \DOMDocument('1.0','utf-8');
- $dom->formatOutput = true;
- $multiStatus = $dom->createElement('d:multistatus');
- $dom->appendChild($multiStatus);
-
- // Adding in default namespaces
- foreach($this->server->xmlNamespaces as $namespace=>$prefix) {
-
- $multiStatus->setAttribute('xmlns:' . $prefix,$namespace);
-
- }
-
- foreach($result as $response) {
- $response->serialize($this->server, $multiStatus);
- }
-
- $xml = $dom->saveXML();
- $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
- $this->server->httpResponse->sendStatus(207);
- $this->server->httpResponse->sendBody($xml);
-
- }
-
- /**
- * This method is used by expandPropertyReport to parse
- * out the entire HTTP request.
- *
- * @param \DOMElement $node
- * @return array
- */
- protected function parseExpandPropertyReportRequest($node) {
-
- $requestedProperties = array();
- do {
-
- if (DAV\XMLUtil::toClarkNotation($node)!=='{DAV:}property') continue;
-
- if ($node->firstChild) {
-
- $children = $this->parseExpandPropertyReportRequest($node->firstChild);
-
- } else {
-
- $children = array();
-
- }
-
- $namespace = $node->getAttribute('namespace');
- if (!$namespace) $namespace = 'DAV:';
-
- $propName = '{'.$namespace.'}' . $node->getAttribute('name');
- $requestedProperties[$propName] = $children;
+ $result = $this->expandProperties($requestUri, $report->properties, $depth);
- } while ($node = $node->nextSibling);
-
- return $requestedProperties;
+ $xml = $this->server->xml->write(
+ '{DAV:}multistatus',
+ new DAV\Xml\Response\MultiStatus($result),
+ $this->server->getBaseUri()
+ );
+ $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
+ $this->server->httpResponse->setStatus(207);
+ $this->server->httpResponse->setBody($xml);
}
@@ -1203,33 +1139,39 @@ class Plugin extends DAV\ServerPlugin {
$foundProperties = $this->server->getPropertiesForPath($path, array_keys($requestedProperties), $depth);
- $result = array();
+ $result = [];
- foreach($foundProperties as $node) {
+ foreach ($foundProperties as $node) {
- foreach($requestedProperties as $propertyName=>$childRequestedProperties) {
+ foreach ($requestedProperties as $propertyName => $childRequestedProperties) {
// We're only traversing if sub-properties were requested
- if(count($childRequestedProperties)===0) continue;
+ if (count($childRequestedProperties) === 0) continue;
// We only have to do the expansion if the property was found
// and it contains an href element.
- if (!array_key_exists($propertyName,$node[200])) continue;
+ if (!array_key_exists($propertyName, $node[200])) continue;
- if ($node[200][$propertyName] instanceof DAV\Property\IHref) {
- $hrefs = array($node[200][$propertyName]->getHref());
- } elseif ($node[200][$propertyName] instanceof DAV\Property\HrefList) {
- $hrefs = $node[200][$propertyName]->getHrefs();
+ if (!$node[200][$propertyName] instanceof DAV\Xml\Property\Href) {
+ continue;
}
- $childProps = array();
- foreach($hrefs as $href) {
- $childProps = array_merge($childProps, $this->expandProperties($href, $childRequestedProperties, 0));
+ $childHrefs = $node[200][$propertyName]->getHrefs();
+ $childProps = [];
+
+ foreach ($childHrefs as $href) {
+ // Gathering the result of the children
+ $childProps[] = [
+ 'name' => '{DAV:}response',
+ 'value' => $this->expandProperties($href, $childRequestedProperties, 0)[0]
+ ];
}
- $node[200][$propertyName] = new DAV\Property\ResponseList($childProps);
+
+ // Replacing the property with its expannded form.
+ $node[200][$propertyName] = $childProps;
}
- $result[] = new DAV\Property\Response($node['href'], $node);
+ $result[] = new DAV\Xml\Element\Response($node['href'], $node);
}
@@ -1245,57 +1187,49 @@ class Plugin extends DAV\ServerPlugin {
* of properties the client may search on, using the
* {DAV:}principal-property-search report.
*
- * @param \DOMDocument $dom
+ * @param Xml\Request\PrincipalSearchPropertySetReport $report
* @return void
*/
- protected function principalSearchPropertySetReport(\DOMDocument $dom) {
+ protected function principalSearchPropertySetReport($report) {
$httpDepth = $this->server->getHTTPDepth(0);
- if ($httpDepth!==0) {
+ if ($httpDepth !== 0) {
throw new DAV\Exception\BadRequest('This report is only defined when Depth: 0');
}
- if ($dom->firstChild->hasChildNodes())
- throw new DAV\Exception\BadRequest('The principal-search-property-set report element is not allowed to have child elements');
-
- $dom = new \DOMDocument('1.0','utf-8');
- $dom->formatOutput = true;
- $root = $dom->createElement('d:principal-search-property-set');
- $dom->appendChild($root);
- // Adding in default namespaces
- foreach($this->server->xmlNamespaces as $namespace=>$prefix) {
-
- $root->setAttribute('xmlns:' . $prefix,$namespace);
-
- }
+ $writer = $this->server->xml->getWriter();
+ $writer->openMemory();
+ $writer->startDocument();
- $nsList = $this->server->xmlNamespaces;
+ $writer->startElement('{DAV:}principal-search-property-set');
- foreach($this->principalSearchPropertySet as $propertyName=>$description) {
+ foreach ($this->principalSearchPropertySet as $propertyName => $description) {
- $psp = $dom->createElement('d:principal-search-property');
- $root->appendChild($psp);
+ $writer->startElement('{DAV:}principal-search-property');
+ $writer->startElement('{DAV:}prop');
- $prop = $dom->createElement('d:prop');
- $psp->appendChild($prop);
+ $writer->writeElement($propertyName);
- $propName = null;
- preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName);
+ $writer->endElement(); // prop
- $currentProperty = $dom->createElement($nsList[$propName[1]] . ':' . $propName[2]);
- $prop->appendChild($currentProperty);
+ if ($description) {
+ $writer->write([[
+ 'name' => '{DAV:}description',
+ 'value' => $description,
+ 'attributes' => ['xml:lang' => 'en']
+ ]]);
+ }
- $descriptionElem = $dom->createElement('d:description');
- $descriptionElem->setAttribute('xml:lang','en');
- $descriptionElem->appendChild($dom->createTextNode($description));
- $psp->appendChild($descriptionElem);
+ $writer->endElement(); // principal-search-property
}
- $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
- $this->server->httpResponse->sendStatus(200);
- $this->server->httpResponse->sendBody($dom->saveXML());
+ $writer->endElement(); // principal-search-property-set
+
+ $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
+ $this->server->httpResponse->setStatus(200);
+ $this->server->httpResponse->setBody($writer->outputMemory());
}
@@ -1307,96 +1241,83 @@ class Plugin extends DAV\ServerPlugin {
* clients to search for groups of principals, based on the value of one
* or more properties.
*
- * @param \DOMDocument $dom
+ * @param Xml\Request\PrincipalPropertySearchReport $report
* @return void
*/
- protected function principalPropertySearchReport(\DOMDocument $dom) {
-
- list($searchProperties, $requestedProperties, $applyToPrincipalCollectionSet) = $this->parsePrincipalPropertySearchReportRequest($dom);
+ protected function principalPropertySearchReport($report) {
$uri = null;
- if (!$applyToPrincipalCollectionSet) {
- $uri = $this->server->getRequestUri();
+ if (!$report->applyToPrincipalCollectionSet) {
+ $uri = $this->server->httpRequest->getPath();
}
- $result = $this->principalSearch($searchProperties, $requestedProperties, $uri);
+ if ($this->server->getHttpDepth('0') !== 0) {
+ throw new BadRequest('Depth must be 0');
+ }
+ $result = $this->principalSearch(
+ $report->searchProperties,
+ $report->properties,
+ $uri,
+ $report->test
+ );
- $prefer = $this->server->getHTTPPRefer();
+ $prefer = $this->server->getHTTPPrefer();
- $this->server->httpResponse->sendStatus(207);
- $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
- $this->server->httpResponse->setHeader('Vary','Brief,Prefer');
- $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal']));
+ $this->server->httpResponse->setStatus(207);
+ $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
+ $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer');
+ $this->server->httpResponse->setBody($this->server->generateMultiStatus($result, $prefer['return'] === 'minimal'));
}
+ /* }}} */
+
/**
- * parsePrincipalPropertySearchReportRequest
- *
- * This method parses the request body from a
- * {DAV:}principal-property-search report.
- *
- * This method returns an array with two elements:
- * 1. an array with properties to search on, and their values
- * 2. a list of propertyvalues that should be returned for the request.
+ * This method is used to generate HTML output for the
+ * DAV\Browser\Plugin. This allows us to generate an interface users
+ * can use to create new calendars.
*
- * @param \DOMDocument $dom
- * @return array
+ * @param DAV\INode $node
+ * @param string $output
+ * @return bool
*/
- protected function parsePrincipalPropertySearchReportRequest($dom) {
-
- $httpDepth = $this->server->getHTTPDepth(0);
- if ($httpDepth!==0) {
- throw new DAV\Exception\BadRequest('This report is only defined when Depth: 0');
- }
-
- $searchProperties = array();
-
- $applyToPrincipalCollectionSet = false;
+ function htmlActionsPanel(DAV\INode $node, &$output) {
- // Parsing the search request
- foreach($dom->firstChild->childNodes as $searchNode) {
-
- if (DAV\XMLUtil::toClarkNotation($searchNode) == '{DAV:}apply-to-principal-collection-set') {
- $applyToPrincipalCollectionSet = true;
- }
-
- if (DAV\XMLUtil::toClarkNotation($searchNode)!=='{DAV:}property-search')
- continue;
-
- $propertyName = null;
- $propertyValue = null;
-
- foreach($searchNode->childNodes as $childNode) {
-
- switch(DAV\XMLUtil::toClarkNotation($childNode)) {
-
- case '{DAV:}prop' :
- $property = DAV\XMLUtil::parseProperties($searchNode);
- reset($property);
- $propertyName = key($property);
- break;
-
- case '{DAV:}match' :
- $propertyValue = $childNode->textContent;
- break;
-
- }
-
-
- }
-
- if (is_null($propertyName) || is_null($propertyValue))
- throw new DAV\Exception\BadRequest('Invalid search request. propertyname: ' . $propertyName . '. propertvvalue: ' . $propertyValue);
-
- $searchProperties[$propertyName] = $propertyValue;
+ if (!$node instanceof PrincipalCollection)
+ return;
- }
+ $output .= '<tr><td colspan="2"><form method="post" action="">
+ <h3>Create new principal</h3>
+ <input type="hidden" name="sabreAction" value="mkcol" />
+ <input type="hidden" name="resourceType" value="{DAV:}principal" />
+ <label>Name (uri):</label> <input type="text" name="name" /><br />
+ <label>Display name:</label> <input type="text" name="{DAV:}displayname" /><br />
+ <label>Email address:</label> <input type="text" name="{http://sabredav*DOT*org/ns}email-address" /><br />
+ <input type="submit" value="create" />
+ </form>
+ </td></tr>';
- return array($searchProperties, array_keys(DAV\XMLUtil::parseProperties($dom->firstChild)), $applyToPrincipalCollectionSet);
+ return false;
}
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
- /* }}} */
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => 'Adds support for WebDAV ACL (rfc3744)',
+ 'link' => 'http://sabre.io/dav/acl/',
+ ];
+ }
}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/Principal.php b/vendor/sabre/dav/lib/DAVACL/Principal.php
index 89277f850..16375d3fc 100644
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/Principal.php
+++ b/vendor/sabre/dav/lib/DAVACL/Principal.php
@@ -3,6 +3,7 @@
namespace Sabre\DAVACL;
use Sabre\DAV;
+use Sabre\HTTP\URLUtil;
/**
* Principal class
@@ -15,7 +16,7 @@ use Sabre\DAV;
* This principal also has basic ACL settings, only allowing the principal
* access it's own principal.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -41,7 +42,7 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL {
* @param IPrincipalBackend $principalBackend
* @param array $principalProperties
*/
- public function __construct(PrincipalBackend\BackendInterface $principalBackend, array $principalProperties = array()) {
+ function __construct(PrincipalBackend\BackendInterface $principalBackend, array $principalProperties = []) {
if (!isset($principalProperties['uri'])) {
throw new DAV\Exception('The principal properties must at least contain the \'uri\' key');
@@ -56,7 +57,7 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL {
*
* @return string
*/
- public function getPrincipalUrl() {
+ function getPrincipalUrl() {
return $this->principalProperties['uri'];
@@ -69,9 +70,9 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL {
*
* @return array
*/
- public function getAlternateUriSet() {
+ function getAlternateUriSet() {
- $uris = array();
+ $uris = [];
if (isset($this->principalProperties['{DAV:}alternate-URI-set'])) {
$uris = $this->principalProperties['{DAV:}alternate-URI-set'];
@@ -94,7 +95,7 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL {
*
* @return array
*/
- public function getGroupMemberSet() {
+ function getGroupMemberSet() {
return $this->principalBackend->getGroupMemberSet($this->principalProperties['uri']);
@@ -108,13 +109,12 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL {
*
* @return array
*/
- public function getGroupMembership() {
+ function getGroupMembership() {
return $this->principalBackend->getGroupMemberShip($this->principalProperties['uri']);
}
-
/**
* Sets a list of group members
*
@@ -126,22 +126,21 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL {
* @param array $groupMembers
* @return void
*/
- public function setGroupMemberSet(array $groupMembers) {
+ function setGroupMemberSet(array $groupMembers) {
$this->principalBackend->setGroupMemberSet($this->principalProperties['uri'], $groupMembers);
}
-
/**
* Returns this principals name.
*
* @return string
*/
- public function getName() {
+ function getName() {
$uri = $this->principalProperties['uri'];
- list(, $name) = DAV\URLUtil::splitPath($uri);
+ list(, $name) = URLUtil::splitPath($uri);
return $name;
}
@@ -151,7 +150,7 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL {
*
* @return string
*/
- public function getDisplayName() {
+ function getDisplayName() {
if (isset($this->principalProperties['{DAV:}displayname'])) {
return $this->principalProperties['{DAV:}displayname'];
@@ -167,10 +166,10 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL {
* @param array $requestedProperties
* @return array
*/
- public function getProperties($requestedProperties) {
+ function getProperties($requestedProperties) {
- $newProperties = array();
- foreach($requestedProperties as $propName) {
+ $newProperties = [];
+ foreach ($requestedProperties as $propName) {
if (isset($this->principalProperties[$propName])) {
$newProperties[$propName] = $this->principalProperties[$propName];
@@ -183,15 +182,23 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL {
}
/**
- * Updates this principals properties.
- *
- * @param array $mutations
- * @see Sabre\DAV\IProperties::updateProperties
- * @return bool|array
+ * Updates properties on this node.
+ *
+ * This method received a PropPatch object, which contains all the
+ * information about the update.
+ *
+ * To update specific properties, call the 'handle' method on this object.
+ * Read the PropPatch documentation for more information.
+ *
+ * @param DAV\PropPatch $propPatch
+ * @return void
*/
- public function updateProperties($mutations) {
+ function propPatch(DAV\PropPatch $propPatch) {
- return $this->principalBackend->updatePrincipal($this->principalProperties['uri'], $mutations);
+ return $this->principalBackend->updatePrincipal(
+ $this->principalProperties['uri'],
+ $propPatch
+ );
}
@@ -202,7 +209,7 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL {
*
* @return string|null
*/
- public function getOwner() {
+ function getOwner() {
return $this->principalProperties['uri'];
@@ -216,7 +223,7 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL {
*
* @return string|null
*/
- public function getGroup() {
+ function getGroup() {
return null;
@@ -234,15 +241,15 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL {
*
* @return array
*/
- public function getACL() {
+ function getACL() {
- return array(
- array(
+ return [
+ [
'privilege' => '{DAV:}read',
- 'principal' => $this->getPrincipalUrl(),
+ 'principal' => '{DAV:}authenticated',
'protected' => true,
- ),
- );
+ ],
+ ];
}
@@ -254,7 +261,7 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL {
* @param array $acl
* @return void
*/
- public function setACL(array $acl) {
+ function setACL(array $acl) {
throw new DAV\Exception\MethodNotAllowed('Updating ACLs is not allowed here');
@@ -272,7 +279,7 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL {
*
* @return array|null
*/
- public function getSupportedPrivilegeSet() {
+ function getSupportedPrivilegeSet() {
return null;
diff --git a/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php
new file mode 100644
index 000000000..9bf9ba445
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Sabre\DAVACL\PrincipalBackend;
+
+/**
+ * Abstract Principal Backend
+ *
+ * Currently this class has no function. It's here for consistency and so we
+ * have a non-bc-breaking way to add a default generic implementation to
+ * functions we may add in the future.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class AbstractBackend implements BackendInterface {
+
+ /**
+ * Finds a principal by its URI.
+ *
+ * This method may receive any type of uri, but mailto: addresses will be
+ * the most common.
+ *
+ * Implementation of this API is optional. It is currently used by the
+ * CalDAV system to find principals based on their email addresses. If this
+ * API is not implemented, some features may not work correctly.
+ *
+ * This method must return a relative principal path, or null, if the
+ * principal was not found or you refuse to find it.
+ *
+ * @param string $uri
+ * @param string $principalPrefix
+ * @return string
+ */
+ function findByUri($uri, $principalPrefix) {
+
+ // Note that the default implementation here is a bit slow and could
+ // likely be optimized.
+ if (substr($uri, 0, 7) !== 'mailto:') {
+ return;
+ }
+ $result = $this->searchPrincipals(
+ $principalPrefix,
+ ['{http://sabredav.org/ns}email-address' => substr($uri, 7)]
+ );
+
+ if ($result) {
+ return $result[0];
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/PrincipalBackend/BackendInterface.php b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php
index d0416ac9c..2cb83071a 100644
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/PrincipalBackend/BackendInterface.php
+++ b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php
@@ -9,7 +9,7 @@ namespace Sabre\DAVACL\PrincipalBackend;
* implement Sabre\DAVACL\IPrincipal directly. This interface is used solely by
* Sabre\DAVACL\AbstractPrincipalCollection.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -46,67 +46,35 @@ interface BackendInterface {
/**
* Updates one ore more webdav properties on a principal.
*
- * The list of mutations is supplied as an array. Each key in the array is
- * a propertyname, such as {DAV:}displayname.
+ * The list of mutations is stored in a Sabre\DAV\PropPatch object.
+ * To do the actual updates, you must tell this object which properties
+ * you're going to process with the handle() method.
*
- * Each value is the actual value to be updated. If a value is null, it
- * must be deleted.
+ * Calling the handle method is like telling the PropPatch object "I
+ * promise I can handle updating this property".
*
- * This method should be atomic. It must either completely succeed, or
- * completely fail. Success and failure can simply be returned as 'true' or
- * 'false'.
- *
- * It is also possible to return detailed failure information. In that case
- * an array such as this should be returned:
- *
- * array(
- * 200 => array(
- * '{DAV:}prop1' => null,
- * ),
- * 201 => array(
- * '{DAV:}prop2' => null,
- * ),
- * 403 => array(
- * '{DAV:}prop3' => null,
- * ),
- * 424 => array(
- * '{DAV:}prop4' => null,
- * ),
- * );
- *
- * In this previous example prop1 was successfully updated or deleted, and
- * prop2 was succesfully created.
- *
- * prop3 failed to update due to '403 Forbidden' and because of this prop4
- * also could not be updated with '424 Failed dependency'.
- *
- * This last example was actually incorrect. While 200 and 201 could appear
- * in 1 response, if there's any error (403) the other properties should
- * always fail with 423 (failed dependency).
- *
- * But anyway, if you don't want to scratch your head over this, just
- * return true or false.
+ * Read the PropPatch documenation for more info and examples.
*
* @param string $path
- * @param array $mutations
- * @return array|bool
+ * @param \Sabre\DAV\PropPatch $propPatch
+ * @return void
*/
- function updatePrincipal($path, $mutations);
+ function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch);
/**
* This method is used to search for principals matching a set of
* properties.
*
* This search is specifically used by RFC3744's principal-property-search
- * REPORT. You should at least allow searching on
- * http://sabredav.org/ns}email-address.
+ * REPORT.
*
* The actual search should be a unicode-non-case-sensitive search. The
* keys in searchProperties are the WebDAV property names, while the values
* are the property values to search on.
*
- * If multiple properties are being searched on, the search should be
- * AND'ed.
+ * By default, if multiple properties are submitted to this method, the
+ * various properties should be combined with 'AND'. If $test is set to
+ * 'anyof', it should be combined using 'OR'.
*
* This method should simply return an array with full principal uri's.
*
@@ -119,9 +87,29 @@ interface BackendInterface {
*
* @param string $prefixPath
* @param array $searchProperties
+ * @param string $test
* @return array
*/
- function searchPrincipals($prefixPath, array $searchProperties);
+ function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof');
+
+ /**
+ * Finds a principal by its URI.
+ *
+ * This method may receive any type of uri, but mailto: addresses will be
+ * the most common.
+ *
+ * Implementation of this API is optional. It is currently used by the
+ * CalDAV system to find principals based on their email addresses. If this
+ * API is not implemented, some features may not work correctly.
+ *
+ * This method must return a relative principal path, or null, if the
+ * principal was not found or you refuse to find it.
+ *
+ * @param string $uri
+ * @param string $principalPrefix
+ * @return string
+ */
+ function findByUri($uri, $principalPrefix);
/**
* Returns the list of members for a group-principal
diff --git a/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/CreatePrincipalSupport.php b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/CreatePrincipalSupport.php
new file mode 100644
index 000000000..e354a697d
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/CreatePrincipalSupport.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Sabre\DAVACL\PrincipalBackend;
+
+use Sabre\DAV\MkCol;
+
+/**
+ * Implement this interface to add support for creating new principals to your
+ * principal backend.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface CreatePrincipalSupport extends BackendInterface {
+
+ /**
+ * Creates a new principal.
+ *
+ * This method receives a full path for the new principal. The mkCol object
+ * contains any additional webdav properties specified during the creation
+ * of the principal.
+ *
+ * @param string $path
+ * @param MkCol $mkCol
+ * @return void
+ */
+ function createPrincipal($path, MkCol $mkCol);
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/PDO.php b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/PDO.php
new file mode 100644
index 000000000..01b7a175c
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/PDO.php
@@ -0,0 +1,431 @@
+<?php
+
+namespace Sabre\DAVACL\PrincipalBackend;
+
+use Sabre\DAV;
+use Sabre\DAV\MkCol;
+use Sabre\HTTP\URLUtil;
+
+/**
+ * PDO principal backend
+ *
+ *
+ * This backend assumes all principals are in a single collection. The default collection
+ * is 'principals/', but this can be overriden.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PDO extends AbstractBackend implements CreatePrincipalSupport {
+
+ /**
+ * PDO table name for 'principals'
+ *
+ * @var string
+ */
+ public $tableName = 'principals';
+
+ /**
+ * PDO table name for 'group members'
+ *
+ * @var string
+ */
+ public $groupMembersTableName = 'groupmembers';
+
+ /**
+ * pdo
+ *
+ * @var PDO
+ */
+ protected $pdo;
+
+ /**
+ * A list of additional fields to support
+ *
+ * @var array
+ */
+ protected $fieldMap = [
+
+ /**
+ * This property can be used to display the users' real name.
+ */
+ '{DAV:}displayname' => [
+ 'dbField' => 'displayname',
+ ],
+
+ /**
+ * This is the users' primary email-address.
+ */
+ '{http://sabredav.org/ns}email-address' => [
+ 'dbField' => 'email',
+ ],
+ ];
+
+ /**
+ * Sets up the backend.
+ *
+ * @param PDO $pdo
+ */
+ function __construct(\PDO $pdo) {
+
+ $this->pdo = $pdo;
+
+ }
+
+ /**
+ * Returns a list of principals based on a prefix.
+ *
+ * This prefix will often contain something like 'principals'. You are only
+ * expected to return principals that are in this base path.
+ *
+ * You are expected to return at least a 'uri' for every user, you can
+ * return any additional properties if you wish so. Common properties are:
+ * {DAV:}displayname
+ * {http://sabredav.org/ns}email-address - This is a custom SabreDAV
+ * field that's actualy injected in a number of other properties. If
+ * you have an email address, use this property.
+ *
+ * @param string $prefixPath
+ * @return array
+ */
+ function getPrincipalsByPrefix($prefixPath) {
+
+ $fields = [
+ 'uri',
+ ];
+
+ foreach ($this->fieldMap as $key => $value) {
+ $fields[] = $value['dbField'];
+ }
+ $result = $this->pdo->query('SELECT ' . implode(',', $fields) . ' FROM ' . $this->tableName);
+
+ $principals = [];
+
+ while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
+
+ // Checking if the principal is in the prefix
+ list($rowPrefix) = URLUtil::splitPath($row['uri']);
+ if ($rowPrefix !== $prefixPath) continue;
+
+ $principal = [
+ 'uri' => $row['uri'],
+ ];
+ foreach ($this->fieldMap as $key => $value) {
+ if ($row[$value['dbField']]) {
+ $principal[$key] = $row[$value['dbField']];
+ }
+ }
+ $principals[] = $principal;
+
+ }
+
+ return $principals;
+
+ }
+
+ /**
+ * Returns a specific principal, specified by it's path.
+ * The returned structure should be the exact same as from
+ * getPrincipalsByPrefix.
+ *
+ * @param string $path
+ * @return array
+ */
+ function getPrincipalByPath($path) {
+
+ $fields = [
+ 'id',
+ 'uri',
+ ];
+
+ foreach ($this->fieldMap as $key => $value) {
+ $fields[] = $value['dbField'];
+ }
+ $stmt = $this->pdo->prepare('SELECT ' . implode(',', $fields) . ' FROM ' . $this->tableName . ' WHERE uri = ?');
+ $stmt->execute([$path]);
+
+ $row = $stmt->fetch(\PDO::FETCH_ASSOC);
+ if (!$row) return;
+
+ $principal = [
+ 'id' => $row['id'],
+ 'uri' => $row['uri'],
+ ];
+ foreach ($this->fieldMap as $key => $value) {
+ if ($row[$value['dbField']]) {
+ $principal[$key] = $row[$value['dbField']];
+ }
+ }
+ return $principal;
+
+ }
+
+ /**
+ * Updates one ore more webdav properties on a principal.
+ *
+ * The list of mutations is stored in a Sabre\DAV\PropPatch object.
+ * To do the actual updates, you must tell this object which properties
+ * you're going to process with the handle() method.
+ *
+ * Calling the handle method is like telling the PropPatch object "I
+ * promise I can handle updating this property".
+ *
+ * Read the PropPatch documenation for more info and examples.
+ *
+ * @param string $path
+ * @param DAV\PropPatch $propPatch
+ */
+ 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;
+ $values[$dbField] = $value;
+
+ }
+
+ $query .= " WHERE uri = :uri";
+ $values['uri'] = $path;
+
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute($values);
+
+ return true;
+
+ });
+
+ }
+
+ /**
+ * This method is used to search for principals matching a set of
+ * properties.
+ *
+ * This search is specifically used by RFC3744's principal-property-search
+ * REPORT.
+ *
+ * The actual search should be a unicode-non-case-sensitive search. The
+ * keys in searchProperties are the WebDAV property names, while the values
+ * are the property values to search on.
+ *
+ * By default, if multiple properties are submitted to this method, the
+ * various properties should be combined with 'AND'. If $test is set to
+ * 'anyof', it should be combined using 'OR'.
+ *
+ * This method should simply return an array with full principal uri's.
+ *
+ * If somebody attempted to search on a property the backend does not
+ * support, you should simply return 0 results.
+ *
+ * You can also just return 0 results if you choose to not support
+ * searching at all, but keep in mind that this may stop certain features
+ * from working.
+ *
+ * @param string $prefixPath
+ * @param array $searchProperties
+ * @param string $test
+ * @return array
+ */
+ function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
+ if (count($searchProperties) == 0) return []; //No criteria
+
+ $query = 'SELECT uri FROM ' . $this->tableName . ' WHERE ';
+ $values = [];
+ foreach ($searchProperties as $property => $value) {
+ switch ($property) {
+ case '{DAV:}displayname' :
+ $column = "displayname";
+ break;
+ case '{http://sabredav.org/ns}email-address' :
+ $column = "email";
+ break;
+ default :
+ // Unsupported property
+ return [];
+ }
+ if (count($values) > 0) $query .= (strcmp($test, "anyof") == 0 ? " 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;
+
+ $principals[] = $row['uri'];
+
+ }
+
+ return $principals;
+
+ }
+
+ /**
+ * Finds a principal by its URI.
+ *
+ * This method may receive any type of uri, but mailto: addresses will be
+ * the most common.
+ *
+ * Implementation of this API is optional. It is currently used by the
+ * CalDAV system to find principals based on their email addresses. If this
+ * API is not implemented, some features may not work correctly.
+ *
+ * This method must return a relative principal path, or null, if the
+ * principal was not found or you refuse to find it.
+ *
+ * @param string $uri
+ * @param string $principalPrefix
+ * @return string
+ */
+ function findByUri($uri, $principalPrefix) {
+ $value = null;
+ $scheme = null;
+ list($scheme, $value) = explode(":", $uri, 2);
+ if ($value == null) return null;
+
+ $uri = null;
+ 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;
+
+ $uri = $row['uri'];
+ break; //Stop on first match
+ }
+ break;
+ default:
+ //unsupported uri scheme
+ return null;
+ }
+ return $uri;
+ }
+
+ /**
+ * Returns the list of members for a group-principal
+ *
+ * @param string $principal
+ * @return array
+ */
+ function getGroupMemberSet($principal) {
+
+ $principal = $this->getPrincipalByPath($principal);
+ if (!$principal) throw new DAV\Exception('Principal not found');
+
+ $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM ' . $this->groupMembersTableName . ' AS groupmembers LEFT JOIN ' . $this->tableName . ' AS principals ON groupmembers.member_id = principals.id WHERE groupmembers.principal_id = ?');
+ $stmt->execute([$principal['id']]);
+
+ $result = [];
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ $result[] = $row['uri'];
+ }
+ return $result;
+
+ }
+
+ /**
+ * Returns the list of groups a principal is a member of
+ *
+ * @param string $principal
+ * @return array
+ */
+ function getGroupMembership($principal) {
+
+ $principal = $this->getPrincipalByPath($principal);
+ if (!$principal) throw new DAV\Exception('Principal not found');
+
+ $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM ' . $this->groupMembersTableName . ' AS groupmembers LEFT JOIN ' . $this->tableName . ' AS principals ON groupmembers.principal_id = principals.id WHERE groupmembers.member_id = ?');
+ $stmt->execute([$principal['id']]);
+
+ $result = [];
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ $result[] = $row['uri'];
+ }
+ return $result;
+
+ }
+
+ /**
+ * Updates the list of group members for a group principal.
+ *
+ * The principals should be passed as a list of uri's.
+ *
+ * @param string $principal
+ * @param array $members
+ * @return void
+ */
+ function setGroupMemberSet($principal, array $members) {
+
+ // Grabbing the list of principal id's.
+ $stmt = $this->pdo->prepare('SELECT id, uri FROM ' . $this->tableName . ' WHERE uri IN (? ' . str_repeat(', ? ', count($members)) . ');');
+ $stmt->execute(array_merge([$principal], $members));
+
+ $memberIds = [];
+ $principalId = null;
+
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ if ($row['uri'] == $principal) {
+ $principalId = $row['id'];
+ } else {
+ $memberIds[] = $row['id'];
+ }
+ }
+ if (!$principalId) throw new DAV\Exception('Principal not found');
+
+ // Wiping out old members
+ $stmt = $this->pdo->prepare('DELETE FROM ' . $this->groupMembersTableName . ' WHERE principal_id = ?;');
+ $stmt->execute([$principalId]);
+
+ foreach ($memberIds as $memberId) {
+
+ $stmt = $this->pdo->prepare('INSERT INTO ' . $this->groupMembersTableName . ' (principal_id, member_id) VALUES (?, ?);');
+ $stmt->execute([$principalId, $memberId]);
+
+ }
+
+ }
+
+ /**
+ * Creates a new principal.
+ *
+ * This method receives a full path for the new principal. The mkCol object
+ * contains any additional webdav properties specified during the creation
+ * of the principal.
+ *
+ * @param string $path
+ * @param MkCol $mkCol
+ * @return void
+ */
+ 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/vendor/sabre/dav/lib/DAVACL/PrincipalCollection.php b/vendor/sabre/dav/lib/DAVACL/PrincipalCollection.php
new file mode 100644
index 000000000..54911e7b5
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/PrincipalCollection.php
@@ -0,0 +1,151 @@
+<?php
+
+namespace Sabre\DAVACL;
+
+use Sabre\DAV\Exception\InvalidResourceType;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\IExtendedCollection;
+use Sabre\DAV\MkCol;
+
+/**
+ * Principals Collection
+ *
+ * This collection represents a list of users.
+ * The users are instances of Sabre\DAVACL\Principal
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PrincipalCollection extends AbstractPrincipalCollection implements IExtendedCollection, IACL {
+
+ /**
+ * This method returns a node for a principal.
+ *
+ * The passed array contains principal information, and is guaranteed to
+ * at least contain a uri item. Other properties may or may not be
+ * supplied by the authentication backend.
+ *
+ * @param array $principal
+ * @return \Sabre\DAV\INode
+ */
+ function getChildForPrincipal(array $principal) {
+
+ return new Principal($this->principalBackend, $principal);
+
+ }
+
+ /**
+ * Creates a new collection.
+ *
+ * This method will receive a MkCol object with all the information about
+ * the new collection that's being created.
+ *
+ * The MkCol object contains information about the resourceType of the new
+ * collection. If you don't support the specified resourceType, you should
+ * throw Exception\InvalidResourceType.
+ *
+ * The object also contains a list of WebDAV properties for the new
+ * collection.
+ *
+ * You should call the handle() method on this object to specify exactly
+ * which properties you are storing. This allows the system to figure out
+ * exactly which properties you didn't store, which in turn allows other
+ * plugins (such as the propertystorage plugin) to handle storing the
+ * property for you.
+ *
+ * @param string $name
+ * @param MkCol $mkCol
+ * @throws Exception\InvalidResourceType
+ * @return void
+ */
+ 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,
+ $mkCol
+ );
+
+ }
+
+ /**
+ * Returns the owner principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getOwner() {
+ return null;
+ }
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getGroup() {
+ return null;
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+ return [
+ [
+ 'principal' => '{DAV:}authenticated',
+ 'privilege' => '{DAV:}read',
+ 'protected' => true,
+ ],
+ ];
+ }
+
+ /**
+ * Updates the ACL
+ *
+ * This method will receive a list of new ACE's as an array argument.
+ *
+ * @param array $acl
+ * @return void
+ */
+ function setACL(array $acl) {
+
+ throw new Forbidden('Updating ACLs is not allowed on this node');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Xml/Property/Acl.php b/vendor/sabre/dav/lib/DAVACL/Xml/Property/Acl.php
new file mode 100644
index 000000000..9f5e40df1
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Property/Acl.php
@@ -0,0 +1,277 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Property;
+
+use Sabre\DAV;
+use Sabre\DAV\Browser\HtmlOutput;
+use Sabre\DAV\Browser\HtmlOutputHelper;
+use Sabre\Xml\Element;
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+
+/**
+ * This class represents the {DAV:}acl property.
+ *
+ * The {DAV:}acl property is a full list of access control entries for a
+ * resource.
+ *
+ * {DAV:}acl is used as a WebDAV property, but it is also used within the body
+ * of the ACL request.
+ *
+ * See:
+ * http://tools.ietf.org/html/rfc3744#section-5.5
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Acl implements Element, HtmlOutput {
+
+ /**
+ * List of privileges
+ *
+ * @var array
+ */
+ protected $privileges;
+
+ /**
+ * Whether or not the server base url is required to be prefixed when
+ * serializing the property.
+ *
+ * @var bool
+ */
+ protected $prefixBaseUrl;
+
+ /**
+ * Constructor
+ *
+ * This object requires a structure similar to the return value from
+ * Sabre\DAVACL\Plugin::getACL().
+ *
+ * Each privilege is a an array with at least a 'privilege' property, and a
+ * 'principal' property. A privilege may have a 'protected' property as
+ * well.
+ *
+ * The prefixBaseUrl should be set to false, if the supplied principal urls
+ * are already full urls. If this is kept to true, the servers base url
+ * will automatically be prefixed.
+ *
+ * @param array $privileges
+ * @param bool $prefixBaseUrl
+ */
+ function __construct(array $privileges, $prefixBaseUrl = true) {
+
+ $this->privileges = $privileges;
+ $this->prefixBaseUrl = $prefixBaseUrl;
+
+ }
+
+ /**
+ * Returns the list of privileges for this property
+ *
+ * @return array
+ */
+ function getPrivileges() {
+
+ return $this->privileges;
+
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ foreach ($this->privileges as $ace) {
+
+ $this->serializeAce($writer, $ace);
+
+ }
+
+ }
+
+ /**
+ * Generate html representation for this value.
+ *
+ * The html output is 100% trusted, and no effort is being made to sanitize
+ * it. It's up to the implementor to sanitize user provided values.
+ *
+ * The output must be in UTF-8.
+ *
+ * The baseUri parameter is a url to the root of the application, and can
+ * be used to construct local links.
+ *
+ * @param HtmlOutputHelper $html
+ * @return string
+ */
+ function toHtml(HtmlOutputHelper $html) {
+
+ ob_start();
+ echo "<table>";
+ echo "<tr><th>Principal</th><th>Privilege</th><th></th></tr>";
+ foreach ($this->privileges as $privilege) {
+
+ echo '<tr>';
+ // if it starts with a {, it's a special principal
+ if ($privilege['principal'][0] === '{') {
+ echo '<td>', $html->xmlName($privilege['principal']), '</td>';
+ } else {
+ echo '<td>', $html->link($privilege['principal']), '</td>';
+ }
+ echo '<td>', $html->xmlName($privilege['privilege']), '</td>';
+ echo '<td>';
+ if (!empty($privilege['protected'])) echo '(protected)';
+ echo '</td>';
+ echo '</tr>';
+
+ }
+ echo "</table>";
+ return ob_get_clean();
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * Important note 2: You are responsible for advancing the reader to the
+ * next element. Not doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $elementMap = [
+ '{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') {
+ continue;
+ }
+ $ace = $element['value'];
+
+ if (empty($ace['{DAV:}principal'])) {
+ throw new DAV\Exception\BadRequest('Each {DAV:}ace element must have one {DAV:}principal element');
+ }
+ $principal = $ace['{DAV:}principal'];
+
+ switch ($principal->getType()) {
+ case Principal::HREF :
+ $principal = $principal->getHref();
+ break;
+ case Principal::AUTHENTICATED :
+ $principal = '{DAV:}authenticated';
+ break;
+ case Principal::UNAUTHENTICATED :
+ $principal = '{DAV:}unauthenticated';
+ break;
+ case Principal::ALL :
+ $principal = '{DAV:}all';
+ break;
+
+ }
+
+ $protected = array_key_exists('{DAV:}protected', $ace);
+
+ if (!isset($ace['{DAV:}grant'])) {
+ 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') {
+ continue;
+ }
+
+ foreach ($elem['value'] as $priv) {
+ $privileges[] = [
+ 'principal' => $principal,
+ 'protected' => $protected,
+ 'privilege' => $priv,
+ ];
+ }
+
+ }
+
+ }
+
+ return new self($privileges);
+
+ }
+
+ /**
+ * Serializes a single access control entry.
+ *
+ * @param Writer $writer
+ * @param array $ace
+ * @return void
+ */
+ private function serializeAce(Writer $writer, array $ace) {
+
+ $writer->startElement('{DAV:}ace');
+
+ switch ($ace['principal']) {
+ case '{DAV:}authenticated' :
+ $principal = new Principal(Principal::AUTHENTICATED);
+ break;
+ case '{DAV:}unauthenticated' :
+ $principal = new Principal(Principal::UNAUTHENTICATED);
+ break;
+ case '{DAV:}all' :
+ $principal = new Principal(Principal::ALL);
+ break;
+ default:
+ $principal = new Principal(Principal::HREF, $ace['principal']);
+ break;
+ }
+
+ $writer->writeElement('{DAV:}principal', $principal);
+ $writer->startElement('{DAV:}grant');
+ $writer->startElement('{DAV:}privilege');
+
+ $writer->writeElement($ace['privilege']);
+
+ $writer->endElement(); // privilege
+ $writer->endElement(); // grant
+
+ if (!empty($ace['protected'])) {
+ $writer->writeElement('{DAV:}protected');
+ }
+
+ $writer->endElement(); // ace
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php b/vendor/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php
new file mode 100644
index 000000000..f669cc5e1
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Property;
+
+use Sabre\Xml\XmlSerializable;
+use Sabre\Xml\Writer;
+
+/**
+ * AclRestrictions property
+ *
+ * This property represents {DAV:}acl-restrictions, as defined in RFC3744.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class AclRestrictions implements XmlSerializable {
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ $writer->writeElement('{DAV:}grant-only');
+ $writer->writeElement('{DAV:}no-invert');
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php b/vendor/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php
new file mode 100644
index 000000000..0a95eb2b7
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php
@@ -0,0 +1,159 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Property;
+
+use Sabre\DAV\Browser\HtmlOutput;
+use Sabre\DAV\Browser\HtmlOutputHelper;
+use Sabre\Xml\Element;
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+
+/**
+ * CurrentUserPrivilegeSet
+ *
+ * This class represents the current-user-privilege-set property. When
+ * requested, it contain all the privileges a user has on a specific node.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class CurrentUserPrivilegeSet implements Element, HtmlOutput {
+
+ /**
+ * List of privileges
+ *
+ * @var array
+ */
+ private $privileges;
+
+ /**
+ * Creates the object
+ *
+ * Pass the privileges in clark-notation
+ *
+ * @param array $privileges
+ */
+ function __construct(array $privileges) {
+
+ $this->privileges = $privileges;
+
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ foreach ($this->privileges as $privName) {
+
+ $writer->startElement('{DAV:}privilege');
+ $writer->writeElement($privName);
+ $writer->endElement();
+
+ }
+
+
+ }
+
+ /**
+ * Returns true or false, whether the specified principal appears in the
+ * list.
+ *
+ * @param string $privilegeName
+ * @return bool
+ */
+ function has($privilegeName) {
+
+ return in_array($privilegeName, $this->privileges);
+
+ }
+
+ /**
+ * Returns the list of privileges.
+ *
+ * @return array
+ */
+ function getValue() {
+
+ return $this->privileges;
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $result = [];
+
+ $tree = $reader->parseInnerTree(['{DAV:}privilege' => 'Sabre\\Xml\\Element\\Elements']);
+ foreach ($tree as $element) {
+ if ($element['name'] !== '{DAV:}privilege') {
+ continue;
+ }
+ $result[] = $element['value'][0];
+ }
+ return new self($result);
+
+ }
+
+ /**
+ * Generate html representation for this value.
+ *
+ * The html output is 100% trusted, and no effort is being made to sanitize
+ * it. It's up to the implementor to sanitize user provided values.
+ *
+ * The output must be in UTF-8.
+ *
+ * The baseUri parameter is a url to the root of the application, and can
+ * be used to construct local links.
+ *
+ * @param HtmlOutputHelper $html
+ * @return string
+ */
+ function toHtml(HtmlOutputHelper $html) {
+
+ return implode(
+ ', ',
+ array_map([$html, 'xmlName'], $this->getValue())
+ );
+
+ }
+
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Xml/Property/Principal.php b/vendor/sabre/dav/lib/DAVACL/Xml/Property/Principal.php
new file mode 100644
index 000000000..d32249d8b
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Property/Principal.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Property;
+
+use Sabre\DAV;
+use Sabre\DAV\Browser\HtmlOutputHelper;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+
+/**
+ * Principal property
+ *
+ * The principal property represents a principal from RFC3744 (ACL).
+ * The property can be used to specify a principal or pseudo principals.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Principal extends DAV\Xml\Property\Href {
+
+ /**
+ * To specify a not-logged-in user, use the UNAUTHENTICATED principal
+ */
+ const UNAUTHENTICATED = 1;
+
+ /**
+ * To specify any principal that is logged in, use AUTHENTICATED
+ */
+ const AUTHENTICATED = 2;
+
+ /**
+ * Specific principals can be specified with the HREF
+ */
+ const HREF = 3;
+
+ /**
+ * Everybody, basically
+ */
+ const ALL = 4;
+
+ /**
+ * Principal-type
+ *
+ * Must be one of the UNAUTHENTICATED, AUTHENTICATED or HREF constants.
+ *
+ * @var int
+ */
+ protected $type;
+
+ /**
+ * Creates the property.
+ *
+ * The 'type' argument must be one of the type constants defined in this class.
+ *
+ * 'href' is only required for the HREF type.
+ *
+ * @param int $type
+ * @param string|null $href
+ */
+ function __construct($type, $href = null) {
+
+ $this->type = $type;
+ if ($type === self::HREF && is_null($href)) {
+ throw new DAV\Exception('The href argument must be specified for the HREF principal type.');
+ }
+ if ($href) {
+ $href = rtrim($href, '/') . '/';
+ parent::__construct($href);
+ }
+
+ }
+
+ /**
+ * Returns the principal type
+ *
+ * @return int
+ */
+ function getType() {
+
+ return $this->type;
+
+ }
+
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ switch ($this->type) {
+
+ case self::UNAUTHENTICATED :
+ $writer->writeElement('{DAV:}unauthenticated');
+ break;
+ case self::AUTHENTICATED :
+ $writer->writeElement('{DAV:}authenticated');
+ break;
+ case self::HREF :
+ parent::xmlSerialize($writer);
+ break;
+ case self::ALL :
+ $writer->writeElement('{DAV:}all');
+ break;
+ }
+
+ }
+
+ /**
+ * Generate html representation for this value.
+ *
+ * The html output is 100% trusted, and no effort is being made to sanitize
+ * it. It's up to the implementor to sanitize user provided values.
+ *
+ * The output must be in UTF-8.
+ *
+ * The baseUri parameter is a url to the root of the application, and can
+ * be used to construct local links.
+ *
+ * @param HtmlOutputHelper $html
+ * @return string
+ */
+ function toHtml(HtmlOutputHelper $html) {
+
+ switch ($this->type) {
+
+ case self::UNAUTHENTICATED :
+ return '<em>unauthenticated</em>';
+ case self::AUTHENTICATED :
+ return '<em>authenticated</em>';
+ case self::HREF :
+ return parent::toHtml($html);
+ case self::ALL :
+ return '<em>all</em>';
+ }
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called staticly, 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
+ * free to return other data as well.
+ *
+ * Important note 2: You are responsible for advancing the reader to the
+ * next element. Not doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $tree = $reader->parseInnerTree()[0];
+
+ switch ($tree['name']) {
+ case '{DAV:}unauthenticated' :
+ return new self(self::UNAUTHENTICATED);
+ 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']);
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php b/vendor/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php
new file mode 100644
index 000000000..572bed4dd
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php
@@ -0,0 +1,159 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Property;
+
+use Sabre\DAV\Browser\HtmlOutput;
+use Sabre\DAV\Browser\HtmlOutputHelper;
+use Sabre\Xml\XmlSerializable;
+use Sabre\Xml\Writer;
+
+/**
+ * SupportedPrivilegeSet property
+ *
+ * This property encodes the {DAV:}supported-privilege-set property, as defined
+ * in rfc3744. Please consult the rfc for details about it's structure.
+ *
+ * This class expects a structure like the one given from
+ * Sabre\DAVACL\Plugin::getSupportedPrivilegeSet as the argument in its
+ * constructor.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class SupportedPrivilegeSet implements XmlSerializable, HtmlOutput {
+
+ /**
+ * privileges
+ *
+ * @var array
+ */
+ protected $privileges;
+
+ /**
+ * Constructor
+ *
+ * @param array $privileges
+ */
+ function __construct(array $privileges) {
+
+ $this->privileges = $privileges;
+
+ }
+
+ /**
+ * Returns the privilege value.
+ *
+ * @return array
+ */
+ function getValue() {
+
+ return $this->privileges;
+
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ $this->serializePriv($writer, $this->privileges);
+
+ }
+
+ /**
+ * Generate html representation for this value.
+ *
+ * The html output is 100% trusted, and no effort is being made to sanitize
+ * it. It's up to the implementor to sanitize user provided values.
+ *
+ * The output must be in UTF-8.
+ *
+ * The baseUri parameter is a url to the root of the application, and can
+ * be used to construct local links.
+ *
+ * @param HtmlOutputHelper $html
+ * @return string
+ */
+ function toHtml(HtmlOutputHelper $html) {
+
+ $traverse = function($priv) use (&$traverse, $html) {
+ echo "<li>";
+ echo $html->xmlName($priv['privilege']);
+ if (isset($priv['abstract']) && $priv['abstract']) {
+ echo " <i>(abstract)</i>";
+ }
+ if (isset($priv['description'])) {
+ echo " " . $html->h($priv['description']);
+ }
+ if (isset($priv['aggregates'])) {
+ echo "\n<ul>\n";
+ foreach ($priv['aggregates'] as $subPriv) {
+ $traverse($subPriv);
+ }
+ echo "</ul>";
+ }
+ echo "</li>\n";
+ };
+
+ ob_start();
+ echo "<ul class=\"tree\">";
+ $traverse($this->getValue());
+ echo "</ul>\n";
+
+ return ob_get_clean();
+
+ }
+
+
+
+ /**
+ * Serializes a property
+ *
+ * This is a recursive function.
+ *
+ * @param Writer $writer
+ * @param array $privilege
+ * @return void
+ */
+ private function serializePriv(Writer $writer, $privilege) {
+
+ $writer->startElement('{DAV:}supported-privilege');
+
+ $writer->startElement('{DAV:}privilege');
+ $writer->writeElement($privilege['privilege']);
+ $writer->endElement(); // privilege
+
+ if (!empty($privilege['abstract'])) {
+ $writer->writeElement('{DAV:}abstract');
+ }
+ if (!empty($privilege['description'])) {
+ $writer->writeElement('{DAV:}description', $privilege['description']);
+ }
+ if (isset($privilege['aggregates'])) {
+ foreach ($privilege['aggregates'] as $subPrivilege) {
+ $this->serializePriv($writer, $subPrivilege);
+ }
+ }
+
+ $writer->endElement(); // supported-privilege
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php b/vendor/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php
new file mode 100644
index 000000000..3f535e301
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Request;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+
+/**
+ * ExpandProperty request parser.
+ *
+ * This class parses the {DAV:}expand-property REPORT, as defined in:
+ *
+ * http://tools.ietf.org/html/rfc3253#section-3.8
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ExpandPropertyReport implements XmlDeserializable {
+
+ /**
+ * An array with requested properties.
+ *
+ * The requested properties will be used as keys in this array. The value
+ * is normally null.
+ *
+ * If the value is an array though, it means the property must be expanded.
+ * Within the array, the sub-properties, which themselves may be null or
+ * arrays.
+ *
+ * @var array
+ */
+ public $properties;
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $elems = $reader->parseInnerTree();
+
+ $obj = new self();
+ $obj->properties = self::traverse($elems);
+
+ return $obj;
+
+ }
+
+ /**
+ * This method is used by deserializeXml, to recursively parse the
+ * {DAV:}property elements.
+ *
+ * @param array $elems
+ * @return void
+ */
+ private static function traverse($elems) {
+
+ $result = [];
+
+ foreach ($elems as $elem) {
+
+ if ($elem['name'] !== '{DAV:}property') {
+ continue;
+ }
+
+ $namespace = isset($elem['attributes']['namespace']) ?
+ $elem['attributes']['namespace'] :
+ 'DAV:';
+
+ $propName = '{' . $namespace . '}' . $elem['attributes']['name'];
+
+ $value = null;
+ if (is_array($elem['value'])) {
+ $value = self::traverse($elem['value']);
+ }
+
+ $result[$propName] = $value;
+
+ }
+
+ return $result;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php b/vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php
new file mode 100644
index 000000000..1e7aa4481
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Request;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+use Sabre\DAV\Exception\BadRequest;
+
+/**
+ * PrincipalSearchPropertySetReport request parser.
+ *
+ * This class parses the {DAV:}principal-property-search REPORT, as defined
+ * in:
+ *
+ * https://tools.ietf.org/html/rfc3744#section-9.4
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PrincipalPropertySearchReport implements XmlDeserializable {
+
+ /**
+ * The requested properties.
+ *
+ * @var array|null
+ */
+ public $properties;
+
+ /**
+ * searchProperties
+ *
+ * @var array
+ */
+ public $searchProperties = [];
+
+ /**
+ * By default the property search will be conducted on the url of the http
+ * request. If this is set to true, it will be applied to the principal
+ * collection set instead.
+ *
+ * @var bool
+ */
+ public $applyToPrincipalCollectionSet = false;
+
+ /**
+ * Search for principals matching ANY of the properties (OR) or a ALL of
+ * the properties (AND).
+ *
+ * This property is either "anyof" or "allof".
+ *
+ * @var string
+ */
+ public $test;
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $self = new self();
+
+ $foundSearchProp = false;
+ $self->test = 'allof';
+ if ($reader->getAttribute('test') === 'anyof') {
+ $self->test = 'anyof';
+ }
+
+ $elemMap = [
+ '{DAV:}property-search' => 'Sabre\\Xml\\Element\\KeyValue',
+ '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue',
+ ];
+
+ foreach ($reader->parseInnerTree($elemMap) as $elem) {
+
+ switch ($elem['name']) {
+
+ case '{DAV:}prop' :
+ $self->properties = array_keys($elem['value']);
+ break;
+ case '{DAV:}property-search' :
+ $foundSearchProp = true;
+ // This property has two sub-elements:
+ // {DAV:}prop - The property to be searched on. This may
+ // also be more than one
+ // {DAV:}match - The value to match with
+ if (!isset($elem['value']['{DAV:}prop']) || !isset($elem['value']['{DAV:}match'])) {
+ throw new BadRequest('The {DAV:}property-search element must contain one {DAV:}match and one {DAV:}prop element');
+ }
+ foreach ($elem['value']['{DAV:}prop'] as $propName => $discard) {
+ $self->searchProperties[$propName] = $elem['value']['{DAV:}match'];
+ }
+ break;
+ 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/vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php b/vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php
new file mode 100644
index 000000000..ade157b19
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Request;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+use Sabre\DAV\Exception\BadRequest;
+
+/**
+ * PrincipalSearchPropertySetReport request parser.
+ *
+ * This class parses the {DAV:}principal-search-property-set REPORT, as defined
+ * in:
+ *
+ * https://tools.ietf.org/html/rfc3744#section-9.5
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PrincipalSearchPropertySetReport implements XmlDeserializable {
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ if (!$reader->isEmptyElement) {
+ throw new BadRequest('The {DAV:}principal-search-property-set element must be empty');
+ }
+
+ // The element is actually empty, so there's not much to do.
+ $reader->next();
+
+ $self = new self();
+ return $self;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Backend/AbstractBackend.php b/vendor/sabre/dav/lib/Sabre/CalDAV/Backend/AbstractBackend.php
deleted file mode 100644
index c83409eab..000000000
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Backend/AbstractBackend.php
+++ /dev/null
@@ -1,155 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Backend;
-
-use Sabre\VObject;
-use Sabre\CalDAV;
-
-/**
- * Abstract Calendaring backend. Extend this class to create your own backends.
- *
- * Checkout the BackendInterface for all the methods that must be implemented.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-abstract class AbstractBackend implements BackendInterface {
-
- /**
- * Updates properties for a calendar.
- *
- * The mutations array uses the propertyName in clark-notation as key,
- * and the array value for the property value. In the case a property
- * should be deleted, the property value will be null.
- *
- * This method must be atomic. If one property cannot be changed, the
- * entire operation must fail.
- *
- * If the operation was successful, true can be returned.
- * If the operation failed, false can be returned.
- *
- * Deletion of a non-existent property is always successful.
- *
- * Lastly, it is optional to return detailed information about any
- * failures. In this case an array should be returned with the following
- * structure:
- *
- * array(
- * 403 => array(
- * '{DAV:}displayname' => null,
- * ),
- * 424 => array(
- * '{DAV:}owner' => null,
- * )
- * )
- *
- * In this example it was forbidden to update {DAV:}displayname.
- * (403 Forbidden), which in turn also caused {DAV:}owner to fail
- * (424 Failed Dependency) because the request needs to be atomic.
- *
- * @param mixed $calendarId
- * @param array $mutations
- * @return bool|array
- */
- public function updateCalendar($calendarId, array $mutations) {
-
- return false;
-
- }
-
- /**
- * Performs a calendar-query on the contents of this calendar.
- *
- * The calendar-query is defined in RFC4791 : CalDAV. Using the
- * calendar-query it is possible for a client to request a specific set of
- * object, based on contents of iCalendar properties, date-ranges and
- * iCalendar component types (VTODO, VEVENT).
- *
- * This method should just return a list of (relative) urls that match this
- * query.
- *
- * The list of filters are specified as an array. The exact array is
- * documented by \Sabre\CalDAV\CalendarQueryParser.
- *
- * Note that it is extremely likely that getCalendarObject for every path
- * returned from this method will be called almost immediately after. You
- * may want to anticipate this to speed up these requests.
- *
- * This method provides a default implementation, which parses *all* the
- * iCalendar objects in the specified calendar.
- *
- * This default may well be good enough for personal use, and calendars
- * that aren't very large. But if you anticipate high usage, big calendars
- * or high loads, you are strongly adviced to optimize certain paths.
- *
- * The best way to do so is override this method and to optimize
- * specifically for 'common filters'.
- *
- * Requests that are extremely common are:
- * * requests for just VEVENTS
- * * requests for just VTODO
- * * requests with a time-range-filter on either VEVENT or VTODO.
- *
- * ..and combinations of these requests. It may not be worth it to try to
- * handle every possible situation and just rely on the (relatively
- * easy to use) CalendarQueryValidator to handle the rest.
- *
- * Note that especially time-range-filters may be difficult to parse. A
- * time-range filter specified on a VEVENT must for instance also handle
- * recurrence rules correctly.
- * A good example of how to interprete all these filters can also simply
- * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct
- * as possible, so it gives you a good idea on what type of stuff you need
- * to think of.
- *
- * @param mixed $calendarId
- * @param array $filters
- * @return array
- */
- public function calendarQuery($calendarId, array $filters) {
-
- $result = array();
- $objects = $this->getCalendarObjects($calendarId);
-
- $validator = new \Sabre\CalDAV\CalendarQueryValidator();
-
- foreach($objects as $object) {
-
- if ($this->validateFilterForObject($object, $filters)) {
- $result[] = $object['uri'];
- }
-
- }
-
- return $result;
-
- }
-
- /**
- * This method validates if a filters (as passed to calendarQuery) matches
- * the given object.
- *
- * @param array $object
- * @param array $filters
- * @return bool
- */
- protected function validateFilterForObject(array $object, array $filters) {
-
- // Unfortunately, setting the 'calendardata' here is optional. If
- // it was excluded, we actually need another call to get this as
- // well.
- if (!isset($object['calendardata'])) {
- $object = $this->getCalendarObject($object['calendarid'], $object['uri']);
- }
-
- $data = is_resource($object['calendardata'])?stream_get_contents($object['calendardata']):$object['calendardata'];
- $vObject = VObject\Reader::read($data);
-
- $validator = new CalDAV\CalendarQueryValidator();
- return $validator->validate($vObject, $filters);
-
- }
-
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Backend/PDO.php b/vendor/sabre/dav/lib/Sabre/CalDAV/Backend/PDO.php
deleted file mode 100644
index 2e93a7b25..000000000
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Backend/PDO.php
+++ /dev/null
@@ -1,691 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Backend;
-
-use Sabre\VObject;
-use Sabre\CalDAV;
-use Sabre\DAV;
-
-/**
- * PDO CalDAV backend
- *
- * This backend is used to store calendar-data in a PDO database, such as
- * sqlite or MySQL
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class PDO extends AbstractBackend {
-
- /**
- * We need to specify a max date, because we need to stop *somewhere*
- *
- * On 32 bit system the maximum for a signed integer is 2147483647, so
- * MAX_DATE cannot be higher than date('Y-m-d', 2147483647) which results
- * in 2038-01-19 to avoid problems when the date is converted
- * to a unix timestamp.
- */
- const MAX_DATE = '2038-01-01';
-
- /**
- * pdo
- *
- * @var \PDO
- */
- protected $pdo;
-
- /**
- * The table name that will be used for calendars
- *
- * @var string
- */
- protected $calendarTableName;
-
- /**
- * The table name that will be used for calendar objects
- *
- * @var string
- */
- protected $calendarObjectTableName;
-
- /**
- * List of CalDAV properties, and how they map to database fieldnames
- * Add your own properties by simply adding on to this array.
- *
- * Note that only string-based properties are supported here.
- *
- * @var array
- */
- public $propertyMap = array(
- '{DAV:}displayname' => 'displayname',
- '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
- '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone',
- '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder',
- '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor',
- );
-
- /**
- * Creates the backend
- *
- * @param \PDO $pdo
- * @param string $calendarTableName
- * @param string $calendarObjectTableName
- */
- public function __construct(\PDO $pdo, $calendarTableName = 'calendars', $calendarObjectTableName = 'calendarobjects') {
-
- $this->pdo = $pdo;
- $this->calendarTableName = $calendarTableName;
- $this->calendarObjectTableName = $calendarObjectTableName;
-
- }
-
- /**
- * Returns a list of calendars for a principal.
- *
- * Every project is an array with the following keys:
- * * id, a unique id that will be used by other functions to modify the
- * calendar. This can be the same as the uri or a database key.
- * * uri, which the basename of the uri with which the calendar is
- * accessed.
- * * principaluri. The owner of the calendar. Almost always the same as
- * principalUri passed to this method.
- *
- * Furthermore it can contain webdav properties in clark notation. A very
- * common one is '{DAV:}displayname'.
- *
- * @param string $principalUri
- * @return array
- */
- public function getCalendarsForUser($principalUri) {
-
- $fields = array_values($this->propertyMap);
- $fields[] = 'id';
- $fields[] = 'uri';
- $fields[] = 'ctag';
- $fields[] = 'components';
- $fields[] = 'principaluri';
- $fields[] = 'transparent';
-
- // Making fields a comma-delimited list
- $fields = implode(', ', $fields);
- $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM ".$this->calendarTableName." WHERE principaluri = ? ORDER BY calendarorder ASC");
- $stmt->execute(array($principalUri));
-
- $calendars = array();
- while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
-
- $components = array();
- if ($row['components']) {
- $components = explode(',',$row['components']);
- }
-
- $calendar = array(
- 'id' => $row['id'],
- 'uri' => $row['uri'],
- 'principaluri' => $row['principaluri'],
- '{' . CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0',
- '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Property\SupportedCalendarComponentSet($components),
- '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' => new CalDAV\Property\ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
- );
-
-
- foreach($this->propertyMap as $xmlName=>$dbName) {
- $calendar[$xmlName] = $row[$dbName];
- }
-
- $calendars[] = $calendar;
-
- }
-
- return $calendars;
-
- }
-
- /**
- * Creates a new calendar for a principal.
- *
- * If the creation was a success, an id must be returned that can be used to reference
- * this calendar in other methods, such as updateCalendar
- *
- * @param string $principalUri
- * @param string $calendarUri
- * @param array $properties
- * @return string
- */
- public function createCalendar($principalUri, $calendarUri, array $properties) {
-
- $fieldNames = array(
- 'principaluri',
- 'uri',
- 'ctag',
- 'transparent',
- );
- $values = array(
- ':principaluri' => $principalUri,
- ':uri' => $calendarUri,
- ':ctag' => 1,
- ':transparent' => 0,
- );
-
- // Default value
- $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
- $fieldNames[] = 'components';
- if (!isset($properties[$sccs])) {
- $values[':components'] = 'VEVENT,VTODO';
- } else {
- if (!($properties[$sccs] instanceof CalDAV\Property\SupportedCalendarComponentSet)) {
- throw new DAV\Exception('The ' . $sccs . ' property must be of type: \Sabre\CalDAV\Property\SupportedCalendarComponentSet');
- }
- $values[':components'] = implode(',',$properties[$sccs]->getValue());
- }
- $transp = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp';
- if (isset($properties[$transp])) {
- $values[':transparent'] = $properties[$transp]->getValue()==='transparent';
- }
-
- foreach($this->propertyMap as $xmlName=>$dbName) {
- if (isset($properties[$xmlName])) {
-
- $values[':' . $dbName] = $properties[$xmlName];
- $fieldNames[] = $dbName;
- }
- }
-
- $stmt = $this->pdo->prepare("INSERT INTO ".$this->calendarTableName." (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")");
- $stmt->execute($values);
-
- return $this->pdo->lastInsertId();
-
- }
-
- /**
- * Updates properties for a calendar.
- *
- * The mutations array uses the propertyName in clark-notation as key,
- * and the array value for the property value. In the case a property
- * should be deleted, the property value will be null.
- *
- * This method must be atomic. If one property cannot be changed, the
- * entire operation must fail.
- *
- * If the operation was successful, true can be returned.
- * If the operation failed, false can be returned.
- *
- * Deletion of a non-existent property is always successful.
- *
- * Lastly, it is optional to return detailed information about any
- * failures. In this case an array should be returned with the following
- * structure:
- *
- * array(
- * 403 => array(
- * '{DAV:}displayname' => null,
- * ),
- * 424 => array(
- * '{DAV:}owner' => null,
- * )
- * )
- *
- * In this example it was forbidden to update {DAV:}displayname.
- * (403 Forbidden), which in turn also caused {DAV:}owner to fail
- * (424 Failed Dependency) because the request needs to be atomic.
- *
- * @param string $calendarId
- * @param array $mutations
- * @return bool|array
- */
- public function updateCalendar($calendarId, array $mutations) {
-
- $newValues = array();
- $result = array(
- 200 => array(), // Ok
- 403 => array(), // Forbidden
- 424 => array(), // Failed Dependency
- );
-
- $hasError = false;
-
- foreach($mutations as $propertyName=>$propertyValue) {
-
- switch($propertyName) {
- case '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' :
- $fieldName = 'transparent';
- $newValues[$fieldName] = $propertyValue->getValue()==='transparent';
- break;
- default :
- // Checking the property map
- if (!isset($this->propertyMap[$propertyName])) {
- // We don't know about this property.
- $hasError = true;
- $result[403][$propertyName] = null;
- unset($mutations[$propertyName]);
- continue;
- }
-
- $fieldName = $this->propertyMap[$propertyName];
- $newValues[$fieldName] = $propertyValue;
- }
-
- }
-
- // If there were any errors we need to fail the request
- if ($hasError) {
- // Properties has the remaining properties
- foreach($mutations as $propertyName=>$propertyValue) {
- $result[424][$propertyName] = null;
- }
-
- // Removing unused statuscodes for cleanliness
- foreach($result as $status=>$properties) {
- if (is_array($properties) && count($properties)===0) unset($result[$status]);
- }
-
- return $result;
-
- }
-
- // Success
-
- // Now we're generating the sql query.
- $valuesSql = array();
- foreach($newValues as $fieldName=>$value) {
- $valuesSql[] = $fieldName . ' = ?';
- }
- $valuesSql[] = 'ctag = ctag + 1';
-
- $stmt = $this->pdo->prepare("UPDATE " . $this->calendarTableName . " SET " . implode(', ',$valuesSql) . " WHERE id = ?");
- $newValues['id'] = $calendarId;
- $stmt->execute(array_values($newValues));
-
- return true;
-
- }
-
- /**
- * Delete a calendar and all it's objects
- *
- * @param string $calendarId
- * @return void
- */
- public function deleteCalendar($calendarId) {
-
- $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?');
- $stmt->execute(array($calendarId));
-
- $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarTableName.' WHERE id = ?');
- $stmt->execute(array($calendarId));
-
- }
-
- /**
- * Returns all calendar objects within a calendar.
- *
- * Every item contains an array with the following keys:
- * * id - unique identifier which will be used for subsequent updates
- * * calendardata - The iCalendar-compatible calendar data
- * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
- * * lastmodified - a timestamp of the last modification time
- * * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
- * ' "abcdef"')
- * * calendarid - The calendarid as it was passed to this function.
- * * size - The size of the calendar objects, in bytes.
- *
- * Note that the etag is optional, but it's highly encouraged to return for
- * speed reasons.
- *
- * The calendardata is also optional. If it's not returned
- * 'getCalendarObject' will be called later, which *is* expected to return
- * calendardata.
- *
- * If neither etag or size are specified, the calendardata will be
- * used/fetched to determine these numbers. If both are specified the
- * amount of times this is needed is reduced by a great degree.
- *
- * @param string $calendarId
- * @return array
- */
- public function getCalendarObjects($calendarId) {
-
- $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?');
- $stmt->execute(array($calendarId));
-
- $result = array();
- foreach($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
- $result[] = array(
- 'id' => $row['id'],
- 'uri' => $row['uri'],
- 'lastmodified' => $row['lastmodified'],
- 'etag' => '"' . $row['etag'] . '"',
- 'calendarid' => $row['calendarid'],
- 'size' => (int)$row['size'],
- );
- }
-
- return $result;
-
- }
-
- /**
- * Returns information from a single calendar object, based on it's object
- * uri.
- *
- * The returned array must have the same keys as getCalendarObjects. The
- * 'calendardata' object is required here though, while it's not required
- * for getCalendarObjects.
- *
- * This method must return null if the object did not exist.
- *
- * @param string $calendarId
- * @param string $objectUri
- * @return array|null
- */
- public function getCalendarObject($calendarId,$objectUri) {
-
- $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?');
- $stmt->execute(array($calendarId, $objectUri));
- $row = $stmt->fetch(\PDO::FETCH_ASSOC);
-
- if(!$row) return null;
-
- return array(
- 'id' => $row['id'],
- 'uri' => $row['uri'],
- 'lastmodified' => $row['lastmodified'],
- 'etag' => '"' . $row['etag'] . '"',
- 'calendarid' => $row['calendarid'],
- 'size' => (int)$row['size'],
- 'calendardata' => $row['calendardata'],
- );
-
- }
-
-
- /**
- * Creates a new calendar object.
- *
- * It is possible return an etag from this function, which will be used in
- * the response to this PUT request. Note that the ETag must be surrounded
- * by double-quotes.
- *
- * However, you should only really return this ETag if you don't mangle the
- * calendar-data. If the result of a subsequent GET to this object is not
- * the exact same as this request body, you should omit the ETag.
- *
- * @param mixed $calendarId
- * @param string $objectUri
- * @param string $calendarData
- * @return string|null
- */
- public function createCalendarObject($calendarId,$objectUri,$calendarData) {
-
- $extraData = $this->getDenormalizedData($calendarData);
-
- $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence) VALUES (?,?,?,?,?,?,?,?,?)');
- $stmt->execute(array(
- $calendarId,
- $objectUri,
- $calendarData,
- time(),
- $extraData['etag'],
- $extraData['size'],
- $extraData['componentType'],
- $extraData['firstOccurence'],
- $extraData['lastOccurence'],
- ));
- $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?');
- $stmt->execute(array($calendarId));
-
- return '"' . $extraData['etag'] . '"';
-
- }
-
- /**
- * Updates an existing calendarobject, based on it's uri.
- *
- * It is possible return an etag from this function, which will be used in
- * the response to this PUT request. Note that the ETag must be surrounded
- * by double-quotes.
- *
- * However, you should only really return this ETag if you don't mangle the
- * calendar-data. If the result of a subsequent GET to this object is not
- * the exact same as this request body, you should omit the ETag.
- *
- * @param mixed $calendarId
- * @param string $objectUri
- * @param string $calendarData
- * @return string|null
- */
- public function updateCalendarObject($calendarId,$objectUri,$calendarData) {
-
- $extraData = $this->getDenormalizedData($calendarData);
-
- $stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ? WHERE calendarid = ? AND uri = ?');
- $stmt->execute(array($calendarData,time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'] ,$calendarId,$objectUri));
- $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?');
- $stmt->execute(array($calendarId));
-
- return '"' . $extraData['etag'] . '"';
-
- }
-
- /**
- * Parses some information from calendar objects, used for optimized
- * calendar-queries.
- *
- * Returns an array with the following keys:
- * * etag
- * * size
- * * componentType
- * * firstOccurence
- * * lastOccurence
- *
- * @param string $calendarData
- * @return array
- */
- protected function getDenormalizedData($calendarData) {
-
- $vObject = VObject\Reader::read($calendarData);
- $componentType = null;
- $component = null;
- $firstOccurence = null;
- $lastOccurence = null;
- foreach($vObject->getComponents() as $component) {
- if ($component->name!=='VTIMEZONE') {
- $componentType = $component->name;
- break;
- }
- }
- if (!$componentType) {
- throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
- }
- if ($componentType === 'VEVENT') {
- $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
- // Finding the last occurence is a bit harder
- if (!isset($component->RRULE)) {
- if (isset($component->DTEND)) {
- $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
- } elseif (isset($component->DURATION)) {
- $endDate = clone $component->DTSTART->getDateTime();
- $endDate->add(VObject\DateTimeParser::parse($component->DURATION->getValue()));
- $lastOccurence = $endDate->getTimeStamp();
- } elseif (!$component->DTSTART->hasTime()) {
- $endDate = clone $component->DTSTART->getDateTime();
- $endDate->modify('+1 day');
- $lastOccurence = $endDate->getTimeStamp();
- } else {
- $lastOccurence = $firstOccurence;
- }
- } else {
- $it = new VObject\RecurrenceIterator($vObject, (string)$component->UID);
- $maxDate = new \DateTime(self::MAX_DATE);
- if ($it->isInfinite()) {
- $lastOccurence = $maxDate->getTimeStamp();
- } else {
- $end = $it->getDtEnd();
- while($it->valid() && $end < $maxDate) {
- $end = $it->getDtEnd();
- $it->next();
-
- }
- $lastOccurence = $end->getTimeStamp();
- }
-
- }
- }
-
- return array(
- 'etag' => md5($calendarData),
- 'size' => strlen($calendarData),
- 'componentType' => $componentType,
- 'firstOccurence' => $firstOccurence,
- 'lastOccurence' => $lastOccurence,
- );
-
- }
-
- /**
- * Deletes an existing calendar object.
- *
- * @param string $calendarId
- * @param string $objectUri
- * @return void
- */
- public function deleteCalendarObject($calendarId,$objectUri) {
-
- $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?');
- $stmt->execute(array($calendarId,$objectUri));
- $stmt = $this->pdo->prepare('UPDATE '. $this->calendarTableName .' SET ctag = ctag + 1 WHERE id = ?');
- $stmt->execute(array($calendarId));
-
- }
-
- /**
- * Performs a calendar-query on the contents of this calendar.
- *
- * The calendar-query is defined in RFC4791 : CalDAV. Using the
- * calendar-query it is possible for a client to request a specific set of
- * object, based on contents of iCalendar properties, date-ranges and
- * iCalendar component types (VTODO, VEVENT).
- *
- * This method should just return a list of (relative) urls that match this
- * query.
- *
- * The list of filters are specified as an array. The exact array is
- * documented by \Sabre\CalDAV\CalendarQueryParser.
- *
- * Note that it is extremely likely that getCalendarObject for every path
- * returned from this method will be called almost immediately after. You
- * may want to anticipate this to speed up these requests.
- *
- * This method provides a default implementation, which parses *all* the
- * iCalendar objects in the specified calendar.
- *
- * This default may well be good enough for personal use, and calendars
- * that aren't very large. But if you anticipate high usage, big calendars
- * or high loads, you are strongly adviced to optimize certain paths.
- *
- * The best way to do so is override this method and to optimize
- * specifically for 'common filters'.
- *
- * Requests that are extremely common are:
- * * requests for just VEVENTS
- * * requests for just VTODO
- * * requests with a time-range-filter on a VEVENT.
- *
- * ..and combinations of these requests. It may not be worth it to try to
- * handle every possible situation and just rely on the (relatively
- * easy to use) CalendarQueryValidator to handle the rest.
- *
- * Note that especially time-range-filters may be difficult to parse. A
- * time-range filter specified on a VEVENT must for instance also handle
- * recurrence rules correctly.
- * A good example of how to interprete all these filters can also simply
- * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct
- * as possible, so it gives you a good idea on what type of stuff you need
- * to think of.
- *
- * This specific implementation (for the PDO) backend optimizes filters on
- * specific components, and VEVENT time-ranges.
- *
- * @param string $calendarId
- * @param array $filters
- * @return array
- */
- public function calendarQuery($calendarId, array $filters) {
-
- $result = array();
- $validator = new \Sabre\CalDAV\CalendarQueryValidator();
-
- $componentType = null;
- $requirePostFilter = true;
- $timeRange = null;
-
- // if no filters were specified, we don't need to filter after a query
- if (!$filters['prop-filters'] && !$filters['comp-filters']) {
- $requirePostFilter = false;
- }
-
- // Figuring out if there's a component filter
- if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) {
- $componentType = $filters['comp-filters'][0]['name'];
-
- // Checking if we need post-filters
- if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) {
- $requirePostFilter = false;
- }
- // There was a time-range filter
- if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) {
- $timeRange = $filters['comp-filters'][0]['time-range'];
-
- // If start time OR the end time is not specified, we can do a
- // 100% accurate mysql query.
- if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) {
- $requirePostFilter = false;
- }
- }
-
- }
-
- if ($requirePostFilter) {
- $query = "SELECT uri, calendardata FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid";
- } else {
- $query = "SELECT uri FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid";
- }
-
- $values = array(
- 'calendarid' => $calendarId,
- );
-
- if ($componentType) {
- $query.=" AND componenttype = :componenttype";
- $values['componenttype'] = $componentType;
- }
-
- if ($timeRange && $timeRange['start']) {
- $query.=" AND lastoccurence > :startdate";
- $values['startdate'] = $timeRange['start']->getTimeStamp();
- }
- if ($timeRange && $timeRange['end']) {
- $query.=" AND firstoccurence < :enddate";
- $values['enddate'] = $timeRange['end']->getTimeStamp();
- }
-
- $stmt = $this->pdo->prepare($query);
- $stmt->execute($values);
-
- $result = array();
- while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
- if ($requirePostFilter) {
- if (!$this->validateFilterForObject($row, $filters)) {
- continue;
- }
- }
- $result[] = $row['uri'];
-
- }
-
- return $result;
-
- }
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Calendar.php b/vendor/sabre/dav/lib/Sabre/CalDAV/Calendar.php
deleted file mode 100644
index 74fa15bb1..000000000
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Calendar.php
+++ /dev/null
@@ -1,376 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV;
-
-use Sabre\DAV;
-use Sabre\DAVACL;
-
-/**
- * This object represents a CalDAV calendar.
- *
- * A calendar can contain multiple TODO and or Events. These are represented
- * as \Sabre\CalDAV\CalendarObject objects.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Calendar implements ICalendar, DAV\IProperties, DAVACL\IACL {
-
- /**
- * This is an array with calendar information
- *
- * @var array
- */
- protected $calendarInfo;
-
- /**
- * CalDAV backend
- *
- * @var Backend\BackendInterface
- */
- protected $caldavBackend;
-
- /**
- * Constructor
- *
- * @param Backend\BackendInterface $caldavBackend
- * @param array $calendarInfo
- */
- public function __construct(Backend\BackendInterface $caldavBackend, $calendarInfo) {
-
- $this->caldavBackend = $caldavBackend;
- $this->calendarInfo = $calendarInfo;
-
- }
-
- /**
- * Returns the name of the calendar
- *
- * @return string
- */
- public function getName() {
-
- return $this->calendarInfo['uri'];
-
- }
-
- /**
- * Updates properties such as the display name and description
- *
- * @param array $mutations
- * @return array
- */
- public function updateProperties($mutations) {
-
- return $this->caldavBackend->updateCalendar($this->calendarInfo['id'],$mutations);
-
- }
-
- /**
- * Returns the list of properties
- *
- * @param array $requestedProperties
- * @return array
- */
- public function getProperties($requestedProperties) {
-
- $response = array();
-
- foreach($requestedProperties as $prop) switch($prop) {
-
- case '{urn:ietf:params:xml:ns:caldav}supported-calendar-data' :
- $response[$prop] = new Property\SupportedCalendarData();
- break;
- case '{urn:ietf:params:xml:ns:caldav}supported-collation-set' :
- $response[$prop] = new Property\SupportedCollationSet();
- break;
- case '{DAV:}owner' :
- $response[$prop] = new DAVACL\Property\Principal(DAVACL\Property\Principal::HREF,$this->calendarInfo['principaluri']);
- break;
- default :
- if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop];
- break;
-
- }
- return $response;
-
- }
-
- /**
- * Returns a calendar object
- *
- * The contained calendar objects are for example Events or Todo's.
- *
- * @param string $name
- * @return \Sabre\CalDAV\ICalendarObject
- */
- public function getChild($name) {
-
- $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name);
-
- if (!$obj) throw new DAV\Exception\NotFound('Calendar object not found');
-
- $obj['acl'] = $this->getACL();
- // Removing the irrelivant
- foreach($obj['acl'] as $key=>$acl) {
- if ($acl['privilege'] === '{' . Plugin::NS_CALDAV . '}read-free-busy') {
- unset($obj['acl'][$key]);
- }
- }
-
- return new CalendarObject($this->caldavBackend,$this->calendarInfo,$obj);
-
- }
-
- /**
- * Returns the full list of calendar objects
- *
- * @return array
- */
- public function getChildren() {
-
- $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']);
- $children = array();
- foreach($objs as $obj) {
- $obj['acl'] = $this->getACL();
- // Removing the irrelivant
- foreach($obj['acl'] as $key=>$acl) {
- if ($acl['privilege'] === '{' . Plugin::NS_CALDAV . '}read-free-busy') {
- unset($obj['acl'][$key]);
- }
- }
- $children[] = new CalendarObject($this->caldavBackend,$this->calendarInfo,$obj);
- }
- return $children;
-
- }
-
- /**
- * Checks if a child-node exists.
- *
- * @param string $name
- * @return bool
- */
- public function childExists($name) {
-
- $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name);
- if (!$obj)
- return false;
- else
- return true;
-
- }
-
- /**
- * Creates a new directory
- *
- * We actually block this, as subdirectories are not allowed in calendars.
- *
- * @param string $name
- * @return void
- */
- public function createDirectory($name) {
-
- throw new DAV\Exception\MethodNotAllowed('Creating collections in calendar objects is not allowed');
-
- }
-
- /**
- * Creates a new file
- *
- * The contents of the new file must be a valid ICalendar string.
- *
- * @param string $name
- * @param resource $calendarData
- * @return string|null
- */
- public function createFile($name,$calendarData = null) {
-
- if (is_resource($calendarData)) {
- $calendarData = stream_get_contents($calendarData);
- }
- return $this->caldavBackend->createCalendarObject($this->calendarInfo['id'],$name,$calendarData);
-
- }
-
- /**
- * Deletes the calendar.
- *
- * @return void
- */
- public function delete() {
-
- $this->caldavBackend->deleteCalendar($this->calendarInfo['id']);
-
- }
-
- /**
- * Renames the calendar. Note that most calendars use the
- * {DAV:}displayname to display a name to display a name.
- *
- * @param string $newName
- * @return void
- */
- public function setName($newName) {
-
- throw new DAV\Exception\MethodNotAllowed('Renaming calendars is not yet supported');
-
- }
-
- /**
- * Returns the last modification date as a unix timestamp.
- *
- * @return void
- */
- public function getLastModified() {
-
- return null;
-
- }
-
- /**
- * Returns the owner principal
- *
- * This must be a url to a principal, or null if there's no owner
- *
- * @return string|null
- */
- public function getOwner() {
-
- return $this->calendarInfo['principaluri'];
-
- }
-
- /**
- * Returns a group principal
- *
- * This must be a url to a principal, or null if there's no owner
- *
- * @return string|null
- */
- public function getGroup() {
-
- return null;
-
- }
-
- /**
- * Returns a list of ACE's for this node.
- *
- * Each ACE has the following properties:
- * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
- * currently the only supported privileges
- * * 'principal', a url to the principal who owns the node
- * * 'protected' (optional), indicating that this ACE is not allowed to
- * be updated.
- *
- * @return array
- */
- public function getACL() {
-
- return array(
- array(
- 'privilege' => '{DAV:}read',
- 'principal' => $this->getOwner(),
- 'protected' => true,
- ),
- array(
- 'privilege' => '{DAV:}write',
- 'principal' => $this->getOwner(),
- 'protected' => true,
- ),
- array(
- 'privilege' => '{DAV:}read',
- 'principal' => $this->getOwner() . '/calendar-proxy-write',
- 'protected' => true,
- ),
- array(
- 'privilege' => '{DAV:}write',
- 'principal' => $this->getOwner() . '/calendar-proxy-write',
- 'protected' => true,
- ),
- array(
- 'privilege' => '{DAV:}read',
- 'principal' => $this->getOwner() . '/calendar-proxy-read',
- 'protected' => true,
- ),
- array(
- 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy',
- 'principal' => '{DAV:}authenticated',
- 'protected' => true,
- ),
-
- );
-
- }
-
- /**
- * Updates the ACL
- *
- * This method will receive a list of new ACE's.
- *
- * @param array $acl
- * @return void
- */
- public function setACL(array $acl) {
-
- throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported');
-
- }
-
- /**
- * Returns the list of supported privileges for this node.
- *
- * The returned data structure is a list of nested privileges.
- * See \Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
- * standard structure.
- *
- * If null is returned from this method, the default privilege set is used,
- * which is fine for most common usecases.
- *
- * @return array|null
- */
- public function getSupportedPrivilegeSet() {
-
- $default = DAVACL\Plugin::getDefaultSupportedPrivilegeSet();
-
- // We need to inject 'read-free-busy' in the tree, aggregated under
- // {DAV:}read.
- foreach($default['aggregates'] as &$agg) {
-
- if ($agg['privilege'] !== '{DAV:}read') continue;
-
- $agg['aggregates'][] = array(
- 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy',
- );
-
- }
- return $default;
-
- }
-
- /**
- * Performs a calendar-query on the contents of this calendar.
- *
- * The calendar-query is defined in RFC4791 : CalDAV. Using the
- * calendar-query it is possible for a client to request a specific set of
- * object, based on contents of iCalendar properties, date-ranges and
- * iCalendar component types (VTODO, VEVENT).
- *
- * This method should just return a list of (relative) urls that match this
- * query.
- *
- * The list of filters are specified as an array. The exact array is
- * documented by Sabre\CalDAV\CalendarQueryParser.
- *
- * @param array $filters
- * @return array
- */
- public function calendarQuery(array $filters) {
-
- return $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters);
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/CalendarQueryParser.php b/vendor/sabre/dav/lib/Sabre/CalDAV/CalendarQueryParser.php
deleted file mode 100644
index 0a3472408..000000000
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/CalendarQueryParser.php
+++ /dev/null
@@ -1,298 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV;
-
-use Sabre\VObject;
-
-/**
- * Parses the calendar-query report request body.
- *
- * Whoever designed this format, and the CalDAV equivalent even more so,
- * has no feel for design.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class CalendarQueryParser {
-
- /**
- * List of requested properties the client wanted
- *
- * @var array
- */
- public $requestedProperties;
-
- /**
- * List of property/component filters.
- *
- * @var array
- */
- public $filters;
-
- /**
- * This property will contain null if CALDAV:expand was not specified,
- * otherwise it will contain an array with 2 elements (start, end). Each
- * contain a DateTime object.
- *
- * If expand is specified, recurring calendar objects are to be expanded
- * into their individual components, and only the components that fall
- * within the specified time-range are to be returned.
- *
- * For more details, see rfc4791, section 9.6.5.
- *
- * @var null|array
- */
- public $expand;
-
- /**
- * DOM Document
- *
- * @var DOMDocument
- */
- protected $dom;
-
- /**
- * DOM XPath object
- *
- * @var DOMXPath
- */
- protected $xpath;
-
- /**
- * Creates the parser
- *
- * @param \DOMDocument $dom
- */
- public function __construct(\DOMDocument $dom) {
-
- $this->dom = $dom;
- $this->xpath = new \DOMXPath($dom);
- $this->xpath->registerNameSpace('cal',Plugin::NS_CALDAV);
- $this->xpath->registerNameSpace('dav','urn:DAV');
-
- }
-
- /**
- * Parses the request.
- *
- * @return void
- */
- public function parse() {
-
- $filterNode = null;
-
- $filter = $this->xpath->query('/cal:calendar-query/cal:filter');
- if ($filter->length !== 1) {
- throw new \Sabre\DAV\Exception\BadRequest('Only one filter element is allowed');
- }
-
- $compFilters = $this->parseCompFilters($filter->item(0));
- if (count($compFilters)!==1) {
- throw new \Sabre\DAV\Exception\BadRequest('There must be exactly 1 top-level comp-filter.');
- }
-
- $this->filters = $compFilters[0];
- $this->requestedProperties = array_keys(\Sabre\DAV\XMLUtil::parseProperties($this->dom->firstChild));
-
- $expand = $this->xpath->query('/cal:calendar-query/dav:prop/cal:calendar-data/cal:expand');
- if ($expand->length>0) {
- $this->expand = $this->parseExpand($expand->item(0));
- }
-
-
- }
-
- /**
- * Parses all the 'comp-filter' elements from a node
- *
- * @param \DOMElement $parentNode
- * @return array
- */
- protected function parseCompFilters(\DOMElement $parentNode) {
-
- $compFilterNodes = $this->xpath->query('cal:comp-filter', $parentNode);
- $result = array();
-
- for($ii=0; $ii < $compFilterNodes->length; $ii++) {
-
- $compFilterNode = $compFilterNodes->item($ii);
-
- $compFilter = array();
- $compFilter['name'] = $compFilterNode->getAttribute('name');
- $compFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $compFilterNode)->length>0;
- $compFilter['comp-filters'] = $this->parseCompFilters($compFilterNode);
- $compFilter['prop-filters'] = $this->parsePropFilters($compFilterNode);
- $compFilter['time-range'] = $this->parseTimeRange($compFilterNode);
-
- if ($compFilter['time-range'] && !in_array($compFilter['name'],array(
- 'VEVENT',
- 'VTODO',
- 'VJOURNAL',
- 'VFREEBUSY',
- 'VALARM',
- ))) {
- throw new \Sabre\DAV\Exception\BadRequest('The time-range filter is not defined for the ' . $compFilter['name'] . ' component');
- };
-
- $result[] = $compFilter;
-
- }
-
- return $result;
-
- }
-
- /**
- * Parses all the prop-filter elements from a node
- *
- * @param \DOMElement $parentNode
- * @return array
- */
- protected function parsePropFilters(\DOMElement $parentNode) {
-
- $propFilterNodes = $this->xpath->query('cal:prop-filter', $parentNode);
- $result = array();
-
- for ($ii=0; $ii < $propFilterNodes->length; $ii++) {
-
- $propFilterNode = $propFilterNodes->item($ii);
- $propFilter = array();
- $propFilter['name'] = $propFilterNode->getAttribute('name');
- $propFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $propFilterNode)->length>0;
- $propFilter['param-filters'] = $this->parseParamFilters($propFilterNode);
- $propFilter['text-match'] = $this->parseTextMatch($propFilterNode);
- $propFilter['time-range'] = $this->parseTimeRange($propFilterNode);
-
- $result[] = $propFilter;
-
- }
-
- return $result;
-
- }
-
- /**
- * Parses the param-filter element
- *
- * @param \DOMElement $parentNode
- * @return array
- */
- protected function parseParamFilters(\DOMElement $parentNode) {
-
- $paramFilterNodes = $this->xpath->query('cal:param-filter', $parentNode);
- $result = array();
-
- for($ii=0;$ii<$paramFilterNodes->length;$ii++) {
-
- $paramFilterNode = $paramFilterNodes->item($ii);
- $paramFilter = array();
- $paramFilter['name'] = $paramFilterNode->getAttribute('name');
- $paramFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $paramFilterNode)->length>0;
- $paramFilter['text-match'] = $this->parseTextMatch($paramFilterNode);
-
- $result[] = $paramFilter;
-
- }
-
- return $result;
-
- }
-
- /**
- * Parses the text-match element
- *
- * @param \DOMElement $parentNode
- * @return array|null
- */
- protected function parseTextMatch(\DOMElement $parentNode) {
-
- $textMatchNodes = $this->xpath->query('cal:text-match', $parentNode);
-
- if ($textMatchNodes->length === 0)
- return null;
-
- $textMatchNode = $textMatchNodes->item(0);
- $negateCondition = $textMatchNode->getAttribute('negate-condition');
- $negateCondition = $negateCondition==='yes';
- $collation = $textMatchNode->getAttribute('collation');
- if (!$collation) $collation = 'i;ascii-casemap';
-
- return array(
- 'negate-condition' => $negateCondition,
- 'collation' => $collation,
- 'value' => $textMatchNode->nodeValue
- );
-
- }
-
- /**
- * Parses the time-range element
- *
- * @param \DOMElement $parentNode
- * @return array|null
- */
- protected function parseTimeRange(\DOMElement $parentNode) {
-
- $timeRangeNodes = $this->xpath->query('cal:time-range', $parentNode);
- if ($timeRangeNodes->length === 0) {
- return null;
- }
-
- $timeRangeNode = $timeRangeNodes->item(0);
-
- if ($start = $timeRangeNode->getAttribute('start')) {
- $start = VObject\DateTimeParser::parseDateTime($start);
- } else {
- $start = null;
- }
- if ($end = $timeRangeNode->getAttribute('end')) {
- $end = VObject\DateTimeParser::parseDateTime($end);
- } else {
- $end = null;
- }
-
- if (!is_null($start) && !is_null($end) && $end <= $start) {
- throw new \Sabre\DAV\Exception\BadRequest('The end-date must be larger than the start-date in the time-range filter');
- }
-
- return array(
- 'start' => $start,
- 'end' => $end,
- );
-
- }
-
- /**
- * Parses the CALDAV:expand element
- *
- * @param \DOMElement $parentNode
- * @return void
- */
- protected function parseExpand(\DOMElement $parentNode) {
-
- $start = $parentNode->getAttribute('start');
- if(!$start) {
- throw new \Sabre\DAV\Exception\BadRequest('The "start" attribute is required for the CALDAV:expand element');
- }
- $start = VObject\DateTimeParser::parseDateTime($start);
-
- $end = $parentNode->getAttribute('end');
- if(!$end) {
- throw new \Sabre\DAV\Exception\BadRequest('The "end" attribute is required for the CALDAV:expand element');
- }
-
- $end = VObject\DateTimeParser::parseDateTime($end);
-
- if ($end <= $start) {
- throw new \Sabre\DAV\Exception\BadRequest('The end-date must be larger than the start-date in the expand element.');
- }
-
- return array(
- 'start' => $start,
- 'end' => $end,
- );
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/ICSExportPlugin.php b/vendor/sabre/dav/lib/Sabre/CalDAV/ICSExportPlugin.php
deleted file mode 100644
index bba7fbd5a..000000000
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/ICSExportPlugin.php
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV;
-
-use Sabre\DAV;
-use Sabre\VObject;
-
-/**
- * ICS Exporter
- *
- * This plugin adds the ability to export entire calendars as .ics files.
- * This is useful for clients that don't support CalDAV yet. They often do
- * support ics files.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class ICSExportPlugin extends DAV\ServerPlugin {
-
- /**
- * Reference to Server class
- *
- * @var \Sabre\DAV\Server
- */
- protected $server;
-
- /**
- * Initializes the plugin and registers event handlers
- *
- * @param \Sabre\DAV\Server $server
- * @return void
- */
- public function initialize(DAV\Server $server) {
-
- $this->server = $server;
- $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90);
-
- }
-
- /**
- * 'beforeMethod' event handles. This event handles intercepts GET requests ending
- * with ?export
- *
- * @param string $method
- * @param string $uri
- * @return bool
- */
- public function beforeMethod($method, $uri) {
-
- if ($method!='GET') return;
- if ($this->server->httpRequest->getQueryString()!='export') return;
-
- // splitting uri
- list($uri) = explode('?',$uri,2);
-
- $node = $this->server->tree->getNodeForPath($uri);
-
- if (!($node instanceof Calendar)) return;
-
- // Checking ACL, if available.
- if ($aclPlugin = $this->server->getPlugin('acl')) {
- $aclPlugin->checkPrivileges($uri, '{DAV:}read');
- }
-
- $this->server->httpResponse->setHeader('Content-Type','text/calendar');
- $this->server->httpResponse->sendStatus(200);
-
- $nodes = $this->server->getPropertiesForPath($uri, array(
- '{' . Plugin::NS_CALDAV . '}calendar-data',
- ),1);
-
- $this->server->httpResponse->sendBody($this->generateICS($nodes));
-
- // Returning false to break the event chain
- return false;
-
- }
-
- /**
- * Merges all calendar objects, and builds one big ics export
- *
- * @param array $nodes
- * @return string
- */
- public function generateICS(array $nodes) {
-
- $calendar = new VObject\Component\VCalendar();
- $calendar->version = '2.0';
- if (DAV\Server::$exposeVersion) {
- $calendar->prodid = '-//SabreDAV//SabreDAV ' . DAV\Version::VERSION . '//EN';
- } else {
- $calendar->prodid = '-//SabreDAV//SabreDAV//EN';
- }
- $calendar->calscale = 'GREGORIAN';
-
- $collectedTimezones = array();
-
- $timezones = array();
- $objects = array();
-
- foreach($nodes as $node) {
-
- if (!isset($node[200]['{' . Plugin::NS_CALDAV . '}calendar-data'])) {
- continue;
- }
- $nodeData = $node[200]['{' . Plugin::NS_CALDAV . '}calendar-data'];
-
- $nodeComp = VObject\Reader::read($nodeData);
-
- foreach($nodeComp->children() as $child) {
-
- switch($child->name) {
- case 'VEVENT' :
- case 'VTODO' :
- case 'VJOURNAL' :
- $objects[] = $child;
- break;
-
- // VTIMEZONE is special, because we need to filter out the duplicates
- case 'VTIMEZONE' :
- // Naively just checking tzid.
- if (in_array((string)$child->TZID, $collectedTimezones)) continue;
-
- $timezones[] = $child;
- $collectedTimezones[] = $child->TZID;
- break;
-
- }
-
- }
-
- }
-
- foreach($timezones as $tz) $calendar->add($tz);
- foreach($objects as $obj) $calendar->add($obj);
-
- return $calendar->serialize();
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Plugin.php b/vendor/sabre/dav/lib/Sabre/CalDAV/Plugin.php
deleted file mode 100644
index 610929388..000000000
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Plugin.php
+++ /dev/null
@@ -1,1338 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV;
-
-use Sabre\DAV;
-use Sabre\DAVACL;
-use Sabre\VObject;
-
-/**
- * CalDAV plugin
- *
- * This plugin provides functionality added by CalDAV (RFC 4791)
- * It implements new reports, and the MKCALENDAR method.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Plugin extends DAV\ServerPlugin {
-
- /**
- * This is the official CalDAV namespace
- */
- const NS_CALDAV = 'urn:ietf:params:xml:ns:caldav';
-
- /**
- * This is the namespace for the proprietary calendarserver extensions
- */
- const NS_CALENDARSERVER = 'http://calendarserver.org/ns/';
-
- /**
- * The hardcoded root for calendar objects. It is unfortunate
- * that we're stuck with it, but it will have to do for now
- */
- const CALENDAR_ROOT = 'calendars';
-
- /**
- * Reference to server object
- *
- * @var DAV\Server
- */
- protected $server;
-
- /**
- * The email handler for invites and other scheduling messages.
- *
- * @var Schedule\IMip
- */
- protected $imipHandler;
-
- /**
- * Sets the iMIP handler.
- *
- * iMIP = The email transport of iCalendar scheduling messages. Setting
- * this is optional, but if you want the server to allow invites to be sent
- * out, you must set a handler.
- *
- * Specifically iCal will plain assume that the server supports this. If
- * the server doesn't, iCal will display errors when inviting people to
- * events.
- *
- * @param Schedule\IMip $imipHandler
- * @return void
- */
- public function setIMipHandler(Schedule\IMip $imipHandler) {
-
- $this->imipHandler = $imipHandler;
-
- }
-
- /**
- * Use this method to tell the server this plugin defines additional
- * HTTP methods.
- *
- * This method is passed a uri. It should only return HTTP methods that are
- * available for the specified uri.
- *
- * @param string $uri
- * @return array
- */
- public function getHTTPMethods($uri) {
-
- // The MKCALENDAR is only available on unmapped uri's, whose
- // parents extend IExtendedCollection
- list($parent, $name) = DAV\URLUtil::splitPath($uri);
-
- $node = $this->server->tree->getNodeForPath($parent);
-
- if ($node instanceof DAV\IExtendedCollection) {
- try {
- $node->getChild($name);
- } catch (DAV\Exception\NotFound $e) {
- return array('MKCALENDAR');
- }
- }
- return array();
-
- }
-
- /**
- * Returns a list of features for the DAV: HTTP header.
- *
- * @return array
- */
- public function getFeatures() {
-
- return array('calendar-access', 'calendar-proxy');
-
- }
-
- /**
- * Returns a plugin name.
- *
- * Using this name other plugins will be able to access other plugins
- * using DAV\Server::getPlugin
- *
- * @return string
- */
- public function getPluginName() {
-
- return 'caldav';
-
- }
-
- /**
- * Returns a list of reports this plugin supports.
- *
- * This will be used in the {DAV:}supported-report-set property.
- * Note that you still need to subscribe to the 'report' event to actually
- * implement them
- *
- * @param string $uri
- * @return array
- */
- public function getSupportedReportSet($uri) {
-
- $node = $this->server->tree->getNodeForPath($uri);
-
- $reports = array();
- if ($node instanceof ICalendar || $node instanceof ICalendarObject) {
- $reports[] = '{' . self::NS_CALDAV . '}calendar-multiget';
- $reports[] = '{' . self::NS_CALDAV . '}calendar-query';
- }
- if ($node instanceof ICalendar) {
- $reports[] = '{' . self::NS_CALDAV . '}free-busy-query';
- }
- return $reports;
-
- }
-
- /**
- * Initializes the plugin
- *
- * @param DAV\Server $server
- * @return void
- */
- public function initialize(DAV\Server $server) {
-
- $this->server = $server;
-
- $server->subscribeEvent('unknownMethod',array($this,'unknownMethod'));
- //$server->subscribeEvent('unknownMethod',array($this,'unknownMethod2'),1000);
- $server->subscribeEvent('report',array($this,'report'));
- $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties'));
- $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel'));
- $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction'));
- $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent'));
- $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile'));
- $server->subscribeEvent('beforeMethod', array($this,'beforeMethod'));
-
- $server->xmlNamespaces[self::NS_CALDAV] = 'cal';
- $server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs';
-
- $server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre\\CalDAV\\Property\\SupportedCalendarComponentSet';
- $server->propertyMap['{' . self::NS_CALDAV . '}schedule-calendar-transp'] = 'Sabre\\CalDAV\\Property\\ScheduleCalendarTransp';
-
- $server->resourceTypeMapping['\\Sabre\\CalDAV\\ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar';
- $server->resourceTypeMapping['\\Sabre\\CalDAV\\Schedule\\IOutbox'] = '{urn:ietf:params:xml:ns:caldav}schedule-outbox';
- $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read';
- $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write';
- $server->resourceTypeMapping['\\Sabre\\CalDAV\\Notifications\\ICollection'] = '{' . self::NS_CALENDARSERVER . '}notification';
-
- array_push($server->protectedProperties,
-
- '{' . self::NS_CALDAV . '}supported-calendar-component-set',
- '{' . self::NS_CALDAV . '}supported-calendar-data',
- '{' . self::NS_CALDAV . '}max-resource-size',
- '{' . self::NS_CALDAV . '}min-date-time',
- '{' . self::NS_CALDAV . '}max-date-time',
- '{' . self::NS_CALDAV . '}max-instances',
- '{' . self::NS_CALDAV . '}max-attendees-per-instance',
- '{' . self::NS_CALDAV . '}calendar-home-set',
- '{' . self::NS_CALDAV . '}supported-collation-set',
- '{' . self::NS_CALDAV . '}calendar-data',
-
- // scheduling extension
- '{' . self::NS_CALDAV . '}schedule-inbox-URL',
- '{' . self::NS_CALDAV . '}schedule-outbox-URL',
- '{' . self::NS_CALDAV . '}calendar-user-address-set',
- '{' . self::NS_CALDAV . '}calendar-user-type',
-
- // CalendarServer extensions
- '{' . self::NS_CALENDARSERVER . '}getctag',
- '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for',
- '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for',
- '{' . self::NS_CALENDARSERVER . '}notification-URL',
- '{' . self::NS_CALENDARSERVER . '}notificationtype'
-
- );
- }
-
- /**
- * This function handles support for the MKCALENDAR method
- *
- * @param string $method
- * @param string $uri
- * @return bool
- */
- public function unknownMethod($method, $uri) {
-
- switch ($method) {
- case 'MKCALENDAR' :
- $this->httpMkCalendar($uri);
- // false is returned to stop the propagation of the
- // unknownMethod event.
- return false;
- case 'POST' :
-
- // Checking if this is a text/calendar content type
- $contentType = $this->server->httpRequest->getHeader('Content-Type');
- if (strpos($contentType, 'text/calendar')!==0) {
- return;
- }
-
- // Checking if we're talking to an outbox
- try {
- $node = $this->server->tree->getNodeForPath($uri);
- } catch (DAV\Exception\NotFound $e) {
- return;
- }
- if (!$node instanceof Schedule\IOutbox)
- return;
-
- $this->outboxRequest($node, $uri);
- return false;
-
- }
-
- }
-
- /**
- * This functions handles REPORT requests specific to CalDAV
- *
- * @param string $reportName
- * @param \DOMNode $dom
- * @return bool
- */
- public function report($reportName,$dom) {
-
- switch($reportName) {
- case '{'.self::NS_CALDAV.'}calendar-multiget' :
- $this->calendarMultiGetReport($dom);
- return false;
- case '{'.self::NS_CALDAV.'}calendar-query' :
- $this->calendarQueryReport($dom);
- return false;
- case '{'.self::NS_CALDAV.'}free-busy-query' :
- $this->freeBusyQueryReport($dom);
- return false;
-
- }
-
-
- }
-
- /**
- * This function handles the MKCALENDAR HTTP method, which creates
- * a new calendar.
- *
- * @param string $uri
- * @return void
- */
- public function httpMkCalendar($uri) {
-
- // Due to unforgivable bugs in iCal, we're completely disabling MKCALENDAR support
- // for clients matching iCal in the user agent
- //$ua = $this->server->httpRequest->getHeader('User-Agent');
- //if (strpos($ua,'iCal/')!==false) {
- // throw new \Sabre\DAV\Exception\Forbidden('iCal has major bugs in it\'s RFC3744 support. Therefore we are left with no other choice but disabling this feature.');
- //}
-
- $body = $this->server->httpRequest->getBody(true);
- $properties = array();
-
- if ($body) {
-
- $dom = DAV\XMLUtil::loadDOMDocument($body);
-
- foreach($dom->firstChild->childNodes as $child) {
-
- if (DAV\XMLUtil::toClarkNotation($child)!=='{DAV:}set') continue;
- foreach(DAV\XMLUtil::parseProperties($child,$this->server->propertyMap) as $k=>$prop) {
- $properties[$k] = $prop;
- }
-
- }
- }
-
- $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar');
-
- $this->server->createCollection($uri,$resourceType,$properties);
-
- $this->server->httpResponse->sendStatus(201);
- $this->server->httpResponse->setHeader('Content-Length',0);
- }
-
- /**
- * beforeGetProperties
- *
- * This method handler is invoked before any after properties for a
- * resource are fetched. This allows us to add in any CalDAV specific
- * properties.
- *
- * @param string $path
- * @param DAV\INode $node
- * @param array $requestedProperties
- * @param array $returnedProperties
- * @return void
- */
- public function beforeGetProperties($path, DAV\INode $node, &$requestedProperties, &$returnedProperties) {
-
- if ($node instanceof DAVACL\IPrincipal) {
-
- // calendar-home-set property
- $calHome = '{' . self::NS_CALDAV . '}calendar-home-set';
- if (in_array($calHome,$requestedProperties)) {
- $principalId = $node->getName();
- $calendarHomePath = self::CALENDAR_ROOT . '/' . $principalId . '/';
-
- unset($requestedProperties[array_search($calHome, $requestedProperties)]);
- $returnedProperties[200][$calHome] = new DAV\Property\Href($calendarHomePath);
-
- }
-
- // schedule-outbox-URL property
- $scheduleProp = '{' . self::NS_CALDAV . '}schedule-outbox-URL';
- if (in_array($scheduleProp,$requestedProperties)) {
- $principalId = $node->getName();
- $outboxPath = self::CALENDAR_ROOT . '/' . $principalId . '/outbox';
-
- unset($requestedProperties[array_search($scheduleProp, $requestedProperties)]);
- $returnedProperties[200][$scheduleProp] = new DAV\Property\Href($outboxPath);
-
- }
-
- // calendar-user-address-set property
- $calProp = '{' . self::NS_CALDAV . '}calendar-user-address-set';
- if (in_array($calProp,$requestedProperties)) {
-
- $addresses = $node->getAlternateUriSet();
- $addresses[] = $this->server->getBaseUri() . DAV\URLUtil::encodePath($node->getPrincipalUrl() . '/');
- unset($requestedProperties[array_search($calProp, $requestedProperties)]);
- $returnedProperties[200][$calProp] = new DAV\Property\HrefList($addresses, false);
-
- }
-
- // These two properties are shortcuts for ical to easily find
- // other principals this principal has access to.
- $propRead = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for';
- $propWrite = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for';
- if (in_array($propRead,$requestedProperties) || in_array($propWrite,$requestedProperties)) {
-
- $aclPlugin = $this->server->getPlugin('acl');
- $membership = $aclPlugin->getPrincipalMembership($path);
- $readList = array();
- $writeList = array();
-
- foreach($membership as $group) {
-
- $groupNode = $this->server->tree->getNodeForPath($group);
-
- // If the node is either ap proxy-read or proxy-write
- // group, we grab the parent principal and add it to the
- // list.
- if ($groupNode instanceof Principal\IProxyRead) {
- list($readList[]) = DAV\URLUtil::splitPath($group);
- }
- if ($groupNode instanceof Principal\IProxyWrite) {
- list($writeList[]) = DAV\URLUtil::splitPath($group);
- }
-
- }
- if (in_array($propRead,$requestedProperties)) {
- unset($requestedProperties[$propRead]);
- $returnedProperties[200][$propRead] = new DAV\Property\HrefList($readList);
- }
- if (in_array($propWrite,$requestedProperties)) {
- unset($requestedProperties[$propWrite]);
- $returnedProperties[200][$propWrite] = new DAV\Property\HrefList($writeList);
- }
-
- }
-
- // notification-URL property
- $notificationUrl = '{' . self::NS_CALENDARSERVER . '}notification-URL';
- if (($index = array_search($notificationUrl, $requestedProperties)) !== false) {
- $principalId = $node->getName();
- $calendarHomePath = 'calendars/' . $principalId . '/notifications/';
- unset($requestedProperties[$index]);
- $returnedProperties[200][$notificationUrl] = new DAV\Property\Href($calendarHomePath);
- }
-
- } // instanceof IPrincipal
-
- if ($node instanceof Notifications\INode) {
-
- $propertyName = '{' . self::NS_CALENDARSERVER . '}notificationtype';
- if (($index = array_search($propertyName, $requestedProperties)) !== false) {
-
- $returnedProperties[200][$propertyName] =
- $node->getNotificationType();
-
- unset($requestedProperties[$index]);
-
- }
-
- } // instanceof Notifications_INode
-
-
- 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.
- $calDataProp = '{' . Plugin::NS_CALDAV . '}calendar-data';
- if (in_array($calDataProp, $requestedProperties)) {
- unset($requestedProperties[$calDataProp]);
- $val = $node->get();
- if (is_resource($val))
- $val = stream_get_contents($val);
-
- // Taking out \r to not screw up the xml output
- $returnedProperties[200][$calDataProp] = str_replace("\r","", $val);
-
- }
- }
-
- }
-
- /**
- * This function handles the calendar-multiget REPORT.
- *
- * This report is used by the client to fetch the content of a series
- * of urls. Effectively avoiding a lot of redundant requests.
- *
- * @param \DOMNode $dom
- * @return void
- */
- public function calendarMultiGetReport($dom) {
-
- $properties = array_keys(DAV\XMLUtil::parseProperties($dom->firstChild));
- $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href');
-
- $xpath = new \DOMXPath($dom);
- $xpath->registerNameSpace('cal',Plugin::NS_CALDAV);
- $xpath->registerNameSpace('dav','urn:DAV');
-
- $expand = $xpath->query('/cal:calendar-multiget/dav:prop/cal:calendar-data/cal:expand');
- if ($expand->length>0) {
- $expandElem = $expand->item(0);
- $start = $expandElem->getAttribute('start');
- $end = $expandElem->getAttribute('end');
- if(!$start || !$end) {
- throw new DAV\Exception\BadRequest('The "start" and "end" attributes are required for the CALDAV:expand element');
- }
- $start = VObject\DateTimeParser::parseDateTime($start);
- $end = VObject\DateTimeParser::parseDateTime($end);
-
- if ($end <= $start) {
- throw new DAV\Exception\BadRequest('The end-date must be larger than the start-date in the expand element.');
- }
-
- $expand = true;
-
- } else {
-
- $expand = false;
-
- }
-
- foreach($hrefElems as $elem) {
- $uri = $this->server->calculateUri($elem->nodeValue);
- list($objProps) = $this->server->getPropertiesForPath($uri,$properties);
-
- if ($expand && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) {
- $vObject = VObject\Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']);
- $vObject->expand($start, $end);
- $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
- }
-
- $propertyList[]=$objProps;
-
- }
-
- $prefer = $this->server->getHTTPPRefer();
-
- $this->server->httpResponse->sendStatus(207);
- $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
- $this->server->httpResponse->setHeader('Vary','Brief,Prefer');
- $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList, $prefer['return-minimal']));
-
- }
-
- /**
- * This function handles the calendar-query REPORT
- *
- * This report is used by clients to request calendar objects based on
- * complex conditions.
- *
- * @param \DOMNode $dom
- * @return void
- */
- public function calendarQueryReport($dom) {
-
- $parser = new CalendarQueryParser($dom);
- $parser->parse();
-
- $node = $this->server->tree->getNodeForPath($this->server->getRequestUri());
- $depth = $this->server->getHTTPDepth(0);
-
- // The default result is an empty array
- $result = array();
-
- // The calendarobject was requested directly. In this case we handle
- // this locally.
- if ($depth == 0 && $node instanceof ICalendarObject) {
-
- $requestedCalendarData = true;
- $requestedProperties = $parser->requestedProperties;
-
- if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) {
-
- // We always retrieve calendar-data, as we need it for filtering.
- $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data';
-
- // If calendar-data wasn't explicitly requested, we need to remove
- // it after processing.
- $requestedCalendarData = false;
- }
-
- $properties = $this->server->getPropertiesForPath(
- $this->server->getRequestUri(),
- $requestedProperties,
- 0
- );
-
- // This array should have only 1 element, the first calendar
- // object.
- $properties = current($properties);
-
- // If there wasn't any calendar-data returned somehow, we ignore
- // this.
- if (isset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) {
-
- $validator = new CalendarQueryValidator();
-
- $vObject = VObject\Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
- if ($validator->validate($vObject,$parser->filters)) {
-
- // If the client didn't require the calendar-data property,
- // we won't give it back.
- if (!$requestedCalendarData) {
- unset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
- } else {
- if ($parser->expand) {
- $vObject->expand($parser->expand['start'], $parser->expand['end']);
- $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
- }
- }
-
- $result = array($properties);
-
- }
-
- }
-
- }
- // If we're dealing with a calendar, the calendar itself is responsible
- // for the calendar-query.
- if ($node instanceof ICalendar && $depth = 1) {
-
- $nodePaths = $node->calendarQuery($parser->filters);
-
- foreach($nodePaths as $path) {
-
- list($properties) =
- $this->server->getPropertiesForPath($this->server->getRequestUri() . '/' . $path, $parser->requestedProperties);
-
- if ($parser->expand) {
- // We need to do some post-processing
- $vObject = VObject\Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
- $vObject->expand($parser->expand['start'], $parser->expand['end']);
- $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
- }
-
- $result[] = $properties;
-
- }
-
- }
-
- $prefer = $this->server->getHTTPPRefer();
-
- $this->server->httpResponse->sendStatus(207);
- $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
- $this->server->httpResponse->setHeader('Vary','Brief,Prefer');
- $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal']));
-
- }
-
- /**
- * This method is responsible for parsing the request and generating the
- * response for the CALDAV:free-busy-query REPORT.
- *
- * @param \DOMNode $dom
- * @return void
- */
- protected function freeBusyQueryReport(\DOMNode $dom) {
-
- $start = null;
- $end = null;
-
- foreach($dom->firstChild->childNodes as $childNode) {
-
- $clark = DAV\XMLUtil::toClarkNotation($childNode);
- if ($clark == '{' . self::NS_CALDAV . '}time-range') {
- $start = $childNode->getAttribute('start');
- $end = $childNode->getAttribute('end');
- break;
- }
-
- }
- if ($start) {
- $start = VObject\DateTimeParser::parseDateTime($start);
- }
- if ($end) {
- $end = VObject\DateTimeParser::parseDateTime($end);
- }
-
- if (!$start && !$end) {
- throw new DAV\Exception\BadRequest('The freebusy report must have a time-range filter');
- }
- $acl = $this->server->getPlugin('acl');
-
- if (!$acl) {
- throw new DAV\Exception('The ACL plugin must be loaded for free-busy queries to work');
- }
- $uri = $this->server->getRequestUri();
- $acl->checkPrivileges($uri,'{' . self::NS_CALDAV . '}read-free-busy');
-
- $calendar = $this->server->tree->getNodeForPath($uri);
- if (!$calendar instanceof ICalendar) {
- throw new DAV\Exception\NotImplemented('The free-busy-query REPORT is only implemented on calendars');
- }
-
- // Doing a calendar-query first, to make sure we get the most
- // performance.
- $urls = $calendar->calendarQuery(array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(
- array(
- 'name' => 'VEVENT',
- 'comp-filters' => array(),
- 'prop-filters' => array(),
- 'is-not-defined' => false,
- 'time-range' => array(
- 'start' => $start,
- 'end' => $end,
- ),
- ),
- ),
- 'prop-filters' => array(),
- 'is-not-defined' => false,
- 'time-range' => null,
- ));
-
- $objects = array_map(function($url) use ($calendar) {
- $obj = $calendar->getChild($url)->get();
- return $obj;
- }, $urls);
-
- $generator = new VObject\FreeBusyGenerator();
- $generator->setObjects($objects);
- $generator->setTimeRange($start, $end);
- $result = $generator->getResult();
- $result = $result->serialize();
-
- $this->server->httpResponse->sendStatus(200);
- $this->server->httpResponse->setHeader('Content-Type', 'text/calendar');
- $this->server->httpResponse->setHeader('Content-Length', strlen($result));
- $this->server->httpResponse->sendBody($result);
-
- }
-
- /**
- * This method is triggered before a file gets updated with new content.
- *
- * This plugin uses this method to ensure that CalDAV objects receive
- * valid calendar data.
- *
- * @param string $path
- * @param DAV\IFile $node
- * @param resource $data
- * @return void
- */
- public function beforeWriteContent($path, DAV\IFile $node, &$data) {
-
- if (!$node instanceof ICalendarObject)
- return;
-
- $this->validateICalendar($data, $path);
-
- }
-
- /**
- * This method is triggered before a new file is created.
- *
- * This plugin uses this method to ensure that newly created calendar
- * objects contain valid calendar data.
- *
- * @param string $path
- * @param resource $data
- * @param DAV\ICollection $parentNode
- * @return void
- */
- public function beforeCreateFile($path, &$data, DAV\ICollection $parentNode) {
-
- if (!$parentNode instanceof Calendar)
- return;
-
- $this->validateICalendar($data, $path);
-
- }
-
- /**
- * This event is triggered before any HTTP request is handled.
- *
- * We use this to intercept GET calls to notification nodes, and return the
- * proper response.
- *
- * @param string $method
- * @param string $path
- * @return void
- */
- public function beforeMethod($method, $path) {
-
- if ($method!=='GET') return;
-
- try {
- $node = $this->server->tree->getNodeForPath($path);
- } catch (DAV\Exception\NotFound $e) {
- return;
- }
-
- if (!$node instanceof Notifications\INode)
- return;
-
- if (!$this->server->checkPreconditions(true)) return false;
- $dom = new \DOMDocument('1.0', 'UTF-8');
-
- $dom->formatOutput = true;
-
- $root = $dom->createElement('cs:notification');
- foreach($this->server->xmlNamespaces as $namespace => $prefix) {
- $root->setAttribute('xmlns:' . $prefix, $namespace);
- }
-
- $dom->appendChild($root);
- $node->getNotificationType()->serializeBody($this->server, $root);
-
- $this->server->httpResponse->setHeader('Content-Type','application/xml');
- $this->server->httpResponse->setHeader('ETag',$node->getETag());
- $this->server->httpResponse->sendStatus(200);
- $this->server->httpResponse->sendBody($dom->saveXML());
-
- return false;
-
- }
-
- /**
- * Checks if the submitted iCalendar data is in fact, valid.
- *
- * An exception is thrown if it's not.
- *
- * @param resource|string $data
- * @param string $path
- * @return void
- */
- protected function validateICalendar(&$data, $path) {
-
- // If it's a stream, we convert it to a string first.
- if (is_resource($data)) {
- $data = stream_get_contents($data);
- }
-
- // Converting the data to unicode, if needed.
- $data = DAV\StringUtil::ensureUTF8($data);
-
- try {
-
- $vobj = VObject\Reader::read($data);
-
- } catch (VObject\ParseException $e) {
-
- throw new DAV\Exception\UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage());
-
- }
-
- if ($vobj->name !== 'VCALENDAR') {
- throw new DAV\Exception\UnsupportedMediaType('This collection can only support iCalendar objects.');
- }
-
- // Get the Supported Components for the target calendar
- list($parentPath,$object) = DAV\URLUtil::splitPath($path);
- $calendarProperties = $this->server->getProperties($parentPath,array('{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'));
- $supportedComponents = $calendarProperties['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']->getValue();
-
- $foundType = null;
- $foundUID = null;
- foreach($vobj->getComponents() as $component) {
- switch($component->name) {
- case 'VTIMEZONE' :
- continue 2;
- case 'VEVENT' :
- case 'VTODO' :
- case 'VJOURNAL' :
- if (is_null($foundType)) {
- $foundType = $component->name;
- if (!in_array($foundType, $supportedComponents)) {
- throw new Exception\InvalidComponentType('This calendar only supports ' . implode(', ', $supportedComponents) . '. We found a ' . $foundType);
- }
- if (!isset($component->UID)) {
- throw new DAV\Exception\BadRequest('Every ' . $component->name . ' component must have an UID');
- }
- $foundUID = (string)$component->UID;
- } else {
- if ($foundType !== $component->name) {
- throw new DAV\Exception\BadRequest('A calendar object must only contain 1 component. We found a ' . $component->name . ' as well as a ' . $foundType);
- }
- if ($foundUID !== (string)$component->UID) {
- throw new DAV\Exception\BadRequest('Every ' . $component->name . ' in this object must have identical UIDs');
- }
- }
- break;
- default :
- throw new DAV\Exception\BadRequest('You are not allowed to create components of type: ' . $component->name . ' here');
-
- }
- }
- if (!$foundType)
- throw new DAV\Exception\BadRequest('iCalendar object must contain at least 1 of VEVENT, VTODO or VJOURNAL');
-
- }
-
- /**
- * This method handles POST requests to the schedule-outbox.
- *
- * Currently, two types of requests are support:
- * * FREEBUSY requests from RFC 6638
- * * Simple iTIP messages from draft-desruisseaux-caldav-sched-04
- *
- * The latter is from an expired early draft of the CalDAV scheduling
- * extensions, but iCal depends on a feature from that spec, so we
- * implement it.
- *
- * @param Schedule\IOutbox $outboxNode
- * @param string $outboxUri
- * @return void
- */
- public function outboxRequest(Schedule\IOutbox $outboxNode, $outboxUri) {
-
- // Parsing the request body
- try {
- $vObject = VObject\Reader::read($this->server->httpRequest->getBody(true));
- } catch (VObject\ParseException $e) {
- throw new DAV\Exception\BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage());
- }
-
- // The incoming iCalendar object must have a METHOD property, and a
- // component. The combination of both determines what type of request
- // this is.
- $componentType = null;
- foreach($vObject->getComponents() as $component) {
- if ($component->name !== 'VTIMEZONE') {
- $componentType = $component->name;
- break;
- }
- }
- if (is_null($componentType)) {
- throw new DAV\Exception\BadRequest('We expected at least one VTODO, VJOURNAL, VFREEBUSY or VEVENT component');
- }
-
- // Validating the METHOD
- $method = strtoupper((string)$vObject->METHOD);
- if (!$method) {
- throw new DAV\Exception\BadRequest('A METHOD property must be specified in iTIP messages');
- }
-
- // So we support two types of requests:
- //
- // REQUEST with a VFREEBUSY component
- // REQUEST, REPLY, ADD, CANCEL on VEVENT components
-
- $acl = $this->server->getPlugin('acl');
-
- if ($componentType === 'VFREEBUSY' && $method === 'REQUEST') {
-
- $acl && $acl->checkPrivileges($outboxUri,'{' . Plugin::NS_CALDAV . '}schedule-query-freebusy');
- $this->handleFreeBusyRequest($outboxNode, $vObject);
-
- } elseif ($componentType === 'VEVENT' && in_array($method, array('REQUEST','REPLY','ADD','CANCEL'))) {
-
- $acl && $acl->checkPrivileges($outboxUri,'{' . Plugin::NS_CALDAV . '}schedule-post-vevent');
- $this->handleEventNotification($outboxNode, $vObject);
-
- } else {
-
- throw new DAV\Exception\NotImplemented('SabreDAV supports only VFREEBUSY (REQUEST) and VEVENT (REQUEST, REPLY, ADD, CANCEL)');
-
- }
-
- }
-
- /**
- * This method handles the REQUEST, REPLY, ADD and CANCEL methods for
- * VEVENT iTip messages.
- *
- * @return void
- */
- protected function handleEventNotification(Schedule\IOutbox $outboxNode, VObject\Component $vObject) {
-
- $originator = $this->server->httpRequest->getHeader('Originator');
- $recipients = $this->server->httpRequest->getHeader('Recipient');
-
- if (!$originator) {
- throw new DAV\Exception\BadRequest('The Originator: header must be specified when making POST requests');
- }
- if (!$recipients) {
- throw new DAV\Exception\BadRequest('The Recipient: header must be specified when making POST requests');
- }
-
- $recipients = explode(',',$recipients);
- foreach($recipients as $k=>$recipient) {
-
- $recipient = trim($recipient);
- if (!preg_match('/^mailto:(.*)@(.*)$/i', $recipient)) {
- throw new DAV\Exception\BadRequest('Recipients must start with mailto: and must be valid email address');
- }
- $recipient = substr($recipient, 7);
- $recipients[$k] = $recipient;
- }
-
- // We need to make sure that 'originator' matches one of the email
- // addresses of the selected principal.
- $principal = $outboxNode->getOwner();
- $props = $this->server->getProperties($principal,array(
- '{' . self::NS_CALDAV . '}calendar-user-address-set',
- ));
-
- $addresses = array();
- if (isset($props['{' . self::NS_CALDAV . '}calendar-user-address-set'])) {
- $addresses = $props['{' . self::NS_CALDAV . '}calendar-user-address-set']->getHrefs();
- }
-
- $found = false;
- foreach($addresses as $address) {
-
- // Trimming the / on both sides, just in case..
- if (rtrim(strtolower($originator),'/') === rtrim(strtolower($address),'/')) {
- $found = true;
- break;
- }
-
- }
-
- if (!$found) {
- throw new DAV\Exception\Forbidden('The addresses specified in the Originator header did not match any addresses in the owners calendar-user-address-set header');
- }
-
- // If the Originator header was a url, and not a mailto: address..
- // we're going to try to pull the mailto: from the vobject body.
- if (strtolower(substr($originator,0,7)) !== 'mailto:') {
- $originator = (string)$vObject->VEVENT->ORGANIZER;
-
- }
- if (strtolower(substr($originator,0,7)) !== 'mailto:') {
- throw new DAV\Exception\Forbidden('Could not find mailto: address in both the Orignator header, and the ORGANIZER property in the VEVENT');
- }
- $originator = substr($originator,7);
-
- $result = $this->iMIPMessage($originator, $recipients, $vObject, $principal);
- $this->server->httpResponse->sendStatus(200);
- $this->server->httpResponse->setHeader('Content-Type','application/xml');
- $this->server->httpResponse->sendBody($this->generateScheduleResponse($result));
-
- }
-
- /**
- * Sends an iMIP message by email.
- *
- * This method must return an array with status codes per recipient.
- * This should look something like:
- *
- * array(
- * 'user1@example.org' => '2.0;Success'
- * )
- *
- * Formatting for this status code can be found at:
- * https://tools.ietf.org/html/rfc5545#section-3.8.8.3
- *
- * A list of valid status codes can be found at:
- * https://tools.ietf.org/html/rfc5546#section-3.6
- *
- * @param string $originator
- * @param array $recipients
- * @param VObject\Component $vObject
- * @param string $principal Principal url
- * @return array
- */
- protected function iMIPMessage($originator, array $recipients, VObject\Component $vObject, $principal) {
-
- if (!$this->imipHandler) {
- $resultStatus = '5.2;This server does not support this operation';
- } else {
- $this->imipHandler->sendMessage($originator, $recipients, $vObject, $principal);
- $resultStatus = '2.0;Success';
- }
-
- $result = array();
- foreach($recipients as $recipient) {
- $result[$recipient] = $resultStatus;
- }
-
- return $result;
-
- }
-
- /**
- * Generates a schedule-response XML body
- *
- * The recipients array is a key->value list, containing email addresses
- * and iTip status codes. See the iMIPMessage method for a description of
- * the value.
- *
- * @param array $recipients
- * @return string
- */
- public function generateScheduleResponse(array $recipients) {
-
- $dom = new \DOMDocument('1.0','utf-8');
- $dom->formatOutput = true;
- $xscheduleResponse = $dom->createElement('cal:schedule-response');
- $dom->appendChild($xscheduleResponse);
-
- foreach($this->server->xmlNamespaces as $namespace=>$prefix) {
-
- $xscheduleResponse->setAttribute('xmlns:' . $prefix, $namespace);
-
- }
-
- foreach($recipients as $recipient=>$status) {
- $xresponse = $dom->createElement('cal:response');
-
- $xrecipient = $dom->createElement('cal:recipient');
- $xrecipient->appendChild($dom->createTextNode($recipient));
- $xresponse->appendChild($xrecipient);
-
- $xrequestStatus = $dom->createElement('cal:request-status');
- $xrequestStatus->appendChild($dom->createTextNode($status));
- $xresponse->appendChild($xrequestStatus);
-
- $xscheduleResponse->appendChild($xresponse);
-
- }
-
- return $dom->saveXML();
-
- }
-
- /**
- * This method is responsible for parsing a free-busy query request and
- * returning it's result.
- *
- * @param Schedule\IOutbox $outbox
- * @param string $request
- * @return string
- */
- protected function handleFreeBusyRequest(Schedule\IOutbox $outbox, VObject\Component $vObject) {
-
- $vFreeBusy = $vObject->VFREEBUSY;
- $organizer = $vFreeBusy->organizer;
-
- $organizer = (string)$organizer;
-
- // Validating if the organizer matches the owner of the inbox.
- $owner = $outbox->getOwner();
-
- $caldavNS = '{' . Plugin::NS_CALDAV . '}';
-
- $uas = $caldavNS . 'calendar-user-address-set';
- $props = $this->server->getProperties($owner,array($uas));
-
- if (empty($props[$uas]) || !in_array($organizer, $props[$uas]->getHrefs())) {
- throw new DAV\Exception\Forbidden('The organizer in the request did not match any of the addresses for the owner of this inbox');
- }
-
- if (!isset($vFreeBusy->ATTENDEE)) {
- throw new DAV\Exception\BadRequest('You must at least specify 1 attendee');
- }
-
- $attendees = array();
- foreach($vFreeBusy->ATTENDEE as $attendee) {
- $attendees[]= (string)$attendee;
- }
-
-
- if (!isset($vFreeBusy->DTSTART) || !isset($vFreeBusy->DTEND)) {
- throw new DAV\Exception\BadRequest('DTSTART and DTEND must both be specified');
- }
-
- $startRange = $vFreeBusy->DTSTART->getDateTime();
- $endRange = $vFreeBusy->DTEND->getDateTime();
-
- $results = array();
- foreach($attendees as $attendee) {
- $results[] = $this->getFreeBusyForEmail($attendee, $startRange, $endRange, $vObject);
- }
-
- $dom = new \DOMDocument('1.0','utf-8');
- $dom->formatOutput = true;
- $scheduleResponse = $dom->createElement('cal:schedule-response');
- foreach($this->server->xmlNamespaces as $namespace=>$prefix) {
-
- $scheduleResponse->setAttribute('xmlns:' . $prefix,$namespace);
-
- }
- $dom->appendChild($scheduleResponse);
-
- foreach($results as $result) {
- $response = $dom->createElement('cal:response');
-
- $recipient = $dom->createElement('cal:recipient');
- $recipientHref = $dom->createElement('d:href');
-
- $recipientHref->appendChild($dom->createTextNode($result['href']));
- $recipient->appendChild($recipientHref);
- $response->appendChild($recipient);
-
- $reqStatus = $dom->createElement('cal:request-status');
- $reqStatus->appendChild($dom->createTextNode($result['request-status']));
- $response->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())));
- $response->appendChild($calendardata);
-
- }
- $scheduleResponse->appendChild($response);
- }
-
- $this->server->httpResponse->sendStatus(200);
- $this->server->httpResponse->setHeader('Content-Type','application/xml');
- $this->server->httpResponse->sendBody($dom->saveXML());
-
- }
-
- /**
- * Returns free-busy information for a specific address. The returned
- * data is an array containing the following properties:
- *
- * calendar-data : A VFREEBUSY VObject
- * request-status : an iTip status code.
- * href: The principal's email address, as requested
- *
- * The following request status codes may be returned:
- * * 2.0;description
- * * 3.7;description
- *
- * @param string $email address
- * @param \DateTime $start
- * @param \DateTime $end
- * @param VObject\Component $request
- * @return array
- */
- protected function getFreeBusyForEmail($email, \DateTime $start, \DateTime $end, VObject\Component $request) {
-
- $caldavNS = '{' . Plugin::NS_CALDAV . '}';
-
- $aclPlugin = $this->server->getPlugin('acl');
- if (substr($email,0,7)==='mailto:') $email = substr($email,7);
-
- $result = $aclPlugin->principalSearch(
- array('{http://sabredav.org/ns}email-address' => $email),
- array(
- '{DAV:}principal-URL', $caldavNS . 'calendar-home-set',
- '{http://sabredav.org/ns}email-address',
- )
- );
-
- if (!count($result)) {
- return array(
- 'request-status' => '3.7;Could not find principal',
- 'href' => 'mailto:' . $email,
- );
- }
-
- if (!isset($result[0][200][$caldavNS . 'calendar-home-set'])) {
- return array(
- 'request-status' => '3.7;No calendar-home-set property found',
- 'href' => 'mailto:' . $email,
- );
- }
- $homeSet = $result[0][200][$caldavNS . 'calendar-home-set']->getHref();
-
- // Grabbing the calendar list
- $objects = array();
- foreach($this->server->tree->getNodeForPath($homeSet)->getChildren() as $node) {
- if (!$node instanceof ICalendar) {
- continue;
- }
- $aclPlugin->checkPrivileges($homeSet . $node->getName() ,$caldavNS . 'read-free-busy');
-
- // Getting the list of object uris within the time-range
- $urls = $node->calendarQuery(array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(
- array(
- 'name' => 'VEVENT',
- 'comp-filters' => array(),
- 'prop-filters' => array(),
- 'is-not-defined' => false,
- 'time-range' => array(
- 'start' => $start,
- 'end' => $end,
- ),
- ),
- ),
- 'prop-filters' => array(),
- 'is-not-defined' => false,
- 'time-range' => null,
- ));
-
- $calObjects = array_map(function($url) use ($node) {
- $obj = $node->getChild($url)->get();
- return $obj;
- }, $urls);
-
- $objects = array_merge($objects,$calObjects);
-
- }
-
- $vcalendar = new VObject\Component\VCalendar();
- $vcalendar->VERSION = '2.0';
- $vcalendar->METHOD = 'REPLY';
- $vcalendar->CALSCALE = 'GREGORIAN';
- $vcalendar->PRODID = '-//SabreDAV//SabreDAV ' . DAV\Version::VERSION . '//EN';
-
- $generator = new VObject\FreeBusyGenerator();
- $generator->setObjects($objects);
- $generator->setTimeRange($start, $end);
- $generator->setBaseObject($vcalendar);
-
- $result = $generator->getResult();
-
- $vcalendar->VFREEBUSY->ATTENDEE = 'mailto:' . $email;
- $vcalendar->VFREEBUSY->UID = (string)$request->VFREEBUSY->UID;
- $vcalendar->VFREEBUSY->ORGANIZER = clone $request->VFREEBUSY->ORGANIZER;
-
- return array(
- 'calendar-data' => $result,
- 'request-status' => '2.0;Success',
- 'href' => 'mailto:' . $email,
- );
- }
-
- /**
- * This method is used to generate HTML output for the
- * DAV\Browser\Plugin. This allows us to generate an interface users
- * can use to create new calendars.
- *
- * @param DAV\INode $node
- * @param string $output
- * @return bool
- */
- public function htmlActionsPanel(DAV\INode $node, &$output) {
-
- if (!$node instanceof UserCalendars)
- return;
-
- $output.= '<tr><td colspan="2"><form method="post" action="">
- <h3>Create new calendar</h3>
- <input type="hidden" name="sabreAction" value="mkcalendar" />
- <label>Name (uri):</label> <input type="text" name="name" /><br />
- <label>Display name:</label> <input type="text" name="{DAV:}displayname" /><br />
- <input type="submit" value="create" />
- </form>
- </td></tr>';
-
- return false;
-
- }
-
- /**
- * This method allows us to intercept the 'mkcalendar' sabreAction. This
- * action enables the user to create new calendars from the browser plugin.
- *
- * @param string $uri
- * @param string $action
- * @param array $postVars
- * @return bool
- */
- public function browserPostAction($uri, $action, array $postVars) {
-
- if ($action!=='mkcalendar')
- return;
-
- $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar');
- $properties = array();
- if (isset($postVars['{DAV:}displayname'])) {
- $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname'];
- }
- $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties);
- return false;
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Property/AllowedSharingModes.php b/vendor/sabre/dav/lib/Sabre/CalDAV/Property/AllowedSharingModes.php
deleted file mode 100644
index ca8312d40..000000000
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Property/AllowedSharingModes.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Property;
-
-use Sabre\DAV;
-
-/**
- * AllowedSharingModes
- *
- * This property encodes the 'allowed-sharing-modes' property, as defined by
- * the 'caldav-sharing-02' spec, in the http://calendarserver.org/ns/
- * namespace.
- *
- * This property is a representation of the supported-calendar_component-set
- * property in the CalDAV namespace. It simply requires an array of components,
- * such as VEVENT, VTODO
- *
- * @see https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-sharing-02.txt
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class AllowedSharingModes extends DAV\Property {
-
- /**
- * Whether or not a calendar can be shared with another user
- *
- * @var bool
- */
- protected $canBeShared;
-
- /**
- * Whether or not the calendar can be placed on a public url.
- *
- * @var bool
- */
- protected $canBePublished;
-
- /**
- * Constructor
- *
- * @param bool $canBeShared
- * @param bool $canBePublished
- * @return void
- */
- public function __construct($canBeShared, $canBePublished) {
-
- $this->canBeShared = $canBeShared;
- $this->canBePublished = $canBePublished;
-
- }
-
- /**
- * Serializes the property in a DOMDocument
- *
- * @param DAV\Server $server
- * @param \DOMElement $node
- * @return void
- */
- public function serialize(DAV\Server $server, \DOMElement $node) {
-
- $doc = $node->ownerDocument;
- if ($this->canBeShared) {
- $xcomp = $doc->createElement('cs:can-be-shared');
- $node->appendChild($xcomp);
- }
- if ($this->canBePublished) {
- $xcomp = $doc->createElement('cs:can-be-published');
- $node->appendChild($xcomp);
- }
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Property/Invite.php b/vendor/sabre/dav/lib/Sabre/CalDAV/Property/Invite.php
deleted file mode 100644
index 86ef69e80..000000000
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Property/Invite.php
+++ /dev/null
@@ -1,227 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Property;
-
-use Sabre\CalDAV\SharingPlugin as SharingPlugin;
-use Sabre\DAV;
-use Sabre\CalDAV;
-
-/**
- * Invite property
- *
- * This property encodes the 'invite' property, as defined by
- * the 'caldav-sharing-02' spec, in the http://calendarserver.org/ns/
- * namespace.
- *
- * @see https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-sharing-02.txt
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Invite extends DAV\Property {
-
- /**
- * The list of users a calendar has been shared to.
- *
- * @var array
- */
- protected $users;
-
- /**
- * The organizer contains information about the person who shared the
- * object.
- *
- * @var array
- */
- protected $organizer;
-
- /**
- * Creates the property.
- *
- * Users is an array. Each element of the array has the following
- * properties:
- *
- * * href - Often a mailto: address
- * * commonName - Optional, for example a first and lastname for a user.
- * * status - One of the SharingPlugin::STATUS_* constants.
- * * readOnly - true or false
- * * summary - Optional, description of the share
- *
- * The organizer key is optional to specify. It's only useful when a
- * 'sharee' requests the sharing information.
- *
- * The organizer may have the following properties:
- * * href - Often a mailto: address.
- * * commonName - Optional human-readable name.
- * * firstName - Optional first name.
- * * lastName - Optional last name.
- *
- * If you wonder why these two structures are so different, I guess a
- * valid answer is that the current spec is still a draft.
- *
- * @param array $users
- */
- public function __construct(array $users, array $organizer = null) {
-
- $this->users = $users;
- $this->organizer = $organizer;
-
- }
-
- /**
- * Returns the list of users, as it was passed to the constructor.
- *
- * @return array
- */
- public function getValue() {
-
- return $this->users;
-
- }
-
- /**
- * Serializes the property in a DOMDocument
- *
- * @param DAV\Server $server
- * @param \DOMElement $node
- * @return void
- */
- public function serialize(DAV\Server $server,\DOMElement $node) {
-
- $doc = $node->ownerDocument;
-
- if (!is_null($this->organizer)) {
-
- $xorganizer = $doc->createElement('cs:organizer');
-
- $href = $doc->createElement('d:href');
- $href->appendChild($doc->createTextNode($this->organizer['href']));
- $xorganizer->appendChild($href);
-
- if (isset($this->organizer['commonName']) && $this->organizer['commonName']) {
- $commonName = $doc->createElement('cs:common-name');
- $commonName->appendChild($doc->createTextNode($this->organizer['commonName']));
- $xorganizer->appendChild($commonName);
- }
- if (isset($this->organizer['firstName']) && $this->organizer['firstName']) {
- $firstName = $doc->createElement('cs:first-name');
- $firstName->appendChild($doc->createTextNode($this->organizer['firstName']));
- $xorganizer->appendChild($firstName);
- }
- if (isset($this->organizer['lastName']) && $this->organizer['lastName']) {
- $lastName = $doc->createElement('cs:last-name');
- $lastName->appendChild($doc->createTextNode($this->organizer['lastName']));
- $xorganizer->appendChild($lastName);
- }
-
- $node->appendChild($xorganizer);
-
-
- }
-
- foreach($this->users as $user) {
-
- $xuser = $doc->createElement('cs:user');
-
- $href = $doc->createElement('d:href');
- $href->appendChild($doc->createTextNode($user['href']));
- $xuser->appendChild($href);
-
- if (isset($user['commonName']) && $user['commonName']) {
- $commonName = $doc->createElement('cs:common-name');
- $commonName->appendChild($doc->createTextNode($user['commonName']));
- $xuser->appendChild($commonName);
- }
-
- switch($user['status']) {
-
- case SharingPlugin::STATUS_ACCEPTED :
- $status = $doc->createElement('cs:invite-accepted');
- $xuser->appendChild($status);
- break;
- case SharingPlugin::STATUS_DECLINED :
- $status = $doc->createElement('cs:invite-declined');
- $xuser->appendChild($status);
- break;
- case SharingPlugin::STATUS_NORESPONSE :
- $status = $doc->createElement('cs:invite-noresponse');
- $xuser->appendChild($status);
- break;
- case SharingPlugin::STATUS_INVALID :
- $status = $doc->createElement('cs:invite-invalid');
- $xuser->appendChild($status);
- break;
-
- }
-
- $xaccess = $doc->createElement('cs:access');
-
- if ($user['readOnly']) {
- $xaccess->appendChild(
- $doc->createElement('cs:read')
- );
- } else {
- $xaccess->appendChild(
- $doc->createElement('cs:read-write')
- );
- }
- $xuser->appendChild($xaccess);
-
- if (isset($user['summary']) && $user['summary']) {
- $summary = $doc->createElement('cs:summary');
- $summary->appendChild($doc->createTextNode($user['summary']));
- $xuser->appendChild($summary);
- }
-
- $node->appendChild($xuser);
-
- }
-
-
- }
-
- /**
- * Unserializes the property.
- *
- * This static method should return a an instance of this object.
- *
- * @param \DOMElement $prop
- * @return DAV\IProperty
- */
- static function unserialize(\DOMElement $prop) {
-
- $xpath = new \DOMXPath($prop->ownerDocument);
- $xpath->registerNamespace('cs', CalDAV\Plugin::NS_CALENDARSERVER);
- $xpath->registerNamespace('d', 'urn:DAV');
-
- $users = array();
-
- foreach($xpath->query('cs:user', $prop) as $user) {
-
- $status = null;
- if ($xpath->evaluate('boolean(cs:invite-accepted)', $user)) {
- $status = SharingPlugin::STATUS_ACCEPTED;
- } elseif ($xpath->evaluate('boolean(cs:invite-declined)', $user)) {
- $status = SharingPlugin::STATUS_DECLINED;
- } elseif ($xpath->evaluate('boolean(cs:invite-noresponse)', $user)) {
- $status = SharingPlugin::STATUS_NORESPONSE;
- } elseif ($xpath->evaluate('boolean(cs:invite-invalid)', $user)) {
- $status = SharingPlugin::STATUS_INVALID;
- } else {
- throw new DAV\Exception('Every cs:user property must have one of cs:invite-accepted, cs:invite-declined, cs:invite-noresponse or cs:invite-invalid');
- }
- $users[] = array(
- 'href' => $xpath->evaluate('string(d:href)', $user),
- 'commonName' => $xpath->evaluate('string(cs:common-name)', $user),
- 'readOnly' => $xpath->evaluate('boolean(cs:access/cs:read)', $user),
- 'summary' => $xpath->evaluate('string(cs:summary)', $user),
- 'status' => $status,
- );
-
- }
-
- return new self($users);
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Property/ScheduleCalendarTransp.php b/vendor/sabre/dav/lib/Sabre/CalDAV/Property/ScheduleCalendarTransp.php
deleted file mode 100644
index 7f12d7585..000000000
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Property/ScheduleCalendarTransp.php
+++ /dev/null
@@ -1,102 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Property;
-
-use Sabre\DAV;
-use Sabre\CalDAV;
-
-/**
- * schedule-calendar-transp property.
- *
- * This property is a representation of the schedule-calendar-transp property.
- * This property is defined in RFC6638 (caldav scheduling).
- *
- * Its values are either 'transparent' or 'opaque'. If it's transparent, it
- * means that this calendar will not be taken into consideration when a
- * different user queries for free-busy information. If it's 'opaque', it will.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class ScheduleCalendarTransp extends DAV\Property {
-
- const TRANSPARENT = 'transparent';
- const OPAQUE = 'opaque';
-
- protected $value;
-
- /**
- * Creates the property
- *
- * @param string $value
- */
- public function __construct($value) {
-
- if ($value !== self::TRANSPARENT && $value !== self::OPAQUE) {
- throw new \InvalidArgumentException('The value must either be specified as "transparent" or "opaque"');
- }
- $this->value = $value;
-
- }
-
- /**
- * Returns the current value
- *
- * @return string
- */
- public function getValue() {
-
- return $this->value;
-
- }
-
- /**
- * Serializes the property in a DOMDocument
- *
- * @param DAV\Server $server
- * @param \DOMElement $node
- * @return void
- */
- public function serialize(DAV\Server $server,\DOMElement $node) {
-
- $doc = $node->ownerDocument;
- switch($this->value) {
- case self::TRANSPARENT :
- $xval = $doc->createElement('cal:transparent');
- break;
- case self::OPAQUE :
- $xval = $doc->createElement('cal:opaque');
- break;
- }
-
- $node->appendChild($xval);
-
- }
-
- /**
- * Unserializes the DOMElement back into a Property class.
- *
- * @param \DOMElement $node
- * @return ScheduleCalendarTransp
- */
- static function unserialize(\DOMElement $node) {
-
- $value = null;
- foreach($node->childNodes as $childNode) {
- switch(DAV\XMLUtil::toClarkNotation($childNode)) {
- case '{' . CalDAV\Plugin::NS_CALDAV . '}opaque' :
- $value = self::OPAQUE;
- break;
- case '{' . CalDAV\Plugin::NS_CALDAV . '}transparent' :
- $value = self::TRANSPARENT;
- break;
- }
- }
- if (is_null($value))
- return null;
-
- return new self($value);
-
- }
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/vendor/sabre/dav/lib/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php
deleted file mode 100644
index 2538fa45d..000000000
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Property;
-
-use Sabre\DAV;
-use Sabre\CalDAV;
-
-/**
- * Supported component set property
- *
- * This property is a representation of the supported-calendar_component-set
- * property in the CalDAV namespace. It simply requires an array of components,
- * such as VEVENT, VTODO
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class SupportedCalendarComponentSet extends DAV\Property {
-
- /**
- * List of supported components, such as "VEVENT, VTODO"
- *
- * @var array
- */
- private $components;
-
- /**
- * Creates the property
- *
- * @param array $components
- */
- public function __construct(array $components) {
-
- $this->components = $components;
-
- }
-
- /**
- * Returns the list of supported components
- *
- * @return array
- */
- public function getValue() {
-
- return $this->components;
-
- }
-
- /**
- * Serializes the property in a DOMDocument
- *
- * @param DAV\Server $server
- * @param \DOMElement $node
- * @return void
- */
- public function serialize(DAV\Server $server,\DOMElement $node) {
-
- $doc = $node->ownerDocument;
- foreach($this->components as $component) {
-
- $xcomp = $doc->createElement('cal:comp');
- $xcomp->setAttribute('name',$component);
- $node->appendChild($xcomp);
-
- }
-
- }
-
- /**
- * Unserializes the DOMElement back into a Property class.
- *
- * @param \DOMElement $node
- * @return Property_SupportedCalendarComponentSet
- */
- static function unserialize(\DOMElement $node) {
-
- $components = array();
- foreach($node->childNodes as $childNode) {
- if (DAV\XMLUtil::toClarkNotation($childNode)==='{' . CalDAV\Plugin::NS_CALDAV . '}comp') {
- $components[] = $childNode->getAttribute('name');
- }
- }
- return new self($components);
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Property/SupportedCalendarData.php b/vendor/sabre/dav/lib/Sabre/CalDAV/Property/SupportedCalendarData.php
deleted file mode 100644
index efab80a91..000000000
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Property/SupportedCalendarData.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Property;
-use Sabre\DAV;
-use Sabre\CalDAV\Plugin;
-
-/**
- * Supported-calendar-data property
- *
- * This property is a representation of the supported-calendar-data property
- * in the CalDAV namespace. SabreDAV only has support for text/calendar;2.0
- * so the value is currently hardcoded.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class SupportedCalendarData extends DAV\Property {
-
- /**
- * Serializes the property in a DOMDocument
- *
- * @param DAV\Server $server
- * @param \DOMElement $node
- * @return void
- */
- public function serialize(DAV\Server $server,\DOMElement $node) {
-
- $doc = $node->ownerDocument;
-
- $prefix = isset($server->xmlNamespaces[Plugin::NS_CALDAV])?$server->xmlNamespaces[Plugin::NS_CALDAV]:'cal';
-
- $caldata = $doc->createElement($prefix . ':calendar-data');
- $caldata->setAttribute('content-type','text/calendar');
- $caldata->setAttribute('version','2.0');
-
- $node->appendChild($caldata);
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Property/SupportedCollationSet.php b/vendor/sabre/dav/lib/Sabre/CalDAV/Property/SupportedCollationSet.php
deleted file mode 100644
index 63c37fe5a..000000000
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Property/SupportedCollationSet.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Property;
-use Sabre\DAV;
-
-/**
- * supported-collation-set property
- *
- * This property is a representation of the supported-collation-set property
- * in the CalDAV namespace.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class SupportedCollationSet extends DAV\Property {
-
- /**
- * Serializes the property in a DOM document
- *
- * @param DAV\Server $server
- * @param \DOMElement $node
- * @return void
- */
- public function serialize(DAV\Server $server,\DOMElement $node) {
-
- $doc = $node->ownerDocument;
-
- $prefix = $node->lookupPrefix('urn:ietf:params:xml:ns:caldav');
- if (!$prefix) $prefix = 'cal';
-
- $node->appendChild(
- $doc->createElement($prefix . ':supported-collation','i;ascii-casemap')
- );
- $node->appendChild(
- $doc->createElement($prefix . ':supported-collation','i;octet')
- );
- $node->appendChild(
- $doc->createElement($prefix . ':supported-collation','i;unicode-casemap')
- );
-
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Schedule/IMip.php b/vendor/sabre/dav/lib/Sabre/CalDAV/Schedule/IMip.php
deleted file mode 100644
index b2b0d98b1..000000000
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Schedule/IMip.php
+++ /dev/null
@@ -1,111 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Schedule;
-
-use Sabre\VObject;
-use Sabre\DAV;
-
-/**
- * iMIP handler.
- *
- * This class is responsible for sending out iMIP messages. iMIP is the
- * email-based transport for iTIP. iTIP deals with scheduling operations for
- * iCalendar objects.
- *
- * If you want to customize the email that gets sent out, you can do so by
- * extending this class and overriding the sendMessage method.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class IMip {
-
- /**
- * Email address used in From: header.
- *
- * @var string
- */
- protected $senderEmail;
-
- /**
- * Creates the email handler.
- *
- * @param string $senderEmail. The 'senderEmail' is the email that shows up
- * in the 'From:' address. This should
- * generally be some kind of no-reply email
- * address you own.
- */
- public function __construct($senderEmail) {
-
- $this->senderEmail = $senderEmail;
-
- }
-
- /**
- * Sends one or more iTip messages through email.
- *
- * @param string $originator Originator Email
- * @param array $recipients Array of email addresses
- * @param VObject\Component $vObject
- * @param string $principal Principal Url of the originator
- * @return void
- */
- public function sendMessage($originator, array $recipients, VObject\Component $vObject, $principal) {
-
- foreach($recipients as $recipient) {
-
- $to = $recipient;
- $replyTo = $originator;
- $subject = 'SabreDAV iTIP message';
-
- switch(strtoupper($vObject->METHOD)) {
- case 'REPLY' :
- $subject = 'Response for: ' . $vObject->VEVENT->SUMMARY;
- break;
- case 'REQUEST' :
- $subject = 'Invitation for: ' .$vObject->VEVENT->SUMMARY;
- break;
- case 'CANCEL' :
- $subject = 'Cancelled event: ' . $vObject->VEVENT->SUMMARY;
- break;
- }
-
- $headers = array();
- $headers[] = 'Reply-To: ' . $replyTo;
- $headers[] = 'From: ' . $this->senderEmail;
- $headers[] = 'Content-Type: text/calendar; method=' . (string)$vObject->method . '; charset=utf-8';
- if (DAV\Server::$exposeVersion) {
- $headers[] = 'X-Sabre-Version: ' . DAV\Version::VERSION . '-' . DAV\Version::STABILITY;
- }
-
- $vcalBody = $vObject->serialize();
-
- $this->mail($to, $subject, $vcalBody, $headers);
-
- }
-
- }
-
- // @codeCoverageIgnoreStart
- // This is deemed untestable in a reasonable manner
-
- /**
- * This function is reponsible for sending the actual email.
- *
- * @param string $to Recipient email address
- * @param string $subject Subject of the email
- * @param string $body iCalendar body
- * @param array $headers List of headers
- * @return void
- */
- protected function mail($to, $subject, $body, array $headers) {
-
-
- mail($to, $subject, $body, implode("\r\n", $headers));
-
- }
-
- // @codeCoverageIgnoreEnd
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/SharingPlugin.php b/vendor/sabre/dav/lib/Sabre/CalDAV/SharingPlugin.php
deleted file mode 100644
index e869cb278..000000000
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/SharingPlugin.php
+++ /dev/null
@@ -1,526 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV;
-
-use Sabre\DAV;
-
-/**
- * This plugin implements support for caldav sharing.
- *
- * This spec is defined at:
- * http://svn.calendarserver.org/repository/calendarserver/CalendarServer/trunk/doc/Extensions/caldav-sharing.txt
- *
- * See:
- * Sabre\CalDAV\Backend\SharingSupport for all the documentation.
- *
- * Note: This feature is experimental, and may change in between different
- * SabreDAV versions.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class SharingPlugin extends DAV\ServerPlugin {
-
- /**
- * These are the various status constants used by sharing-messages.
- */
- const STATUS_ACCEPTED = 1;
- const STATUS_DECLINED = 2;
- const STATUS_DELETED = 3;
- const STATUS_NORESPONSE = 4;
- const STATUS_INVALID = 5;
-
- /**
- * Reference to SabreDAV server object.
- *
- * @var Sabre\DAV\Server
- */
- protected $server;
-
- /**
- * This method should return a list of server-features.
- *
- * This is for example 'versioning' and is added to the DAV: header
- * in an OPTIONS response.
- *
- * @return array
- */
- public function getFeatures() {
-
- return array('calendarserver-sharing');
-
- }
-
- /**
- * Returns a plugin name.
- *
- * Using this name other plugins will be able to access other plugins
- * using Sabre\DAV\Server::getPlugin
- *
- * @return string
- */
- public function getPluginName() {
-
- return 'caldav-sharing';
-
- }
-
- /**
- * This initializes the plugin.
- *
- * This function is called by Sabre\DAV\Server, after
- * addPlugin is called.
- *
- * This method should set up the required event subscriptions.
- *
- * @param DAV\Server $server
- * @return void
- */
- public function initialize(DAV\Server $server) {
-
- $this->server = $server;
- $server->resourceTypeMapping['Sabre\\CalDAV\\ISharedCalendar'] = '{' . Plugin::NS_CALENDARSERVER . '}shared';
-
- array_push(
- $this->server->protectedProperties,
- '{' . Plugin::NS_CALENDARSERVER . '}invite',
- '{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes',
- '{' . Plugin::NS_CALENDARSERVER . '}shared-url'
- );
-
- $this->server->subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties'));
- $this->server->subscribeEvent('afterGetProperties', array($this, 'afterGetProperties'));
- $this->server->subscribeEvent('updateProperties', array($this, 'updateProperties'));
- $this->server->subscribeEvent('unknownMethod', array($this,'unknownMethod'));
-
- }
-
- /**
- * This event is triggered when properties are requested for a certain
- * node.
- *
- * This allows us to inject any properties early.
- *
- * @param string $path
- * @param DAV\INode $node
- * @param array $requestedProperties
- * @param array $returnedProperties
- * @return void
- */
- public function beforeGetProperties($path, DAV\INode $node, &$requestedProperties, &$returnedProperties) {
-
- if ($node instanceof IShareableCalendar) {
- if (($index = array_search('{' . Plugin::NS_CALENDARSERVER . '}invite', $requestedProperties))!==false) {
-
- unset($requestedProperties[$index]);
- $returnedProperties[200]['{' . Plugin::NS_CALENDARSERVER . '}invite'] =
- new Property\Invite(
- $node->getShares()
- );
-
- }
-
- }
-
- if ($node instanceof ISharedCalendar) {
-
- if (($index = array_search('{' . Plugin::NS_CALENDARSERVER . '}shared-url', $requestedProperties))!==false) {
-
- unset($requestedProperties[$index]);
- $returnedProperties[200]['{' . Plugin::NS_CALENDARSERVER . '}shared-url'] =
- new DAV\Property\Href(
- $node->getSharedUrl()
- );
-
- }
- // The 'invite' property is slightly different for the 'shared'
- // instance of the calendar, as it also contains the owner
- // information.
- if (($index = array_search('{' . Plugin::NS_CALENDARSERVER . '}invite', $requestedProperties))!==false) {
-
- unset($requestedProperties[$index]);
-
- // Fetching owner information
- $props = $this->server->getPropertiesForPath($node->getOwner(), array(
- '{http://sabredav.org/ns}email-address',
- '{DAV:}displayname',
- ), 1);
-
- $ownerInfo = array(
- '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'];
- }
-
- }
-
- $returnedProperties[200]['{' . Plugin::NS_CALENDARSERVER . '}invite'] =
- new Property\Invite(
- $node->getShares(),
- $ownerInfo
- );
-
- }
-
-
- }
-
- }
-
- /**
- * This method is triggered *after* all properties have been retrieved.
- * This allows us to inject the correct resourcetype for calendars that
- * have been shared.
- *
- * @param string $path
- * @param array $properties
- * @param DAV\INode $node
- * @return void
- */
- public function afterGetProperties($path, &$properties, DAV\INode $node) {
-
- if ($node instanceof IShareableCalendar) {
- if (isset($properties[200]['{DAV:}resourcetype'])) {
- if (count($node->getShares())>0) {
- $properties[200]['{DAV:}resourcetype']->add(
- '{' . Plugin::NS_CALENDARSERVER . '}shared-owner'
- );
- }
- }
- $propName = '{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes';
- if (array_key_exists($propName, $properties[404])) {
- unset($properties[404][$propName]);
- $properties[200][$propName] = new Property\AllowedSharingModes(true,false);
- }
-
- }
-
- }
-
- /**
- * This method is trigged when a user attempts to update a node's
- * properties.
- *
- * A previous draft of the sharing spec stated that it was possible to use
- * PROPPATCH to remove 'shared-owner' from the resourcetype, thus unsharing
- * the calendar.
- *
- * Even though this is no longer in the current spec, we keep this around
- * because OS X 10.7 may still make use of this feature.
- *
- * @param array $mutations
- * @param array $result
- * @param DAV\INode $node
- * @return void
- */
- public function updateProperties(array &$mutations, array &$result, DAV\INode $node) {
-
- if (!$node instanceof IShareableCalendar)
- return;
-
- if (!isset($mutations['{DAV:}resourcetype'])) {
- return;
- }
-
- // Only doing something if shared-owner is indeed not in the list.
- if($mutations['{DAV:}resourcetype']->is('{' . Plugin::NS_CALENDARSERVER . '}shared-owner')) return;
-
- $shares = $node->getShares();
- $remove = array();
- foreach($shares as $share) {
- $remove[] = $share['href'];
- }
- $node->updateShares(array(), $remove);
-
- // We're marking this update as 200 OK
- $result[200]['{DAV:}resourcetype'] = null;
-
- // Removing it from the mutations list
- unset($mutations['{DAV:}resourcetype']);
-
- }
-
- /**
- * This event is triggered when the server didn't know how to handle a
- * certain request.
- *
- * We intercept this to handle POST requests on calendars.
- *
- * @param string $method
- * @param string $uri
- * @return null|bool
- */
- public function unknownMethod($method, $uri) {
-
- if ($method!=='POST') {
- return;
- }
-
- // Only handling xml
- $contentType = $this->server->httpRequest->getHeader('Content-Type');
- if (strpos($contentType,'application/xml')===false && strpos($contentType,'text/xml')===false)
- return;
-
- // Making sure the node exists
- try {
- $node = $this->server->tree->getNodeForPath($uri);
- } catch (DAV\Exception\NotFound $e) {
- return;
- }
-
- $requestBody = $this->server->httpRequest->getBody(true);
-
- // If this request handler could not deal with this POST request, it
- // will return 'null' and other plugins get a chance to handle the
- // request.
- //
- // However, we already requested the full body. This is a problem,
- // because a body can only be read once. This is why we preemptively
- // re-populated the request body with the existing data.
- $this->server->httpRequest->setBody($requestBody);
-
- $dom = DAV\XMLUtil::loadDOMDocument($requestBody);
-
- $documentType = DAV\XMLUtil::toClarkNotation($dom->firstChild);
-
- switch($documentType) {
-
- // Dealing with the 'share' document, which modified invitees on a
- // calendar.
- case '{' . Plugin::NS_CALENDARSERVER . '}share' :
-
- // We can only deal with IShareableCalendar objects
- if (!$node instanceof IShareableCalendar) {
- return;
- }
-
- // Getting ACL info
- $acl = $this->server->getPlugin('acl');
-
- // If there's no ACL support, we allow everything
- if ($acl) {
- $acl->checkPrivileges($uri, '{DAV:}write');
- }
-
- $mutations = $this->parseShareRequest($dom);
-
- $node->updateShares($mutations[0], $mutations[1]);
-
- $this->server->httpResponse->sendStatus(200);
- // Adding this because sending a response body may cause issues,
- // and I wanted some type of indicator the response was handled.
- $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well');
-
- // Breaking the event chain
- return false;
-
- // The invite-reply document is sent when the user replies to an
- // invitation of a calendar share.
- case '{'. Plugin::NS_CALENDARSERVER.'}invite-reply' :
-
- // This only works on the calendar-home-root node.
- if (!$node instanceof UserCalendars) {
- return;
- }
-
- // Getting ACL info
- $acl = $this->server->getPlugin('acl');
-
- // If there's no ACL support, we allow everything
- if ($acl) {
- $acl->checkPrivileges($uri, '{DAV:}write');
- }
-
- $message = $this->parseInviteReplyRequest($dom);
-
- $url = $node->shareReply(
- $message['href'],
- $message['status'],
- $message['calendarUri'],
- $message['inReplyTo'],
- $message['summary']
- );
-
- $this->server->httpResponse->sendStatus(200);
- // Adding this because sending a response body may cause issues,
- // and I wanted some type of indicator the response was handled.
- $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well');
-
- if ($url) {
- $dom = new \DOMDocument('1.0', 'UTF-8');
- $dom->formatOutput = true;
-
- $root = $dom->createElement('cs:shared-as');
- foreach($this->server->xmlNamespaces as $namespace => $prefix) {
- $root->setAttribute('xmlns:' . $prefix, $namespace);
- }
-
- $dom->appendChild($root);
- $href = new DAV\Property\Href($url);
-
- $href->serialize($this->server, $root);
- $this->server->httpResponse->setHeader('Content-Type','application/xml');
- $this->server->httpResponse->sendBody($dom->saveXML());
-
- }
-
- // Breaking the event chain
- return false;
-
- case '{' . Plugin::NS_CALENDARSERVER . '}publish-calendar' :
-
- // We can only deal with IShareableCalendar objects
- if (!$node instanceof IShareableCalendar) {
- return;
- }
-
- // Getting ACL info
- $acl = $this->server->getPlugin('acl');
-
- // If there's no ACL support, we allow everything
- if ($acl) {
- $acl->checkPrivileges($uri, '{DAV:}write');
- }
-
- $node->setPublishStatus(true);
-
- // iCloud sends back the 202, so we will too.
- $this->server->httpResponse->sendStatus(202);
-
- // Adding this because sending a response body may cause issues,
- // and I wanted some type of indicator the response was handled.
- $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well');
-
- // Breaking the event chain
- return false;
-
- case '{' . Plugin::NS_CALENDARSERVER . '}unpublish-calendar' :
-
- // We can only deal with IShareableCalendar objects
- if (!$node instanceof IShareableCalendar) {
- return;
- }
-
- // Getting ACL info
- $acl = $this->server->getPlugin('acl');
-
- // If there's no ACL support, we allow everything
- if ($acl) {
- $acl->checkPrivileges($uri, '{DAV:}write');
- }
-
- $node->setPublishStatus(false);
-
- $this->server->httpResponse->sendStatus(200);
-
- // Adding this because sending a response body may cause issues,
- // and I wanted some type of indicator the response was handled.
- $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well');
-
- // Breaking the event chain
- return false;
-
- }
-
-
-
- }
-
- /**
- * Parses the 'share' POST request.
- *
- * This method returns an array, containing two arrays.
- * The first array is a list of new sharees. Every element is a struct
- * containing a:
- * * href element. (usually a mailto: address)
- * * commonName element (often a first and lastname, but can also be
- * false)
- * * readOnly (true or false)
- * * summary (A description of the share, can also be false)
- *
- * The second array is a list of sharees that are to be removed. This is
- * just a simple array with 'hrefs'.
- *
- * @param \DOMDocument $dom
- * @return array
- */
- protected function parseShareRequest(\DOMDocument $dom) {
-
- $xpath = new \DOMXPath($dom);
- $xpath->registerNamespace('cs', Plugin::NS_CALENDARSERVER);
- $xpath->registerNamespace('d', 'urn:DAV');
-
- $set = array();
- $elems = $xpath->query('cs:set');
-
- for($i=0; $i < $elems->length; $i++) {
-
- $xset = $elems->item($i);
- $set[] = array(
- 'href' => $xpath->evaluate('string(d:href)', $xset),
- 'commonName' => $xpath->evaluate('string(cs:common-name)', $xset),
- 'summary' => $xpath->evaluate('string(cs:summary)', $xset),
- 'readOnly' => $xpath->evaluate('boolean(cs:read)', $xset)!==false
- );
-
- }
-
- $remove = array();
- $elems = $xpath->query('cs:remove');
-
- for($i=0; $i < $elems->length; $i++) {
-
- $xremove = $elems->item($i);
- $remove[] = $xpath->evaluate('string(d:href)', $xremove);
-
- }
-
- return array($set, $remove);
-
- }
-
- /**
- * Parses the 'invite-reply' POST request.
- *
- * This method returns an array, containing the following properties:
- * * href - The sharee who is replying
- * * status - One of the self::STATUS_* constants
- * * calendarUri - The url of the shared calendar
- * * inReplyTo - The unique id of the share invitation.
- * * summary - Optional description of the reply.
- *
- * @param \DOMDocument $dom
- * @return array
- */
- protected function parseInviteReplyRequest(\DOMDocument $dom) {
-
- $xpath = new \DOMXPath($dom);
- $xpath->registerNamespace('cs', Plugin::NS_CALENDARSERVER);
- $xpath->registerNamespace('d', 'urn:DAV');
-
- $hostHref = $xpath->evaluate('string(cs:hosturl/d:href)');
- if (!$hostHref) {
- throw new DAV\Exception\BadRequest('The {' . Plugin::NS_CALENDARSERVER . '}hosturl/{DAV:}href element is required');
- }
-
- return array(
- 'href' => $xpath->evaluate('string(d:href)'),
- 'calendarUri' => $this->server->calculateUri($hostHref),
- 'inReplyTo' => $xpath->evaluate('string(cs:in-reply-to)'),
- 'summary' => $xpath->evaluate('string(cs:summary)'),
- 'status' => $xpath->evaluate('boolean(cs:invite-accepted)')?self::STATUS_ACCEPTED:self::STATUS_DECLINED
- );
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CalDAV/Version.php b/vendor/sabre/dav/lib/Sabre/CalDAV/Version.php
deleted file mode 100644
index f30fc20ea..000000000
--- a/vendor/sabre/dav/lib/Sabre/CalDAV/Version.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV;
-
-/**
- * This class contains the Sabre\CalDAV version constants.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Version {
-
- /**
- * Full version number
- */
- const VERSION = '1.8.7';
-
- /**
- * Stability : alpha, beta, stable
- */
- const STABILITY = 'stable';
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CardDAV/AddressBook.php b/vendor/sabre/dav/lib/Sabre/CardDAV/AddressBook.php
deleted file mode 100644
index 399f38e8d..000000000
--- a/vendor/sabre/dav/lib/Sabre/CardDAV/AddressBook.php
+++ /dev/null
@@ -1,315 +0,0 @@
-<?php
-
-namespace Sabre\CardDAV;
-
-use Sabre\DAV;
-use Sabre\DAVACL;
-
-/**
- * The AddressBook class represents a CardDAV addressbook, owned by a specific user
- *
- * The AddressBook can contain multiple vcards
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class AddressBook extends DAV\Collection implements IAddressBook, DAV\IProperties, DAVACL\IACL {
-
- /**
- * This is an array with addressbook information
- *
- * @var array
- */
- protected $addressBookInfo;
-
- /**
- * CardDAV backend
- *
- * @var Backend\BackendInterface
- */
- protected $carddavBackend;
-
- /**
- * Constructor
- *
- * @param Backend\BackendInterface $carddavBackend
- * @param array $addressBookInfo
- */
- public function __construct(Backend\BackendInterface $carddavBackend, array $addressBookInfo) {
-
- $this->carddavBackend = $carddavBackend;
- $this->addressBookInfo = $addressBookInfo;
-
- }
-
- /**
- * Returns the name of the addressbook
- *
- * @return string
- */
- public function getName() {
-
- return $this->addressBookInfo['uri'];
-
- }
-
- /**
- * Returns a card
- *
- * @param string $name
- * @return \ICard
- */
- 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);
-
- }
-
- /**
- * Returns the full list of cards
- *
- * @return array
- */
- public function getChildren() {
-
- $objs = $this->carddavBackend->getCards($this->addressBookInfo['id']);
- $children = array();
- foreach($objs as $obj) {
- $children[] = new Card($this->carddavBackend,$this->addressBookInfo,$obj);
- }
- return $children;
-
- }
-
- /**
- * Creates a new directory
- *
- * We actually block this, as subdirectories are not allowed in addressbooks.
- *
- * @param string $name
- * @return void
- */
- public function createDirectory($name) {
-
- throw new DAV\Exception\MethodNotAllowed('Creating collections in addressbooks is not allowed');
-
- }
-
- /**
- * Creates a new file
- *
- * The contents of the new file must be a valid VCARD.
- *
- * This method may return an ETag.
- *
- * @param string $name
- * @param resource $vcardData
- * @return string|null
- */
- public function createFile($name,$vcardData = null) {
-
- if (is_resource($vcardData)) {
- $vcardData = stream_get_contents($vcardData);
- }
- // Converting to UTF-8, if needed
- $vcardData = DAV\StringUtil::ensureUTF8($vcardData);
-
- return $this->carddavBackend->createCard($this->addressBookInfo['id'],$name,$vcardData);
-
- }
-
- /**
- * Deletes the entire addressbook.
- *
- * @return void
- */
- public function delete() {
-
- $this->carddavBackend->deleteAddressBook($this->addressBookInfo['id']);
-
- }
-
- /**
- * Renames the addressbook
- *
- * @param string $newName
- * @return void
- */
- public function setName($newName) {
-
- throw new DAV\Exception\MethodNotAllowed('Renaming addressbooks is not yet supported');
-
- }
-
- /**
- * Returns the last modification date as a unix timestamp.
- *
- * @return void
- */
- public function getLastModified() {
-
- return null;
-
- }
-
- /**
- * Updates properties on this node,
- *
- * The properties array uses the propertyName in clark-notation as key,
- * and the array value for the property value. In the case a property
- * should be deleted, the property value will be null.
- *
- * This method must be atomic. If one property cannot be changed, the
- * entire operation must fail.
- *
- * If the operation was successful, true can be returned.
- * If the operation failed, false can be returned.
- *
- * Deletion of a non-existent property is always successful.
- *
- * Lastly, it is optional to return detailed information about any
- * failures. In this case an array should be returned with the following
- * structure:
- *
- * array(
- * 403 => array(
- * '{DAV:}displayname' => null,
- * ),
- * 424 => array(
- * '{DAV:}owner' => null,
- * )
- * )
- *
- * In this example it was forbidden to update {DAV:}displayname.
- * (403 Forbidden), which in turn also caused {DAV:}owner to fail
- * (424 Failed Dependency) because the request needs to be atomic.
- *
- * @param array $mutations
- * @return bool|array
- */
- public function updateProperties($mutations) {
-
- return $this->carddavBackend->updateAddressBook($this->addressBookInfo['id'], $mutations);
-
- }
-
- /**
- * Returns a list of properties for this nodes.
- *
- * The properties list is a list of propertynames the client requested,
- * encoded in clark-notation {xmlnamespace}tagname
- *
- * If the array is empty, it means 'all properties' were requested.
- *
- * @param array $properties
- * @return array
- */
- public function getProperties($properties) {
-
- $response = array();
- foreach($properties as $propertyName) {
-
- if (isset($this->addressBookInfo[$propertyName])) {
-
- $response[$propertyName] = $this->addressBookInfo[$propertyName];
-
- }
-
- }
-
- return $response;
-
- }
-
- /**
- * Returns the owner principal
- *
- * This must be a url to a principal, or null if there's no owner
- *
- * @return string|null
- */
- public function getOwner() {
-
- return $this->addressBookInfo['principaluri'];
-
- }
-
- /**
- * Returns a group principal
- *
- * This must be a url to a principal, or null if there's no owner
- *
- * @return string|null
- */
- public function getGroup() {
-
- return null;
-
- }
-
- /**
- * Returns a list of ACE's for this node.
- *
- * Each ACE has the following properties:
- * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
- * currently the only supported privileges
- * * 'principal', a url to the principal who owns the node
- * * 'protected' (optional), indicating that this ACE is not allowed to
- * be updated.
- *
- * @return array
- */
- public function getACL() {
-
- return array(
- array(
- 'privilege' => '{DAV:}read',
- 'principal' => $this->addressBookInfo['principaluri'],
- 'protected' => true,
- ),
- array(
- 'privilege' => '{DAV:}write',
- 'principal' => $this->addressBookInfo['principaluri'],
- 'protected' => true,
- ),
-
- );
-
- }
-
- /**
- * Updates the ACL
- *
- * This method will receive a list of new ACE's.
- *
- * @param array $acl
- * @return void
- */
- public function setACL(array $acl) {
-
- throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported');
-
- }
-
- /**
- * Returns the list of supported privileges for this node.
- *
- * The returned data structure is a list of nested privileges.
- * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
- * standard structure.
- *
- * If null is returned from this method, the default privilege set is used,
- * which is fine for most common usecases.
- *
- * @return array|null
- */
- public function getSupportedPrivilegeSet() {
-
- return null;
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CardDAV/AddressBookQueryParser.php b/vendor/sabre/dav/lib/Sabre/CardDAV/AddressBookQueryParser.php
deleted file mode 100644
index 3277d98b0..000000000
--- a/vendor/sabre/dav/lib/Sabre/CardDAV/AddressBookQueryParser.php
+++ /dev/null
@@ -1,221 +0,0 @@
-<?php
-
-namespace Sabre\CardDAV;
-
-use Sabre\DAV;
-
-/**
- * Parses the addressbook-query report request body.
- *
- * Whoever designed this format, and the CalDAV equivalent even more so,
- * has no feel for design.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class AddressBookQueryParser {
-
- const TEST_ANYOF = 'anyof';
- const TEST_ALLOF = 'allof';
-
- /**
- * List of requested properties the client wanted
- *
- * @var array
- */
- public $requestedProperties;
-
- /**
- * The number of results the client wants
- *
- * null means it wasn't specified, which in most cases means 'all results'.
- *
- * @var int|null
- */
- public $limit;
-
- /**
- * List of property filters.
- *
- * @var array
- */
- public $filters;
-
- /**
- * Either TEST_ANYOF or TEST_ALLOF
- *
- * @var string
- */
- public $test;
-
- /**
- * DOM Document
- *
- * @var DOMDocument
- */
- protected $dom;
-
- /**
- * DOM XPath object
- *
- * @var DOMXPath
- */
- protected $xpath;
-
- /**
- * Creates the parser
- *
- * @param \DOMDocument $dom
- */
- public function __construct(\DOMDocument $dom) {
-
- $this->dom = $dom;
-
- $this->xpath = new \DOMXPath($dom);
- $this->xpath->registerNameSpace('card',Plugin::NS_CARDDAV);
-
- }
-
- /**
- * Parses the request.
- *
- * @return void
- */
- public function parse() {
-
- $filterNode = null;
-
- $limit = $this->xpath->evaluate('number(/card:addressbook-query/card:limit/card:nresults)');
- if (is_nan($limit)) $limit = null;
-
- $filter = $this->xpath->query('/card:addressbook-query/card:filter');
-
- // According to the CardDAV spec there needs to be exactly 1 filter
- // element. However, KDE 4.8.2 contains a bug that will encode 0 filter
- // elements, so this is a workaround for that.
- //
- // See: https://bugs.kde.org/show_bug.cgi?id=300047
- if ($filter->length === 0) {
- $test = null;
- $filter = null;
- } elseif ($filter->length === 1) {
- $filter = $filter->item(0);
- $test = $this->xpath->evaluate('string(@test)', $filter);
- } else {
- throw new DAV\Exception\BadRequest('Only one filter element is allowed');
- }
-
- if (!$test) $test = self::TEST_ANYOF;
- if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) {
- throw new DAV\Exception\BadRequest('The test attribute must either hold "anyof" or "allof"');
- }
-
- $propFilters = array();
-
- $propFilterNodes = $this->xpath->query('card:prop-filter', $filter);
- for($ii=0; $ii < $propFilterNodes->length; $ii++) {
-
- $propFilters[] = $this->parsePropFilterNode($propFilterNodes->item($ii));
-
-
- }
-
- $this->filters = $propFilters;
- $this->limit = $limit;
- $this->requestedProperties = array_keys(DAV\XMLUtil::parseProperties($this->dom->firstChild));
- $this->test = $test;
-
- }
-
- /**
- * Parses the prop-filter xml element
- *
- * @param \DOMElement $propFilterNode
- * @return array
- */
- protected function parsePropFilterNode(\DOMElement $propFilterNode) {
-
- $propFilter = array();
- $propFilter['name'] = $propFilterNode->getAttribute('name');
- $propFilter['test'] = $propFilterNode->getAttribute('test');
- if (!$propFilter['test']) $propFilter['test'] = 'anyof';
-
- $propFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $propFilterNode)->length>0;
-
- $paramFilterNodes = $this->xpath->query('card:param-filter', $propFilterNode);
-
- $propFilter['param-filters'] = array();
-
-
- for($ii=0;$ii<$paramFilterNodes->length;$ii++) {
-
- $propFilter['param-filters'][] = $this->parseParamFilterNode($paramFilterNodes->item($ii));
-
- }
- $propFilter['text-matches'] = array();
- $textMatchNodes = $this->xpath->query('card:text-match', $propFilterNode);
-
- for($ii=0;$ii<$textMatchNodes->length;$ii++) {
-
- $propFilter['text-matches'][] = $this->parseTextMatchNode($textMatchNodes->item($ii));
-
- }
-
- return $propFilter;
-
- }
-
- /**
- * Parses the param-filter element
- *
- * @param \DOMElement $paramFilterNode
- * @return array
- */
- public function parseParamFilterNode(\DOMElement $paramFilterNode) {
-
- $paramFilter = array();
- $paramFilter['name'] = $paramFilterNode->getAttribute('name');
- $paramFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $paramFilterNode)->length>0;
- $paramFilter['text-match'] = null;
-
- $textMatch = $this->xpath->query('card:text-match', $paramFilterNode);
- if ($textMatch->length>0) {
- $paramFilter['text-match'] = $this->parseTextMatchNode($textMatch->item(0));
- }
-
- return $paramFilter;
-
- }
-
- /**
- * Text match
- *
- * @param \DOMElement $textMatchNode
- * @return array
- */
- public function parseTextMatchNode(\DOMElement $textMatchNode) {
-
- $matchType = $textMatchNode->getAttribute('match-type');
- if (!$matchType) $matchType = 'contains';
-
- if (!in_array($matchType, array('contains', 'equals', 'starts-with', 'ends-with'))) {
- throw new DAV\Exception\BadRequest('Unknown match-type: ' . $matchType);
- }
-
- $negateCondition = $textMatchNode->getAttribute('negate-condition');
- $negateCondition = $negateCondition==='yes';
- $collation = $textMatchNode->getAttribute('collation');
- if (!$collation) $collation = 'i;unicode-casemap';
-
- return array(
- 'negate-condition' => $negateCondition,
- 'collation' => $collation,
- 'match-type' => $matchType,
- 'value' => $textMatchNode->nodeValue
- );
-
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CardDAV/Backend/AbstractBackend.php b/vendor/sabre/dav/lib/Sabre/CardDAV/Backend/AbstractBackend.php
deleted file mode 100644
index 46909efef..000000000
--- a/vendor/sabre/dav/lib/Sabre/CardDAV/Backend/AbstractBackend.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-namespace Sabre\CardDAV\Backend;
-
-/**
- * CardDAV abstract Backend
- *
- * This class serves as a base-class for addressbook backends
- *
- * This class doesn't do much, but it was added for consistency.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-abstract class AbstractBackend implements BackendInterface {
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CardDAV/Backend/PDO.php b/vendor/sabre/dav/lib/Sabre/CardDAV/Backend/PDO.php
deleted file mode 100644
index 67e0ae3aa..000000000
--- a/vendor/sabre/dav/lib/Sabre/CardDAV/Backend/PDO.php
+++ /dev/null
@@ -1,333 +0,0 @@
-<?php
-
-namespace Sabre\CardDAV\Backend;
-
-use Sabre\CardDAV;
-use Sabre\DAV;
-
-/**
- * PDO CardDAV backend
- *
- * This CardDAV backend uses PDO to store addressbooks
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class PDO extends AbstractBackend {
-
- /**
- * PDO connection
- *
- * @var PDO
- */
- protected $pdo;
-
- /**
- * The PDO table name used to store addressbooks
- */
- protected $addressBooksTableName;
-
- /**
- * The PDO table name used to store cards
- */
- protected $cardsTableName;
-
- /**
- * Sets up the object
- *
- * @param \PDO $pdo
- * @param string $addressBooksTableName
- * @param string $cardsTableName
- */
- public function __construct(\PDO $pdo, $addressBooksTableName = 'addressbooks', $cardsTableName = 'cards') {
-
- $this->pdo = $pdo;
- $this->addressBooksTableName = $addressBooksTableName;
- $this->cardsTableName = $cardsTableName;
-
- }
-
- /**
- * Returns the list of addressbooks for a specific user.
- *
- * @param string $principalUri
- * @return array
- */
- public function getAddressBooksForUser($principalUri) {
-
- $stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, ctag FROM '.$this->addressBooksTableName.' WHERE principaluri = ?');
- $stmt->execute(array($principalUri));
-
- $addressBooks = array();
-
- foreach($stmt->fetchAll() as $row) {
-
- $addressBooks[] = array(
- 'id' => $row['id'],
- 'uri' => $row['uri'],
- 'principaluri' => $row['principaluri'],
- '{DAV:}displayname' => $row['displayname'],
- '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
- '{http://calendarserver.org/ns/}getctag' => $row['ctag'],
- '{' . CardDAV\Plugin::NS_CARDDAV . '}supported-address-data' =>
- new CardDAV\Property\SupportedAddressData(),
- );
-
- }
-
- return $addressBooks;
-
- }
-
-
- /**
- * Updates an addressbook's properties
- *
- * See Sabre\DAV\IProperties for a description of the mutations array, as
- * well as the return value.
- *
- * @param mixed $addressBookId
- * @param array $mutations
- * @see Sabre\DAV\IProperties::updateProperties
- * @return bool|array
- */
- public function updateAddressBook($addressBookId, array $mutations) {
-
- $updates = array();
-
- foreach($mutations as $property=>$newValue) {
-
- switch($property) {
- case '{DAV:}displayname' :
- $updates['displayname'] = $newValue;
- break;
- case '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' :
- $updates['description'] = $newValue;
- break;
- default :
- // If any unsupported values were being updated, we must
- // let the entire request fail.
- return false;
- }
-
- }
-
- // No values are being updated?
- if (!$updates) {
- return false;
- }
-
- $query = 'UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 ';
- foreach($updates as $key=>$value) {
- $query.=', `' . $key . '` = :' . $key . ' ';
- }
- $query.=' WHERE id = :addressbookid';
-
- $stmt = $this->pdo->prepare($query);
- $updates['addressbookid'] = $addressBookId;
-
- $stmt->execute($updates);
-
- return true;
-
- }
-
- /**
- * Creates a new address book
- *
- * @param string $principalUri
- * @param string $url Just the 'basename' of the url.
- * @param array $properties
- * @return void
- */
- public function createAddressBook($principalUri, $url, array $properties) {
-
- $values = array(
- 'displayname' => null,
- 'description' => null,
- 'principaluri' => $principalUri,
- 'uri' => $url,
- );
-
- foreach($properties as $property=>$newValue) {
-
- switch($property) {
- case '{DAV:}displayname' :
- $values['displayname'] = $newValue;
- break;
- case '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' :
- $values['description'] = $newValue;
- break;
- default :
- throw new DAV\Exception\BadRequest('Unknown property: ' . $property);
- }
-
- }
-
- $query = 'INSERT INTO ' . $this->addressBooksTableName . ' (uri, displayname, description, principaluri, ctag) VALUES (:uri, :displayname, :description, :principaluri, 1)';
- $stmt = $this->pdo->prepare($query);
- $stmt->execute($values);
-
- }
-
- /**
- * Deletes an entire addressbook and all its contents
- *
- * @param int $addressBookId
- * @return void
- */
- public function deleteAddressBook($addressBookId) {
-
- $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?');
- $stmt->execute(array($addressBookId));
-
- $stmt = $this->pdo->prepare('DELETE FROM ' . $this->addressBooksTableName . ' WHERE id = ?');
- $stmt->execute(array($addressBookId));
-
- }
-
- /**
- * Returns all cards for a specific addressbook id.
- *
- * This method should return the following properties for each card:
- * * carddata - raw vcard data
- * * uri - Some unique url
- * * lastmodified - A unix timestamp
- *
- * It's recommended to also return the following properties:
- * * etag - A unique etag. This must change every time the card changes.
- * * size - The size of the card in bytes.
- *
- * If these last two properties are provided, less time will be spent
- * calculating them. If they are specified, you can also ommit carddata.
- * This may speed up certain requests, especially with large cards.
- *
- * @param mixed $addressbookId
- * @return array
- */
- public function getCards($addressbookId) {
-
- $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?');
- $stmt->execute(array($addressbookId));
-
- return $stmt->fetchAll(\PDO::FETCH_ASSOC);
-
-
- }
-
- /**
- * Returns a specfic card.
- *
- * The same set of properties must be returned as with getCards. The only
- * exception is that 'carddata' is absolutely required.
- *
- * @param mixed $addressBookId
- * @param string $cardUri
- * @return array
- */
- public function getCard($addressBookId, $cardUri) {
-
- $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ? LIMIT 1');
- $stmt->execute(array($addressBookId, $cardUri));
-
- $result = $stmt->fetchAll(\PDO::FETCH_ASSOC);
-
- return (count($result)>0?$result[0]:false);
-
- }
-
- /**
- * Creates a new card.
- *
- * The addressbook id will be passed as the first argument. This is the
- * same id as it is returned from the getAddressbooksForUser method.
- *
- * The cardUri is a base uri, and doesn't include the full path. The
- * cardData argument is the vcard body, and is passed as a string.
- *
- * It is possible to return an ETag from this method. This ETag is for the
- * newly created resource, and must be enclosed with double quotes (that
- * is, the string itself must contain the double quotes).
- *
- * You should only return the ETag if you store the carddata as-is. If a
- * subsequent GET request on the same card does not have the same body,
- * byte-by-byte and you did return an ETag here, clients tend to get
- * confused.
- *
- * If you don't return an ETag, you can just return null.
- *
- * @param mixed $addressBookId
- * @param string $cardUri
- * @param string $cardData
- * @return string|null
- */
- public function createCard($addressBookId, $cardUri, $cardData) {
-
- $stmt = $this->pdo->prepare('INSERT INTO ' . $this->cardsTableName . ' (carddata, uri, lastmodified, addressbookid) VALUES (?, ?, ?, ?)');
-
- $result = $stmt->execute(array($cardData, $cardUri, time(), $addressBookId));
-
- $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?');
- $stmt2->execute(array($addressBookId));
-
- return '"' . md5($cardData) . '"';
-
- }
-
- /**
- * Updates a card.
- *
- * The addressbook id will be passed as the first argument. This is the
- * same id as it is returned from the getAddressbooksForUser method.
- *
- * The cardUri is a base uri, and doesn't include the full path. The
- * cardData argument is the vcard body, and is passed as a string.
- *
- * It is possible to return an ETag from this method. This ETag should
- * match that of the updated resource, and must be enclosed with double
- * quotes (that is: the string itself must contain the actual quotes).
- *
- * You should only return the ETag if you store the carddata as-is. If a
- * subsequent GET request on the same card does not have the same body,
- * byte-by-byte and you did return an ETag here, clients tend to get
- * confused.
- *
- * If you don't return an ETag, you can just return null.
- *
- * @param mixed $addressBookId
- * @param string $cardUri
- * @param string $cardData
- * @return string|null
- */
- public function updateCard($addressBookId, $cardUri, $cardData) {
-
- $stmt = $this->pdo->prepare('UPDATE ' . $this->cardsTableName . ' SET carddata = ?, lastmodified = ? WHERE uri = ? AND addressbookid =?');
- $stmt->execute(array($cardData, time(), $cardUri, $addressBookId));
-
- $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?');
- $stmt2->execute(array($addressBookId));
-
- return '"' . md5($cardData) . '"';
-
- }
-
- /**
- * Deletes a card
- *
- * @param mixed $addressBookId
- * @param string $cardUri
- * @return bool
- */
- public function deleteCard($addressBookId, $cardUri) {
-
- $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ?');
- $stmt->execute(array($addressBookId, $cardUri));
-
- $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?');
- $stmt2->execute(array($addressBookId));
-
- return $stmt->rowCount()===1;
-
- }
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CardDAV/Plugin.php b/vendor/sabre/dav/lib/Sabre/CardDAV/Plugin.php
deleted file mode 100644
index 71a61fefc..000000000
--- a/vendor/sabre/dav/lib/Sabre/CardDAV/Plugin.php
+++ /dev/null
@@ -1,706 +0,0 @@
-<?php
-
-namespace Sabre\CardDAV;
-
-use Sabre\DAV;
-use Sabre\DAVACL;
-use Sabre\VObject;
-
-/**
- * CardDAV plugin
- *
- * The CardDAV plugin adds CardDAV functionality to the WebDAV server
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Plugin extends DAV\ServerPlugin {
-
- /**
- * Url to the addressbooks
- */
- const ADDRESSBOOK_ROOT = 'addressbooks';
-
- /**
- * xml namespace for CardDAV elements
- */
- const NS_CARDDAV = 'urn:ietf:params:xml:ns:carddav';
-
- /**
- * Add urls to this property to have them automatically exposed as
- * 'directories' to the user.
- *
- * @var array
- */
- public $directories = array();
-
- /**
- * Server class
- *
- * @var Sabre\DAV\Server
- */
- protected $server;
-
- /**
- * Initializes the plugin
- *
- * @param DAV\Server $server
- * @return void
- */
- public function initialize(DAV\Server $server) {
-
- /* Events */
- $server->subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties'));
- $server->subscribeEvent('afterGetProperties', array($this, 'afterGetProperties'));
- $server->subscribeEvent('updateProperties', array($this, 'updateProperties'));
- $server->subscribeEvent('report', array($this,'report'));
- $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel'));
- $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction'));
- $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent'));
- $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile'));
-
- /* Namespaces */
- $server->xmlNamespaces[self::NS_CARDDAV] = 'card';
-
- /* Mapping Interfaces to {DAV:}resourcetype values */
- $server->resourceTypeMapping['Sabre\\CardDAV\\IAddressBook'] = '{' . self::NS_CARDDAV . '}addressbook';
- $server->resourceTypeMapping['Sabre\\CardDAV\\IDirectory'] = '{' . self::NS_CARDDAV . '}directory';
-
- /* Adding properties that may never be changed */
- $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-address-data';
- $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}max-resource-size';
- $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}addressbook-home-set';
- $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-collation-set';
-
- $server->propertyMap['{http://calendarserver.org/ns/}me-card'] = 'Sabre\\DAV\\Property\\Href';
-
- $this->server = $server;
-
- }
-
- /**
- * Returns a list of supported features.
- *
- * This is used in the DAV: header in the OPTIONS and PROPFIND requests.
- *
- * @return array
- */
- public function getFeatures() {
-
- return array('addressbook');
-
- }
-
- /**
- * Returns a list of reports this plugin supports.
- *
- * This will be used in the {DAV:}supported-report-set property.
- * Note that you still need to subscribe to the 'report' event to actually
- * implement them
- *
- * @param string $uri
- * @return array
- */
- public function getSupportedReportSet($uri) {
-
- $node = $this->server->tree->getNodeForPath($uri);
- if ($node instanceof IAddressBook || $node instanceof ICard) {
- return array(
- '{' . self::NS_CARDDAV . '}addressbook-multiget',
- '{' . self::NS_CARDDAV . '}addressbook-query',
- );
- }
- return array();
-
- }
-
-
- /**
- * Adds all CardDAV-specific properties
- *
- * @param string $path
- * @param DAV\INode $node
- * @param array $requestedProperties
- * @param array $returnedProperties
- * @return void
- */
- public function beforeGetProperties($path, DAV\INode $node, array &$requestedProperties, array &$returnedProperties) {
-
- if ($node instanceof DAVACL\IPrincipal) {
-
- // calendar-home-set property
- $addHome = '{' . self::NS_CARDDAV . '}addressbook-home-set';
- if (in_array($addHome,$requestedProperties)) {
- $principalId = $node->getName();
- $addressbookHomePath = self::ADDRESSBOOK_ROOT . '/' . $principalId . '/';
- unset($requestedProperties[array_search($addHome, $requestedProperties)]);
- $returnedProperties[200][$addHome] = new DAV\Property\Href($addressbookHomePath);
- }
-
- $directories = '{' . self::NS_CARDDAV . '}directory-gateway';
- if ($this->directories && in_array($directories, $requestedProperties)) {
- unset($requestedProperties[array_search($directories, $requestedProperties)]);
- $returnedProperties[200][$directories] = new DAV\Property\HrefList($this->directories);
- }
-
- }
-
- if ($node instanceof ICard) {
-
- // The address-data property is not supposed to be a 'real'
- // property, but in large chunks of the spec it does act as such.
- // Therefore we simply expose it as a property.
- $addressDataProp = '{' . self::NS_CARDDAV . '}address-data';
- if (in_array($addressDataProp, $requestedProperties)) {
- unset($requestedProperties[$addressDataProp]);
- $val = $node->get();
- if (is_resource($val))
- $val = stream_get_contents($val);
-
- $returnedProperties[200][$addressDataProp] = $val;
-
- }
- }
-
- if ($node instanceof UserAddressBooks) {
-
- $meCardProp = '{http://calendarserver.org/ns/}me-card';
- if (in_array($meCardProp, $requestedProperties)) {
-
- $props = $this->server->getProperties($node->getOwner(), array('{http://sabredav.org/ns}vcard-url'));
- if (isset($props['{http://sabredav.org/ns}vcard-url'])) {
-
- $returnedProperties[200][$meCardProp] = new DAV\Property\Href(
- $props['{http://sabredav.org/ns}vcard-url']
- );
- $pos = array_search($meCardProp, $requestedProperties);
- unset($requestedProperties[$pos]);
-
- }
-
- }
-
- }
-
- }
-
- /**
- * This event is triggered when a PROPPATCH method is executed
- *
- * @param array $mutations
- * @param array $result
- * @param DAV\INode $node
- * @return bool
- */
- public function updateProperties(&$mutations, &$result, DAV\INode $node) {
-
- if (!$node instanceof UserAddressBooks) {
- return true;
- }
-
- $meCard = '{http://calendarserver.org/ns/}me-card';
-
- // The only property we care about
- if (!isset($mutations[$meCard]))
- return true;
-
- $value = $mutations[$meCard];
- unset($mutations[$meCard]);
-
- if ($value instanceof DAV\Property\IHref) {
- $value = $value->getHref();
- $value = $this->server->calculateUri($value);
- } elseif (!is_null($value)) {
- $result[400][$meCard] = null;
- return false;
- }
-
- $innerResult = $this->server->updateProperties(
- $node->getOwner(),
- array(
- '{http://sabredav.org/ns}vcard-url' => $value,
- )
- );
-
- $closureResult = false;
- foreach($innerResult as $status => $props) {
- if (is_array($props) && array_key_exists('{http://sabredav.org/ns}vcard-url', $props)) {
- $result[$status][$meCard] = null;
- $closureResult = ($status>=200 && $status<300);
- }
-
- }
-
- return $result;
-
- }
-
- /**
- * This functions handles REPORT requests specific to CardDAV
- *
- * @param string $reportName
- * @param \DOMNode $dom
- * @return bool
- */
- public function report($reportName,$dom) {
-
- switch($reportName) {
- case '{'.self::NS_CARDDAV.'}addressbook-multiget' :
- $this->addressbookMultiGetReport($dom);
- return false;
- case '{'.self::NS_CARDDAV.'}addressbook-query' :
- $this->addressBookQueryReport($dom);
- return false;
- default :
- return;
-
- }
-
-
- }
-
- /**
- * This function handles the addressbook-multiget REPORT.
- *
- * This report is used by the client to fetch the content of a series
- * of urls. Effectively avoiding a lot of redundant requests.
- *
- * @param \DOMNode $dom
- * @return void
- */
- public function addressbookMultiGetReport($dom) {
-
- $properties = array_keys(DAV\XMLUtil::parseProperties($dom->firstChild));
-
- $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href');
- $propertyList = array();
-
- foreach($hrefElems as $elem) {
-
- $uri = $this->server->calculateUri($elem->nodeValue);
- list($propertyList[]) = $this->server->getPropertiesForPath($uri,$properties);
-
- }
-
- $prefer = $this->server->getHTTPPRefer();
-
- $this->server->httpResponse->sendStatus(207);
- $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
- $this->server->httpResponse->setHeader('Vary','Brief,Prefer');
- $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList, $prefer['return-minimal']));
-
- }
-
- /**
- * This method is triggered before a file gets updated with new content.
- *
- * This plugin uses this method to ensure that Card nodes receive valid
- * vcard data.
- *
- * @param string $path
- * @param DAV\IFile $node
- * @param resource $data
- * @return void
- */
- public function beforeWriteContent($path, DAV\IFile $node, &$data) {
-
- if (!$node instanceof ICard)
- return;
-
- $this->validateVCard($data);
-
- }
-
- /**
- * This method is triggered before a new file is created.
- *
- * This plugin uses this method to ensure that Card nodes receive valid
- * vcard data.
- *
- * @param string $path
- * @param resource $data
- * @param DAV\ICollection $parentNode
- * @return void
- */
- public function beforeCreateFile($path, &$data, DAV\ICollection $parentNode) {
-
- if (!$parentNode instanceof IAddressBook)
- return;
-
- $this->validateVCard($data);
-
- }
-
- /**
- * Checks if the submitted iCalendar data is in fact, valid.
- *
- * An exception is thrown if it's not.
- *
- * @param resource|string $data
- * @return void
- */
- protected function validateVCard(&$data) {
-
- // If it's a stream, we convert it to a string first.
- if (is_resource($data)) {
- $data = stream_get_contents($data);
- }
-
- // Converting the data to unicode, if needed.
- $data = DAV\StringUtil::ensureUTF8($data);
-
- try {
-
- $vobj = VObject\Reader::read($data);
-
- } catch (VObject\ParseException $e) {
-
- throw new DAV\Exception\UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage());
-
- }
-
- if ($vobj->name !== 'VCARD') {
- throw new DAV\Exception\UnsupportedMediaType('This collection can only support vcard objects.');
- }
-
- if (!isset($vobj->UID)) {
- // No UID in vcards is invalid, but we'll just add it in anyway.
- $vobj->add('UID', DAV\UUIDUtil::getUUID());
- $data = $vobj->serialize();
- }
-
- }
-
-
- /**
- * This function handles the addressbook-query REPORT
- *
- * This report is used by the client to filter an addressbook based on a
- * complex query.
- *
- * @param \DOMNode $dom
- * @return void
- */
- protected function addressbookQueryReport($dom) {
-
- $query = new AddressBookQueryParser($dom);
- $query->parse();
-
- $depth = $this->server->getHTTPDepth(0);
-
- if ($depth==0) {
- $candidateNodes = array(
- $this->server->tree->getNodeForPath($this->server->getRequestUri())
- );
- } else {
- $candidateNodes = $this->server->tree->getChildren($this->server->getRequestUri());
- }
-
- $validNodes = array();
- foreach($candidateNodes as $node) {
-
- if (!$node instanceof ICard)
- continue;
-
- $blob = $node->get();
- if (is_resource($blob)) {
- $blob = stream_get_contents($blob);
- }
-
- if (!$this->validateFilters($blob, $query->filters, $query->test)) {
- continue;
- }
-
- $validNodes[] = $node;
-
- if ($query->limit && $query->limit <= count($validNodes)) {
- // We hit the maximum number of items, we can stop now.
- break;
- }
-
- }
-
- $result = array();
- foreach($validNodes as $validNode) {
-
- if ($depth==0) {
- $href = $this->server->getRequestUri();
- } else {
- $href = $this->server->getRequestUri() . '/' . $validNode->getName();
- }
-
- list($result[]) = $this->server->getPropertiesForPath($href, $query->requestedProperties, 0);
-
- }
-
- $prefer = $this->server->getHTTPPRefer();
-
- $this->server->httpResponse->sendStatus(207);
- $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
- $this->server->httpResponse->setHeader('Vary','Brief,Prefer');
- $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal']));
-
- }
-
- /**
- * Validates if a vcard makes it throught a list of filters.
- *
- * @param string $vcardData
- * @param array $filters
- * @param string $test anyof or allof (which means OR or AND)
- * @return bool
- */
- public function validateFilters($vcardData, array $filters, $test) {
-
- $vcard = VObject\Reader::read($vcardData);
-
- if (!$filters) return true;
-
- foreach($filters as $filter) {
-
- $isDefined = isset($vcard->{$filter['name']});
- if ($filter['is-not-defined']) {
- if ($isDefined) {
- $success = false;
- } else {
- $success = true;
- }
- } elseif ((!$filter['param-filters'] && !$filter['text-matches']) || !$isDefined) {
-
- // We only need to check for existence
- $success = $isDefined;
-
- } else {
-
- $vProperties = $vcard->select($filter['name']);
-
- $results = array();
- if ($filter['param-filters']) {
- $results[] = $this->validateParamFilters($vProperties, $filter['param-filters'], $filter['test']);
- }
- if ($filter['text-matches']) {
- $texts = array();
- foreach($vProperties as $vProperty)
- $texts[] = $vProperty->getValue();
-
- $results[] = $this->validateTextMatches($texts, $filter['text-matches'], $filter['test']);
- }
-
- if (count($results)===1) {
- $success = $results[0];
- } else {
- if ($filter['test'] === 'anyof') {
- $success = $results[0] || $results[1];
- } else {
- $success = $results[0] && $results[1];
- }
- }
-
- } // else
-
- // There are two conditions where we can already determine whether
- // or not this filter succeeds.
- if ($test==='anyof' && $success) {
- return true;
- }
- if ($test==='allof' && !$success) {
- return false;
- }
-
- } // foreach
-
- // If we got all the way here, it means we haven't been able to
- // determine early if the test failed or not.
- //
- // This implies for 'anyof' that the test failed, and for 'allof' that
- // we succeeded. Sounds weird, but makes sense.
- return $test==='allof';
-
- }
-
- /**
- * Validates if a param-filter can be applied to a specific property.
- *
- * @todo currently we're only validating the first parameter of the passed
- * property. Any subsequence parameters with the same name are
- * ignored.
- * @param array $vProperties
- * @param array $filters
- * @param string $test
- * @return bool
- */
- protected function validateParamFilters(array $vProperties, array $filters, $test) {
-
- foreach($filters as $filter) {
-
- $isDefined = false;
- foreach($vProperties as $vProperty) {
- $isDefined = isset($vProperty[$filter['name']]);
- if ($isDefined) break;
- }
-
- if ($filter['is-not-defined']) {
- if ($isDefined) {
- $success = false;
- } else {
- $success = true;
- }
-
- // If there's no text-match, we can just check for existence
- } elseif (!$filter['text-match'] || !$isDefined) {
-
- $success = $isDefined;
-
- } else {
-
- $success = false;
- foreach($vProperties as $vProperty) {
- // If we got all the way here, we'll need to validate the
- // text-match filter.
- $success = DAV\StringUtil::textMatch($vProperty[$filter['name']]->getValue(), $filter['text-match']['value'], $filter['text-match']['collation'], $filter['text-match']['match-type']);
- if ($success) break;
- }
- if ($filter['text-match']['negate-condition']) {
- $success = !$success;
- }
-
- } // else
-
- // There are two conditions where we can already determine whether
- // or not this filter succeeds.
- if ($test==='anyof' && $success) {
- return true;
- }
- if ($test==='allof' && !$success) {
- return false;
- }
-
- }
-
- // If we got all the way here, it means we haven't been able to
- // determine early if the test failed or not.
- //
- // This implies for 'anyof' that the test failed, and for 'allof' that
- // we succeeded. Sounds weird, but makes sense.
- return $test==='allof';
-
- }
-
- /**
- * Validates if a text-filter can be applied to a specific property.
- *
- * @param array $texts
- * @param array $filters
- * @param string $test
- * @return bool
- */
- protected function validateTextMatches(array $texts, array $filters, $test) {
-
- foreach($filters as $filter) {
-
- $success = false;
- foreach($texts as $haystack) {
- $success = DAV\StringUtil::textMatch($haystack, $filter['value'], $filter['collation'], $filter['match-type']);
-
- // Breaking on the first match
- if ($success) break;
- }
- if ($filter['negate-condition']) {
- $success = !$success;
- }
-
- if ($success && $test==='anyof')
- return true;
-
- if (!$success && $test=='allof')
- return false;
-
-
- }
-
- // If we got all the way here, it means we haven't been able to
- // determine early if the test failed or not.
- //
- // This implies for 'anyof' that the test failed, and for 'allof' that
- // we succeeded. Sounds weird, but makes sense.
- return $test==='allof';
-
- }
-
- /**
- * This event is triggered after webdav-properties have been retrieved.
- *
- * @return bool
- */
- public function afterGetProperties($uri, &$properties) {
-
- // 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 (!isset($properties[200]['{DAV:}getcontenttype']))
- return;
-
- if (strpos($this->server->httpRequest->getHeader('User-Agent'),'Thunderbird')===false) {
- return;
- }
-
- if (strpos($properties[200]['{DAV:}getcontenttype'],'text/x-vcard')===0) {
- $properties[200]['{DAV:}getcontenttype'] = 'text/x-vcard';
- }
-
- }
-
- /**
- * This method is used to generate HTML output for the
- * Sabre\DAV\Browser\Plugin. This allows us to generate an interface users
- * can use to create new calendars.
- *
- * @param DAV\INode $node
- * @param string $output
- * @return bool
- */
- public function htmlActionsPanel(DAV\INode $node, &$output) {
-
- if (!$node instanceof UserAddressBooks)
- return;
-
- $output.= '<tr><td colspan="2"><form method="post" action="">
- <h3>Create new address book</h3>
- <input type="hidden" name="sabreAction" value="mkaddressbook" />
- <label>Name (uri):</label> <input type="text" name="name" /><br />
- <label>Display name:</label> <input type="text" name="{DAV:}displayname" /><br />
- <input type="submit" value="create" />
- </form>
- </td></tr>';
-
- return false;
-
- }
-
- /**
- * This method allows us to intercept the 'mkcalendar' sabreAction. This
- * action enables the user to create new calendars from the browser plugin.
- *
- * @param string $uri
- * @param string $action
- * @param array $postVars
- * @return bool
- */
- public function browserPostAction($uri, $action, array $postVars) {
-
- if ($action!=='mkaddressbook')
- return;
-
- $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:carddav}addressbook');
- $properties = array();
- if (isset($postVars['{DAV:}displayname'])) {
- $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname'];
- }
- $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties);
- return false;
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CardDAV/Property/SupportedAddressData.php b/vendor/sabre/dav/lib/Sabre/CardDAV/Property/SupportedAddressData.php
deleted file mode 100644
index 9d8dd2e6d..000000000
--- a/vendor/sabre/dav/lib/Sabre/CardDAV/Property/SupportedAddressData.php
+++ /dev/null
@@ -1,72 +0,0 @@
-<?php
-
-namespace Sabre\CardDAV\Property;
-
-use Sabre\DAV;
-use Sabre\CardDAV;
-
-/**
- * Supported-address-data property
- *
- * This property is a representation of the supported-address-data property
- * in the CardDAV namespace.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class SupportedAddressData extends DAV\Property {
-
- /**
- * supported versions
- *
- * @var array
- */
- protected $supportedData = array();
-
- /**
- * Creates the property
- *
- * @param array|null $supportedData
- */
- public function __construct(array $supportedData = null) {
-
- if (is_null($supportedData)) {
- $supportedData = array(
- array('contentType' => 'text/vcard', 'version' => '3.0'),
- // array('contentType' => 'text/vcard', 'version' => '4.0'),
- );
- }
-
- $this->supportedData = $supportedData;
-
- }
-
- /**
- * Serializes the property in a DOMDocument
- *
- * @param DAV\Server $server
- * @param \DOMElement $node
- * @return void
- */
- public function serialize(DAV\Server $server,\DOMElement $node) {
-
- $doc = $node->ownerDocument;
-
- $prefix =
- isset($server->xmlNamespaces[CardDAV\Plugin::NS_CARDDAV]) ?
- $server->xmlNamespaces[CardDAV\Plugin::NS_CARDDAV] :
- 'card';
-
- foreach($this->supportedData as $supported) {
-
- $caldata = $doc->createElementNS(CardDAV\Plugin::NS_CARDDAV, $prefix . ':address-data-type');
- $caldata->setAttribute('content-type',$supported['contentType']);
- $caldata->setAttribute('version',$supported['version']);
- $node->appendChild($caldata);
-
- }
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CardDAV/VCFExportPlugin.php b/vendor/sabre/dav/lib/Sabre/CardDAV/VCFExportPlugin.php
deleted file mode 100644
index 3f91a3012..000000000
--- a/vendor/sabre/dav/lib/Sabre/CardDAV/VCFExportPlugin.php
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-
-namespace Sabre\CardDAV;
-
-use Sabre\DAV;
-use Sabre\VObject;
-
-/**
- * VCF Exporter
- *
- * This plugin adds the ability to export entire address books as .vcf files.
- * This is useful for clients that don't support CardDAV yet. They often do
- * support vcf files.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @author Thomas Tanghus (http://tanghus.net/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class VCFExportPlugin extends DAV\ServerPlugin {
-
- /**
- * Reference to Server class
- *
- * @var Sabre\DAV\Server
- */
- protected $server;
-
- /**
- * Initializes the plugin and registers event handlers
- *
- * @param DAV\Server $server
- * @return void
- */
- public function initialize(DAV\Server $server) {
-
- $this->server = $server;
- $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90);
-
- }
-
- /**
- * 'beforeMethod' event handles. This event handles intercepts GET requests ending
- * with ?export
- *
- * @param string $method
- * @param string $uri
- * @return bool
- */
- public function beforeMethod($method, $uri) {
-
- if ($method!='GET') return;
- if ($this->server->httpRequest->getQueryString()!='export') return;
-
- // splitting uri
- list($uri) = explode('?',$uri,2);
-
- $node = $this->server->tree->getNodeForPath($uri);
-
- if (!($node instanceof IAddressBook)) return;
-
- // Checking ACL, if available.
- if ($aclPlugin = $this->server->getPlugin('acl')) {
- $aclPlugin->checkPrivileges($uri, '{DAV:}read');
- }
-
- $this->server->httpResponse->setHeader('Content-Type','text/directory');
- $this->server->httpResponse->sendStatus(200);
-
- $nodes = $this->server->getPropertiesForPath($uri, array(
- '{' . Plugin::NS_CARDDAV . '}address-data',
- ),1);
-
- $this->server->httpResponse->sendBody($this->generateVCF($nodes));
-
- // Returning false to break the event chain
- return false;
-
- }
-
- /**
- * Merges all vcard objects, and builds one big vcf export
- *
- * @param array $nodes
- * @return string
- */
- public function generateVCF(array $nodes) {
-
- $output = "";
-
- foreach($nodes as $node) {
-
- if (!isset($node[200]['{' . Plugin::NS_CARDDAV . '}address-data'])) {
- continue;
- }
- $nodeData = $node[200]['{' . Plugin::NS_CARDDAV . '}address-data'];
-
- // Parsing this node so VObject can clean up the output.
- $output .=
- VObject\Reader::read($nodeData)->serialize();
-
- }
-
- return $output;
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/CardDAV/Version.php b/vendor/sabre/dav/lib/Sabre/CardDAV/Version.php
deleted file mode 100644
index 00221941b..000000000
--- a/vendor/sabre/dav/lib/Sabre/CardDAV/Version.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-namespace Sabre\CardDAV;
-
-/**
- * Version Class
- *
- * This class contains the Sabre\CardDAV version information
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Version {
-
- /**
- * Full version number
- */
- const VERSION = '1.8.7';
-
- /**
- * Stability : alpha, beta, stable
- */
- const STABILITY = 'stable';
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/AbstractBasic.php b/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/AbstractBasic.php
deleted file mode 100644
index 599f932d4..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/AbstractBasic.php
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Auth\Backend;
-
-use Sabre\DAV;
-use Sabre\HTTP;
-
-/**
- * HTTP Basic authentication backend class
- *
- * This class can be used by authentication objects wishing to use HTTP Basic
- * Most of the digest logic is handled, implementors just need to worry about
- * the validateUserPass method.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author James David Low (http://jameslow.com/)
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-abstract class AbstractBasic implements BackendInterface {
-
- /**
- * This variable holds the currently logged in username.
- *
- * @var string|null
- */
- protected $currentUser;
-
- /**
- * 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
- */
- abstract protected function validateUserPass($username, $password);
-
- /**
- * Returns information about the currently logged in username.
- *
- * If nobody is currently logged in, this method should return null.
- *
- * @return string|null
- */
- public function getCurrentUser() {
- return $this->currentUser;
- }
-
-
- /**
- * Authenticates the user based on the current request.
- *
- * If authentication is successful, true must be returned.
- * If authentication fails, an exception must be thrown.
- *
- * @param DAV\Server $server
- * @param string $realm
- * @throws DAV\Exception\NotAuthenticated
- * @return bool
- */
- public function authenticate(DAV\Server $server, $realm) {
-
- $auth = new HTTP\BasicAuth();
- $auth->setHTTPRequest($server->httpRequest);
- $auth->setHTTPResponse($server->httpResponse);
- $auth->setRealm($realm);
- $userpass = $auth->getUserPass();
- if (!$userpass) {
- $auth->requireLogin();
- throw new DAV\Exception\NotAuthenticated('No basic authentication headers were found');
- }
-
- // Authenticates the user
- if (!$this->validateUserPass($userpass[0],$userpass[1])) {
- $auth->requireLogin();
- throw new DAV\Exception\NotAuthenticated('Username or password does not match');
- }
- $this->currentUser = $userpass[0];
- return true;
- }
-
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/AbstractDigest.php b/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/AbstractDigest.php
deleted file mode 100644
index dc00438c9..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/AbstractDigest.php
+++ /dev/null
@@ -1,101 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Auth\Backend;
-
-use Sabre\HTTP;
-use Sabre\DAV;
-
-/**
- * HTTP Digest authentication backend class
- *
- * This class can be used by authentication objects wishing to use HTTP Digest
- * Most of the digest logic is handled, implementors just need to worry about
- * the getDigestHash method
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-abstract class AbstractDigest implements BackendInterface {
-
- /**
- * This variable holds the currently logged in username.
- *
- * @var array|null
- */
- protected $currentUser;
-
- /**
- * Returns a users digest hash based on the username and realm.
- *
- * If the user was not known, null must be returned.
- *
- * @param string $realm
- * @param string $username
- * @return string|null
- */
- abstract public function getDigestHash($realm, $username);
-
- /**
- * Authenticates the user based on the current request.
- *
- * If authentication is successful, true must be returned.
- * If authentication fails, an exception must be thrown.
- *
- * @param DAV\Server $server
- * @param string $realm
- * @throws DAV\Exception\NotAuthenticated
- * @return bool
- */
- public function authenticate(DAV\Server $server, $realm) {
-
- $digest = new HTTP\DigestAuth();
-
- // Hooking up request and response objects
- $digest->setHTTPRequest($server->httpRequest);
- $digest->setHTTPResponse($server->httpResponse);
-
- $digest->setRealm($realm);
- $digest->init();
-
- $username = $digest->getUsername();
-
- // No username was given
- if (!$username) {
- $digest->requireLogin();
- throw new DAV\Exception\NotAuthenticated('No digest authentication headers were found');
- }
-
- $hash = $this->getDigestHash($realm, $username);
- // If this was false, the user account didn't exist
- if ($hash===false || is_null($hash)) {
- $digest->requireLogin();
- throw new DAV\Exception\NotAuthenticated('The supplied username was not on file');
- }
- if (!is_string($hash)) {
- throw new DAV\Exception('The returned value from getDigestHash must be a string or null');
- }
-
- // If this was false, the password or part of the hash was incorrect.
- if (!$digest->validateA1($hash)) {
- $digest->requireLogin();
- throw new DAV\Exception\NotAuthenticated('Incorrect username');
- }
-
- $this->currentUser = $username;
- return true;
-
- }
-
- /**
- * Returns the currently logged in username.
- *
- * @return string|null
- */
- public function getCurrentUser() {
-
- return $this->currentUser;
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/Apache.php b/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/Apache.php
deleted file mode 100644
index 66fdd91e1..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/Apache.php
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Auth\Backend;
-use Sabre\DAV;
-
-/**
- * Apache authenticator
- *
- * This authentication backend assumes that authentication has been
- * configured in apache, rather than within SabreDAV.
- *
- * Make sure apache is properly configured for this to work.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Apache implements BackendInterface {
-
- /**
- * Current apache user
- *
- * @var string
- */
- protected $remoteUser;
-
- /**
- * Authenticates the user based on the current request.
- *
- * If authentication is successful, true must be returned.
- * If authentication fails, an exception must be thrown.
- *
- * @param DAV\Server $server
- * @param string $realm
- * @return bool
- */
- public function authenticate(DAV\Server $server, $realm) {
-
- $remoteUser = $server->httpRequest->getRawServerValue('REMOTE_USER');
- if (is_null($remoteUser)) {
- throw new DAV\Exception('We did not receive the $_SERVER[REMOTE_USER] property. This means that apache might have been misconfigured');
- }
-
- $this->remoteUser = $remoteUser;
- return true;
-
- }
-
- /**
- * Returns information about the currently logged in user.
- *
- * If nobody is currently logged in, this method should return null.
- *
- * @return array|null
- */
- public function getCurrentUser() {
-
- return $this->remoteUser;
-
- }
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/BackendInterface.php b/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/BackendInterface.php
deleted file mode 100644
index b8d04e2e1..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Auth/Backend/BackendInterface.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Auth\Backend;
-
-/**
- * This is the base class for any authentication object.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-interface BackendInterface {
-
- /**
- * Authenticates the user based on the current request.
- *
- * If authentication is successful, true must be returned.
- * If authentication fails, an exception must be thrown.
- *
- * @param \Sabre\DAV\Server $server
- * @param string $realm
- * @return bool
- */
- function authenticate(\Sabre\DAV\Server $server,$realm);
-
- /**
- * Returns information about the currently logged in username.
- *
- * If nobody is currently logged in, this method should return null.
- *
- * @return string|null
- */
- function getCurrentUser();
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Auth/Plugin.php b/vendor/sabre/dav/lib/Sabre/DAV/Auth/Plugin.php
deleted file mode 100644
index dbebc20f0..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Auth/Plugin.php
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Auth;
-use Sabre\DAV;
-
-/**
- * This plugin provides Authentication for a WebDAV server.
- *
- * It relies on a Backend object, which provides user information.
- *
- * Additionally, it provides support for:
- * * {DAV:}current-user-principal property from RFC5397
- * * {DAV:}principal-collection-set property from RFC3744
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Plugin extends DAV\ServerPlugin {
-
- /**
- * Reference to main server object
- *
- * @var Sabre\DAV\Server
- */
- protected $server;
-
- /**
- * Authentication backend
- *
- * @var Backend\BackendInterface
- */
- protected $authBackend;
-
- /**
- * The authentication realm.
- *
- * @var string
- */
- private $realm;
-
- /**
- * __construct
- *
- * @param Backend\BackendInterface $authBackend
- * @param string $realm
- */
- public function __construct(Backend\BackendInterface $authBackend, $realm) {
-
- $this->authBackend = $authBackend;
- $this->realm = $realm;
-
- }
-
- /**
- * Initializes the plugin. This function is automatically called by the server
- *
- * @param DAV\Server $server
- * @return void
- */
- public function initialize(DAV\Server $server) {
-
- $this->server = $server;
- $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),10);
-
- }
-
- /**
- * Returns a plugin name.
- *
- * Using this name other plugins will be able to access other plugins
- * using DAV\Server::getPlugin
- *
- * @return string
- */
- public function getPluginName() {
-
- return 'auth';
-
- }
-
- /**
- * Returns the current users' principal uri.
- *
- * If nobody is logged in, this will return null.
- *
- * @return string|null
- */
- public function getCurrentUser() {
-
- $userInfo = $this->authBackend->getCurrentUser();
- if (!$userInfo) return null;
-
- return $userInfo;
-
- }
-
- /**
- * This method is called before any HTTP method and forces users to be authenticated
- *
- * @param string $method
- * @param string $uri
- * @throws Sabre\DAV\Exception\NotAuthenticated
- * @return bool
- */
- public function beforeMethod($method, $uri) {
-
- $this->authBackend->authenticate($this->server,$this->realm);
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Browser/Plugin.php b/vendor/sabre/dav/lib/Sabre/DAV/Browser/Plugin.php
deleted file mode 100644
index 751c22965..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Browser/Plugin.php
+++ /dev/null
@@ -1,491 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Browser;
-
-use Sabre\DAV;
-
-/**
- * Browser Plugin
- *
- * This plugin provides a html representation, so that a WebDAV server may be accessed
- * using a browser.
- *
- * The class intercepts GET requests to collection resources and generates a simple
- * html index.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Plugin extends DAV\ServerPlugin {
-
- /**
- * List of default icons for nodes.
- *
- * This is an array with class / interface names as keys, and asset names
- * as values.
- *
- * The evaluation order is reversed. The last item in the list gets
- * precendence.
- *
- * @var array
- */
- public $iconMap = array(
- 'Sabre\\DAV\\IFile' => 'icons/file',
- 'Sabre\\DAV\\ICollection' => 'icons/collection',
- 'Sabre\\DAVACL\\IPrincipal' => 'icons/principal',
- 'Sabre\\CalDAV\\ICalendar' => 'icons/calendar',
- 'Sabre\\CardDAV\\IAddressBook' => 'icons/addressbook',
- 'Sabre\\CardDAV\\ICard' => 'icons/card',
- );
-
- /**
- * The file extension used for all icons
- *
- * @var string
- */
- public $iconExtension = '.png';
-
- /**
- * reference to server class
- *
- * @var Sabre\DAV\Server
- */
- protected $server;
-
- /**
- * enablePost turns on the 'actions' panel, which allows people to create
- * folders and upload files straight from a browser.
- *
- * @var bool
- */
- protected $enablePost = true;
-
- /**
- * By default the browser plugin will generate a favicon and other images.
- * To turn this off, set this property to false.
- *
- * @var bool
- */
- protected $enableAssets = true;
-
- /**
- * Creates the object.
- *
- * By default it will allow file creation and uploads.
- * Specify the first argument as false to disable this
- *
- * @param bool $enablePost
- * @param bool $enableAssets
- */
- public function __construct($enablePost=true, $enableAssets = true) {
-
- $this->enablePost = $enablePost;
- $this->enableAssets = $enableAssets;
-
- }
-
- /**
- * Initializes the plugin and subscribes to events
- *
- * @param DAV\Server $server
- * @return void
- */
- public function initialize(DAV\Server $server) {
-
- $this->server = $server;
- $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor'));
- $this->server->subscribeEvent('onHTMLActionsPanel', array($this, 'htmlActionsPanel'),200);
- if ($this->enablePost) $this->server->subscribeEvent('unknownMethod',array($this,'httpPOSTHandler'));
- }
-
- /**
- * This method intercepts GET requests to collections and returns the html
- *
- * @param string $method
- * @param string $uri
- * @return bool
- */
- public function httpGetInterceptor($method, $uri) {
-
- if ($method !== 'GET') return true;
-
- // We're not using straight-up $_GET, because we want everything to be
- // unit testable.
- $getVars = array();
- parse_str($this->server->httpRequest->getQueryString(), $getVars);
-
- if (isset($getVars['sabreAction']) && $getVars['sabreAction'] === 'asset' && isset($getVars['assetName'])) {
- $this->serveAsset($getVars['assetName']);
- return false;
- }
-
- try {
- $node = $this->server->tree->getNodeForPath($uri);
- } catch (DAV\Exception\NotFound $e) {
- // We're simply stopping when the file isn't found to not interfere
- // with other plugins.
- return;
- }
- if ($node instanceof DAV\IFile)
- return;
-
- $this->server->httpResponse->sendStatus(200);
- $this->server->httpResponse->setHeader('Content-Type','text/html; charset=utf-8');
-
- $this->server->httpResponse->sendBody(
- $this->generateDirectoryIndex($uri)
- );
-
- return false;
-
- }
-
- /**
- * Handles POST requests for tree operations.
- *
- * @param string $method
- * @param string $uri
- * @return bool
- */
- public function httpPOSTHandler($method, $uri) {
-
- if ($method!='POST') return;
- $contentType = $this->server->httpRequest->getHeader('Content-Type');
- list($contentType) = explode(';', $contentType);
- if ($contentType !== 'application/x-www-form-urlencoded' &&
- $contentType !== 'multipart/form-data') {
- return;
- }
- $postVars = $this->server->httpRequest->getPostVars();
-
- if (!isset($postVars['sabreAction']))
- return;
-
- if ($this->server->broadcastEvent('onBrowserPostAction', array($uri, $postVars['sabreAction'], $postVars))) {
-
- switch($postVars['sabreAction']) {
-
- case 'mkcol' :
- if (isset($postVars['name']) && trim($postVars['name'])) {
- // Using basename() because we won't allow slashes
- list(, $folderName) = DAV\URLUtil::splitPath(trim($postVars['name']));
- $this->server->createDirectory($uri . '/' . $folderName);
- }
- break;
- case 'put' :
- if ($_FILES) $file = current($_FILES);
- else break;
-
- list(, $newName) = DAV\URLUtil::splitPath(trim($file['name']));
- if (isset($postVars['name']) && trim($postVars['name']))
- $newName = trim($postVars['name']);
-
- // Making sure we only have a 'basename' component
- list(, $newName) = DAV\URLUtil::splitPath($newName);
-
- if (is_uploaded_file($file['tmp_name'])) {
- $this->server->createFile($uri . '/' . $newName, fopen($file['tmp_name'],'r'));
- }
- break;
-
- }
-
- }
- $this->server->httpResponse->setHeader('Location',$this->server->httpRequest->getUri());
- $this->server->httpResponse->sendStatus(302);
- return false;
-
- }
-
- /**
- * Escapes a string for html.
- *
- * @param string $value
- * @return string
- */
- public function escapeHTML($value) {
-
- return htmlspecialchars($value,ENT_QUOTES,'UTF-8');
-
- }
-
- /**
- * Generates the html directory index for a given url
- *
- * @param string $path
- * @return string
- */
- public function generateDirectoryIndex($path) {
-
- $version = '';
- if (DAV\Server::$exposeVersion) {
- $version = DAV\Version::VERSION ."-". DAV\Version::STABILITY;
- }
-
- $html = "<html>
-<head>
- <title>Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . $version . "</title>
- <style type=\"text/css\">
- body { Font-family: arial}
- h1 { font-size: 150% }
- </style>
- ";
-
- if ($this->enableAssets) {
- $html.='<link rel="shortcut icon" href="'.$this->getAssetUrl('favicon.ico').'" type="image/vnd.microsoft.icon" />';
- }
-
- $html .= "</head>
-<body>
- <h1>Index for " . $this->escapeHTML($path) . "/</h1>
- <table>
- <tr><th width=\"24\"></th><th>Name</th><th>Type</th><th>Size</th><th>Last modified</th></tr>
- <tr><td colspan=\"5\"><hr /></td></tr>";
-
- $files = $this->server->getPropertiesForPath($path,array(
- '{DAV:}displayname',
- '{DAV:}resourcetype',
- '{DAV:}getcontenttype',
- '{DAV:}getcontentlength',
- '{DAV:}getlastmodified',
- ),1);
-
- $parent = $this->server->tree->getNodeForPath($path);
-
-
- if ($path) {
-
- list($parentUri) = DAV\URLUtil::splitPath($path);
- $fullPath = DAV\URLUtil::encodePath($this->server->getBaseUri() . $parentUri);
-
- $icon = $this->enableAssets?'<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl('icons/parent' . $this->iconExtension) . '" width="24" alt="Parent" /></a>':'';
- $html.= "<tr>
- <td>$icon</td>
- <td><a href=\"{$fullPath}\">..</a></td>
- <td>[parent]</td>
- <td></td>
- <td></td>
- </tr>";
-
- }
-
- foreach($files as $file) {
-
- // This is the current directory, we can skip it
- if (rtrim($file['href'],'/')==$path) continue;
-
- list(, $name) = DAV\URLUtil::splitPath($file['href']);
-
- $type = null;
-
-
- if (isset($file[200]['{DAV:}resourcetype'])) {
- $type = $file[200]['{DAV:}resourcetype']->getValue();
-
- // resourcetype can have multiple values
- if (!is_array($type)) $type = array($type);
-
- foreach($type as $k=>$v) {
-
- // Some name mapping is preferred
- switch($v) {
- case '{DAV:}collection' :
- $type[$k] = 'Collection';
- break;
- case '{DAV:}principal' :
- $type[$k] = 'Principal';
- break;
- case '{urn:ietf:params:xml:ns:carddav}addressbook' :
- $type[$k] = 'Addressbook';
- break;
- case '{urn:ietf:params:xml:ns:caldav}calendar' :
- $type[$k] = 'Calendar';
- break;
- case '{urn:ietf:params:xml:ns:caldav}schedule-inbox' :
- $type[$k] = 'Schedule Inbox';
- break;
- case '{urn:ietf:params:xml:ns:caldav}schedule-outbox' :
- $type[$k] = 'Schedule Outbox';
- break;
- case '{http://calendarserver.org/ns/}calendar-proxy-read' :
- $type[$k] = 'Proxy-Read';
- break;
- case '{http://calendarserver.org/ns/}calendar-proxy-write' :
- $type[$k] = 'Proxy-Write';
- break;
- }
-
- }
- $type = implode(', ', $type);
- }
-
- // If no resourcetype was found, we attempt to use
- // the contenttype property
- if (!$type && isset($file[200]['{DAV:}getcontenttype'])) {
- $type = $file[200]['{DAV:}getcontenttype'];
- }
- if (!$type) $type = 'Unknown';
-
- $size = isset($file[200]['{DAV:}getcontentlength'])?(int)$file[200]['{DAV:}getcontentlength']:'';
- $lastmodified = isset($file[200]['{DAV:}getlastmodified'])?$file[200]['{DAV:}getlastmodified']->getTime()->format(\DateTime::ATOM):'';
-
- $fullPath = DAV\URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path?$path . '/':'') . $name,'/'));
-
- $displayName = isset($file[200]['{DAV:}displayname'])?$file[200]['{DAV:}displayname']:$name;
-
- $displayName = $this->escapeHTML($displayName);
- $type = $this->escapeHTML($type);
-
- $icon = '';
-
- if ($this->enableAssets) {
- $node = $this->server->tree->getNodeForPath(($path?$path.'/':'') . $name);
- foreach(array_reverse($this->iconMap) as $class=>$iconName) {
-
- if ($node instanceof $class) {
- $icon = '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl($iconName . $this->iconExtension) . '" alt="" width="24" /></a>';
- break;
- }
-
-
- }
-
- }
-
- $html.= "<tr>
- <td>$icon</td>
- <td><a href=\"{$fullPath}\">{$displayName}</a></td>
- <td>{$type}</td>
- <td>{$size}</td>
- <td>{$lastmodified}</td>
- </tr>";
-
- }
-
- $html.= "<tr><td colspan=\"5\"><hr /></td></tr>";
-
- $output = '';
-
- if ($this->enablePost) {
- $this->server->broadcastEvent('onHTMLActionsPanel',array($parent, &$output));
- }
-
- $html.=$output;
-
- $html.= "</table>
- <address>Generated by SabreDAV " . $version . " (c)2007-2014 <a href=\"http://sabre.io/\">http://sabre.io/</a></address>
- </body>
- </html>";
-
- return $html;
-
- }
-
- /**
- * This method is used to generate the 'actions panel' output for
- * collections.
- *
- * This specifically generates the interfaces for creating new files, and
- * creating new directories.
- *
- * @param DAV\INode $node
- * @param mixed $output
- * @return void
- */
- public function htmlActionsPanel(DAV\INode $node, &$output) {
-
- if (!$node instanceof DAV\ICollection)
- return;
-
- // We also know fairly certain that if an object is a non-extended
- // SimpleCollection, we won't need to show the panel either.
- if (get_class($node)==='Sabre\\DAV\\SimpleCollection')
- return;
-
- $output.= '<tr><td colspan="2"><form method="post" action="">
- <h3>Create new folder</h3>
- <input type="hidden" name="sabreAction" value="mkcol" />
- Name: <input type="text" name="name" /><br />
- <input type="submit" value="create" />
- </form>
- <form method="post" action="" enctype="multipart/form-data">
- <h3>Upload file</h3>
- <input type="hidden" name="sabreAction" value="put" />
- Name (optional): <input type="text" name="name" /><br />
- File: <input type="file" name="file" /><br />
- <input type="submit" value="upload" />
- </form>
- </td></tr>';
-
- }
-
- /**
- * This method takes a path/name of an asset and turns it into url
- * suiteable for http access.
- *
- * @param string $assetName
- * @return string
- */
- protected function getAssetUrl($assetName) {
-
- return $this->server->getBaseUri() . '?sabreAction=asset&assetName=' . urlencode($assetName);
-
- }
-
- /**
- * This method returns a local pathname to an asset.
- *
- * @param string $assetName
- * @return string
- */
- protected function getLocalAssetPath($assetName) {
-
- $assetDir = __DIR__ . '/assets/';
- $path = $assetDir . $assetName;
-
- // Making sure people aren't trying to escape from the base path.
- if (strpos(realpath($path), realpath($assetDir)) === 0) {
- return $path;
- }
- throw new DAV\Exception\Forbidden('Path does not exist, or escaping from the base path was detected');
- }
-
- /**
- * This method reads an asset from disk and generates a full http response.
- *
- * @param string $assetName
- * @return void
- */
- protected function serveAsset($assetName) {
-
- $assetPath = $this->getLocalAssetPath($assetName);
- if (!file_exists($assetPath)) {
- throw new DAV\Exception\NotFound('Could not find an asset with this name');
- }
- // Rudimentary mime type detection
- switch(strtolower(substr($assetPath,strpos($assetPath,'.')+1))) {
-
- case 'ico' :
- $mime = 'image/vnd.microsoft.icon';
- break;
-
- case 'png' :
- $mime = 'image/png';
- break;
-
- default:
- $mime = 'application/octet-stream';
- break;
-
- }
-
- $this->server->httpResponse->setHeader('Content-Type', $mime);
- $this->server->httpResponse->setHeader('Content-Length', filesize($assetPath));
- $this->server->httpResponse->setHeader('Cache-Control', 'public, max-age=1209600');
- $this->server->httpResponse->sendStatus(200);
- $this->server->httpResponse->sendBody(fopen($assetPath,'r'));
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/addressbook.png b/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/addressbook.png
deleted file mode 100644
index c9acc8417..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/addressbook.png
+++ /dev/null
Binary files differ
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/calendar.png b/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/calendar.png
deleted file mode 100644
index 3ecd6a800..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/calendar.png
+++ /dev/null
Binary files differ
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/card.png b/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/card.png
deleted file mode 100644
index 2ce954866..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/card.png
+++ /dev/null
Binary files differ
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/collection.png b/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/collection.png
deleted file mode 100644
index 156fa64fd..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/collection.png
+++ /dev/null
Binary files differ
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/file.png b/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/file.png
deleted file mode 100644
index 3b98551ce..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/file.png
+++ /dev/null
Binary files differ
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/parent.png b/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/parent.png
deleted file mode 100644
index 156fa64fd..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/parent.png
+++ /dev/null
Binary files differ
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/principal.png b/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/principal.png
deleted file mode 100644
index f8988f828..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Browser/assets/icons/principal.png
+++ /dev/null
Binary files differ
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Client.php b/vendor/sabre/dav/lib/Sabre/DAV/Client.php
deleted file mode 100644
index 705b32195..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Client.php
+++ /dev/null
@@ -1,575 +0,0 @@
-<?php
-
-namespace Sabre\DAV;
-
-/**
- * SabreDAV DAV client
- *
- * This client wraps around Curl to provide a convenient API to a WebDAV
- * server.
- *
- * NOTE: This class is experimental, it's api will likely change in the future.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Client {
-
- /**
- * The propertyMap is a key-value array.
- *
- * If you use the propertyMap, any {DAV:}multistatus responses with the
- * proeprties listed in this array, will automatically be mapped to a
- * respective class.
- *
- * The {DAV:}resourcetype property is automatically added. This maps to
- * Sabre\DAV\Property\ResourceType
- *
- * @var array
- */
- public $propertyMap = array();
-
- protected $baseUri;
- protected $userName;
- protected $password;
- protected $proxy;
- protected $trustedCertificates;
-
- /**
- * Basic authentication
- */
- const AUTH_BASIC = 1;
-
- /**
- * Digest authentication
- */
- const AUTH_DIGEST = 2;
-
- /**
- * The authentication type we're using.
- *
- * This is a bitmask of AUTH_BASIC and AUTH_DIGEST.
- *
- * If DIGEST is used, the client makes 1 extra request per request, to get
- * the authentication tokens.
- *
- * @var int
- */
- protected $authType;
-
- /**
- * Indicates if SSL verification is enabled or not.
- *
- * @var boolean
- */
- protected $verifyPeer;
-
- /**
- * Constructor
- *
- * Settings are provided through the 'settings' argument. The following
- * settings are supported:
- *
- * * baseUri
- * * userName (optional)
- * * password (optional)
- * * proxy (optional)
- *
- * @param array $settings
- */
- public function __construct(array $settings) {
-
- if (!isset($settings['baseUri'])) {
- throw new \InvalidArgumentException('A baseUri must be provided');
- }
-
- $validSettings = array(
- 'baseUri',
- 'userName',
- 'password',
- 'proxy',
- );
-
- foreach($validSettings as $validSetting) {
- if (isset($settings[$validSetting])) {
- $this->$validSetting = $settings[$validSetting];
- }
- }
-
- if (isset($settings['authType'])) {
- $this->authType = $settings['authType'];
- } else {
- $this->authType = self::AUTH_BASIC | self::AUTH_DIGEST;
- }
-
- $this->propertyMap['{DAV:}resourcetype'] = 'Sabre\\DAV\\Property\\ResourceType';
-
- }
-
- /**
- * Add trusted root certificates to the webdav client.
- *
- * The parameter certificates should be a absolute path to a file
- * which contains all trusted certificates
- *
- * @param string $certificates
- */
- public function addTrustedCertificates($certificates) {
- $this->trustedCertificates = $certificates;
- }
-
- /**
- * Enables/disables SSL peer verification
- *
- * @param boolean $value
- */
- public function setVerifyPeer($value) {
- $this->verifyPeer = $value;
- }
-
- /**
- * Does a PROPFIND request
- *
- * The list of requested properties must be specified as an array, in clark
- * notation.
- *
- * The returned array will contain a list of filenames as keys, and
- * properties as values.
- *
- * The properties array will contain the list of properties. Only properties
- * that are actually returned from the server (without error) will be
- * returned, anything else is discarded.
- *
- * Depth should be either 0 or 1. A depth of 1 will cause a request to be
- * made to the server to also return all child resources.
- *
- * @param string $url
- * @param array $properties
- * @param int $depth
- * @return array
- */
- public function propFind($url, array $properties, $depth = 0) {
-
- $body = '<?xml version="1.0"?>' . "\n";
- $body.= '<d:propfind xmlns:d="DAV:">' . "\n";
- $body.= ' <d:prop>' . "\n";
-
- foreach($properties as $property) {
-
- list(
- $namespace,
- $elementName
- ) = XMLUtil::parseClarkNotation($property);
-
- if ($namespace === 'DAV:') {
- $body.=' <d:' . $elementName . ' />' . "\n";
- } else {
- $body.=" <x:" . $elementName . " xmlns:x=\"" . $namespace . "\"/>\n";
- }
-
- }
-
- $body.= ' </d:prop>' . "\n";
- $body.= '</d:propfind>';
-
- $response = $this->request('PROPFIND', $url, $body, array(
- 'Depth' => $depth,
- 'Content-Type' => 'application/xml'
- ));
-
- $result = $this->parseMultiStatus($response['body']);
-
- // If depth was 0, we only return the top item
- if ($depth===0) {
- reset($result);
- $result = current($result);
- return isset($result[200])?$result[200]:array();
- }
-
- $newResult = array();
- foreach($result as $href => $statusList) {
-
- $newResult[$href] = isset($statusList[200])?$statusList[200]:array();
-
- }
-
- return $newResult;
-
- }
-
- /**
- * Updates a list of properties on the server
- *
- * The list of properties must have clark-notation properties for the keys,
- * and the actual (string) value for the value. If the value is null, an
- * attempt is made to delete the property.
- *
- * @todo Must be building the request using the DOM, and does not yet
- * support complex properties.
- * @param string $url
- * @param array $properties
- * @return void
- */
- public function propPatch($url, array $properties) {
-
- $body = '<?xml version="1.0"?>' . "\n";
- $body.= '<d:propertyupdate xmlns:d="DAV:">' . "\n";
-
- foreach($properties as $propName => $propValue) {
-
- list(
- $namespace,
- $elementName
- ) = XMLUtil::parseClarkNotation($propName);
-
- if ($propValue === null) {
-
- $body.="<d:remove><d:prop>\n";
-
- if ($namespace === 'DAV:') {
- $body.=' <d:' . $elementName . ' />' . "\n";
- } else {
- $body.=" <x:" . $elementName . " xmlns:x=\"" . $namespace . "\"/>\n";
- }
-
- $body.="</d:prop></d:remove>\n";
-
- } else {
-
- $body.="<d:set><d:prop>\n";
- if ($namespace === 'DAV:') {
- $body.=' <d:' . $elementName . '>';
- } else {
- $body.=" <x:" . $elementName . " xmlns:x=\"" . $namespace . "\">";
- }
- // Shitty.. i know
- $body.=htmlspecialchars($propValue, ENT_NOQUOTES, 'UTF-8');
- if ($namespace === 'DAV:') {
- $body.='</d:' . $elementName . '>' . "\n";
- } else {
- $body.="</x:" . $elementName . ">\n";
- }
- $body.="</d:prop></d:set>\n";
-
- }
-
- }
-
- $body.= '</d:propertyupdate>';
-
- $this->request('PROPPATCH', $url, $body, array(
- 'Content-Type' => 'application/xml'
- ));
-
- }
-
- /**
- * Performs an HTTP options request
- *
- * This method returns all the features from the 'DAV:' header as an array.
- * If there was no DAV header, or no contents this method will return an
- * empty array.
- *
- * @return array
- */
- public function options() {
-
- $result = $this->request('OPTIONS');
- if (!isset($result['headers']['dav'])) {
- return array();
- }
-
- $features = explode(',', $result['headers']['dav']);
- foreach($features as &$v) {
- $v = trim($v);
- }
- return $features;
-
- }
-
- /**
- * Performs an actual HTTP request, and returns the result.
- *
- * If the specified url is relative, it will be expanded based on the base
- * url.
- *
- * The returned array contains 3 keys:
- * * body - the response body
- * * httpCode - a HTTP code (200, 404, etc)
- * * headers - a list of response http headers. The header names have
- * been lowercased.
- *
- * @param string $method
- * @param string $url
- * @param string $body
- * @param array $headers
- * @return array
- */
- public function request($method, $url = '', $body = null, $headers = array()) {
-
- $url = $this->getAbsoluteUrl($url);
-
- $curlSettings = array(
- CURLOPT_RETURNTRANSFER => true,
- // Return headers as part of the response
- CURLOPT_HEADER => true,
-
- // 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.
- CURLOPT_POSTFIELDS => (string)$body,
- // Automatically follow redirects
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_MAXREDIRS => 5,
- );
-
- if($this->verifyPeer !== null) {
- $curlSettings[CURLOPT_SSL_VERIFYPEER] = $this->verifyPeer;
- }
-
- if($this->trustedCertificates) {
- $curlSettings[CURLOPT_CAINFO] = $this->trustedCertificates;
- }
-
- switch ($method) {
- case 'HEAD' :
-
- // do not read body with HEAD requests (this is necessary because cURL does not ignore the body with HEAD
- // requests when the Content-Length header is given - which in turn is perfectly valid according to HTTP
- // specs...) cURL does unfortunately return an error in this case ("transfer closed transfer closed with
- // ... bytes remaining to read") this can be circumvented by explicitly telling cURL to ignore the
- // response body
- $curlSettings[CURLOPT_NOBODY] = true;
- $curlSettings[CURLOPT_CUSTOMREQUEST] = 'HEAD';
- break;
-
- default:
- $curlSettings[CURLOPT_CUSTOMREQUEST] = $method;
- break;
-
- }
-
- // Adding HTTP headers
- $nHeaders = array();
- foreach($headers as $key=>$value) {
-
- $nHeaders[] = $key . ': ' . $value;
-
- }
- $curlSettings[CURLOPT_HTTPHEADER] = $nHeaders;
-
- if ($this->proxy) {
- $curlSettings[CURLOPT_PROXY] = $this->proxy;
- }
-
- if ($this->userName && $this->authType) {
- $curlType = 0;
- if ($this->authType & self::AUTH_BASIC) {
- $curlType |= CURLAUTH_BASIC;
- }
- if ($this->authType & self::AUTH_DIGEST) {
- $curlType |= CURLAUTH_DIGEST;
- }
- $curlSettings[CURLOPT_HTTPAUTH] = $curlType;
- $curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password;
- }
-
- list(
- $response,
- $curlInfo,
- $curlErrNo,
- $curlError
- ) = $this->curlRequest($url, $curlSettings);
-
- $headerBlob = substr($response, 0, $curlInfo['header_size']);
- $response = substr($response, $curlInfo['header_size']);
-
- // In the case of 100 Continue, or redirects we'll have multiple lists
- // of headers for each separate HTTP response. We can easily split this
- // because they are separated by \r\n\r\n
- $headerBlob = explode("\r\n\r\n", trim($headerBlob, "\r\n"));
-
- // We only care about the last set of headers
- $headerBlob = $headerBlob[count($headerBlob)-1];
-
- // Splitting headers
- $headerBlob = explode("\r\n", $headerBlob);
-
- $headers = array();
- foreach($headerBlob as $header) {
- $parts = explode(':', $header, 2);
- if (count($parts)==2) {
- $headers[strtolower(trim($parts[0]))] = trim($parts[1]);
- }
- }
-
- $response = array(
- 'body' => $response,
- 'statusCode' => $curlInfo['http_code'],
- 'headers' => $headers
- );
-
- if ($curlErrNo) {
- throw new Exception('[CURL] Error while making request: ' . $curlError . ' (error code: ' . $curlErrNo . ')');
- }
-
- if ($response['statusCode']>=400) {
- switch ($response['statusCode']) {
- case 400 :
- throw new Exception\BadRequest('Bad request');
- case 401 :
- throw new Exception\NotAuthenticated('Not authenticated');
- case 402 :
- throw new Exception\PaymentRequired('Payment required');
- case 403 :
- throw new Exception\Forbidden('Forbidden');
- case 404:
- throw new Exception\NotFound('Resource not found.');
- case 405 :
- throw new Exception\MethodNotAllowed('Method not allowed');
- case 409 :
- throw new Exception\Conflict('Conflict');
- case 412 :
- throw new Exception\PreconditionFailed('Precondition failed');
- case 416 :
- throw new Exception\RequestedRangeNotSatisfiable('Requested Range Not Satisfiable');
- case 500 :
- throw new Exception('Internal server error');
- case 501 :
- throw new Exception\NotImplemented('Not Implemented');
- case 507 :
- throw new Exception\InsufficientStorage('Insufficient storage');
- default:
- throw new Exception('HTTP error response. (errorcode ' . $response['statusCode'] . ')');
- }
- }
-
- return $response;
-
- }
-
- /**
- * Wrapper for all curl functions.
- *
- * The only reason this was split out in a separate method, is so it
- * becomes easier to unittest.
- *
- * @param string $url
- * @param array $settings
- * @return array
- */
- // @codeCoverageIgnoreStart
- protected function curlRequest($url, $settings) {
-
- $curl = curl_init($url);
- curl_setopt_array($curl, $settings);
-
- return array(
- curl_exec($curl),
- curl_getinfo($curl),
- curl_errno($curl),
- curl_error($curl)
- );
-
- }
- // @codeCoverageIgnoreEnd
-
- /**
- * Returns the full url based on the given url (which may be relative). All
- * urls are expanded based on the base url as given by the server.
- *
- * @param string $url
- * @return string
- */
- protected function getAbsoluteUrl($url) {
-
- // If the url starts with http:// or https://, the url is already absolute.
- if (preg_match('/^http(s?):\/\//', $url)) {
- return $url;
- }
-
- // If the url starts with a slash, we must calculate the url based off
- // the root of the base url.
- if (strpos($url,'/') === 0) {
- $parts = parse_url($this->baseUri);
- return $parts['scheme'] . '://' . $parts['host'] . (isset($parts['port'])?':' . $parts['port']:'') . $url;
- }
-
- // Otherwise...
- return $this->baseUri . $url;
-
- }
-
- /**
- * Parses a WebDAV multistatus response body
- *
- * This method returns an array with the following structure
- *
- * array(
- * 'url/to/resource' => array(
- * '200' => array(
- * '{DAV:}property1' => 'value1',
- * '{DAV:}property2' => 'value2',
- * ),
- * '404' => array(
- * '{DAV:}property1' => null,
- * '{DAV:}property2' => null,
- * ),
- * )
- * 'url/to/resource2' => array(
- * .. etc ..
- * )
- * )
- *
- *
- * @param string $body xml body
- * @return array
- */
- public function parseMultiStatus($body) {
-
- $body = XMLUtil::convertDAVNamespace($body);
-
- // Fixes an XXE vulnerability on PHP versions older than 5.3.23 or
- // 5.4.13.
- $previous = libxml_disable_entity_loader(true);
- $responseXML = simplexml_load_string($body, null, LIBXML_NOBLANKS | LIBXML_NOCDATA);
- libxml_disable_entity_loader($previous);
-
- if ($responseXML===false) {
- throw new \InvalidArgumentException('The passed data is not valid XML');
- }
-
- $responseXML->registerXPathNamespace('d', 'urn:DAV');
-
- $propResult = array();
-
- foreach($responseXML->xpath('d:response') as $response) {
- $response->registerXPathNamespace('d', 'urn:DAV');
- $href = $response->xpath('d:href');
- $href = (string)$href[0];
-
- $properties = array();
-
- foreach($response->xpath('d:propstat') as $propStat) {
-
- $propStat->registerXPathNamespace('d', 'urn:DAV');
- $status = $propStat->xpath('d:status');
- list($httpVersion, $statusCode, $message) = explode(' ', (string)$status[0],3);
-
- // Only using the propertymap for results with status 200.
- $propertyMap = $statusCode==='200' ? $this->propertyMap : array();
-
- $properties[$statusCode] = XMLUtil::parseProperties(dom_import_simplexml($propStat), $propertyMap);
-
- }
-
- $propResult[$href] = $properties;
-
- }
-
- return $propResult;
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Exception/FileNotFound.php b/vendor/sabre/dav/lib/Sabre/DAV/Exception/FileNotFound.php
deleted file mode 100644
index aa4844cb9..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Exception/FileNotFound.php
+++ /dev/null
@@ -1,19 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Exception;
-
-/**
- * FileNotFound
- *
- * Deprecated: Warning, this class is deprecated and will be removed in a
- * future version of SabreDAV. Please use Sabre\DAV\Exception\NotFound instead.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @deprecated Use Sabre\DAV\Exception\NotFound instead
- * @license http://sabre.io/license/ Modified BSD License
- */
-class FileNotFound extends NotFound {
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/FSExt/Directory.php b/vendor/sabre/dav/lib/Sabre/DAV/FSExt/Directory.php
deleted file mode 100644
index da3d2cc69..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/FSExt/Directory.php
+++ /dev/null
@@ -1,159 +0,0 @@
-<?php
-
-namespace Sabre\DAV\FSExt;
-
-use Sabre\DAV;
-
-/**
- * Directory class
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Directory extends Node implements DAV\ICollection, DAV\IQuota {
-
- /**
- * Creates a new file in the directory
- *
- * Data will either be supplied as a stream resource, or in certain cases
- * as a string. Keep in mind that you may have to support either.
- *
- * After successful creation of the file, you may choose to return the ETag
- * of the new file here.
- *
- * The returned ETag must be surrounded by double-quotes (The quotes should
- * be part of the actual string).
- *
- * If you cannot accurately determine the ETag, you should not return it.
- * If you don't store the file exactly as-is (you're transforming it
- * somehow) you should also not return an ETag.
- *
- * This means that if a subsequent GET to this new file does not exactly
- * return the same contents of what was submitted here, you are strongly
- * recommended to omit the ETag.
- *
- * @param string $name Name of the file
- * @param resource|string $data Initial payload
- * @return null|string
- */
- public function createFile($name, $data = null) {
-
- // We're not allowing dots
- if ($name=='.' || $name=='..') throw new DAV\Exception\Forbidden('Permission denied to . and ..');
- $newPath = $this->path . '/' . $name;
- file_put_contents($newPath,$data);
-
- return '"' . md5_file($newPath) . '"';
-
- }
-
- /**
- * Creates a new subdirectory
- *
- * @param string $name
- * @return void
- */
- 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;
- mkdir($newPath);
-
- }
-
- /**
- * Returns a specific child node, referenced by its name
- *
- * This method must throw Sabre\DAV\Exception\NotFound if the node does not
- * exist.
- *
- * @param string $name
- * @throws DAV\Exception\NotFound
- * @return DAV\INode
- */
- 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 Directory($path);
-
- } else {
-
- return new File($path);
-
- }
-
- }
-
- /**
- * Checks if a child exists.
- *
- * @param string $name
- * @return bool
- */
- public function childExists($name) {
-
- if ($name=='.' || $name=='..')
- throw new DAV\Exception\Forbidden('Permission denied to . and ..');
-
- $path = $this->path . '/' . $name;
- return file_exists($path);
-
- }
-
- /**
- * Returns an array with all the child nodes
- *
- * @return DAV\INode[]
- */
- public function getChildren() {
-
- $nodes = array();
- foreach(scandir($this->path) as $node) if($node!='.' && $node!='..' && $node!='.sabredav') $nodes[] = $this->getChild($node);
- return $nodes;
-
- }
-
- /**
- * Deletes all files in this directory, and then itself
- *
- * @return bool
- */
- public function delete() {
-
- // Deleting all children
- foreach($this->getChildren() as $child) $child->delete();
-
- // Removing resource info, if its still around
- if (file_exists($this->path . '/.sabredav')) unlink($this->path . '/.sabredav');
-
- // Removing the directory itself
- rmdir($this->path);
-
- return parent::delete();
-
- }
-
- /**
- * Returns available diskspace information
- *
- * @return array
- */
- public function getQuotaInfo() {
-
- return array(
- disk_total_space($this->path)-disk_free_space($this->path),
- disk_free_space($this->path)
- );
-
- }
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/FSExt/Node.php b/vendor/sabre/dav/lib/Sabre/DAV/FSExt/Node.php
deleted file mode 100644
index 0e11582f3..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/FSExt/Node.php
+++ /dev/null
@@ -1,214 +0,0 @@
-<?php
-
-namespace Sabre\DAV\FSExt;
-
-use Sabre\DAV;
-
-/**
- * Base node-class
- *
- * The node class implements the method used by both the File and the Directory classes
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-abstract class Node extends DAV\FS\Node implements DAV\IProperties {
-
- /**
- * Updates properties on this node,
- *
- * @param array $properties
- * @see Sabre\DAV\IProperties::updateProperties
- * @return bool|array
- */
- public function updateProperties($properties) {
-
- $resourceData = $this->getResourceData();
-
- foreach($properties as $propertyName=>$propertyValue) {
-
- // If it was null, we need to delete the property
- if (is_null($propertyValue)) {
- if (isset($resourceData['properties'][$propertyName])) {
- unset($resourceData['properties'][$propertyName]);
- }
- } else {
- $resourceData['properties'][$propertyName] = $propertyValue;
- }
-
- }
-
- $this->putResourceData($resourceData);
- return true;
- }
-
- /**
- * Returns a list of properties for this nodes.;
- *
- * The properties list is a list of propertynames the client requested, encoded as xmlnamespace#tagName, for example: http://www.example.org/namespace#author
- * If the array is empty, all properties should be returned
- *
- * @param array $properties
- * @return array
- */
- function getProperties($properties) {
-
- $resourceData = $this->getResourceData();
-
- // if the array was empty, we need to return everything
- if (!$properties) return $resourceData['properties'];
-
- $props = array();
- foreach($properties as $property) {
- if (isset($resourceData['properties'][$property])) $props[$property] = $resourceData['properties'][$property];
- }
-
- return $props;
-
- }
-
- /**
- * Returns the path to the resource file
- *
- * @return string
- */
- protected function getResourceInfoPath() {
-
- list($parentDir) = DAV\URLUtil::splitPath($this->path);
- return $parentDir . '/.sabredav';
-
- }
-
- /**
- * Returns all the stored resource information
- *
- * @return array
- */
- protected function getResourceData() {
-
- $path = $this->getResourceInfoPath();
- if (!file_exists($path)) return array('properties' => array());
-
- // opening up the file, and creating a shared lock
- $handle = fopen($path,'r');
- flock($handle,LOCK_SH);
- $data = '';
-
- // Reading data until the eof
- while(!feof($handle)) {
- $data.=fread($handle,8192);
- }
-
- // We're all good
- fclose($handle);
-
- // Unserializing and checking if the resource file contains data for this file
- $data = unserialize($data);
- if (!isset($data[$this->getName()])) {
- return array('properties' => array());
- }
-
- $data = $data[$this->getName()];
- if (!isset($data['properties'])) $data['properties'] = array();
- return $data;
-
- }
-
- /**
- * Updates the resource information
- *
- * @param array $newData
- * @return void
- */
- protected function putResourceData(array $newData) {
-
- $path = $this->getResourceInfoPath();
-
- // opening up the file, and creating a shared lock
- $handle = fopen($path,'a+');
- flock($handle,LOCK_EX);
- $data = '';
-
- rewind($handle);
-
- // Reading data until the eof
- while(!feof($handle)) {
- $data.=fread($handle,8192);
- }
-
- // Unserializing and checking if the resource file contains data for this file
- $data = unserialize($data);
- $data[$this->getName()] = $newData;
- ftruncate($handle,0);
- rewind($handle);
-
- fwrite($handle,serialize($data));
- fclose($handle);
-
- }
-
- /**
- * Renames the node
- *
- * @param string $name The new name
- * @return void
- */
- public function setName($name) {
-
- list($parentPath, ) = DAV\URLUtil::splitPath($this->path);
- list(, $newName) = DAV\URLUtil::splitPath($name);
- $newPath = $parentPath . '/' . $newName;
-
- // We're deleting the existing resourcedata, and recreating it
- // for the new path.
- $resourceData = $this->getResourceData();
- $this->deleteResourceData();
-
- rename($this->path,$newPath);
- $this->path = $newPath;
- $this->putResourceData($resourceData);
-
-
- }
-
- /**
- * @return bool
- */
- public function deleteResourceData() {
-
- // When we're deleting this node, we also need to delete any resource information
- $path = $this->getResourceInfoPath();
- if (!file_exists($path)) return true;
-
- // opening up the file, and creating a shared lock
- $handle = fopen($path,'a+');
- flock($handle,LOCK_EX);
- $data = '';
-
- rewind($handle);
-
- // Reading data until the eof
- while(!feof($handle)) {
- $data.=fread($handle,8192);
- }
-
- // Unserializing and checking if the resource file contains data for this file
- $data = unserialize($data);
- if (isset($data[$this->getName()])) unset($data[$this->getName()]);
- ftruncate($handle,0);
- rewind($handle);
- fwrite($handle,serialize($data));
- fclose($handle);
-
- return true;
- }
-
- public function delete() {
-
- return $this->deleteResourceData();
-
- }
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/IExtendedCollection.php b/vendor/sabre/dav/lib/Sabre/DAV/IExtendedCollection.php
deleted file mode 100644
index 8d3748467..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/IExtendedCollection.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-namespace Sabre\DAV;
-
-/**
- * The IExtendedCollection interface.
- *
- * This interface can be used to create special-type of collection-resources
- * as defined by RFC 5689.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-interface IExtendedCollection extends ICollection {
-
- /**
- * Creates a new collection
- *
- * @param string $name
- * @param array $resourceType
- * @param array $properties
- * @return void
- */
- function createExtendedCollection($name, array $resourceType, array $properties);
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/IProperties.php b/vendor/sabre/dav/lib/Sabre/DAV/IProperties.php
deleted file mode 100644
index f3601575b..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/IProperties.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-namespace Sabre\DAV;
-
-/**
- * IProperties interface
- *
- * Implement this interface to support custom WebDAV properties requested and sent from clients.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-interface IProperties extends INode {
-
- /**
- * Updates properties on this node,
- *
- * The properties array uses the propertyName in clark-notation as key,
- * and the array value for the property value. In the case a property
- * should be deleted, the property value will be null.
- *
- * This method must be atomic. If one property cannot be changed, the
- * entire operation must fail.
- *
- * If the operation was successful, true can be returned.
- * If the operation failed, false can be returned.
- *
- * Deletion of a non-existent property is always successful.
- *
- * Lastly, it is optional to return detailed information about any
- * failures. In this case an array should be returned with the following
- * structure:
- *
- * array(
- * 403 => array(
- * '{DAV:}displayname' => null,
- * ),
- * 424 => array(
- * '{DAV:}owner' => null,
- * )
- * )
- *
- * In this example it was forbidden to update {DAV:}displayname.
- * (403 Forbidden), which in turn also caused {DAV:}owner to fail
- * (424 Failed Dependency) because the request needs to be atomic.
- *
- * @param array $mutations
- * @return bool|array
- */
- function updateProperties($mutations);
-
- /**
- * Returns a list of properties for this nodes.
- *
- * The properties list is a list of propertynames the client requested,
- * encoded in clark-notation {xmlnamespace}tagname
- *
- * If the array is empty, it means 'all properties' were requested.
- *
- * Note that it's fine to liberally give properties back, instead of
- * conforming to the list of requested properties.
- * The Server class will filter out the extra.
- *
- * @param array $properties
- * @return void
- */
- function getProperties($properties);
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/FS.php b/vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/FS.php
deleted file mode 100644
index a9b0aaaaf..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Locks/Backend/FS.php
+++ /dev/null
@@ -1,193 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Locks\Backend;
-
-use Sabre\DAV\Locks\LockInfo;
-
-/**
- * This Lock Backend stores all its data in the filesystem in separate file per
- * node.
- *
- * This Lock Manager is now deprecated. It has a bug that allows parent
- * collections to be deletes when children deeper in the tree are locked.
- *
- * This also means that using this backend means you will not pass the Neon
- * Litmus test.
- *
- * You are recommended to use either the PDO or the File backend instead.
- *
- * @deprecated
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class FS extends AbstractBackend {
-
- /**
- * The default data directory
- *
- * @var string
- */
- private $dataDir;
-
- public function __construct($dataDir) {
-
- $this->dataDir = $dataDir;
-
- }
-
- protected function getFileNameForUri($uri) {
-
- return $this->dataDir . '/sabredav_' . md5($uri) . '.locks';
-
- }
-
-
- /**
- * Returns a list of Sabre\DAV\Locks\LockInfo objects
- *
- * This method should return all the locks for a particular uri, including
- * locks that might be set on a parent uri.
- *
- * If returnChildLocks is set to true, this method should also look for
- * any locks in the subtree of the uri for locks.
- *
- * @param string $uri
- * @param bool $returnChildLocks
- * @return array
- */
- public function getLocks($uri, $returnChildLocks) {
-
- $lockList = array();
- $currentPath = '';
-
- foreach(explode('/',$uri) as $uriPart) {
-
- // weird algorithm that can probably be improved, but we're traversing the path top down
- if ($currentPath) $currentPath.='/';
- $currentPath.=$uriPart;
-
- $uriLocks = $this->getData($currentPath);
-
- foreach($uriLocks as $uriLock) {
-
- // Unless we're on the leaf of the uri-tree we should ignore locks with depth 0
- if($uri==$currentPath || $uriLock->depth!=0) {
- $uriLock->uri = $currentPath;
- $lockList[] = $uriLock;
- }
-
- }
-
- }
-
- // Checking if we can remove any of these locks
- foreach($lockList as $k=>$lock) {
- if (time() > $lock->timeout + $lock->created) unset($lockList[$k]);
- }
- return $lockList;
-
- }
-
- /**
- * Locks a uri
- *
- * @param string $uri
- * @param LockInfo $lockInfo
- * @return bool
- */
- public function lock($uri, LockInfo $lockInfo) {
-
- // We're making the lock timeout 30 minutes
- $lockInfo->timeout = 1800;
- $lockInfo->created = time();
-
- $locks = $this->getLocks($uri,false);
- foreach($locks as $k=>$lock) {
- if ($lock->token == $lockInfo->token) unset($locks[$k]);
- }
- $locks[] = $lockInfo;
- $this->putData($uri,$locks);
- return true;
-
- }
-
- /**
- * Removes a lock from a uri
- *
- * @param string $uri
- * @param LockInfo $lockInfo
- * @return bool
- */
- public function unlock($uri, LockInfo $lockInfo) {
-
- $locks = $this->getLocks($uri,false);
- foreach($locks as $k=>$lock) {
-
- if ($lock->token == $lockInfo->token) {
-
- unset($locks[$k]);
- $this->putData($uri,$locks);
- return true;
-
- }
- }
- return false;
-
- }
-
- /**
- * Returns the stored data for a uri
- *
- * @param string $uri
- * @return array
- */
- protected function getData($uri) {
-
- $path = $this->getFilenameForUri($uri);
- if (!file_exists($path)) return array();
-
- // opening up the file, and creating a shared lock
- $handle = fopen($path,'r');
- flock($handle,LOCK_SH);
- $data = '';
-
- // Reading data until the eof
- while(!feof($handle)) {
- $data.=fread($handle,8192);
- }
-
- // We're all good
- fclose($handle);
-
- // Unserializing and checking if the resource file contains data for this file
- $data = unserialize($data);
- if (!$data) return array();
- return $data;
-
- }
-
- /**
- * Updates the lock information
- *
- * @param string $uri
- * @param array $newData
- * @return void
- */
- protected function putData($uri,array $newData) {
-
- $path = $this->getFileNameForUri($uri);
-
- // opening up the file, and creating a shared lock
- $handle = fopen($path,'a+');
- flock($handle,LOCK_EX);
- ftruncate($handle,0);
- rewind($handle);
-
- fwrite($handle,serialize($newData));
- fclose($handle);
-
- }
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Locks/Plugin.php b/vendor/sabre/dav/lib/Sabre/DAV/Locks/Plugin.php
deleted file mode 100644
index 001f8175d..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Locks/Plugin.php
+++ /dev/null
@@ -1,649 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Locks;
-
-use Sabre\DAV;
-
-/**
- * Locking plugin
- *
- * This plugin provides locking support to a WebDAV server.
- * The easiest way to get started, is by hooking it up as such:
- *
- * $lockBackend = new Sabre\DAV\Locks\Backend\File('./mylockdb');
- * $lockPlugin = new Sabre\DAV\Locks\Plugin($lockBackend);
- * $server->addPlugin($lockPlugin);
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Plugin extends DAV\ServerPlugin {
-
- /**
- * locksBackend
- *
- * @var Backend\Backend\Interface
- */
- protected $locksBackend;
-
- /**
- * server
- *
- * @var Sabre\DAV\Server
- */
- protected $server;
-
- /**
- * __construct
- *
- * @param Backend\BackendInterface $locksBackend
- */
- public function __construct(Backend\BackendInterface $locksBackend = null) {
-
- $this->locksBackend = $locksBackend;
-
- }
-
- /**
- * Initializes the plugin
- *
- * This method is automatically called by the Server class after addPlugin.
- *
- * @param DAV\Server $server
- * @return void
- */
- public function initialize(DAV\Server $server) {
-
- $this->server = $server;
- $server->subscribeEvent('unknownMethod',array($this,'unknownMethod'));
- $server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),50);
- $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties'));
-
- }
-
- /**
- * Returns a plugin name.
- *
- * Using this name other plugins will be able to access other plugins
- * using Sabre\DAV\Server::getPlugin
- *
- * @return string
- */
- public function getPluginName() {
-
- return 'locks';
-
- }
-
- /**
- * This method is called by the Server if the user used an HTTP method
- * the server didn't recognize.
- *
- * This plugin intercepts the LOCK and UNLOCK methods.
- *
- * @param string $method
- * @param string $uri
- * @return bool
- */
- public function unknownMethod($method, $uri) {
-
- switch($method) {
-
- case 'LOCK' : $this->httpLock($uri); return false;
- case 'UNLOCK' : $this->httpUnlock($uri); return false;
-
- }
-
- }
-
- /**
- * This method is called after most properties have been found
- * it allows us to add in any Lock-related properties
- *
- * @param string $path
- * @param array $newProperties
- * @return bool
- */
- public function afterGetProperties($path, &$newProperties) {
-
- foreach($newProperties[404] as $propName=>$discard) {
-
- switch($propName) {
-
- case '{DAV:}supportedlock' :
- $val = false;
- if ($this->locksBackend) $val = true;
- $newProperties[200][$propName] = new DAV\Property\SupportedLock($val);
- unset($newProperties[404][$propName]);
- break;
-
- case '{DAV:}lockdiscovery' :
- $newProperties[200][$propName] = new DAV\Property\LockDiscovery($this->getLocks($path));
- unset($newProperties[404][$propName]);
- break;
-
- }
-
-
- }
- return true;
-
- }
-
-
- /**
- * This method is called before the logic for any HTTP method is
- * handled.
- *
- * This plugin uses that feature to intercept access to locked resources.
- *
- * @param string $method
- * @param string $uri
- * @return bool
- */
- public function beforeMethod($method, $uri) {
-
- switch($method) {
-
- case 'DELETE' :
- $lastLock = null;
- if (!$this->validateLock($uri,$lastLock, true))
- throw new DAV\Exception\Locked($lastLock);
- break;
- case 'MKCOL' :
- case 'PROPPATCH' :
- case 'PUT' :
- case 'PATCH' :
- $lastLock = null;
- if (!$this->validateLock($uri,$lastLock))
- throw new DAV\Exception\Locked($lastLock);
- break;
- case 'MOVE' :
- $lastLock = null;
- if (!$this->validateLock(array(
- $uri,
- $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')),
- ),$lastLock, true))
- throw new DAV\Exception\Locked($lastLock);
- break;
- case 'COPY' :
- $lastLock = null;
- if (!$this->validateLock(
- $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')),
- $lastLock, true))
- throw new DAV\Exception\Locked($lastLock);
- break;
- }
-
- return true;
-
- }
-
- /**
- * Use this method to tell the server this plugin defines additional
- * HTTP methods.
- *
- * This method is passed a uri. It should only return HTTP methods that are
- * available for the specified uri.
- *
- * @param string $uri
- * @return array
- */
- public function getHTTPMethods($uri) {
-
- if ($this->locksBackend)
- return array('LOCK','UNLOCK');
-
- return array();
-
- }
-
- /**
- * Returns a list of features for the HTTP OPTIONS Dav: header.
- *
- * In this case this is only the number 2. The 2 in the Dav: header
- * indicates the server supports locks.
- *
- * @return array
- */
- public function getFeatures() {
-
- return array(2);
-
- }
-
- /**
- * Returns all lock information on a particular uri
- *
- * This function should return an array with Sabre\DAV\Locks\LockInfo objects. If there are no locks on a file, return an empty array.
- *
- * Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree
- * If the $returnChildLocks argument is set to true, we'll also traverse all the children of the object
- * for any possible locks and return those as well.
- *
- * @param string $uri
- * @param bool $returnChildLocks
- * @return array
- */
- public function getLocks($uri, $returnChildLocks = false) {
-
- $lockList = array();
-
- if ($this->locksBackend)
- $lockList = array_merge($lockList,$this->locksBackend->getLocks($uri, $returnChildLocks));
-
- return $lockList;
-
- }
-
- /**
- * Locks an uri
- *
- * The WebDAV lock request can be operated to either create a new lock on a file, or to refresh an existing lock
- * If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type
- * of lock (shared or exclusive) and the owner of the lock
- *
- * If a lock is to be refreshed, no body should be supplied and there should be a valid If header containing the lock
- *
- * Additionally, a lock can be requested for a non-existent file. In these case we're obligated to create an empty file as per RFC4918:S7.3
- *
- * @param string $uri
- * @return void
- */
- protected function httpLock($uri) {
-
- $lastLock = null;
- if (!$this->validateLock($uri,$lastLock)) {
-
- // If the existing lock was an exclusive lock, we need to fail
- if (!$lastLock || $lastLock->scope == LockInfo::EXCLUSIVE) {
- //var_dump($lastLock);
- throw new DAV\Exception\ConflictingLock($lastLock);
- }
-
- }
-
- if ($body = $this->server->httpRequest->getBody(true)) {
- // This is a new lock request
- $lockInfo = $this->parseLockRequest($body);
- $lockInfo->depth = $this->server->getHTTPDepth();
- $lockInfo->uri = $uri;
- if($lastLock && $lockInfo->scope != LockInfo::SHARED) throw new DAV\Exception\ConflictingLock($lastLock);
-
- } elseif ($lastLock) {
-
- // This must have been a lock refresh
- $lockInfo = $lastLock;
-
- // The resource could have been locked through another uri.
- if ($uri!=$lockInfo->uri) $uri = $lockInfo->uri;
-
- } else {
-
- // There was neither a lock refresh nor a new lock request
- throw new DAV\Exception\BadRequest('An xml body is required for lock requests');
-
- }
-
- if ($timeout = $this->getTimeoutHeader()) $lockInfo->timeout = $timeout;
-
- $newFile = false;
-
- // If we got this far.. we should go check if this node actually exists. If this is not the case, we need to create it first
- try {
- $this->server->tree->getNodeForPath($uri);
-
- // We need to call the beforeWriteContent event for RFC3744
- // Edit: looks like this is not used, and causing problems now.
- //
- // See Issue 222
- // $this->server->broadcastEvent('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);
-
- $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
- $this->server->httpResponse->setHeader('Lock-Token','<opaquelocktoken:' . $lockInfo->token . '>');
- $this->server->httpResponse->sendStatus($newFile?201:200);
- $this->server->httpResponse->sendBody($this->generateLockResponse($lockInfo));
-
- }
-
- /**
- * 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 string $uri
- * @return void
- */
- protected function httpUnlock($uri) {
-
- $lockToken = $this->server->httpRequest->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');
-
- $locks = $this->getLocks($uri);
-
- // Windows sometimes forgets to include < and > in the Lock-Token
- // header
- if ($lockToken[0]!=='<') $lockToken = '<' . $lockToken . '>';
-
- foreach($locks as $lock) {
-
- if ('<opaquelocktoken:' . $lock->token . '>' == $lockToken) {
-
- $this->unlockNode($uri,$lock);
- $this->server->httpResponse->setHeader('Content-Length','0');
- $this->server->httpResponse->sendStatus(204);
- return;
-
- }
-
- }
-
- // If we got here, it means the locktoken was invalid
- throw new DAV\Exception\LockTokenMatchesRequestUri();
-
- }
-
- /**
- * Locks a uri
- *
- * All the locking information is supplied in the lockInfo object. The object has a suggested timeout, but this can be safely ignored
- * It is important that if the existing timeout is ignored, the property is overwritten, as this needs to be sent back to the client
- *
- * @param string $uri
- * @param LockInfo $lockInfo
- * @return bool
- */
- public function lockNode($uri,LockInfo $lockInfo) {
-
- if (!$this->server->broadcastEvent('beforeLock',array($uri,$lockInfo))) return;
-
- if ($this->locksBackend) return $this->locksBackend->lock($uri,$lockInfo);
- throw new DAV\Exception\MethodNotAllowed('Locking support is not enabled for this resource. No Locking backend was found so if you didn\'t expect this error, please check your configuration.');
-
- }
-
- /**
- * Unlocks a uri
- *
- * This method removes a lock from a uri. It is assumed all the supplied information is correct and verified
- *
- * @param string $uri
- * @param LockInfo $lockInfo
- * @return bool
- */
- public function unlockNode($uri, LockInfo $lockInfo) {
-
- if (!$this->server->broadcastEvent('beforeUnlock',array($uri,$lockInfo))) return;
- if ($this->locksBackend) return $this->locksBackend->unlock($uri,$lockInfo);
-
- }
-
-
- /**
- * Returns the contents of the HTTP Timeout header.
- *
- * The method formats the header into an integer.
- *
- * @return int
- */
- public function getTimeoutHeader() {
-
- $header = $this->server->httpRequest->getHeader('Timeout');
-
- if ($header) {
-
- if (stripos($header,'second-')===0) $header = (int)(substr($header,7));
- else if (strtolower($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
- *
- * @param LockInfo $lockInfo
- * @return string
- */
- protected function generateLockResponse(LockInfo $lockInfo) {
-
- $dom = new \DOMDocument('1.0','utf-8');
- $dom->formatOutput = true;
-
- $prop = $dom->createElementNS('DAV:','d:prop');
- $dom->appendChild($prop);
-
- $lockDiscovery = $dom->createElementNS('DAV:','d:lockdiscovery');
- $prop->appendChild($lockDiscovery);
-
- $lockObj = new DAV\Property\LockDiscovery(array($lockInfo),true);
- $lockObj->serialize($this->server,$lockDiscovery);
-
- return $dom->saveXML();
-
- }
-
- /**
- * validateLock should be called when a write operation is about to happen
- * It will check if the requested url is locked, and see if the correct lock tokens are passed
- *
- * @param mixed $urls List of relevant urls. Can be an array, a string or nothing at all for the current request uri
- * @param mixed $lastLock This variable will be populated with the last checked lock object (Sabre\DAV\Locks\LockInfo)
- * @param bool $checkChildLocks If set to true, this function will also look for any locks set on child resources of the supplied urls. This is needed for for example deletion of entire trees.
- * @return bool
- */
- protected function validateLock($urls = null,&$lastLock = null, $checkChildLocks = false) {
-
- if (is_null($urls)) {
- $urls = array($this->server->getRequestUri());
- } elseif (is_string($urls)) {
- $urls = array($urls);
- } elseif (!is_array($urls)) {
- throw new DAV\Exception('The urls parameter should either be null, a string or an array');
- }
-
- $conditions = $this->getIfConditions();
-
- // We're going to loop through the urls and make sure all lock conditions are satisfied
- foreach($urls as $url) {
-
- $locks = $this->getLocks($url, $checkChildLocks);
-
- // If there were no conditions, but there were locks, we fail
- if (!$conditions && $locks) {
- reset($locks);
- $lastLock = current($locks);
- return false;
- }
-
- // If there were no locks or conditions, we go to the next url
- if (!$locks && !$conditions) continue;
-
- foreach($conditions as $condition) {
-
- if (!$condition['uri']) {
- $conditionUri = $this->server->getRequestUri();
- } else {
- $conditionUri = $this->server->calculateUri($condition['uri']);
- }
-
- // If the condition has a url, and it isn't part of the affected url at all, check the next condition
- if ($conditionUri && strpos($url,$conditionUri)!==0) continue;
-
- // The tokens array contians arrays with 2 elements. 0=true/false for normal/not condition, 1=locktoken
- // At least 1 condition has to be satisfied
- foreach($condition['tokens'] as $conditionToken) {
-
- $etagValid = true;
- $lockValid = true;
-
- // key 2 can contain an etag
- if ($conditionToken[2]) {
-
- $uri = $conditionUri?$conditionUri:$this->server->getRequestUri();
- $node = $this->server->tree->getNodeForPath($uri);
- $etagValid = $node instanceof DAV\IFile && $node->getETag()==$conditionToken[2];
-
- }
-
- // key 1 can contain a lock token
- if ($conditionToken[1]) {
-
- $lockValid = false;
- // Match all the locks
- foreach($locks as $lockIndex=>$lock) {
-
- $lockToken = 'opaquelocktoken:' . $lock->token;
-
- // Checking NOT
- if (!$conditionToken[0] && $lockToken != $conditionToken[1]) {
-
- // Condition valid, onto the next
- $lockValid = true;
- break;
- }
- if ($conditionToken[0] && $lockToken == $conditionToken[1]) {
-
- $lastLock = $lock;
- // Condition valid and lock matched
- unset($locks[$lockIndex]);
- $lockValid = true;
- break;
-
- }
-
- }
-
- }
-
- // If, after checking both etags and locks they are stil valid,
- // we can continue with the next condition.
- if ($etagValid && $lockValid) continue 2;
- }
- // No conditions matched, so we fail
- throw new DAV\Exception\PreconditionFailed('The tokens provided in the if header did not match','If');
- }
-
- // Conditions were met, we'll also need to check if all the locks are gone
- if (count($locks)) {
-
- reset($locks);
-
- // There's still locks, we fail
- $lastLock = current($locks);
- return false;
-
- }
-
-
- }
-
- // We got here, this means every condition was satisfied
- return true;
-
- }
-
- /**
- * This method is created to extract information from the WebDAV HTTP 'If:' header
- *
- * The If header can be quite complex, and has a bunch of features. We're using a regex to extract all relevant information
- * The function will return an array, containing structs with the following keys
- *
- * * uri - the uri the condition applies to. If this is returned as an
- * empty string, this implies it's referring to the request url.
- * * tokens - The lock token. another 2 dimensional array containing 2 elements (0 = true/false.. If this is a negative condition its set to false, 1 = the actual token)
- * * etag - an etag, if supplied
- *
- * @return array
- */
- public function getIfConditions() {
-
- $header = $this->server->httpRequest->getHeader('If');
- if (!$header) return array();
-
- $matches = array();
-
- $regex = '/(?:\<(?P<uri>.*?)\>\s)?\((?P<not>Not\s)?(?:\<(?P<token>[^\>]*)\>)?(?:\s?)(?:\[(?P<etag>[^\]]*)\])?\)/im';
- preg_match_all($regex,$header,$matches,PREG_SET_ORDER);
-
- $conditions = array();
-
- foreach($matches as $match) {
-
- $condition = array(
- 'uri' => $match['uri'],
- 'tokens' => array(
- array($match['not']?0:1,$match['token'],isset($match['etag'])?$match['etag']:'')
- ),
- );
-
- if (!$condition['uri'] && count($conditions)) $conditions[count($conditions)-1]['tokens'][] = array(
- $match['not']?0:1,
- $match['token'],
- isset($match['etag'])?$match['etag']:''
- );
- else {
- $conditions[] = $condition;
- }
-
- }
-
- return $conditions;
-
- }
-
- /**
- * Parses a webdav lock xml body, and returns a new Sabre\DAV\Locks\LockInfo object
- *
- * @param string $body
- * @return DAV\Locks\LockInfo
- */
- protected function parseLockRequest($body) {
-
- // Fixes an XXE vulnerability on PHP versions older than 5.3.23 or
- // 5.4.13.
- $previous = libxml_disable_entity_loader(true);
-
-
- $xml = simplexml_load_string(
- DAV\XMLUtil::convertDAVNamespace($body),
- null,
- LIBXML_NOWARNING);
- libxml_disable_entity_loader($previous);
-
- $xml->registerXPathNamespace('d','urn:DAV');
- $lockInfo = new LockInfo();
-
- $children = $xml->children("urn:DAV");
- $lockInfo->owner = (string)$children->owner;
-
- $lockInfo->token = DAV\UUIDUtil::getUUID();
- $lockInfo->scope = count($xml->xpath('d:lockscope/d:exclusive'))>0 ? LockInfo::EXCLUSIVE : LockInfo::SHARED;
-
- return $lockInfo;
-
- }
-
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/ObjectTree.php b/vendor/sabre/dav/lib/Sabre/DAV/ObjectTree.php
deleted file mode 100644
index 3e7c0fcf8..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/ObjectTree.php
+++ /dev/null
@@ -1,159 +0,0 @@
-<?php
-
-namespace Sabre\DAV;
-
-/**
- * ObjectTree class
- *
- * This implementation of the Tree class makes use of the INode, IFile and ICollection API's
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class ObjectTree extends Tree {
-
- /**
- * The root node
- *
- * @var ICollection
- */
- protected $rootNode;
-
- /**
- * This is the node cache. Accessed nodes are stored here
- *
- * @var array
- */
- protected $cache = array();
-
- /**
- * Creates the object
- *
- * This method expects the rootObject to be passed as a parameter
- *
- * @param ICollection $rootNode
- */
- public function __construct(ICollection $rootNode) {
-
- $this->rootNode = $rootNode;
-
- }
-
- /**
- * Returns the INode object for the requested path
- *
- * @param string $path
- * @return INode
- */
- public function getNodeForPath($path) {
-
- $path = trim($path,'/');
- if (isset($this->cache[$path])) return $this->cache[$path];
-
- // Is it the root node?
- if (!strlen($path)) {
- return $this->rootNode;
- }
-
- // Attempting to fetch its parent
- list($parentName, $baseName) = URLUtil::splitPath($path);
-
- // If there was no parent, we must simply ask it from the root node.
- if ($parentName==="") {
- $node = $this->rootNode->getChild($baseName);
- } else {
- // Otherwise, we recursively grab the parent and ask him/her.
- $parent = $this->getNodeForPath($parentName);
-
- if (!($parent instanceof ICollection))
- throw new Exception\NotFound('Could not find node at path: ' . $path);
-
- $node = $parent->getChild($baseName);
-
- }
-
- $this->cache[$path] = $node;
- return $node;
-
- }
-
- /**
- * This function allows you to check if a node exists.
- *
- * @param string $path
- * @return bool
- */
- public function nodeExists($path) {
-
- try {
-
- // The root always exists
- if ($path==='') return true;
-
- list($parent, $base) = URLUtil::splitPath($path);
-
- $parentNode = $this->getNodeForPath($parent);
- if (!$parentNode instanceof ICollection) return false;
- return $parentNode->childExists($base);
-
- } catch (Exception\NotFound $e) {
-
- return false;
-
- }
-
- }
-
- /**
- * Returns a list of childnodes for a given path.
- *
- * @param string $path
- * @return array
- */
- public function getChildren($path) {
-
- $node = $this->getNodeForPath($path);
- $children = $node->getChildren();
- foreach($children as $child) {
-
- $this->cache[trim($path,'/') . '/' . $child->getName()] = $child;
-
- }
- return $children;
-
- }
-
- /**
- * This method is called with every tree update
- *
- * Examples of tree updates are:
- * * node deletions
- * * node creations
- * * copy
- * * move
- * * renaming nodes
- *
- * If Tree classes implement a form of caching, this will allow
- * them to make sure caches will be expired.
- *
- * If a path is passed, it is assumed that the entire subtree is dirty
- *
- * @param string $path
- * @return void
- */
- 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 ($nodePath == $path || strpos($nodePath,$path.'/')===0)
- unset($this->cache[$nodePath]);
-
- }
-
- }
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/PartialUpdate/IFile.php b/vendor/sabre/dav/lib/Sabre/DAV/PartialUpdate/IFile.php
deleted file mode 100644
index 9cfb47377..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/PartialUpdate/IFile.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-namespace Sabre\DAV\PartialUpdate;
-
-use Sabre\DAV;
-
-/**
- * This interface is deprecated. Use IPatchSupport instead.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/)
- * @license http://sabre.io/license/ Modified BSD License
- * @deprecated
- */
-interface IFile extends DAV\IFile {
-
- /**
- * Updates the data at a given offset
- *
- * The data argument is a readable stream resource.
- * The offset argument is an integer describing the offset. Contrary to
- * what's sent in the request, the offset here is a 0-based index.
- *
- * After a successful put operation, you may choose to return an ETag. The
- * etag must always be surrounded by double-quotes. These quotes must
- * appear in the actual string you're returning.
- *
- * Clients may use the ETag from a PUT request to later on make sure that
- * when they update the file, the contents haven't changed in the mean
- * time.
- *
- * @param resource $data
- * @param integer $offset
- * @return string|null
- */
- function putRange($data, $offset);
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Property.php b/vendor/sabre/dav/lib/Sabre/DAV/Property.php
deleted file mode 100644
index d0c265907..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Property.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-namespace Sabre\DAV;
-
-/**
- * Abstract property class
- *
- * Extend this class to create custom complex properties
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-abstract class Property implements PropertyInterface {
-
- /**
- * Unserializes the property.
- *
- * This static method should return a an instance of this object.
- *
- * @param \DOMElement $prop
- * @return DAV\IProperty
- */
- static function unserialize(\DOMElement $prop) {
-
- throw new Exception('Unserialize has not been implemented for this class');
-
- }
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Property/GetLastModified.php b/vendor/sabre/dav/lib/Sabre/DAV/Property/GetLastModified.php
deleted file mode 100644
index 987e3fc02..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Property/GetLastModified.php
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-use Sabre\DAV;
-use Sabre\HTTP;
-
-/**
- * This property represents the {DAV:}getlastmodified property.
- *
- * Although this is normally a simple property, windows requires us to add
- * some new attributes.
- *
- * This class uses unix timestamps internally, and converts them to RFC 1123 times for
- * serialization
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class GetLastModified extends DAV\Property {
-
- /**
- * time
- *
- * @var int
- */
- public $time;
-
- /**
- * __construct
- *
- * @param int|DateTime $time
- */
- public function __construct($time) {
-
- if ($time instanceof \DateTime) {
- $this->time = $time;
- } elseif (is_int($time) || ctype_digit($time)) {
- $this->time = new \DateTime('@' . $time);
- } else {
- $this->time = new \DateTime($time);
- }
-
- // Setting timezone to UTC
- $this->time->setTimezone(new \DateTimeZone('UTC'));
-
- }
-
- /**
- * serialize
- *
- * @param DAV\Server $server
- * @param \DOMElement $prop
- * @return void
- */
- public function serialize(DAV\Server $server, \DOMElement $prop) {
-
- $doc = $prop->ownerDocument;
- //$prop->setAttribute('xmlns:b','urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/');
- //$prop->setAttribute('b:dt','dateTime.rfc1123');
- $prop->nodeValue = HTTP\Util::toHTTPDate($this->time);
-
- }
-
- /**
- * getTime
- *
- * @return \DateTime
- */
- public function getTime() {
-
- return $this->time;
-
- }
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Property/Href.php b/vendor/sabre/dav/lib/Sabre/DAV/Property/Href.php
deleted file mode 100644
index f0c162706..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Property/Href.php
+++ /dev/null
@@ -1,99 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-use Sabre\DAV;
-
-/**
- * Href property
- *
- * The href property represents a url within a {DAV:}href element.
- * This is used by many WebDAV extensions, but not really within the WebDAV core spec
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Href extends DAV\Property implements IHref {
-
- /**
- * href
- *
- * @var string
- */
- private $href;
-
- /**
- * Automatically prefix the url with the server base directory
- *
- * @var bool
- */
- private $autoPrefix = true;
-
- /**
- * __construct
- *
- * @param string $href
- * @param bool $autoPrefix
- */
- public function __construct($href, $autoPrefix = true) {
-
- $this->href = $href;
- $this->autoPrefix = $autoPrefix;
-
- }
-
- /**
- * Returns the uri
- *
- * @return string
- */
- public function getHref() {
-
- return $this->href;
-
- }
-
- /**
- * Serializes this property.
- *
- * It will additionally prepend the href property with the server's base uri.
- *
- * @param DAV\Server $server
- * @param \DOMElement $dom
- * @return void
- */
- public function serialize(DAV\Server $server, \DOMElement $dom) {
-
- $prefix = $server->xmlNamespaces['DAV:'];
- $elem = $dom->ownerDocument->createElement($prefix . ':href');
-
- if ($this->autoPrefix) {
- $value = $server->getBaseUri() . DAV\URLUtil::encodePath($this->href);
- } else {
- $value = $this->href;
- }
- $elem->appendChild($dom->ownerDocument->createTextNode($value));
-
- $dom->appendChild($elem);
-
- }
-
- /**
- * Unserializes this property from a DOM Element
- *
- * This method returns an instance of this class.
- * It will only decode {DAV:}href values. For non-compatible elements null will be returned.
- *
- * @param \DOMElement $dom
- * @return DAV\Property\Href
- */
- static function unserialize(\DOMElement $dom) {
-
- if ($dom->firstChild && DAV\XMLUtil::toClarkNotation($dom->firstChild)==='{DAV:}href') {
- return new self($dom->firstChild->textContent,false);
- }
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Property/HrefList.php b/vendor/sabre/dav/lib/Sabre/DAV/Property/HrefList.php
deleted file mode 100644
index a5bad4ace..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Property/HrefList.php
+++ /dev/null
@@ -1,105 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-use Sabre\DAV;
-
-/**
- * HrefList property
- *
- * This property contains multiple {DAV:}href elements, each containing a url.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class HrefList extends DAV\Property {
-
- /**
- * hrefs
- *
- * @var array
- */
- private $hrefs;
-
- /**
- * Automatically prefix the url with the server base directory
- *
- * @var bool
- */
- private $autoPrefix = true;
-
- /**
- * __construct
- *
- * @param array $hrefs
- * @param bool $autoPrefix
- */
- public function __construct(array $hrefs, $autoPrefix = true) {
-
- $this->hrefs = $hrefs;
- $this->autoPrefix = $autoPrefix;
-
- }
-
- /**
- * Returns the uris
- *
- * @return array
- */
- public function getHrefs() {
-
- return $this->hrefs;
-
- }
-
- /**
- * Serializes this property.
- *
- * It will additionally prepend the href property with the server's base uri.
- *
- * @param DAV\Server $server
- * @param \DOMElement $dom
- * @return void
- */
- public function serialize(DAV\Server $server,\DOMElement $dom) {
-
- $prefix = $server->xmlNamespaces['DAV:'];
-
- foreach($this->hrefs as $href) {
-
- $elem = $dom->ownerDocument->createElement($prefix . ':href');
- if ($this->autoPrefix) {
- $value = $server->getBaseUri() . DAV\URLUtil::encodePath($href);
- } else {
- $value = $href;
- }
- $elem->appendChild($dom->ownerDocument->createTextNode($value));
-
- $dom->appendChild($elem);
- }
-
- }
-
- /**
- * Unserializes this property from a DOM Element
- *
- * This method returns an instance of this class.
- * It will only decode {DAV:}href values.
- *
- * @param \DOMElement $dom
- * @return DAV\Property\HrefList
- */
- static function unserialize(\DOMElement $dom) {
-
- $hrefs = array();
- foreach($dom->childNodes as $child) {
- if (DAV\XMLUtil::toClarkNotation($child)==='{DAV:}href') {
- $hrefs[] = $child->textContent;
- }
- }
- return new self($hrefs, false);
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Property/IHref.php b/vendor/sabre/dav/lib/Sabre/DAV/Property/IHref.php
deleted file mode 100644
index 268ab8d51..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Property/IHref.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-/**
- * IHref interface
- *
- * Any property implementing this interface can expose a related url.
- * This is used by certain subsystems to aquire more information about for example
- * the owner of a file
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-interface IHref {
-
- /**
- * getHref
- *
- * @return string
- */
- function getHref();
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Property/LockDiscovery.php b/vendor/sabre/dav/lib/Sabre/DAV/Property/LockDiscovery.php
deleted file mode 100644
index 6b47935af..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Property/LockDiscovery.php
+++ /dev/null
@@ -1,104 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-use Sabre\DAV;
-
-/**
- * Represents {DAV:}lockdiscovery property
- *
- * This property contains all the open locks on a given resource
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class LockDiscovery extends DAV\Property {
-
- /**
- * locks
- *
- * @var array
- */
- public $locks;
-
- /**
- * Should we show the locktoken as well?
- *
- * @var bool
- */
- public $revealLockToken;
-
- /**
- * Hides the {DAV:}lockroot element from the response.
- *
- * It was reported that showing the lockroot in the response can break
- * Office 2000 compatibility.
- */
- static public $hideLockRoot = false;
-
- /**
- * __construct
- *
- * @param array $locks
- * @param bool $revealLockToken
- */
- public function __construct($locks, $revealLockToken = false) {
-
- $this->locks = $locks;
- $this->revealLockToken = $revealLockToken;
-
- }
-
- /**
- * serialize
- *
- * @param DAV\Server $server
- * @param \DOMElement $prop
- * @return void
- */
- public function serialize(DAV\Server $server, \DOMElement $prop) {
-
- $doc = $prop->ownerDocument;
-
- foreach($this->locks as $lock) {
-
- $activeLock = $doc->createElementNS('DAV:','d:activelock');
- $prop->appendChild($activeLock);
-
- $lockScope = $doc->createElementNS('DAV:','d:lockscope');
- $activeLock->appendChild($lockScope);
-
- $lockScope->appendChild($doc->createElementNS('DAV:','d:' . ($lock->scope==DAV\Locks\LockInfo::EXCLUSIVE?'exclusive':'shared')));
-
- $lockType = $doc->createElementNS('DAV:','d:locktype');
- $activeLock->appendChild($lockType);
-
- $lockType->appendChild($doc->createElementNS('DAV:','d:write'));
-
- /* {DAV:}lockroot */
- if (!self::$hideLockRoot) {
- $lockRoot = $doc->createElementNS('DAV:','d:lockroot');
- $activeLock->appendChild($lockRoot);
- $href = $doc->createElementNS('DAV:','d:href');
- $href->appendChild($doc->createTextNode($server->getBaseUri() . $lock->uri));
- $lockRoot->appendChild($href);
- }
-
- $activeLock->appendChild($doc->createElementNS('DAV:','d:depth',($lock->depth == DAV\Server::DEPTH_INFINITY?'infinity':$lock->depth)));
- $activeLock->appendChild($doc->createElementNS('DAV:','d:timeout','Second-' . $lock->timeout));
-
- if ($this->revealLockToken) {
- $lockToken = $doc->createElementNS('DAV:','d:locktoken');
- $activeLock->appendChild($lockToken);
- $lockToken->appendChild($doc->createElementNS('DAV:','d:href','opaquelocktoken:' . $lock->token));
- }
-
- $activeLock->appendChild($doc->createElementNS('DAV:','d:owner',$lock->owner));
-
- }
-
- }
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Property/ResourceType.php b/vendor/sabre/dav/lib/Sabre/DAV/Property/ResourceType.php
deleted file mode 100644
index 68134f3f9..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Property/ResourceType.php
+++ /dev/null
@@ -1,127 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-use Sabre\DAV;
-
-/**
- * This class represents the {DAV:}resourcetype property
- *
- * Normally for files this is empty, and for collection {DAV:}collection.
- * However, other specs define different values for this.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class ResourceType extends DAV\Property {
-
- /**
- * resourceType
- *
- * @var array
- */
- public $resourceType = array();
-
- /**
- * __construct
- *
- * @param mixed $resourceType
- */
- public function __construct($resourceType = array()) {
-
- if ($resourceType === DAV\Server::NODE_FILE)
- $this->resourceType = array();
- elseif ($resourceType === DAV\Server::NODE_DIRECTORY)
- $this->resourceType = array('{DAV:}collection');
- elseif (is_array($resourceType))
- $this->resourceType = $resourceType;
- else
- $this->resourceType = array($resourceType);
-
- }
-
- /**
- * serialize
- *
- * @param DAV\Server $server
- * @param \DOMElement $prop
- * @return void
- */
- public function serialize(DAV\Server $server, \DOMElement $prop) {
-
- $propName = null;
- $rt = $this->resourceType;
-
- foreach($rt as $resourceType) {
- if (preg_match('/^{([^}]*)}(.*)$/',$resourceType,$propName)) {
-
- if (isset($server->xmlNamespaces[$propName[1]])) {
- $prop->appendChild($prop->ownerDocument->createElement($server->xmlNamespaces[$propName[1]] . ':' . $propName[2]));
- } else {
- $prop->appendChild($prop->ownerDocument->createElementNS($propName[1],'custom:' . $propName[2]));
- }
-
- }
- }
-
- }
-
- /**
- * Returns the values in clark-notation
- *
- * For example array('{DAV:}collection')
- *
- * @return array
- */
- public function getValue() {
-
- return $this->resourceType;
-
- }
-
- /**
- * Checks if the principal contains a certain value
- *
- * @param string $type
- * @return bool
- */
- public function is($type) {
-
- return in_array($type, $this->resourceType);
-
- }
-
- /**
- * Adds a resourcetype value to this property
- *
- * @param string $type
- * @return void
- */
- public function add($type) {
-
- $this->resourceType[] = $type;
- $this->resourceType = array_unique($this->resourceType);
-
- }
-
- /**
- * Unserializes a DOM element into a ResourceType property.
- *
- * @param \DOMElement $dom
- * @return DAV\Property\ResourceType
- */
- static public function unserialize(\DOMElement $dom) {
-
- $value = array();
- foreach($dom->childNodes as $child) {
-
- $value[] = DAV\XMLUtil::toClarkNotation($child);
-
- }
-
- return new self($value);
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Property/Response.php b/vendor/sabre/dav/lib/Sabre/DAV/Property/Response.php
deleted file mode 100644
index 370abc26b..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Property/Response.php
+++ /dev/null
@@ -1,157 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-use Sabre\DAV;
-
-/**
- * Response property
- *
- * This class represents the {DAV:}response XML element.
- * This is used by the Server class to encode individual items within a multistatus
- * response.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Response extends DAV\Property implements IHref {
-
- /**
- * Url for the response
- *
- * @var string
- */
- private $href;
-
- /**
- * Propertylist, ordered by HTTP status code
- *
- * @var array
- */
- private $responseProperties;
-
- /**
- * The responseProperties argument is a list of properties
- * within an array with keys representing HTTP status codes
- *
- * @param string $href
- * @param array $responseProperties
- */
- public function __construct($href, array $responseProperties) {
-
- $this->href = $href;
- $this->responseProperties = $responseProperties;
-
- }
-
- /**
- * Returns the url
- *
- * @return string
- */
- public function getHref() {
-
- return $this->href;
-
- }
-
- /**
- * Returns the property list
- *
- * @return array
- */
- public function getResponseProperties() {
-
- return $this->responseProperties;
-
- }
-
- /**
- * serialize
- *
- * @param DAV\Server $server
- * @param \DOMElement $dom
- * @return void
- */
- public function serialize(DAV\Server $server, \DOMElement $dom) {
-
- $document = $dom->ownerDocument;
- $properties = $this->responseProperties;
-
- $xresponse = $document->createElement('d:response');
- $dom->appendChild($xresponse);
-
- $uri = DAV\URLUtil::encodePath($this->href);
-
- // Adding the baseurl to the beginning of the url
- $uri = $server->getBaseUri() . $uri;
-
- $xresponse->appendChild($document->createElement('d:href',$uri));
-
- // The properties variable is an array containing properties, grouped by
- // HTTP status
- foreach($properties as $httpStatus=>$propertyGroup) {
-
- // The 'href' is also in this array, and it's special cased.
- // We will ignore it
- if ($httpStatus=='href') continue;
-
- // If there are no properties in this group, we can also just carry on
- if (!count($propertyGroup)) continue;
-
- $xpropstat = $document->createElement('d:propstat');
- $xresponse->appendChild($xpropstat);
-
- $xprop = $document->createElement('d:prop');
- $xpropstat->appendChild($xprop);
-
- $nsList = $server->xmlNamespaces;
-
- foreach($propertyGroup as $propertyName=>$propertyValue) {
-
- $propName = null;
- preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName);
-
- // special case for empty namespaces
- if ($propName[1]=='') {
-
- $currentProperty = $document->createElement($propName[2]);
- $xprop->appendChild($currentProperty);
- $currentProperty->setAttribute('xmlns','');
-
- } else {
-
- if (!isset($nsList[$propName[1]])) {
- $nsList[$propName[1]] = 'x' . count($nsList);
- }
-
- // If the namespace was defined in the top-level xml namespaces, it means
- // there was already a namespace declaration, and we don't have to worry about it.
- if (isset($server->xmlNamespaces[$propName[1]])) {
- $currentProperty = $document->createElement($nsList[$propName[1]] . ':' . $propName[2]);
- } else {
- $currentProperty = $document->createElementNS($propName[1],$nsList[$propName[1]].':' . $propName[2]);
- }
- $xprop->appendChild($currentProperty);
-
- }
-
- if (is_scalar($propertyValue)) {
- $text = $document->createTextNode($propertyValue);
- $currentProperty->appendChild($text);
- } elseif ($propertyValue instanceof DAV\PropertyInterface) {
- $propertyValue->serialize($server,$currentProperty);
- } elseif (!is_null($propertyValue)) {
- throw new DAV\Exception('Unknown property value type: ' . gettype($propertyValue) . ' for property: ' . $propertyName);
- }
-
- }
-
- $xpropstat->appendChild($document->createElement('d:status',$server->httpResponse->getStatusMessage($httpStatus)));
-
- }
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Property/ResponseList.php b/vendor/sabre/dav/lib/Sabre/DAV/Property/ResponseList.php
deleted file mode 100644
index 9db6cbbf5..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Property/ResponseList.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-use Sabre\DAV;
-
-/**
- * ResponseList property
- *
- * This class represents multiple {DAV:}response XML elements.
- * This is used by the Server class to encode items within a multistatus
- * response.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class ResponseList extends DAV\Property {
-
- /**
- * Response objects.
- *
- * @var array
- */
- private $responses;
-
- /**
- * The only valid argument is a list of Sabre\DAV\Property\Response
- * objects.
- *
- * @param array $responses;
- */
- public function __construct($responses) {
-
- foreach($responses as $response) {
- if (!($response instanceof Response)) {
- throw new \InvalidArgumentException('You must pass an array of Sabre\DAV\Property\Response objects');
- }
- }
- $this->responses = $responses;
-
- }
-
- /**
- * serialize
- *
- * @param DAV\Server $server
- * @param \DOMElement $dom
- * @return void
- */
- public function serialize(DAV\Server $server,\DOMElement $dom) {
-
- foreach($this->responses as $response) {
- $response->serialize($server, $dom);
- }
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Property/SupportedLock.php b/vendor/sabre/dav/lib/Sabre/DAV/Property/SupportedLock.php
deleted file mode 100644
index 035c2f330..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Property/SupportedLock.php
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-use Sabre\DAV;
-
-/**
- * This class represents the {DAV:}supportedlock property
- *
- * This property contains information about what kind of locks
- * this server supports.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class SupportedLock extends DAV\Property {
-
- /**
- * supportsLocks
- *
- * @var mixed
- */
- public $supportsLocks = false;
-
- /**
- * __construct
- *
- * @param mixed $supportsLocks
- */
- public function __construct($supportsLocks) {
-
- $this->supportsLocks = $supportsLocks;
-
- }
-
- /**
- * serialize
- *
- * @param DAV\Server $server
- * @param \DOMElement $prop
- * @return void
- */
- public function serialize(DAV\Server $server,\DOMElement $prop) {
-
- $doc = $prop->ownerDocument;
-
- if (!$this->supportsLocks) return null;
-
- $lockEntry1 = $doc->createElement('d:lockentry');
- $lockEntry2 = $doc->createElement('d:lockentry');
-
- $prop->appendChild($lockEntry1);
- $prop->appendChild($lockEntry2);
-
- $lockScope1 = $doc->createElement('d:lockscope');
- $lockScope2 = $doc->createElement('d:lockscope');
- $lockType1 = $doc->createElement('d:locktype');
- $lockType2 = $doc->createElement('d:locktype');
-
- $lockEntry1->appendChild($lockScope1);
- $lockEntry1->appendChild($lockType1);
- $lockEntry2->appendChild($lockScope2);
- $lockEntry2->appendChild($lockType2);
-
- $lockScope1->appendChild($doc->createElement('d:exclusive'));
- $lockScope2->appendChild($doc->createElement('d:shared'));
-
- $lockType1->appendChild($doc->createElement('d:write'));
- $lockType2->appendChild($doc->createElement('d:write'));
-
- //$frag->appendXML('<d:lockentry><d:lockscope><d:exclusive /></d:lockscope><d:locktype><d:write /></d:locktype></d:lockentry>');
- //$frag->appendXML('<d:lockentry><d:lockscope><d:shared /></d:lockscope><d:locktype><d:write /></d:locktype></d:lockentry>');
-
- }
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Property/SupportedReportSet.php b/vendor/sabre/dav/lib/Sabre/DAV/Property/SupportedReportSet.php
deleted file mode 100644
index a8a90bb18..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Property/SupportedReportSet.php
+++ /dev/null
@@ -1,111 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-use Sabre\DAV;
-
-/**
- * supported-report-set property.
- *
- * This property is defined in RFC3253, but since it's
- * so common in other webdav-related specs, it is part of the core server.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class SupportedReportSet extends DAV\Property {
-
- /**
- * List of reports
- *
- * @var array
- */
- protected $reports = array();
-
- /**
- * Creates the property
- *
- * Any reports passed in the constructor
- * should be valid report-types in clark-notation.
- *
- * Either a string or an array of strings must be passed.
- *
- * @param mixed $reports
- */
- public function __construct($reports = null) {
-
- if (!is_null($reports))
- $this->addReport($reports);
-
- }
-
- /**
- * Adds a report to this property
- *
- * The report must be a string in clark-notation.
- * Multiple reports can be specified as an array.
- *
- * @param mixed $report
- * @return void
- */
- public function addReport($report) {
-
- if (!is_array($report)) $report = array($report);
-
- foreach($report as $r) {
-
- if (!preg_match('/^{([^}]*)}(.*)$/',$r))
- throw new DAV\Exception('Reportname must be in clark-notation');
-
- $this->reports[] = $r;
-
- }
-
- }
-
- /**
- * Returns the list of supported reports
- *
- * @return array
- */
- public function getValue() {
-
- return $this->reports;
-
- }
-
- /**
- * Serializes the node
- *
- * @param DAV\Server $server
- * @param \DOMElement $prop
- * @return void
- */
- public function serialize(DAV\Server $server, \DOMElement $prop) {
-
- foreach($this->reports as $reportName) {
-
- $supportedReport = $prop->ownerDocument->createElement('d:supported-report');
- $prop->appendChild($supportedReport);
-
- $report = $prop->ownerDocument->createElement('d:report');
- $supportedReport->appendChild($report);
-
- preg_match('/^{([^}]*)}(.*)$/',$reportName,$matches);
-
- list(, $namespace, $element) = $matches;
-
- $prefix = isset($server->xmlNamespaces[$namespace])?$server->xmlNamespaces[$namespace]:null;
-
- if ($prefix) {
- $report->appendChild($prop->ownerDocument->createElement($prefix . ':' . $element));
- } else {
- $report->appendChild($prop->ownerDocument->createElementNS($namespace, 'x:' . $element));
- }
-
- }
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/PropertyInterface.php b/vendor/sabre/dav/lib/Sabre/DAV/PropertyInterface.php
deleted file mode 100644
index 2fb0d7db6..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/PropertyInterface.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-namespace Sabre\DAV;
-
-/**
- * PropertyInterface
- *
- * Implement this interface to create new complex properties
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-interface PropertyInterface {
-
- public function serialize(Server $server, \DOMElement $prop);
-
- static function unserialize(\DOMElement $prop);
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Server.php b/vendor/sabre/dav/lib/Sabre/DAV/Server.php
deleted file mode 100644
index e0a68ab50..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Server.php
+++ /dev/null
@@ -1,2178 +0,0 @@
-<?php
-
-namespace Sabre\DAV;
-use Sabre\HTTP;
-
-/**
- * Main DAV server class
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Server {
-
- /**
- * Infinity is used for some request supporting the HTTP Depth header and indicates that the operation should traverse the entire tree
- */
- const DEPTH_INFINITY = -1;
-
- /**
- * Nodes that are files, should have this as the type property
- */
- const NODE_FILE = 1;
-
- /**
- * Nodes that are directories, should use this value as the type property
- */
- const NODE_DIRECTORY = 2;
-
- /**
- * XML namespace for all SabreDAV related elements
- */
- const NS_SABREDAV = 'http://sabredav.org/ns';
-
- /**
- * The tree object
- *
- * @var Sabre\DAV\Tree
- */
- public $tree;
-
- /**
- * The base uri
- *
- * @var string
- */
- protected $baseUri = null;
-
- /**
- * httpResponse
- *
- * @var Sabre\HTTP\Response
- */
- public $httpResponse;
-
- /**
- * httpRequest
- *
- * @var Sabre\HTTP\Request
- */
- public $httpRequest;
-
- /**
- * The list of plugins
- *
- * @var array
- */
- protected $plugins = array();
-
- /**
- * This array contains a list of callbacks we should call when certain events are triggered
- *
- * @var array
- */
- protected $eventSubscriptions = array();
-
- /**
- * This is a default list of namespaces.
- *
- * If you are defining your own custom namespace, add it here to reduce
- * bandwidth and improve legibility of xml bodies.
- *
- * @var array
- */
- public $xmlNamespaces = array(
- 'DAV:' => 'd',
- 'http://sabredav.org/ns' => 's',
- );
-
- /**
- * The propertymap can be used to map properties from
- * requests to property classes.
- *
- * @var array
- */
- public $propertyMap = array(
- '{DAV:}resourcetype' => 'Sabre\\DAV\\Property\\ResourceType',
- );
-
- public $protectedProperties = array(
- // RFC4918
- '{DAV:}getcontentlength',
- '{DAV:}getetag',
- '{DAV:}getlastmodified',
- '{DAV:}lockdiscovery',
- '{DAV:}supportedlock',
-
- // RFC4331
- '{DAV:}quota-available-bytes',
- '{DAV:}quota-used-bytes',
-
- // RFC3744
- '{DAV:}supported-privilege-set',
- '{DAV:}current-user-privilege-set',
- '{DAV:}acl',
- '{DAV:}acl-restrictions',
- '{DAV:}inherited-acl-set',
-
- );
-
- /**
- * This is a flag that allow or not showing file, line and code
- * of the exception in the returned XML
- *
- * @var bool
- */
- public $debugExceptions = false;
-
- /**
- * This property allows you to automatically add the 'resourcetype' value
- * based on a node's classname or interface.
- *
- * The preset ensures that {DAV:}collection is automaticlly added for nodes
- * implementing Sabre\DAV\ICollection.
- *
- * @var array
- */
- public $resourceTypeMapping = array(
- 'Sabre\\DAV\\ICollection' => '{DAV:}collection',
- );
-
- /**
- * If this setting is turned off, SabreDAV's version number will be hidden
- * from various places.
- *
- * Some people feel this is a good security measure.
- *
- * @var bool
- */
- static public $exposeVersion = true;
-
- /**
- * Sets up the server
- *
- * If a Sabre\DAV\Tree object is passed as an argument, it will
- * use it as the directory tree. If a Sabre\DAV\INode is passed, it
- * will create a Sabre\DAV\ObjectTree and use the node as the root.
- *
- * If nothing is passed, a Sabre\DAV\SimpleCollection is created in
- * a Sabre\DAV\ObjectTree.
- *
- * If an array is passed, we automatically create a root node, and use
- * the nodes in the array as top-level children.
- *
- * @param Tree|INode|array|null $treeOrNode The tree object
- */
- public function __construct($treeOrNode = null) {
-
- if ($treeOrNode instanceof Tree) {
- $this->tree = $treeOrNode;
- } elseif ($treeOrNode instanceof INode) {
- $this->tree = new ObjectTree($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 ObjectTree($root);
-
- } elseif (is_null($treeOrNode)) {
- $root = new SimpleCollection('root');
- $this->tree = new ObjectTree($root);
- } else {
- throw new Exception('Invalid argument passed to constructor. Argument must either be an instance of Sabre\\DAV\\Tree, Sabre\\DAV\\INode, an array or null');
- }
- $this->httpResponse = new HTTP\Response();
- $this->httpRequest = new HTTP\Request();
-
- }
-
- /**
- * Starts the DAV Server
- *
- * @return void
- */
- public function exec() {
-
- try {
-
- // If nginx (pre-1.2) is used as a proxy server, and SabreDAV as an
- // origin, we must make sure we send back HTTP/1.0 if this was
- // requested.
- // This is mainly because nginx doesn't support Chunked Transfer
- // Encoding, and this forces the webserver SabreDAV is running on,
- // to buffer entire responses to calculate Content-Length.
- $this->httpResponse->defaultHttpVersion = $this->httpRequest->getHTTPVersion();
-
- $this->invokeMethod($this->httpRequest->getMethod(), $this->getRequestUri());
-
- } catch (Exception $e) {
-
- try {
- $this->broadcastEvent('exception', array($e));
- } catch (Exception $ignore) {
- }
- $DOM = new \DOMDocument('1.0','utf-8');
- $DOM->formatOutput = true;
-
- $error = $DOM->createElementNS('DAV:','d:error');
- $error->setAttribute('xmlns:s',self::NS_SABREDAV);
- $DOM->appendChild($error);
-
- $h = function($v) {
-
- return htmlspecialchars($v, ENT_NOQUOTES, 'UTF-8');
-
- };
-
- $error->appendChild($DOM->createElement('s:exception',$h(get_class($e))));
- $error->appendChild($DOM->createElement('s:message',$h($e->getMessage())));
- if ($this->debugExceptions) {
- $error->appendChild($DOM->createElement('s:file',$h($e->getFile())));
- $error->appendChild($DOM->createElement('s:line',$h($e->getLine())));
- $error->appendChild($DOM->createElement('s:code',$h($e->getCode())));
- $error->appendChild($DOM->createElement('s:stacktrace',$h($e->getTraceAsString())));
-
- }
- if (self::$exposeVersion) {
- $error->appendChild($DOM->createElement('s:sabredav-version',$h(Version::VERSION)));
- }
-
- if($e instanceof Exception) {
-
- $httpCode = $e->getHTTPCode();
- $e->serialize($this,$error);
- $headers = $e->getHTTPHeaders($this);
-
- } else {
-
- $httpCode = 500;
- $headers = array();
-
- }
- $headers['Content-Type'] = 'application/xml; charset=utf-8';
-
- $this->httpResponse->sendStatus($httpCode);
- $this->httpResponse->setHeaders($headers);
- $this->httpResponse->sendBody($DOM->saveXML());
-
- }
-
- }
-
- /**
- * Sets the base server uri
- *
- * @param string $uri
- * @return void
- */
- public function setBaseUri($uri) {
-
- // If the baseUri does not end with a slash, we must add it
- if ($uri[strlen($uri)-1]!=='/')
- $uri.='/';
-
- $this->baseUri = $uri;
-
- }
-
- /**
- * Returns the base responding uri
- *
- * @return string
- */
- public function getBaseUri() {
-
- if (is_null($this->baseUri)) $this->baseUri = $this->guessBaseUri();
- return $this->baseUri;
-
- }
-
- /**
- * This method attempts to detect the base uri.
- * Only the PATH_INFO variable is considered.
- *
- * If this variable is not set, the root (/) is assumed.
- *
- * @return string
- */
- 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,'?'))
- $uri = substr($uri,0,$pos);
-
- // PATH_INFO is only set for urls, such as: /example.php/path
- // in that case PATH_INFO contains '/path'.
- // Note that REQUEST_URI is percent encoded, while PATH_INFO is
- // not, Therefore they are only comparable if we first decode
- // REQUEST_INFO as well.
- $decodedUri = URLUtil::decodePath($uri);
-
- // A simple sanity check:
- if(substr($decodedUri,strlen($decodedUri)-strlen($pathInfo))===$pathInfo) {
- $baseUri = substr($decodedUri,0,strlen($decodedUri)-strlen($pathInfo));
- return rtrim($baseUri,'/') . '/';
- }
-
- throw new Exception('The REQUEST_URI ('. $uri . ') did not end with the contents of PATH_INFO (' . $pathInfo . '). This server might be misconfigured.');
-
- }
-
- // The last fallback is that we're just going to assume the server root.
- return '/';
-
- }
-
- /**
- * Adds a plugin to the server
- *
- * For more information, console the documentation of Sabre\DAV\ServerPlugin
- *
- * @param ServerPlugin $plugin
- * @return void
- */
- public function addPlugin(ServerPlugin $plugin) {
-
- $this->plugins[$plugin->getPluginName()] = $plugin;
- $plugin->initialize($this);
-
- }
-
- /**
- * Returns an initialized plugin by it's name.
- *
- * This function returns null if the plugin was not found.
- *
- * @param string $name
- * @return ServerPlugin
- */
- public function getPlugin($name) {
-
- if (isset($this->plugins[$name]))
- return $this->plugins[$name];
-
- // This is a fallback and deprecated.
- foreach($this->plugins as $plugin) {
- if (get_class($plugin)===$name) return $plugin;
- }
-
- return null;
-
- }
-
- /**
- * Returns all plugins
- *
- * @return array
- */
- public function getPlugins() {
-
- return $this->plugins;
-
- }
-
-
- /**
- * Subscribe to an event.
- *
- * When the event is triggered, we'll call all the specified callbacks.
- * It is possible to control the order of the callbacks through the
- * priority argument.
- *
- * This is for example used to make sure that the authentication plugin
- * is triggered before anything else. If it's not needed to change this
- * number, it is recommended to ommit.
- *
- * @param string $event
- * @param callback $callback
- * @param int $priority
- * @return void
- */
- public function subscribeEvent($event, $callback, $priority = 100) {
-
- if (!isset($this->eventSubscriptions[$event])) {
- $this->eventSubscriptions[$event] = array();
- }
- while(isset($this->eventSubscriptions[$event][$priority])) $priority++;
- $this->eventSubscriptions[$event][$priority] = $callback;
- ksort($this->eventSubscriptions[$event]);
-
- }
-
- /**
- * Broadcasts an event
- *
- * This method will call all subscribers. If one of the subscribers returns false, the process stops.
- *
- * The arguments parameter will be sent to all subscribers
- *
- * @param string $eventName
- * @param array $arguments
- * @return bool
- */
- public function broadcastEvent($eventName,$arguments = array()) {
-
- if (isset($this->eventSubscriptions[$eventName])) {
-
- foreach($this->eventSubscriptions[$eventName] as $subscriber) {
-
- $result = call_user_func_array($subscriber,$arguments);
- if ($result===false) return false;
-
- }
-
- }
-
- return true;
-
- }
-
- /**
- * Handles a http request, and execute a method based on its name
- *
- * @param string $method
- * @param string $uri
- * @return void
- */
- public function invokeMethod($method, $uri) {
-
- $method = strtoupper($method);
-
- if (!$this->broadcastEvent('beforeMethod',array($method, $uri))) return;
-
- // Make sure this is a HTTP method we support
- $internalMethods = array(
- 'OPTIONS',
- 'GET',
- 'HEAD',
- 'DELETE',
- 'PROPFIND',
- 'MKCOL',
- 'PUT',
- 'PROPPATCH',
- 'COPY',
- 'MOVE',
- 'REPORT'
- );
-
- if (in_array($method,$internalMethods)) {
-
- call_user_func(array($this,'http' . $method), $uri);
-
- } else {
-
- if ($this->broadcastEvent('unknownMethod',array($method, $uri))) {
- // Unsupported method
- throw new Exception\NotImplemented('There was no handler found for this "' . $method . '" method');
- }
-
- }
-
- }
-
- // {{{ HTTP Method implementations
-
- /**
- * HTTP OPTIONS
- *
- * @param string $uri
- * @return void
- */
- protected function httpOptions($uri) {
-
- $methods = $this->getAllowedMethods($uri);
-
- $this->httpResponse->setHeader('Allow',strtoupper(implode(', ',$methods)));
- $features = array('1','3', 'extended-mkcol');
-
- foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures());
-
- $this->httpResponse->setHeader('DAV',implode(', ',$features));
- $this->httpResponse->setHeader('MS-Author-Via','DAV');
- $this->httpResponse->setHeader('Accept-Ranges','bytes');
- if (self::$exposeVersion) {
- $this->httpResponse->setHeader('X-Sabre-Version',Version::VERSION);
- }
- $this->httpResponse->setHeader('Content-Length',0);
- $this->httpResponse->sendStatus(200);
-
- }
-
- /**
- * HTTP GET
- *
- * This method simply fetches the contents of a uri, like normal
- *
- * @param string $uri
- * @return bool
- */
- protected function httpGet($uri) {
-
- $node = $this->tree->getNodeForPath($uri,0);
-
- if (!$this->checkPreconditions(true)) return false;
- if (!$node instanceof IFile) throw new Exception\NotImplemented('GET is only implemented on File objects');
-
- $body = $node->get();
-
- // Converting string into stream, if needed.
- if (is_string($body)) {
- $stream = fopen('php://temp','r+');
- fwrite($stream,$body);
- rewind($stream);
- $body = $stream;
- }
-
- /*
- * TODO: getetag, getlastmodified, getsize should also be used using
- * this method
- */
- $httpHeaders = $this->getHTTPHeaders($uri);
-
- /* ContentType needs to get a default, because many webservers will otherwise
- * default to text/html, and we don't want this for security reasons.
- */
- if (!isset($httpHeaders['Content-Type'])) {
- $httpHeaders['Content-Type'] = 'application/octet-stream';
- }
-
-
- if (isset($httpHeaders['Content-Length'])) {
-
- $nodeSize = $httpHeaders['Content-Length'];
-
- // Need to unset Content-Length, because we'll handle that during figuring out the range
- unset($httpHeaders['Content-Length']);
-
- } else {
- $nodeSize = null;
- }
-
- $this->httpResponse->setHeaders($httpHeaders);
-
- $range = $this->getHTTPRange();
- $ifRange = $this->httpRequest->getHeader('If-Range');
- $ignoreRangeHeader = false;
-
- // If ifRange is set, and range is specified, we first need to check
- // the precondition.
- if ($nodeSize && $range && $ifRange) {
-
- // if IfRange is parsable as a date we'll treat it as a DateTime
- // otherwise, we must treat it as an etag.
- try {
- $ifRangeDate = new \DateTime($ifRange);
-
- // It's a date. We must check if the entity is modified since
- // the specified date.
- if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true;
- else {
- $modified = new \DateTime($httpHeaders['Last-Modified']);
- if($modified > $ifRangeDate) $ignoreRangeHeader = true;
- }
-
- } catch (\Exception $e) {
-
- // It's an entity. We can do a simple comparison.
- if (!isset($httpHeaders['ETag'])) $ignoreRangeHeader = true;
- elseif ($httpHeaders['ETag']!==$ifRange) $ignoreRangeHeader = true;
- }
- }
-
- // We're only going to support HTTP ranges if the backend provided a filesize
- if (!$ignoreRangeHeader && $nodeSize && $range) {
-
- // Determining the exact byte offsets
- if (!is_null($range[0])) {
-
- $start = $range[0];
- $end = $range[1]?$range[1]:$nodeSize-1;
- if($start >= $nodeSize)
- throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')');
-
- if($end < $start) throw new Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')');
- if($end >= $nodeSize) $end = $nodeSize-1;
-
- } else {
-
- $start = $nodeSize-$range[1];
- $end = $nodeSize-1;
-
- if ($start<0) $start = 0;
-
- }
-
- // New read/write stream
- $newStream = fopen('php://temp','r+');
-
- // stream_copy_to_stream() has a bug/feature: the `whence` argument
- // is interpreted as SEEK_SET (count from absolute offset 0), while
- // for a stream it should be SEEK_CUR (count from current offset).
- // If a stream is nonseekable, the function fails. So we *emulate*
- // the correct behaviour with fseek():
- if ($start > 0) {
- if (($curOffs = ftell($body)) === false) $curOffs = 0;
- fseek($body, $start - $curOffs, SEEK_CUR);
- }
- stream_copy_to_stream($body, $newStream, $end-$start+1);
- rewind($newStream);
-
- $this->httpResponse->setHeader('Content-Length', $end-$start+1);
- $this->httpResponse->setHeader('Content-Range','bytes ' . $start . '-' . $end . '/' . $nodeSize);
- $this->httpResponse->sendStatus(206);
- $this->httpResponse->sendBody($newStream);
-
-
- } else {
-
- if ($nodeSize) $this->httpResponse->setHeader('Content-Length',$nodeSize);
- $this->httpResponse->sendStatus(200);
- $this->httpResponse->sendBody($body);
-
- }
-
- }
-
- /**
- * 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 string $uri
- * @return void
- */
- protected function httpHead($uri) {
-
- $node = $this->tree->getNodeForPath($uri);
- /* This information is only collection for File objects.
- * Ideally we want to throw 405 Method Not Allowed for every
- * non-file, but MS Office does not like this
- */
- if ($node instanceof IFile) {
- $headers = $this->getHTTPHeaders($this->getRequestUri());
- if (!isset($headers['Content-Type'])) {
- $headers['Content-Type'] = 'application/octet-stream';
- }
- $this->httpResponse->setHeaders($headers);
- }
- $this->httpResponse->sendStatus(200);
-
- }
-
- /**
- * HTTP Delete
- *
- * The HTTP delete method, deletes a given uri
- *
- * @param string $uri
- * @return void
- */
- protected function httpDelete($uri) {
-
- // Checking If-None-Match and related headers.
- if (!$this->checkPreconditions()) return;
-
- if (!$this->broadcastEvent('beforeUnbind',array($uri))) return;
- $this->tree->delete($uri);
- $this->broadcastEvent('afterUnbind',array($uri));
-
- $this->httpResponse->sendStatus(204);
- $this->httpResponse->setHeader('Content-Length','0');
-
- }
-
-
- /**
- * WebDAV PROPFIND
- *
- * This WebDAV method requests information about an uri resource, or a list of resources
- * If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value
- * If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory)
- *
- * The request body contains an XML data structure that has a list of properties the client understands
- * The response body is also an xml document, containing information about every uri resource and the requested properties
- *
- * It has to return a HTTP 207 Multi-status status code
- *
- * @param string $uri
- * @return void
- */
- protected function httpPropfind($uri) {
-
- $requestedProperties = $this->parsePropFindRequest($this->httpRequest->getBody(true));
-
- $depth = $this->getHTTPDepth(1);
- // The only two options for the depth of a propfind is 0 or 1
- if ($depth!=0) $depth = 1;
-
- $newProperties = $this->getPropertiesForPath($uri,$requestedProperties,$depth);
-
- // This is a multi-status response
- $this->httpResponse->sendStatus(207);
- $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
- $this->httpResponse->setHeader('Vary','Brief,Prefer');
-
- // Normally this header is only needed for OPTIONS responses, however..
- // iCal seems to also depend on these being set for PROPFIND. Since
- // this is not harmful, we'll add it.
- $features = array('1','3', 'extended-mkcol');
- foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures());
- $this->httpResponse->setHeader('DAV',implode(', ',$features));
-
- $prefer = $this->getHTTPPrefer();
- $minimal = $prefer['return-minimal'];
-
- $data = $this->generateMultiStatus($newProperties, $minimal);
- $this->httpResponse->sendBody($data);
-
- }
-
- /**
- * 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 string $uri
- * @return void
- */
- protected function httpPropPatch($uri) {
-
- $newProperties = $this->parsePropPatchRequest($this->httpRequest->getBody(true));
-
- $result = $this->updateProperties($uri, $newProperties);
-
- $prefer = $this->getHTTPPrefer();
- $this->httpResponse->setHeader('Vary','Brief,Prefer');
-
- if ($prefer['return-minimal']) {
-
- // If return-minimal is specified, we only have to check if the
- // request was succesful, and don't need to return the
- // multi-status.
- $ok = true;
- foreach($result as $code=>$prop) {
- if ((int)$code > 299) {
- $ok = false;
- }
- }
-
- if ($ok) {
-
- $this->httpResponse->sendStatus(204);
- return;
-
- }
-
- }
-
- $this->httpResponse->sendStatus(207);
- $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
-
- $this->httpResponse->sendBody(
- $this->generateMultiStatus(array($result))
- );
-
- }
-
- /**
- * 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 string $uri
- * @return bool
- */
- protected function httpPut($uri) {
-
- $body = $this->httpRequest->getBody();
-
- // Intercepting Content-Range
- if ($this->httpRequest->getHeader('Content-Range')) {
- /**
- Content-Range is dangerous for PUT requests: PUT per definition
- stores a full resource. draft-ietf-httpbis-p2-semantics-15 says
- in section 7.6:
- An origin server SHOULD reject any PUT request that contains a
- Content-Range header field, since it might be misinterpreted as
- partial content (or might be partial content that is being mistakenly
- PUT as a full representation). Partial content updates are possible
- by targeting a separately identified resource with state that
- overlaps a portion of the larger resource, or by using a different
- method that has been specifically defined for partial updates (for
- example, the PATCH method defined in [RFC5789]).
- This clarifies RFC2616 section 9.6:
- The recipient of the entity MUST NOT ignore any Content-*
- (e.g. Content-Range) headers that it does not understand or implement
- and MUST return a 501 (Not Implemented) response in such cases.
- OTOH is a PUT request with a Content-Range currently the only way to
- continue an aborted upload request and is supported by curl, mod_dav,
- Tomcat and others. Since some clients do use this feature which results
- in unexpected behaviour (cf PEAR::HTTP_WebDAV_Client 1.0.1), we reject
- all PUT requests with a Content-Range for now.
- */
-
- throw new Exception\NotImplemented('PUT with Content-Range is not allowed.');
- }
-
- // Intercepting the Finder problem
- if (($expected = $this->httpRequest->getHeader('X-Expected-Entity-Length')) && $expected > 0) {
-
- /**
- Many webservers will not cooperate well with Finder PUT requests,
- because it uses 'Chunked' transfer encoding for the request body.
-
- The symptom of this problem is that Finder sends files to the
- server, but they arrive as 0-length files in PHP.
-
- If we don't do anything, the user might think they are uploading
- files successfully, but they end up empty on the server. Instead,
- we throw back an error if we detect this.
-
- The reason Finder uses Chunked, is because it thinks the files
- might change as it's being uploaded, and therefore the
- Content-Length can vary.
-
- Instead it sends the X-Expected-Entity-Length header with the size
- of the file at the very start of the request. If this header is set,
- but we don't get a request body we will fail the request to
- protect the end-user.
- */
-
- // Only reading first byte
- $firstByte = fread($body,1);
- if (strlen($firstByte)!==1) {
- throw new Exception\Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.');
- }
-
- // The body needs to stay intact, so we copy everything to a
- // temporary stream.
-
- $newBody = fopen('php://temp','r+');
- fwrite($newBody,$firstByte);
- stream_copy_to_stream($body, $newBody);
- rewind($newBody);
-
- $body = $newBody;
-
- }
-
- // Checking If-None-Match and related headers.
- if (!$this->checkPreconditions()) return;
-
- if ($this->tree->nodeExists($uri)) {
-
- $node = $this->tree->getNodeForPath($uri);
-
- // If the node is a collection, we'll deny it
- if (!($node instanceof IFile)) throw new Exception\Conflict('PUT is not allowed on non-files.');
- if (!$this->broadcastEvent('beforeWriteContent',array($uri, $node, &$body))) return false;
-
- $etag = $node->put($body);
-
- $this->broadcastEvent('afterWriteContent',array($uri, $node));
-
- $this->httpResponse->setHeader('Content-Length','0');
- if ($etag) $this->httpResponse->setHeader('ETag',$etag);
- $this->httpResponse->sendStatus(204);
-
- } else {
-
- $etag = null;
- // If we got here, the resource didn't exist yet.
- if (!$this->createFile($this->getRequestUri(),$body,$etag)) {
- // For one reason or another the file was not created.
- return;
- }
-
- $this->httpResponse->setHeader('Content-Length','0');
- if ($etag) $this->httpResponse->setHeader('ETag', $etag);
- $this->httpResponse->sendStatus(201);
-
- }
-
- }
-
-
- /**
- * WebDAV MKCOL
- *
- * The MKCOL method is used to create a new collection (directory) on the server
- *
- * @param string $uri
- * @return void
- */
- protected function httpMkcol($uri) {
-
- $requestBody = $this->httpRequest->getBody(true);
-
- if ($requestBody) {
-
- $contentType = $this->httpRequest->getHeader('Content-Type');
- if (strpos($contentType,'application/xml')!==0 && strpos($contentType,'text/xml')!==0) {
-
- // We must throw 415 for unsupported mkcol bodies
- throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type');
-
- }
-
- $dom = XMLUtil::loadDOMDocument($requestBody);
- if (XMLUtil::toClarkNotation($dom->firstChild)!=='{DAV:}mkcol') {
-
- // We must throw 415 for unsupported mkcol bodies
- throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must be a {DAV:}mkcol request construct.');
-
- }
-
- $properties = array();
- foreach($dom->firstChild->childNodes as $childNode) {
-
- if (XMLUtil::toClarkNotation($childNode)!=='{DAV:}set') continue;
- $properties = array_merge($properties, XMLUtil::parseProperties($childNode, $this->propertyMap));
-
- }
- if (!isset($properties['{DAV:}resourcetype']))
- throw new Exception\BadRequest('The mkcol request must include a {DAV:}resourcetype property');
-
- $resourceType = $properties['{DAV:}resourcetype']->getValue();
- unset($properties['{DAV:}resourcetype']);
-
- } else {
-
- $properties = array();
- $resourceType = array('{DAV:}collection');
-
- }
-
- $result = $this->createCollection($uri, $resourceType, $properties);
-
- if (is_array($result)) {
- $this->httpResponse->sendStatus(207);
- $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
-
- $this->httpResponse->sendBody(
- $this->generateMultiStatus(array($result))
- );
-
- } else {
- $this->httpResponse->setHeader('Content-Length','0');
- $this->httpResponse->sendStatus(201);
- }
-
- }
-
- /**
- * 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 string $uri
- * @return bool
- */
- protected function httpMove($uri) {
-
- $moveInfo = $this->getCopyAndMoveInfo();
-
- // If the destination is part of the source tree, we must fail
- if ($moveInfo['destination']==$uri)
- throw new Exception\Forbidden('Source and destination uri are identical.');
-
- if ($moveInfo['destinationExists']) {
-
- if (!$this->broadcastEvent('beforeUnbind',array($moveInfo['destination']))) return false;
- $this->tree->delete($moveInfo['destination']);
- $this->broadcastEvent('afterUnbind',array($moveInfo['destination']));
-
- }
-
- if (!$this->broadcastEvent('beforeUnbind',array($uri))) return false;
- if (!$this->broadcastEvent('beforeBind',array($moveInfo['destination']))) return false;
- $this->tree->move($uri,$moveInfo['destination']);
- $this->broadcastEvent('afterUnbind',array($uri));
- $this->broadcastEvent('afterBind',array($moveInfo['destination']));
-
- // If a resource was overwritten we should send a 204, otherwise a 201
- $this->httpResponse->setHeader('Content-Length','0');
- $this->httpResponse->sendStatus($moveInfo['destinationExists']?204:201);
-
- }
-
- /**
- * 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 string $uri
- * @return bool
- */
- protected function httpCopy($uri) {
-
- $copyInfo = $this->getCopyAndMoveInfo();
- // If the destination is part of the source tree, we must fail
- if ($copyInfo['destination']==$uri)
- throw new Exception\Forbidden('Source and destination uri are identical.');
-
- if ($copyInfo['destinationExists']) {
- if (!$this->broadcastEvent('beforeUnbind',array($copyInfo['destination']))) return false;
- $this->tree->delete($copyInfo['destination']);
-
- }
- if (!$this->broadcastEvent('beforeBind',array($copyInfo['destination']))) return false;
- $this->tree->copy($uri,$copyInfo['destination']);
- $this->broadcastEvent('afterBind',array($copyInfo['destination']));
-
- // If a resource was overwritten we should send a 204, otherwise a 201
- $this->httpResponse->setHeader('Content-Length','0');
- $this->httpResponse->sendStatus($copyInfo['destinationExists']?204:201);
-
- }
-
-
-
- /**
- * 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 string $uri
- * @return void
- */
- protected function httpReport($uri) {
-
- $body = $this->httpRequest->getBody(true);
- $dom = XMLUtil::loadDOMDocument($body);
-
- $reportName = XMLUtil::toClarkNotation($dom->firstChild);
-
- if ($this->broadcastEvent('report',array($reportName,$dom, $uri))) {
-
- // If broadcastEvent returned true, it means the report was not supported
- throw new Exception\ReportNotSupported();
-
- }
-
- }
-
- // }}}
- // {{{ HTTP/WebDAV protocol helpers
-
- /**
- * Returns an array with all the supported HTTP methods for a specific uri.
- *
- * @param string $uri
- * @return array
- */
- public function getAllowedMethods($uri) {
-
- $methods = array(
- 'OPTIONS',
- 'GET',
- 'HEAD',
- 'DELETE',
- 'PROPFIND',
- 'PUT',
- 'PROPPATCH',
- 'COPY',
- 'MOVE',
- 'REPORT'
- );
-
- // The MKCOL is only allowed on an unmapped uri
- try {
- $this->tree->getNodeForPath($uri);
- } catch (Exception\NotFound $e) {
- $methods[] = 'MKCOL';
- }
-
- // We're also checking if any of the plugins register any new methods
- foreach($this->plugins as $plugin) $methods = array_merge($methods, $plugin->getHTTPMethods($uri));
- array_unique($methods);
-
- return $methods;
-
- }
-
- /**
- * Gets the uri for the request, keeping the base uri into consideration
- *
- * @return string
- */
- public function getRequestUri() {
-
- return $this->calculateUri($this->httpRequest->getUri());
-
- }
-
- /**
- * Calculates the uri for a request, making sure that the base uri is stripped out
- *
- * @param string $uri
- * @throws Exception\Forbidden A permission denied exception is thrown whenever there was an attempt to supply a uri outside of the base uri
- * @return string
- */
- public function calculateUri($uri) {
-
- if ($uri[0]!='/' && strpos($uri,'://')) {
-
- $uri = parse_url($uri,PHP_URL_PATH);
-
- }
-
- $uri = str_replace('//','/',$uri);
-
- if (strpos($uri,$this->getBaseUri())===0) {
-
- return trim(URLUtil::decodePath(substr($uri,strlen($this->getBaseUri()))),'/');
-
- // A special case, if the baseUri was accessed without a trailing
- // slash, we'll accept it as well.
- } elseif ($uri.'/' === $this->getBaseUri()) {
-
- return '';
-
- } else {
-
- throw new Exception\Forbidden('Requested uri (' . $uri . ') is out of base uri (' . $this->getBaseUri() . ')');
-
- }
-
- }
-
- /**
- * Returns the HTTP depth header
- *
- * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre\DAV\Server::DEPTH_INFINITY object
- * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existent
- *
- * @param mixed $default
- * @return int
- */
- 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 its an unknown value. we'll grab the default
- if (!ctype_digit($depth)) return $default;
-
- return (int)$depth;
-
- }
-
- /**
- * Returns the HTTP range header
- *
- * This method returns null if there is no well-formed HTTP range request
- * header or array($start, $end).
- *
- * The first number is the offset of the first byte in the range.
- * The second number is the offset of the last byte in the range.
- *
- * If the second offset is null, it should be treated as the offset of the last byte of the entity
- * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity
- *
- * @return array|null
- */
- public function getHTTPRange() {
-
- $range = $this->httpRequest->getHeader('range');
- if (is_null($range)) return null;
-
- // Matching "Range: bytes=1234-5678: both numbers are optional
-
- if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i',$range,$matches)) return null;
-
- if ($matches[1]==='' && $matches[2]==='') return null;
-
- return array(
- $matches[1]!==''?$matches[1]:null,
- $matches[2]!==''?$matches[2]:null,
- );
-
- }
-
- /**
- * Returns the HTTP Prefer header information.
- *
- * The prefer header is defined in:
- * http://tools.ietf.org/html/draft-snell-http-prefer-14
- *
- * This method will return an array with options.
- *
- * Currently, the following options may be returned:
- * array(
- * 'return-asynch' => true,
- * 'return-minimal' => true,
- * 'return-representation' => true,
- * 'wait' => 30,
- * 'strict' => true,
- * 'lenient' => true,
- * )
- *
- * This method also supports the Brief header, and will also return
- * 'return-minimal' if the brief header was set to 't'.
- *
- * For the boolean options, false will be returned if the headers are not
- * specified. For the integer options it will be 'null'.
- *
- * @return array
- */
- public function getHTTPPrefer() {
-
- $result = array(
- 'return-asynch' => false,
- 'return-minimal' => false,
- 'return-representation' => false,
- 'wait' => null,
- 'strict' => false,
- 'lenient' => false,
- );
-
- if ($prefer = $this->httpRequest->getHeader('Prefer')) {
-
- $parameters = array_map('trim',
- explode(',', $prefer)
- );
-
- foreach($parameters as $parameter) {
-
- // Right now our regex only supports the tokens actually
- // specified in the draft. We may need to expand this if new
- // tokens get registered.
- if(!preg_match('/^(?P<token>[a-z0-9-]+)(?:=(?P<value>[0-9]+))?$/', $parameter, $matches)) {
- continue;
- }
-
- switch($matches['token']) {
-
- case 'return-asynch' :
- case 'return-minimal' :
- case 'return-representation' :
- case 'strict' :
- case 'lenient' :
- $result[$matches['token']] = true;
- break;
- case 'wait' :
- $result[$matches['token']] = $matches['value'];
- break;
-
- }
-
- }
-
- }
-
- if ($this->httpRequest->getHeader('Brief')=='t') {
- $result['return-minimal'] = true;
- }
-
- return $result;
-
- }
-
-
- /**
- * Returns information about Copy and Move requests
- *
- * This function is created to help getting information about the source and the destination for the
- * WebDAV MOVE and COPY HTTP request. It also validates a lot of information and throws proper exceptions
- *
- * The returned value is an array with the following keys:
- * * destination - Destination path
- * * destinationExists - Whether or not the destination is an existing url (and should therefore be overwritten)
- *
- * @return array
- */
- public function getCopyAndMoveInfo() {
-
- // Collecting the relevant HTTP headers
- if (!$this->httpRequest->getHeader('Destination')) throw new Exception\BadRequest('The destination header was not supplied');
- $destination = $this->calculateUri($this->httpRequest->getHeader('Destination'));
- $overwrite = $this->httpRequest->getHeader('Overwrite');
- if (!$overwrite) $overwrite = 'T';
- if (strtoupper($overwrite)=='T') $overwrite = true;
- elseif (strtoupper($overwrite)=='F') $overwrite = false;
- // We need to throw a bad request exception, if the header was invalid
- else throw new Exception\BadRequest('The HTTP Overwrite header should be either T or F');
-
- list($destinationDir) = URLUtil::splitPath($destination);
-
- try {
- $destinationParent = $this->tree->getNodeForPath($destinationDir);
- if (!($destinationParent instanceof ICollection)) throw new Exception\UnsupportedMediaType('The destination node is not a collection');
- } catch (Exception\NotFound $e) {
-
- // If the destination parent node is not found, we throw a 409
- throw new Exception\Conflict('The destination node is not found');
- }
-
- try {
-
- $destinationNode = $this->tree->getNodeForPath($destination);
-
- // If this succeeded, it means the destination already exists
- // we'll need to throw precondition failed in case overwrite is false
- if (!$overwrite) throw new Exception\PreconditionFailed('The destination node already exists, and the overwrite header is set to false','Overwrite');
-
- } catch (Exception\NotFound $e) {
-
- // Destination didn't exist, we're all good
- $destinationNode = false;
-
-
-
- }
-
- // These are the three relevant properties we need to return
- return array(
- 'destination' => $destination,
- 'destinationExists' => $destinationNode==true,
- 'destinationNode' => $destinationNode,
- );
-
- }
-
- /**
- * Returns a list of properties for a path
- *
- * This is a simplified version getPropertiesForPath.
- * if you aren't interested in status codes, but you just
- * want to have a flat list of properties. Use this method.
- *
- * @param string $path
- * @param array $propertyNames
- */
- public function getProperties($path, $propertyNames) {
-
- $result = $this->getPropertiesForPath($path,$propertyNames,0);
- return $result[0][200];
-
- }
-
- /**
- * A kid-friendly way to fetch properties for a node's children.
- *
- * The returned array will be indexed by the path of the of child node.
- * Only properties that are actually found will be returned.
- *
- * The parent node will not be returned.
- *
- * @param string $path
- * @param array $propertyNames
- * @return array
- */
- public function getPropertiesForChildren($path, $propertyNames) {
-
- $result = array();
- foreach($this->getPropertiesForPath($path,$propertyNames,1) as $k=>$row) {
-
- // Skipping the parent path
- if ($k === 0) continue;
-
- $result[$row['href']] = $row[200];
-
- }
- return $result;
-
- }
-
- /**
- * Returns a list of HTTP headers for a particular resource
- *
- * The generated http headers are based on properties provided by the
- * resource. The method basically provides a simple mapping between
- * DAV property and HTTP header.
- *
- * The headers are intended to be used for HEAD and GET requests.
- *
- * @param string $path
- * @return array
- */
- public function getHTTPHeaders($path) {
-
- $propertyMap = array(
- '{DAV:}getcontenttype' => 'Content-Type',
- '{DAV:}getcontentlength' => 'Content-Length',
- '{DAV:}getlastmodified' => 'Last-Modified',
- '{DAV:}getetag' => 'ETag',
- );
-
- $properties = $this->getProperties($path,array_keys($propertyMap));
-
- $headers = array();
- foreach($propertyMap as $property=>$header) {
- if (!isset($properties[$property])) continue;
-
- if (is_scalar($properties[$property])) {
- $headers[$header] = $properties[$property];
-
- // GetLastModified gets special cased
- } elseif ($properties[$property] instanceof Property\GetLastModified) {
- $headers[$header] = HTTP\Util::toHTTPDate($properties[$property]->getTime());
- }
-
- }
-
- return $headers;
-
- }
-
- /**
- * Returns a list of properties for a given path
- *
- * The path that should be supplied should have the baseUrl stripped out
- * The list of properties should be supplied in Clark notation. If the list is empty
- * 'allprops' is assumed.
- *
- * If a depth of 1 is requested child elements will also be returned.
- *
- * @param string $path
- * @param array $propertyNames
- * @param int $depth
- * @return array
- */
- public function getPropertiesForPath($path, $propertyNames = array(), $depth = 0) {
-
- if ($depth!=0) $depth = 1;
-
- $path = rtrim($path,'/');
-
- // This event allows people to intercept these requests early on in the
- // process.
- //
- // We're not doing anything with the result, but this can be helpful to
- // pre-fetch certain expensive live properties.
- $this->broadCastEvent('beforeGetPropertiesForPath', array($path, $propertyNames, $depth));
-
- $returnPropertyList = array();
-
- $parentNode = $this->tree->getNodeForPath($path);
- $nodes = array(
- $path => $parentNode
- );
- if ($depth==1 && $parentNode instanceof ICollection) {
- foreach($this->tree->getChildren($path) as $childNode)
- $nodes[$path . '/' . $childNode->getName()] = $childNode;
- }
-
- // If the propertyNames array is empty, it means all properties are requested.
- // We shouldn't actually return everything we know though, and only return a
- // sensible list.
- $allProperties = count($propertyNames)==0;
-
- foreach($nodes as $myPath=>$node) {
-
- $currentPropertyNames = $propertyNames;
-
- $newProperties = array(
- '200' => array(),
- '404' => array(),
- );
-
- if ($allProperties) {
- // Default list of propertyNames, when all properties were requested.
- $currentPropertyNames = array(
- '{DAV:}getlastmodified',
- '{DAV:}getcontentlength',
- '{DAV:}resourcetype',
- '{DAV:}quota-used-bytes',
- '{DAV:}quota-available-bytes',
- '{DAV:}getetag',
- '{DAV:}getcontenttype',
- );
- }
-
- // If the resourceType was not part of the list, we manually add it
- // and mark it for removal. We need to know the resourcetype in order
- // to make certain decisions about the entry.
- // WebDAV dictates we should add a / and the end of href's for collections
- $removeRT = false;
- if (!in_array('{DAV:}resourcetype',$currentPropertyNames)) {
- $currentPropertyNames[] = '{DAV:}resourcetype';
- $removeRT = true;
- }
-
- $result = $this->broadcastEvent('beforeGetProperties',array($myPath, $node, &$currentPropertyNames, &$newProperties));
- // If this method explicitly returned false, we must ignore this
- // node as it is inaccessible.
- if ($result===false) continue;
-
- if (count($currentPropertyNames) > 0) {
-
- if ($node instanceof IProperties) {
- $nodeProperties = $node->getProperties($currentPropertyNames);
-
- // The getProperties method may give us too much,
- // properties, in case the implementor was lazy.
- //
- // So as we loop through this list, we will only take the
- // properties that were actually requested and discard the
- // rest.
- foreach($currentPropertyNames as $k=>$currentPropertyName) {
- if (isset($nodeProperties[$currentPropertyName])) {
- unset($currentPropertyNames[$k]);
- $newProperties[200][$currentPropertyName] = $nodeProperties[$currentPropertyName];
- }
- }
-
- }
-
- }
-
- foreach($currentPropertyNames as $prop) {
-
- if (isset($newProperties[200][$prop])) continue;
-
- switch($prop) {
- case '{DAV:}getlastmodified' : if ($node->getLastModified()) $newProperties[200][$prop] = new Property\GetLastModified($node->getLastModified()); break;
- case '{DAV:}getcontentlength' :
- if ($node instanceof IFile) {
- $size = $node->getSize();
- if (!is_null($size)) {
- $newProperties[200][$prop] = (int)$node->getSize();
- }
- }
- break;
- case '{DAV:}quota-used-bytes' :
- if ($node instanceof IQuota) {
- $quotaInfo = $node->getQuotaInfo();
- $newProperties[200][$prop] = $quotaInfo[0];
- }
- break;
- case '{DAV:}quota-available-bytes' :
- if ($node instanceof IQuota) {
- $quotaInfo = $node->getQuotaInfo();
- $newProperties[200][$prop] = $quotaInfo[1];
- }
- break;
- case '{DAV:}getetag' : if ($node instanceof IFile && $etag = $node->getETag()) $newProperties[200][$prop] = $etag; break;
- case '{DAV:}getcontenttype' : if ($node instanceof IFile && $ct = $node->getContentType()) $newProperties[200][$prop] = $ct; break;
- case '{DAV:}supported-report-set' :
- $reports = array();
- foreach($this->plugins as $plugin) {
- $reports = array_merge($reports, $plugin->getSupportedReportSet($myPath));
- }
- $newProperties[200][$prop] = new Property\SupportedReportSet($reports);
- break;
- case '{DAV:}resourcetype' :
- $newProperties[200]['{DAV:}resourcetype'] = new Property\ResourceType();
- foreach($this->resourceTypeMapping as $className => $resourceType) {
- if ($node instanceof $className) $newProperties[200]['{DAV:}resourcetype']->add($resourceType);
- }
- break;
-
- }
-
- // If we were unable to find the property, we will list it as 404.
- if (!$allProperties && !isset($newProperties[200][$prop])) $newProperties[404][$prop] = null;
-
- }
-
- $this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties, $node));
-
- $newProperties['href'] = trim($myPath,'/');
-
- // Its is a WebDAV recommendation to add a trailing slash to collectionnames.
- // Apple's iCal also requires a trailing slash for principals (rfc 3744), though this is non-standard.
- if ($myPath!='' && isset($newProperties[200]['{DAV:}resourcetype'])) {
- $rt = $newProperties[200]['{DAV:}resourcetype'];
- if ($rt->is('{DAV:}collection') || $rt->is('{DAV:}principal')) {
- $newProperties['href'] .='/';
- }
- }
-
- // If the resourcetype property was manually added to the requested property list,
- // we will remove it again.
- if ($removeRT) unset($newProperties[200]['{DAV:}resourcetype']);
-
- $returnPropertyList[] = $newProperties;
-
- }
-
- return $returnPropertyList;
-
- }
-
- /**
- * This method is invoked by sub-systems creating a new file.
- *
- * Currently this is done by HTTP PUT and HTTP LOCK (in the Locks_Plugin).
- * It was important to get this done through a centralized function,
- * allowing plugins to intercept this using the beforeCreateFile event.
- *
- * This method will return true if the file was actually created
- *
- * @param string $uri
- * @param resource $data
- * @param string $etag
- * @return bool
- */
- public function createFile($uri,$data, &$etag = null) {
-
- list($dir,$name) = URLUtil::splitPath($uri);
-
- if (!$this->broadcastEvent('beforeBind',array($uri))) return false;
-
- $parent = $this->tree->getNodeForPath($dir);
- if (!$parent instanceof ICollection) {
- throw new Exception\Conflict('Files can only be created as children of collections');
- }
-
- if (!$this->broadcastEvent('beforeCreateFile',array($uri, &$data, $parent))) return false;
-
- $etag = $parent->createFile($name,$data);
- $this->tree->markDirty($dir . '/' . $name);
-
- $this->broadcastEvent('afterBind',array($uri));
- $this->broadcastEvent('afterCreateFile',array($uri, $parent));
-
- return true;
- }
-
- /**
- * This method is invoked by sub-systems creating a new directory.
- *
- * @param string $uri
- * @return void
- */
- public function createDirectory($uri) {
-
- $this->createCollection($uri,array('{DAV:}collection'),array());
-
- }
-
- /**
- * Use this method to create a new collection
- *
- * The {DAV:}resourcetype is specified using the resourceType array.
- * At the very least it must contain {DAV:}collection.
- *
- * The properties array can contain a list of additional properties.
- *
- * @param string $uri The new uri
- * @param array $resourceType The resourceType(s)
- * @param array $properties A list of properties
- * @return array|null
- */
- public function createCollection($uri, array $resourceType, array $properties) {
-
- list($parentUri,$newName) = URLUtil::splitPath($uri);
-
- // Making sure {DAV:}collection was specified as resourceType
- if (!in_array('{DAV:}collection', $resourceType)) {
- throw new Exception\InvalidResourceType('The resourceType for this collection must at least include {DAV:}collection');
- }
-
-
- // Making sure the parent exists
- try {
-
- $parent = $this->tree->getNodeForPath($parentUri);
-
- } catch (Exception\NotFound $e) {
-
- throw new Exception\Conflict('Parent node does not exist');
-
- }
-
- // Making sure the parent is a collection
- if (!$parent instanceof ICollection) {
- throw new Exception\Conflict('Parent node is not a collection');
- }
-
-
-
- // Making sure the child does not already exist
- try {
- $parent->getChild($newName);
-
- // If we got here.. it means there's already a node on that url, and we need to throw a 405
- throw new Exception\MethodNotAllowed('The resource you tried to create already exists');
-
- } catch (Exception\NotFound $e) {
- // This is correct
- }
-
-
- if (!$this->broadcastEvent('beforeBind',array($uri))) return;
-
- // There are 2 modes of operation. The standard collection
- // creates the directory, and then updates properties
- // the extended collection can create it directly.
- if ($parent instanceof IExtendedCollection) {
-
- $parent->createExtendedCollection($newName, $resourceType, $properties);
-
- } else {
-
- // No special resourcetypes are supported
- if (count($resourceType)>1) {
- throw new Exception\InvalidResourceType('The {DAV:}resourcetype you specified is not supported here.');
- }
-
- $parent->createDirectory($newName);
- $rollBack = false;
- $exception = null;
- $errorResult = null;
-
- if (count($properties)>0) {
-
- try {
-
- $errorResult = $this->updateProperties($uri, $properties);
- if (!isset($errorResult[200])) {
- $rollBack = true;
- }
-
- } catch (Exception $e) {
-
- $rollBack = true;
- $exception = $e;
-
- }
-
- }
-
- if ($rollBack) {
- if (!$this->broadcastEvent('beforeUnbind',array($uri))) return;
- $this->tree->delete($uri);
-
- // Re-throwing exception
- if ($exception) throw $exception;
-
- return $errorResult;
- }
-
- }
- $this->tree->markDirty($parentUri);
- $this->broadcastEvent('afterBind',array($uri));
-
- }
-
- /**
- * This method updates a resource's properties
- *
- * The properties array must be a list of properties. Array-keys are
- * property names in clarknotation, array-values are it's values.
- * If a property must be deleted, the value should be null.
- *
- * Note that this request should either completely succeed, or
- * completely fail.
- *
- * The response is an array with statuscodes for keys, which in turn
- * contain arrays with propertynames. This response can be used
- * to generate a multistatus body.
- *
- * @param string $uri
- * @param array $properties
- * @return array
- */
- public function updateProperties($uri, array $properties) {
-
- // we'll start by grabbing the node, this will throw the appropriate
- // exceptions if it doesn't.
- $node = $this->tree->getNodeForPath($uri);
-
- $result = array(
- 200 => array(),
- 403 => array(),
- 424 => array(),
- );
- $remainingProperties = $properties;
- $hasError = false;
-
- // Running through all properties to make sure none of them are protected
- if (!$hasError) foreach($properties as $propertyName => $value) {
- if(in_array($propertyName, $this->protectedProperties)) {
- $result[403][$propertyName] = null;
- unset($remainingProperties[$propertyName]);
- $hasError = true;
- }
- }
-
- if (!$hasError) {
- // Allowing plugins to take care of property updating
- $hasError = !$this->broadcastEvent('updateProperties',array(
- &$remainingProperties,
- &$result,
- $node
- ));
- }
-
- // If the node is not an instance of Sabre\DAV\IProperties, every
- // property is 403 Forbidden
- if (!$hasError && count($remainingProperties) && !($node instanceof IProperties)) {
- $hasError = true;
- foreach($properties as $propertyName=> $value) {
- $result[403][$propertyName] = null;
- }
- $remainingProperties = array();
- }
-
- // Only if there were no errors we may attempt to update the resource
- if (!$hasError) {
-
- if (count($remainingProperties)>0) {
-
- $updateResult = $node->updateProperties($remainingProperties);
-
- if ($updateResult===true) {
- // success
- foreach($remainingProperties as $propertyName=>$value) {
- $result[200][$propertyName] = null;
- }
-
- } elseif ($updateResult===false) {
- // The node failed to update the properties for an
- // unknown reason
- foreach($remainingProperties as $propertyName=>$value) {
- $result[403][$propertyName] = null;
- }
-
- } elseif (is_array($updateResult)) {
-
- // The node has detailed update information
- // We need to merge the results with the earlier results.
- foreach($updateResult as $status => $props) {
- if (is_array($props)) {
- if (!isset($result[$status]))
- $result[$status] = array();
-
- $result[$status] = array_merge($result[$status], $updateResult[$status]);
- }
- }
-
- } else {
- throw new Exception('Invalid result from updateProperties');
- }
- $remainingProperties = array();
- }
-
- }
-
- foreach($remainingProperties as $propertyName=>$value) {
- // if there are remaining properties, it must mean
- // there's a dependency failure
- $result[424][$propertyName] = null;
- }
-
- // Removing empty array values
- foreach($result as $status=>$props) {
-
- if (count($props)===0) unset($result[$status]);
-
- }
- $result['href'] = $uri;
- return $result;
-
- }
-
- /**
- * This method checks the main HTTP preconditions.
- *
- * Currently these are:
- * * If-Match
- * * If-None-Match
- * * If-Modified-Since
- * * If-Unmodified-Since
- *
- * The method will return true if all preconditions are met
- * The method will return false, or throw an exception if preconditions
- * failed. If false is returned the operation should be aborted, and
- * the appropriate HTTP response headers are already set.
- *
- * Normally this method will throw 412 Precondition Failed for failures
- * related to If-None-Match, If-Match and If-Unmodified Since. It will
- * set the status to 304 Not Modified for If-Modified_since.
- *
- * If the $handleAsGET argument is set to true, it will also return 304
- * Not Modified for failure of the If-None-Match precondition. This is the
- * desired behaviour for HTTP GET and HTTP HEAD requests.
- *
- * @param bool $handleAsGET
- * @return bool
- */
- public function checkPreconditions($handleAsGET = false) {
-
- $uri = $this->getRequestUri();
- $node = null;
- $lastMod = null;
- $etag = null;
-
- if ($ifMatch = $this->httpRequest->getHeader('If-Match')) {
-
- // If-Match contains an entity tag. Only if the entity-tag
- // matches we are allowed to make the request succeed.
- // If the entity-tag is '*' we are only allowed to make the
- // request succeed if a resource exists at that url.
- try {
- $node = $this->tree->getNodeForPath($uri);
- } catch (Exception\NotFound $e) {
- throw new Exception\PreconditionFailed('An If-Match header was specified and the resource did not exist','If-Match');
- }
-
- // Only need to check entity tags if they are not *
- if ($ifMatch!=='*') {
-
- // There can be multiple etags
- $ifMatch = explode(',',$ifMatch);
- $haveMatch = false;
- foreach($ifMatch as $ifMatchItem) {
-
- // Stripping any extra spaces
- $ifMatchItem = trim($ifMatchItem,' ');
-
- $etag = $node->getETag();
- if ($etag===$ifMatchItem) {
- $haveMatch = true;
- } else {
- // Evolution has a bug where it sometimes prepends the "
- // with a \. This is our workaround.
- if (str_replace('\\"','"', $ifMatchItem) === $etag) {
- $haveMatch = true;
- }
- }
-
- }
- if (!$haveMatch) {
- throw new Exception\PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.','If-Match');
- }
- }
- }
-
- if ($ifNoneMatch = $this->httpRequest->getHeader('If-None-Match')) {
-
- // The If-None-Match header contains an etag.
- // Only if the ETag does not match the current ETag, the request will succeed
- // The header can also contain *, in which case the request
- // will only succeed if the entity does not exist at all.
- $nodeExists = true;
- if (!$node) {
- try {
- $node = $this->tree->getNodeForPath($uri);
- } catch (Exception\NotFound $e) {
- $nodeExists = false;
- }
- }
- if ($nodeExists) {
- $haveMatch = false;
- if ($ifNoneMatch==='*') $haveMatch = true;
- else {
-
- // There might be multiple etags
- $ifNoneMatch = explode(',', $ifNoneMatch);
- $etag = $node->getETag();
-
- foreach($ifNoneMatch as $ifNoneMatchItem) {
-
- // Stripping any extra spaces
- $ifNoneMatchItem = trim($ifNoneMatchItem,' ');
-
- if ($etag===$ifNoneMatchItem) $haveMatch = true;
-
- }
-
- }
-
- if ($haveMatch) {
- if ($handleAsGET) {
- $this->httpResponse->sendStatus(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 = $this->httpRequest->getHeader('If-Modified-Since'))) {
-
- // The If-Modified-Since header contains a date. We
- // will only return the entity if it has been changed since
- // that date. If it hasn't been changed, we return a 304
- // header
- // Note that this header only has to be checked if there was no If-None-Match header
- // as per the HTTP spec.
- $date = HTTP\Util::parseHTTPDate($ifModifiedSince);
-
- if ($date) {
- if (is_null($node)) {
- $node = $this->tree->getNodeForPath($uri);
- }
- $lastMod = $node->getLastModified();
- if ($lastMod) {
- $lastMod = new \DateTime('@' . $lastMod);
- if ($lastMod <= $date) {
- $this->httpResponse->sendStatus(304);
- $this->httpResponse->setHeader('Last-Modified', HTTP\Util::toHTTPDate($lastMod));
- return false;
- }
- }
- }
- }
-
- if ($ifUnmodifiedSince = $this->httpRequest->getHeader('If-Unmodified-Since')) {
-
- // The If-Unmodified-Since will allow allow the request if the
- // entity has not changed since the specified date.
- $date = HTTP\Util::parseHTTPDate($ifUnmodifiedSince);
-
- // We must only check the date if it's valid
- if ($date) {
- if (is_null($node)) {
- $node = $this->tree->getNodeForPath($uri);
- }
- $lastMod = $node->getLastModified();
- if ($lastMod) {
- $lastMod = new \DateTime('@' . $lastMod);
- if ($lastMod > $date) {
- throw new Exception\PreconditionFailed('An If-Unmodified-Since header was specified, but the entity has been changed since the specified date.','If-Unmodified-Since');
- }
- }
- }
-
- }
- return true;
-
- }
-
- // }}}
- // {{{ XML Readers & Writers
-
-
- /**
- * Generates a WebDAV propfind response body based on a list of nodes.
- *
- * If 'strip404s' is set to true, all 404 responses will be removed.
- *
- * @param array $fileProperties The list with nodes
- * @param bool strip404s
- * @return string
- */
- public function generateMultiStatus(array $fileProperties, $strip404s = false) {
-
- $dom = new \DOMDocument('1.0','utf-8');
- //$dom->formatOutput = true;
- $multiStatus = $dom->createElement('d:multistatus');
- $dom->appendChild($multiStatus);
-
- // Adding in default namespaces
- foreach($this->xmlNamespaces as $namespace=>$prefix) {
-
- $multiStatus->setAttribute('xmlns:' . $prefix,$namespace);
-
- }
-
- foreach($fileProperties as $entry) {
-
- $href = $entry['href'];
- unset($entry['href']);
-
- if ($strip404s && isset($entry[404])) {
- unset($entry[404]);
- }
-
- $response = new Property\Response($href,$entry);
- $response->serialize($this,$multiStatus);
-
- }
-
- return $dom->saveXML();
-
- }
-
- /**
- * This method parses a PropPatch request
- *
- * PropPatch changes the properties for a resource. This method
- * returns a list of properties.
- *
- * The keys in the returned array contain the property name (e.g.: {DAV:}displayname,
- * and the value contains the property value. If a property is to be removed the value
- * will be null.
- *
- * @param string $body xml body
- * @return array list of properties in need of updating or deletion
- */
- public function parsePropPatchRequest($body) {
-
- //We'll need to change the DAV namespace declaration to something else in order to make it parsable
- $dom = XMLUtil::loadDOMDocument($body);
-
- $newProperties = array();
-
- foreach($dom->firstChild->childNodes as $child) {
-
- if ($child->nodeType !== XML_ELEMENT_NODE) continue;
-
- $operation = XMLUtil::toClarkNotation($child);
-
- if ($operation!=='{DAV:}set' && $operation!=='{DAV:}remove') continue;
-
- $innerProperties = XMLUtil::parseProperties($child, $this->propertyMap);
-
- foreach($innerProperties as $propertyName=>$propertyValue) {
-
- if ($operation==='{DAV:}remove') {
- $propertyValue = null;
- }
-
- $newProperties[$propertyName] = $propertyValue;
-
- }
-
- }
-
- return $newProperties;
-
- }
-
- /**
- * This method parses the PROPFIND request and returns its information
- *
- * This will either be a list of properties, or an empty array; in which case
- * an {DAV:}allprop was requested.
- *
- * @param string $body
- * @return array
- */
- public function parsePropFindRequest($body) {
-
- // If the propfind body was empty, it means IE is requesting 'all' properties
- if (!$body) return array();
-
- $dom = XMLUtil::loadDOMDocument($body);
- $elem = $dom->getElementsByTagNameNS('urn:DAV','propfind')->item(0);
- return array_keys(XMLUtil::parseProperties($elem));
-
- }
-
- // }}}
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Tree.php b/vendor/sabre/dav/lib/Sabre/DAV/Tree.php
deleted file mode 100644
index ab94168b2..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Tree.php
+++ /dev/null
@@ -1,193 +0,0 @@
-<?php
-
-namespace Sabre\DAV;
-
-/**
- * Abstract tree object
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-abstract class Tree {
-
- /**
- * This function must return an INode object for a path
- * If a Path doesn't exist, thrown a Exception_NotFound
- *
- * @param string $path
- * @throws Exception\NotFound
- * @return INode
- */
- abstract function getNodeForPath($path);
-
- /**
- * This function allows you to check if a node exists.
- *
- * Implementors of this class should override this method to make
- * it cheaper.
- *
- * @param string $path
- * @return bool
- */
- public function nodeExists($path) {
-
- try {
-
- $this->getNodeForPath($path);
- return true;
-
- } catch (Exception\NotFound $e) {
-
- return false;
-
- }
-
- }
-
- /**
- * Copies a file from path to another
- *
- * @param string $sourcePath The source location
- * @param string $destinationPath The full destination path
- * @return void
- */
- public function copy($sourcePath, $destinationPath) {
-
- $sourceNode = $this->getNodeForPath($sourcePath);
-
- // grab the dirname and basename components
- list($destinationDir, $destinationName) = URLUtil::splitPath($destinationPath);
-
- $destinationParent = $this->getNodeForPath($destinationDir);
- $this->copyNode($sourceNode,$destinationParent,$destinationName);
-
- $this->markDirty($destinationDir);
-
- }
-
- /**
- * Moves a file from one location to another
- *
- * @param string $sourcePath The path to the file which should be moved
- * @param string $destinationPath The full destination path, so not just the destination parent node
- * @return int
- */
- public function move($sourcePath, $destinationPath) {
-
- list($sourceDir, $sourceName) = URLUtil::splitPath($sourcePath);
- list($destinationDir, $destinationName) = URLUtil::splitPath($destinationPath);
-
- if ($sourceDir===$destinationDir) {
- $renameable = $this->getNodeForPath($sourcePath);
- $renameable->setName($destinationName);
- } else {
- $this->copy($sourcePath,$destinationPath);
- $this->getNodeForPath($sourcePath)->delete();
- }
- $this->markDirty($sourceDir);
- $this->markDirty($destinationDir);
-
- }
-
- /**
- * Deletes a node from the tree
- *
- * @param string $path
- * @return void
- */
- public function delete($path) {
-
- $node = $this->getNodeForPath($path);
- $node->delete();
-
- list($parent) = URLUtil::splitPath($path);
- $this->markDirty($parent);
-
- }
-
- /**
- * Returns a list of childnodes for a given path.
- *
- * @param string $path
- * @return array
- */
- public function getChildren($path) {
-
- $node = $this->getNodeForPath($path);
- return $node->getChildren();
-
- }
-
- /**
- * This method is called with every tree update
- *
- * Examples of tree updates are:
- * * node deletions
- * * node creations
- * * copy
- * * move
- * * renaming nodes
- *
- * If Tree classes implement a form of caching, this will allow
- * them to make sure caches will be expired.
- *
- * If a path is passed, it is assumed that the entire subtree is dirty
- *
- * @param string $path
- * @return void
- */
- public function markDirty($path) {
-
-
- }
-
- /**
- * copyNode
- *
- * @param INode $source
- * @param ICollection $destinationParent
- * @param string $destinationName
- * @return void
- */
- protected function copyNode(INode $source,ICollection $destinationParent,$destinationName = null) {
-
- if (!$destinationName) $destinationName = $source->getName();
-
- if ($source instanceof IFile) {
-
- $data = $source->get();
-
- // If the body was a string, we need to convert it to a stream
- if (is_string($data)) {
- $stream = fopen('php://temp','r+');
- fwrite($stream,$data);
- rewind($stream);
- $data = $stream;
- }
- $destinationParent->createFile($destinationName,$data);
- $destination = $destinationParent->getChild($destinationName);
-
- } elseif ($source instanceof ICollection) {
-
- $destinationParent->createDirectory($destinationName);
-
- $destination = $destinationParent->getChild($destinationName);
- foreach($source->getChildren() as $child) {
-
- $this->copyNode($child,$destination);
-
- }
-
- }
- if ($source instanceof IProperties && $destination instanceof IProperties) {
-
- $props = $source->getProperties(array());
- $destination->updateProperties($props);
-
- }
-
- }
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/Tree/Filesystem.php b/vendor/sabre/dav/lib/Sabre/DAV/Tree/Filesystem.php
deleted file mode 100644
index a477725a5..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/Tree/Filesystem.php
+++ /dev/null
@@ -1,133 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Tree;
-
-use Sabre\DAV;
-
-/**
- * FileSystem Tree
- *
- * This class is an alternative to the standard ObjectTree. This tree can only
- * use Sabre\DAV\FS\Directory and File classes, but as a result it allows for a few
- * optimizations that otherwise wouldn't be possible.
- *
- * Specifically copying and moving are much, much faster.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Filesystem extends DAV\Tree {
-
- /**
- * Base url on the filesystem.
- *
- * @var string
- */
- protected $basePath;
-
- /**
- * Creates this tree
- *
- * Supply the path you'd like to share.
- *
- * @param string $basePath
- */
- public function __construct($basePath) {
-
- $this->basePath = $basePath;
-
- }
-
- /**
- * Returns a new node for the given path
- *
- * @param string $path
- * @return DAV\FS\Node
- */
- public function getNodeForPath($path) {
-
- $realPath = $this->getRealPath($path);
- if (!file_exists($realPath)) {
- throw new DAV\Exception\NotFound('File at location ' . $realPath . ' not found');
- }
- if (is_dir($realPath)) {
- return new DAV\FS\Directory($realPath);
- } else {
- return new DAV\FS\File($realPath);
- }
-
- }
-
- /**
- * Returns the real filesystem path for a webdav url.
- *
- * @param string $publicPath
- * @return string
- */
- protected function getRealPath($publicPath) {
-
- return rtrim($this->basePath,'/') . '/' . trim($publicPath,'/');
-
- }
-
- /**
- * Copies a file or directory.
- *
- * This method must work recursively and delete the destination
- * if it exists
- *
- * @param string $source
- * @param string $destination
- * @return void
- */
- public function copy($source,$destination) {
-
- $source = $this->getRealPath($source);
- $destination = $this->getRealPath($destination);
- $this->realCopy($source,$destination);
-
- }
-
- /**
- * Used by self::copy
- *
- * @param string $source
- * @param string $destination
- * @return void
- */
- protected function realCopy($source,$destination) {
-
- if (is_file($source)) {
- copy($source,$destination);
- } else {
- mkdir($destination);
- foreach(scandir($source) as $subnode) {
-
- if ($subnode=='.' || $subnode=='..') continue;
- $this->realCopy($source.'/'.$subnode,$destination.'/'.$subnode);
-
- }
- }
-
- }
-
- /**
- * Moves a file or directory recursively.
- *
- * If the destination exists, delete it first.
- *
- * @param string $source
- * @param string $destination
- * @return void
- */
- public function move($source,$destination) {
-
- $source = $this->getRealPath($source);
- $destination = $this->getRealPath($destination);
- rename($source,$destination);
-
- }
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/URLUtil.php b/vendor/sabre/dav/lib/Sabre/DAV/URLUtil.php
deleted file mode 100644
index b7254e9a1..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/URLUtil.php
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-
-namespace Sabre\DAV;
-
-/**
- * URL utility class
- *
- * This class provides methods to deal with encoding and decoding url (percent encoded) strings.
- *
- * It was not possible to use PHP's built-in methods for this, because some clients don't like
- * encoding of certain characters.
- *
- * Specifically, it was found that GVFS (gnome's webdav client) does not like encoding of ( and
- * ). Since these are reserved, but don't have a reserved meaning in url, these characters are
- * kept as-is.
- *
- * It was also discovered that versions of the SOGO connector for thunderbird
- * has issues with urlencoded colons.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class URLUtil {
-
- /**
- * Encodes the path of a url.
- *
- * slashes (/) are treated as path-separators.
- *
- * @param string $path
- * @return string
- */
- static function encodePath($path) {
-
- return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\)\/:])/',function($match) {
-
- return '%'.sprintf('%02x',ord($match[0]));
-
- }, $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
- */
- static function encodePathSegment($pathSegment) {
-
- 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
- */
- static function decodePath($path) {
-
- return self::decodePathSegment($path);
-
- }
-
- /**
- * Decodes a url-encoded path segment
- *
- * @param string $path
- * @return string
- */
- static function decodePathSegment($path) {
-
- $path = rawurldecode($path);
- $encoding = mb_detect_encoding($path, array('UTF-8','ISO-8859-1'));
-
- switch($encoding) {
-
- case 'ISO-8859-1' :
- $path = utf8_encode($path);
-
- }
-
- return $path;
-
- }
-
- /**
- * Returns the 'dirname' and 'basename' for a path.
- *
- * The reason there is a custom function for this purpose, is because
- * basename() is locale aware (behaviour changes if C locale or a UTF-8 locale 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.
- *
- * 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
- */
- static function splitPath($path) {
-
- $matches = array();
- if(preg_match('/^(?:(?:(.*)(?:\/+))?([^\/]+))(?:\/?)$/u',$path,$matches)) {
- return array($matches[1],$matches[2]);
- } else {
- return array(null,null);
- }
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAV/XMLUtil.php b/vendor/sabre/dav/lib/Sabre/DAV/XMLUtil.php
deleted file mode 100644
index 2bf81b3b8..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAV/XMLUtil.php
+++ /dev/null
@@ -1,191 +0,0 @@
-<?php
-
-namespace Sabre\DAV;
-
-/**
- * XML utilities for WebDAV
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class XMLUtil {
-
- /**
- * Returns the 'clark notation' for an element.
- *
- * For example, and element encoded as:
- * <b:myelem xmlns:b="http://www.example.org/" />
- * will be returned as:
- * {http://www.example.org}myelem
- *
- * This format is used throughout the SabreDAV sourcecode.
- * Elements encoded with the urn:DAV namespace will
- * be returned as if they were in the DAV: namespace. This is to avoid
- * compatibility problems.
- *
- * This function will return null if a nodetype other than an Element is passed.
- *
- * @param \DOMNode $dom
- * @return string
- */
- static function toClarkNotation(\DOMNode $dom) {
-
- if ($dom->nodeType !== XML_ELEMENT_NODE) return null;
-
- // Mapping back to the real namespace, in case it was dav
- if ($dom->namespaceURI=='urn:DAV') $ns = 'DAV:'; else $ns = $dom->namespaceURI;
-
- // Mapping to clark notation
- return '{' . $ns . '}' . $dom->localName;
-
- }
-
- /**
- * Parses a clark-notation string, and returns the namespace and element
- * name components.
- *
- * If the string was invalid, it will throw an InvalidArgumentException.
- *
- * @param string $str
- * @throws InvalidArgumentException
- * @return array
- */
- static function parseClarkNotation($str) {
-
- if (!preg_match('/^{([^}]*)}(.*)$/',$str,$matches)) {
- throw new \InvalidArgumentException('\'' . $str . '\' is not a valid clark-notation formatted string');
- }
-
- return array(
- $matches[1],
- $matches[2]
- );
-
- }
-
- /**
- * This method takes an XML document (as string) and converts all instances of the
- * DAV: namespace to urn:DAV
- *
- * This is unfortunately needed, because the DAV: namespace violates the xml namespaces
- * spec, and causes the DOM to throw errors
- *
- * @param string $xmlDocument
- * @return array|string|null
- */
- static function convertDAVNamespace($xmlDocument) {
-
- // This is used to map the DAV: namespace to urn:DAV. This is needed, because the DAV:
- // namespace is actually a violation of the XML namespaces specification, and will cause errors
- return preg_replace("/xmlns(:[A-Za-z0-9_]*)?=(\"|\')DAV:(\\2)/","xmlns\\1=\\2urn:DAV\\2",$xmlDocument);
-
- }
-
- /**
- * This method provides a generic way to load a DOMDocument for WebDAV use.
- *
- * This method throws a Sabre\DAV\Exception\BadRequest exception for any xml errors.
- * It does not preserve whitespace, and it converts the DAV: namespace to urn:DAV.
- *
- * @param string $xml
- * @throws Sabre\DAV\Exception\BadRequest
- * @return DOMDocument
- */
- static function loadDOMDocument($xml) {
-
- if (empty($xml))
- throw new Exception\BadRequest('Empty XML document sent');
-
- // The BitKinex client sends xml documents as UTF-16. PHP 5.3.1 (and presumably lower)
- // does not support this, so we must intercept this and convert to UTF-8.
- if (substr($xml,0,12) === "\x3c\x00\x3f\x00\x78\x00\x6d\x00\x6c\x00\x20\x00") {
-
- // Note: the preceeding byte sequence is "<?xml" encoded as UTF_16, without the BOM.
- $xml = iconv('UTF-16LE','UTF-8',$xml);
-
- // Because the xml header might specify the encoding, we must also change this.
- // This regex looks for the string encoding="UTF-16" and replaces it with
- // encoding="UTF-8".
- $xml = preg_replace('|<\?xml([^>]*)encoding="UTF-16"([^>]*)>|u','<?xml\1encoding="UTF-8"\2>',$xml);
-
- }
-
- // Retaining old error setting
- $oldErrorSetting = libxml_use_internal_errors(true);
- // Fixes an XXE vulnerability on PHP versions older than 5.3.23 or
- // 5.4.13.
- $oldEntityLoaderSetting = libxml_disable_entity_loader(true);
-
- // Clearing any previous errors
- libxml_clear_errors();
-
- $dom = new \DOMDocument();
-
- // We don't generally care about any whitespace
- $dom->preserveWhiteSpace = false;
-
- $dom->loadXML(self::convertDAVNamespace($xml),LIBXML_NOWARNING | LIBXML_NOERROR);
-
- if ($error = libxml_get_last_error()) {
- libxml_clear_errors();
- throw new Exception\BadRequest('The request body had an invalid XML body. (message: ' . $error->message . ', errorcode: ' . $error->code . ', line: ' . $error->line . ')');
- }
-
- // Restoring old mechanism for error handling
- if ($oldErrorSetting===false) libxml_use_internal_errors(false);
- if ($oldEntityLoaderSetting===false) libxml_disable_entity_loader(false);
-
- return $dom;
-
- }
-
- /**
- * Parses all WebDAV properties out of a DOM Element
- *
- * Generally WebDAV properties are enclosed in {DAV:}prop elements. This
- * method helps by going through all these and pulling out the actual
- * propertynames, making them array keys and making the property values,
- * well.. the array values.
- *
- * If no value was given (self-closing element) null will be used as the
- * value. This is used in for example PROPFIND requests.
- *
- * Complex values are supported through the propertyMap argument. The
- * propertyMap should have the clark-notation properties as it's keys, and
- * classnames as values.
- *
- * When any of these properties are found, the unserialize() method will be
- * (statically) called. The result of this method is used as the value.
- *
- * @param \DOMElement $parentNode
- * @param array $propertyMap
- * @return array
- */
- static function parseProperties(\DOMElement $parentNode, array $propertyMap = array()) {
-
- $propList = array();
- foreach($parentNode->childNodes as $propNode) {
-
- if (self::toClarkNotation($propNode)!=='{DAV:}prop') continue;
-
- foreach($propNode->childNodes as $propNodeData) {
-
- /* If there are no elements in here, we actually get 1 text node, this special case is dedicated to netdrive */
- if ($propNodeData->nodeType != XML_ELEMENT_NODE) continue;
-
- $propertyName = self::toClarkNotation($propNodeData);
- if (isset($propertyMap[$propertyName])) {
- $propList[$propertyName] = call_user_func(array($propertyMap[$propertyName],'unserialize'),$propNodeData);
- } else {
- $propList[$propertyName] = $propNodeData->textContent;
- }
- }
-
-
- }
- return $propList;
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/PrincipalBackend/AbstractBackend.php b/vendor/sabre/dav/lib/Sabre/DAVACL/PrincipalBackend/AbstractBackend.php
deleted file mode 100644
index 984f9ad82..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/PrincipalBackend/AbstractBackend.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-namespace Sabre\DAVACL\PrincipalBackend;
-
-/**
- * Abstract Principal Backend
- *
- * Currently this class has no function. It's here for consistency and so we
- * have a non-bc-breaking way to add a default generic implementation to
- * functions we may add in the future.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-abstract class AbstractBackend implements BackendInterface {
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/PrincipalBackend/PDO.php b/vendor/sabre/dav/lib/Sabre/DAVACL/PrincipalBackend/PDO.php
deleted file mode 100644
index 0921768c3..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/PrincipalBackend/PDO.php
+++ /dev/null
@@ -1,428 +0,0 @@
-<?php
-
-namespace Sabre\DAVACL\PrincipalBackend;
-
-use Sabre\DAV;
-use Sabre\DAVACL;
-
-/**
- * PDO principal backend
- *
- *
- * This backend assumes all principals are in a single collection. The default collection
- * is 'principals/', but this can be overriden.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class PDO extends AbstractBackend {
-
- /**
- * pdo
- *
- * @var PDO
- */
- protected $pdo;
-
- /**
- * PDO table name for 'principals'
- *
- * @var string
- */
- protected $tableName;
-
- /**
- * PDO table name for 'group members'
- *
- * @var string
- */
- protected $groupMembersTableName;
-
- /**
- * A list of additional fields to support
- *
- * @var array
- */
- protected $fieldMap = array(
-
- /**
- * This property can be used to display the users' real name.
- */
- '{DAV:}displayname' => array(
- 'dbField' => 'displayname',
- ),
-
- /**
- * This property is actually used by the CardDAV plugin, where it gets
- * mapped to {http://calendarserver.orgi/ns/}me-card.
- *
- * The reason we don't straight-up use that property, is because
- * me-card is defined as a property on the users' addressbook
- * collection.
- */
- '{http://sabredav.org/ns}vcard-url' => array(
- 'dbField' => 'vcardurl',
- ),
- /**
- * This is the users' primary email-address.
- */
- '{http://sabredav.org/ns}email-address' => array(
- 'dbField' => 'email',
- ),
- );
-
- /**
- * Sets up the backend.
- *
- * @param PDO $pdo
- * @param string $tableName
- * @param string $groupMembersTableName
- */
- public function __construct(\PDO $pdo, $tableName = 'principals', $groupMembersTableName = 'groupmembers') {
-
- $this->pdo = $pdo;
- $this->tableName = $tableName;
- $this->groupMembersTableName = $groupMembersTableName;
-
- }
-
-
- /**
- * Returns a list of principals based on a prefix.
- *
- * This prefix will often contain something like 'principals'. You are only
- * expected to return principals that are in this base path.
- *
- * You are expected to return at least a 'uri' for every user, you can
- * return any additional properties if you wish so. Common properties are:
- * {DAV:}displayname
- * {http://sabredav.org/ns}email-address - This is a custom SabreDAV
- * field that's actualy injected in a number of other properties. If
- * you have an email address, use this property.
- *
- * @param string $prefixPath
- * @return array
- */
- public function getPrincipalsByPrefix($prefixPath) {
-
- $fields = array(
- 'uri',
- );
-
- foreach($this->fieldMap as $key=>$value) {
- $fields[] = $value['dbField'];
- }
- $result = $this->pdo->query('SELECT '.implode(',', $fields).' FROM '. $this->tableName);
-
- $principals = array();
-
- while($row = $result->fetch(\PDO::FETCH_ASSOC)) {
-
- // Checking if the principal is in the prefix
- list($rowPrefix) = DAV\URLUtil::splitPath($row['uri']);
- if ($rowPrefix !== $prefixPath) continue;
-
- $principal = array(
- 'uri' => $row['uri'],
- );
- foreach($this->fieldMap as $key=>$value) {
- if ($row[$value['dbField']]) {
- $principal[$key] = $row[$value['dbField']];
- }
- }
- $principals[] = $principal;
-
- }
-
- return $principals;
-
- }
-
- /**
- * Returns a specific principal, specified by it's path.
- * The returned structure should be the exact same as from
- * getPrincipalsByPrefix.
- *
- * @param string $path
- * @return array
- */
- public function getPrincipalByPath($path) {
-
- $fields = array(
- 'id',
- 'uri',
- );
-
- foreach($this->fieldMap as $key=>$value) {
- $fields[] = $value['dbField'];
- }
- $stmt = $this->pdo->prepare('SELECT '.implode(',', $fields).' FROM '. $this->tableName . ' WHERE uri = ?');
- $stmt->execute(array($path));
-
- $row = $stmt->fetch(\PDO::FETCH_ASSOC);
- if (!$row) return;
-
- $principal = array(
- 'id' => $row['id'],
- 'uri' => $row['uri'],
- );
- foreach($this->fieldMap as $key=>$value) {
- if ($row[$value['dbField']]) {
- $principal[$key] = $row[$value['dbField']];
- }
- }
- return $principal;
-
- }
-
- /**
- * Updates one ore more webdav properties on a principal.
- *
- * The list of mutations is supplied as an array. Each key in the array is
- * a propertyname, such as {DAV:}displayname.
- *
- * Each value is the actual value to be updated. If a value is null, it
- * must be deleted.
- *
- * This method should be atomic. It must either completely succeed, or
- * completely fail. Success and failure can simply be returned as 'true' or
- * 'false'.
- *
- * It is also possible to return detailed failure information. In that case
- * an array such as this should be returned:
- *
- * array(
- * 200 => array(
- * '{DAV:}prop1' => null,
- * ),
- * 201 => array(
- * '{DAV:}prop2' => null,
- * ),
- * 403 => array(
- * '{DAV:}prop3' => null,
- * ),
- * 424 => array(
- * '{DAV:}prop4' => null,
- * ),
- * );
- *
- * In this previous example prop1 was successfully updated or deleted, and
- * prop2 was succesfully created.
- *
- * prop3 failed to update due to '403 Forbidden' and because of this prop4
- * also could not be updated with '424 Failed dependency'.
- *
- * This last example was actually incorrect. While 200 and 201 could appear
- * in 1 response, if there's any error (403) the other properties should
- * always fail with 423 (failed dependency).
- *
- * But anyway, if you don't want to scratch your head over this, just
- * return true or false.
- *
- * @param string $path
- * @param array $mutations
- * @return array|bool
- */
- public function updatePrincipal($path, $mutations) {
-
- $updateAble = array();
- foreach($mutations as $key=>$value) {
-
- // We are not aware of this field, we must fail.
- if (!isset($this->fieldMap[$key])) {
-
- $response = array(
- 403 => array(
- $key => null,
- ),
- 424 => array(),
- );
-
- // Adding the rest to the response as a 424
- foreach($mutations as $subKey=>$subValue) {
- if ($subKey !== $key) {
- $response[424][$subKey] = null;
- }
- }
- return $response;
- }
-
- $updateAble[$this->fieldMap[$key]['dbField']] = $value;
-
- }
-
- // No fields to update
- $query = "UPDATE " . $this->tableName . " SET ";
-
- $first = true;
- foreach($updateAble as $key => $value) {
- if (!$first) {
- $query.= ', ';
- }
- $first = false;
- $query.= "$key = :$key ";
- }
- $query.='WHERE uri = :uri';
- $stmt = $this->pdo->prepare($query);
- $updateAble['uri'] = $path;
- $stmt->execute($updateAble);
-
- return true;
-
- }
-
- /**
- * This method is used to search for principals matching a set of
- * properties.
- *
- * This search is specifically used by RFC3744's principal-property-search
- * REPORT. You should at least allow searching on
- * http://sabredav.org/ns}email-address.
- *
- * The actual search should be a unicode-non-case-sensitive search. The
- * keys in searchProperties are the WebDAV property names, while the values
- * are the property values to search on.
- *
- * If multiple properties are being searched on, the search should be
- * AND'ed.
- *
- * This method should simply return an array with full principal uri's.
- *
- * If somebody attempted to search on a property the backend does not
- * support, you should simply return 0 results.
- *
- * You can also just return 0 results if you choose to not support
- * searching at all, but keep in mind that this may stop certain features
- * from working.
- *
- * @param string $prefixPath
- * @param array $searchProperties
- * @return array
- */
- public function searchPrincipals($prefixPath, array $searchProperties) {
-
- $query = 'SELECT uri FROM ' . $this->tableName . ' WHERE 1=1 ';
- $values = array();
- foreach($searchProperties as $property => $value) {
-
- switch($property) {
-
- case '{DAV:}displayname' :
- $query.=' AND displayname LIKE ?';
- $values[] = '%' . $value . '%';
- break;
- case '{http://sabredav.org/ns}email-address' :
- $query.=' AND email LIKE ?';
- $values[] = '%' . $value . '%';
- break;
- default :
- // Unsupported property
- return array();
-
- }
-
- }
- $stmt = $this->pdo->prepare($query);
- $stmt->execute($values);
-
- $principals = array();
- while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
-
- // Checking if the principal is in the prefix
- list($rowPrefix) = DAV\URLUtil::splitPath($row['uri']);
- if ($rowPrefix !== $prefixPath) continue;
-
- $principals[] = $row['uri'];
-
- }
-
- return $principals;
-
- }
-
- /**
- * Returns the list of members for a group-principal
- *
- * @param string $principal
- * @return array
- */
- 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 = ?');
- $stmt->execute(array($principal['id']));
-
- $result = array();
- while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
- $result[] = $row['uri'];
- }
- return $result;
-
- }
-
- /**
- * Returns the list of groups a principal is a member of
- *
- * @param string $principal
- * @return array
- */
- 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 = ?');
- $stmt->execute(array($principal['id']));
-
- $result = array();
- while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
- $result[] = $row['uri'];
- }
- return $result;
-
- }
-
- /**
- * Updates the list of group members for a group principal.
- *
- * The principals should be passed as a list of uri's.
- *
- * @param string $principal
- * @param array $members
- * @return void
- */
- 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->execute(array_merge(array($principal), $members));
-
- $memberIds = array();
- $principalId = null;
-
- while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
- if ($row['uri'] == $principal) {
- $principalId = $row['id'];
- } else {
- $memberIds[] = $row['id'];
- }
- }
- if (!$principalId) throw new DAV\Exception('Principal not found');
-
- // Wiping out old members
- $stmt = $this->pdo->prepare('DELETE FROM '.$this->groupMembersTableName.' WHERE principal_id = ?;');
- $stmt->execute(array($principalId));
-
- foreach($memberIds as $memberId) {
-
- $stmt = $this->pdo->prepare('INSERT INTO '.$this->groupMembersTableName.' (principal_id, member_id) VALUES (?, ?);');
- $stmt->execute(array($principalId, $memberId));
-
- }
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/PrincipalCollection.php b/vendor/sabre/dav/lib/Sabre/DAVACL/PrincipalCollection.php
deleted file mode 100644
index 3aadf399d..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/PrincipalCollection.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-namespace Sabre\DAVACL;
-
-/**
- * Principals Collection
- *
- * This collection represents a list of users.
- * The users are instances of Sabre\DAVACL\Principal
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class PrincipalCollection extends AbstractPrincipalCollection {
-
- /**
- * This method returns a node for a principal.
- *
- * The passed array contains principal information, and is guaranteed to
- * at least contain a uri item. Other properties may or may not be
- * supplied by the authentication backend.
- *
- * @param array $principal
- * @return \Sabre\DAV\INode
- */
- public function getChildForPrincipal(array $principal) {
-
- return new Principal($this->principalBackend, $principal);
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/Property/Acl.php b/vendor/sabre/dav/lib/Sabre/DAVACL/Property/Acl.php
deleted file mode 100644
index e6a70ce91..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/Property/Acl.php
+++ /dev/null
@@ -1,211 +0,0 @@
-<?php
-
-namespace Sabre\DAVACL\Property;
-
-use Sabre\DAV;
-
-/**
- * This class represents the {DAV:}acl property
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Acl extends DAV\Property {
-
- /**
- * List of privileges
- *
- * @var array
- */
- private $privileges;
-
- /**
- * Whether or not the server base url is required to be prefixed when
- * serializing the property.
- *
- * @var boolean
- */
- private $prefixBaseUrl;
-
- /**
- * Constructor
- *
- * This object requires a structure similar to the return value from
- * Sabre\DAVACL\Plugin::getACL().
- *
- * Each privilege is a an array with at least a 'privilege' property, and a
- * 'principal' property. A privilege may have a 'protected' property as
- * well.
- *
- * The prefixBaseUrl should be set to false, if the supplied principal urls
- * are already full urls. If this is kept to true, the servers base url
- * will automatically be prefixed.
- *
- * @param bool $prefixBaseUrl
- * @param array $privileges
- */
- public function __construct(array $privileges, $prefixBaseUrl = true) {
-
- $this->privileges = $privileges;
- $this->prefixBaseUrl = $prefixBaseUrl;
-
- }
-
- /**
- * Returns the list of privileges for this property
- *
- * @return array
- */
- public function getPrivileges() {
-
- return $this->privileges;
-
- }
-
- /**
- * Serializes the property into a DOMElement
- *
- * @param DAV\Server $server
- * @param \DOMElement $node
- * @return void
- */
- public function serialize(DAV\Server $server,\DOMElement $node) {
-
- $doc = $node->ownerDocument;
- foreach($this->privileges as $ace) {
-
- $this->serializeAce($doc, $node, $ace, $server);
-
- }
-
- }
-
- /**
- * Unserializes the {DAV:}acl xml element.
- *
- * @param \DOMElement $dom
- * @return Acl
- */
- static public function unserialize(\DOMElement $dom) {
-
- $privileges = array();
- $xaces = $dom->getElementsByTagNameNS('urn:DAV','ace');
- for($ii=0; $ii < $xaces->length; $ii++) {
-
- $xace = $xaces->item($ii);
- $principal = $xace->getElementsByTagNameNS('urn:DAV','principal');
- if ($principal->length !== 1) {
- throw new DAV\Exception\BadRequest('Each {DAV:}ace element must have one {DAV:}principal element');
- }
- $principal = Principal::unserialize($principal->item(0));
-
- switch($principal->getType()) {
- case Principal::HREF :
- $principal = $principal->getHref();
- break;
- case Principal::AUTHENTICATED :
- $principal = '{DAV:}authenticated';
- break;
- case Principal::UNAUTHENTICATED :
- $principal = '{DAV:}unauthenticated';
- break;
- case Principal::ALL :
- $principal = '{DAV:}all';
- break;
-
- }
-
- $protected = false;
-
- if ($xace->getElementsByTagNameNS('urn:DAV','protected')->length > 0) {
- $protected = true;
- }
-
- $grants = $xace->getElementsByTagNameNS('urn:DAV','grant');
- if ($grants->length < 1) {
- throw new DAV\Exception\NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported');
- }
- $grant = $grants->item(0);
-
- $xprivs = $grant->getElementsByTagNameNS('urn:DAV','privilege');
- for($jj=0; $jj<$xprivs->length; $jj++) {
-
- $xpriv = $xprivs->item($jj);
-
- $privilegeName = null;
-
- for ($kk=0;$kk<$xpriv->childNodes->length;$kk++) {
-
- $childNode = $xpriv->childNodes->item($kk);
- if ($t = DAV\XMLUtil::toClarkNotation($childNode)) {
- $privilegeName = $t;
- break;
- }
- }
- if (is_null($privilegeName)) {
- throw new DAV\Exception\BadRequest('{DAV:}privilege elements must have a privilege element contained within them.');
- }
-
- $privileges[] = array(
- 'principal' => $principal,
- 'protected' => $protected,
- 'privilege' => $privilegeName,
- );
-
- }
-
- }
-
- return new self($privileges);
-
- }
-
- /**
- * Serializes a single access control entry.
- *
- * @param \DOMDocument $doc
- * @param \DOMElement $node
- * @param array $ace
- * @param DAV\Server $server
- * @return void
- */
- private function serializeAce($doc,$node,$ace, DAV\Server $server) {
-
- $xace = $doc->createElementNS('DAV:','d:ace');
- $node->appendChild($xace);
-
- $principal = $doc->createElementNS('DAV:','d:principal');
- $xace->appendChild($principal);
- switch($ace['principal']) {
- case '{DAV:}authenticated' :
- $principal->appendChild($doc->createElementNS('DAV:','d:authenticated'));
- break;
- case '{DAV:}unauthenticated' :
- $principal->appendChild($doc->createElementNS('DAV:','d:unauthenticated'));
- break;
- case '{DAV:}all' :
- $principal->appendChild($doc->createElementNS('DAV:','d:all'));
- break;
- default:
- $principal->appendChild($doc->createElementNS('DAV:','d:href',($this->prefixBaseUrl?$server->getBaseUri():'') . $ace['principal'] . '/'));
- }
-
- $grant = $doc->createElementNS('DAV:','d:grant');
- $xace->appendChild($grant);
-
- $privParts = null;
-
- preg_match('/^{([^}]*)}(.*)$/',$ace['privilege'],$privParts);
-
- $xprivilege = $doc->createElementNS('DAV:','d:privilege');
- $grant->appendChild($xprivilege);
-
- $xprivilege->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2]));
-
- if (isset($ace['protected']) && $ace['protected'])
- $xace->appendChild($doc->createElement('d:protected'));
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/Property/AclRestrictions.php b/vendor/sabre/dav/lib/Sabre/DAVACL/Property/AclRestrictions.php
deleted file mode 100644
index aa6fd17d6..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/Property/AclRestrictions.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-namespace Sabre\DAVACL\Property;
-
-use Sabre\DAV;
-
-/**
- * AclRestrictions property
- *
- * This property represents {DAV:}acl-restrictions, as defined in RFC3744.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class AclRestrictions extends DAV\Property {
-
- /**
- * Serializes the property into a DOMElement
- *
- * @param DAV\Server $server
- * @param \DOMElement $elem
- * @return void
- */
- public function serialize(DAV\Server $server,\DOMElement $elem) {
-
- $doc = $elem->ownerDocument;
-
- $elem->appendChild($doc->createElementNS('DAV:','d:grant-only'));
- $elem->appendChild($doc->createElementNS('DAV:','d:no-invert'));
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php b/vendor/sabre/dav/lib/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php
deleted file mode 100644
index e0501dbd6..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-
-namespace Sabre\DAVACL\Property;
-
-use Sabre\DAV;
-
-/**
- * CurrentUserPrivilegeSet
- *
- * This class represents the current-user-privilege-set property. When
- * requested, it contain all the privileges a user has on a specific node.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class CurrentUserPrivilegeSet extends DAV\Property {
-
- /**
- * List of privileges
- *
- * @var array
- */
- private $privileges;
-
- /**
- * Creates the object
- *
- * Pass the privileges in clark-notation
- *
- * @param array $privileges
- */
- public function __construct(array $privileges) {
-
- $this->privileges = $privileges;
-
- }
-
- /**
- * Serializes the property in the DOM
- *
- * @param DAV\Server $server
- * @param \DOMElement $node
- * @return void
- */
- public function serialize(DAV\Server $server,\DOMElement $node) {
-
- $doc = $node->ownerDocument;
- foreach($this->privileges as $privName) {
-
- $this->serializePriv($doc,$node,$privName);
-
- }
-
- }
-
- /**
- * Returns true or false, whether the specified principal appears in the
- * list.
- *
- * @return bool
- */
- public function has($privilegeName) {
-
- return in_array($privilegeName, $this->privileges);
-
- }
-
- /**
- * Serializes one privilege
- *
- * @param \DOMDocument $doc
- * @param \DOMElement $node
- * @param string $privName
- * @return void
- */
- protected function serializePriv($doc,$node,$privName) {
-
- $xp = $doc->createElementNS('DAV:','d:privilege');
- $node->appendChild($xp);
-
- $privParts = null;
- preg_match('/^{([^}]*)}(.*)$/',$privName,$privParts);
-
- $xp->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2]));
-
- }
-
- /**
- * Unserializes the {DAV:}current-user-privilege-set element.
- *
- * @param DOMElement $node
- * @return CurrentUserPrivilegeSet
- */
- static public function unserialize(\DOMElement $node) {
-
- $result = array();
-
- $xprivs = $node->getElementsByTagNameNS('urn:DAV','privilege');
-
- for($jj=0; $jj<$xprivs->length; $jj++) {
-
- $xpriv = $xprivs->item($jj);
-
- $privilegeName = null;
-
- for ($kk=0;$kk<$xpriv->childNodes->length;$kk++) {
-
- $childNode = $xpriv->childNodes->item($kk);
- if ($t = DAV\XMLUtil::toClarkNotation($childNode)) {
- $privilegeName = $t;
- break;
- }
- }
-
- $result[] = $privilegeName;
-
- }
-
- return new self($result);
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/Property/Principal.php b/vendor/sabre/dav/lib/Sabre/DAVACL/Property/Principal.php
deleted file mode 100644
index 6c644b024..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/Property/Principal.php
+++ /dev/null
@@ -1,161 +0,0 @@
-<?php
-
-namespace Sabre\DAVACL\Property;
-use Sabre\DAV;
-
-/**
- * Principal property
- *
- * The principal property represents a principal from RFC3744 (ACL).
- * The property can be used to specify a principal or pseudo principals.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Principal extends DAV\Property implements DAV\Property\IHref {
-
- /**
- * To specify a not-logged-in user, use the UNAUTHENTICATED principal
- */
- const UNAUTHENTICATED = 1;
-
- /**
- * To specify any principal that is logged in, use AUTHENTICATED
- */
- const AUTHENTICATED = 2;
-
- /**
- * Specific principals can be specified with the HREF
- */
- const HREF = 3;
-
- /**
- * Everybody, basically
- */
- const ALL = 4;
-
- /**
- * Principal-type
- *
- * Must be one of the UNAUTHENTICATED, AUTHENTICATED or HREF constants.
- *
- * @var int
- */
- private $type;
-
- /**
- * Url to principal
- *
- * This value is only used for the HREF principal type.
- *
- * @var string
- */
- private $href;
-
- /**
- * Creates the property.
- *
- * The 'type' argument must be one of the type constants defined in this class.
- *
- * 'href' is only required for the HREF type.
- *
- * @param int $type
- * @param string|null $href
- */
- public function __construct($type, $href = null) {
-
- $this->type = $type;
-
- if ($type===self::HREF && is_null($href)) {
- throw new DAV\Exception('The href argument must be specified for the HREF principal type.');
- }
- $this->href = $href;
-
- }
-
- /**
- * Returns the principal type
- *
- * @return int
- */
- public function getType() {
-
- return $this->type;
-
- }
-
- /**
- * Returns the principal uri.
- *
- * @return string
- */
- public function getHref() {
-
- return $this->href;
-
- }
-
- /**
- * Serializes the property into a DOMElement.
- *
- * @param DAV\Server $server
- * @param \DOMElement $node
- * @return void
- */
- public function serialize(DAV\Server $server, \DOMElement $node) {
-
- $prefix = $server->xmlNamespaces['DAV:'];
- switch($this->type) {
-
- case self::UNAUTHENTICATED :
- $node->appendChild(
- $node->ownerDocument->createElement($prefix . ':unauthenticated')
- );
- break;
- case self::AUTHENTICATED :
- $node->appendChild(
- $node->ownerDocument->createElement($prefix . ':authenticated')
- );
- break;
- case self::HREF :
- $href = $node->ownerDocument->createElement($prefix . ':href');
- $href->nodeValue = $server->getBaseUri() . DAV\URLUtil::encodePath($this->href);
- $node->appendChild($href);
- break;
-
- }
-
- }
-
- /**
- * Deserializes a DOM element into a property object.
- *
- * @param \DOMElement $dom
- * @return Principal
- */
- static public function unserialize(\DOMElement $dom) {
-
- $parent = $dom->firstChild;
- while(!DAV\XMLUtil::toClarkNotation($parent)) {
- $parent = $parent->nextSibling;
- }
-
- switch(DAV\XMLUtil::toClarkNotation($parent)) {
-
- case '{DAV:}unauthenticated' :
- return new self(self::UNAUTHENTICATED);
- case '{DAV:}authenticated' :
- return new self(self::AUTHENTICATED);
- case '{DAV:}href':
- return new self(self::HREF, $parent->textContent);
- case '{DAV:}all':
- return new self(self::ALL);
- default :
- throw new DAV\Exception\BadRequest('Unexpected element (' . DAV\XMLUtil::toClarkNotation($parent) . '). Could not deserialize');
-
- }
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/Property/SupportedPrivilegeSet.php b/vendor/sabre/dav/lib/Sabre/DAVACL/Property/SupportedPrivilegeSet.php
deleted file mode 100644
index 5f152d9e5..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/Property/SupportedPrivilegeSet.php
+++ /dev/null
@@ -1,94 +0,0 @@
-<?php
-
-namespace Sabre\DAVACL\Property;
-
-use Sabre\DAV;
-
-/**
- * SupportedPrivilegeSet property
- *
- * This property encodes the {DAV:}supported-privilege-set property, as defined
- * in rfc3744. Please consult the rfc for details about it's structure.
- *
- * This class expects a structure like the one given from
- * Sabre\DAVACL\Plugin::getSupportedPrivilegeSet as the argument in its
- * constructor.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class SupportedPrivilegeSet extends DAV\Property {
-
- /**
- * privileges
- *
- * @var array
- */
- private $privileges;
-
- /**
- * Constructor
- *
- * @param array $privileges
- */
- public function __construct(array $privileges) {
-
- $this->privileges = $privileges;
-
- }
-
- /**
- * Serializes the property into a domdocument.
- *
- * @param DAV\Server $server
- * @param \DOMElement $node
- * @return void
- */
- public function serialize(DAV\Server $server,\DOMElement $node) {
-
- $doc = $node->ownerDocument;
- $this->serializePriv($doc, $node, $this->privileges);
-
- }
-
- /**
- * Serializes a property
- *
- * This is a recursive function.
- *
- * @param \DOMDocument $doc
- * @param \DOMElement $node
- * @param array $privilege
- * @return void
- */
- private function serializePriv($doc,$node,$privilege) {
-
- $xsp = $doc->createElementNS('DAV:','d:supported-privilege');
- $node->appendChild($xsp);
-
- $xp = $doc->createElementNS('DAV:','d:privilege');
- $xsp->appendChild($xp);
-
- $privParts = null;
- preg_match('/^{([^}]*)}(.*)$/',$privilege['privilege'],$privParts);
-
- $xp->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2]));
-
- if (isset($privilege['abstract']) && $privilege['abstract']) {
- $xsp->appendChild($doc->createElementNS('DAV:','d:abstract'));
- }
-
- if (isset($privilege['description'])) {
- $xsp->appendChild($doc->createElementNS('DAV:','d:description',$privilege['description']));
- }
-
- if (isset($privilege['aggregates'])) {
- foreach($privilege['aggregates'] as $subPrivilege) {
- $this->serializePriv($doc,$xsp,$subPrivilege);
- }
- }
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/DAVACL/Version.php b/vendor/sabre/dav/lib/Sabre/DAVACL/Version.php
deleted file mode 100644
index 344e22d7b..000000000
--- a/vendor/sabre/dav/lib/Sabre/DAVACL/Version.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-namespace Sabre\DAVACL;
-
-/**
- * This class contains the SabreDAV version constants.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Version {
-
- /**
- * Full version number
- */
- const VERSION = '1.8.7';
-
- /**
- * Stability : alpha, beta, stable
- */
- const STABILITY = 'stable';
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/HTTP/AbstractAuth.php b/vendor/sabre/dav/lib/Sabre/HTTP/AbstractAuth.php
deleted file mode 100644
index 1ddf412b7..000000000
--- a/vendor/sabre/dav/lib/Sabre/HTTP/AbstractAuth.php
+++ /dev/null
@@ -1,111 +0,0 @@
-<?php
-
-namespace Sabre\HTTP;
-
-/**
- * HTTP Authentication baseclass
- *
- * This class has the common functionality for BasicAuth and DigestAuth
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-abstract class AbstractAuth {
-
- /**
- * The realm will be displayed in the dialog boxes
- *
- * This identifier can be changed through setRealm()
- *
- * @var string
- */
- protected $realm = 'SabreDAV';
-
- /**
- * HTTP response helper
- *
- * @var Sabre\HTTP\Response
- */
- protected $httpResponse;
-
-
- /**
- * HTTP request helper
- *
- * @var Sabre\HTTP\Request
- */
- protected $httpRequest;
-
- /**
- * __construct
- *
- */
- public function __construct() {
-
- $this->httpResponse = new Response();
- $this->httpRequest = new Request();
-
- }
-
- /**
- * Sets an alternative HTTP response object
- *
- * @param Response $response
- * @return void
- */
- public function setHTTPResponse(Response $response) {
-
- $this->httpResponse = $response;
-
- }
-
- /**
- * Sets an alternative HTTP request object
- *
- * @param Request $request
- * @return void
- */
- public function setHTTPRequest(Request $request) {
-
- $this->httpRequest = $request;
-
- }
-
-
- /**
- * Sets the realm
- *
- * The realm is often displayed in authentication dialog boxes
- * Commonly an application name displayed here
- *
- * @param string $realm
- * @return void
- */
- public function setRealm($realm) {
-
- $this->realm = $realm;
-
- }
-
- /**
- * Returns the realm
- *
- * @return string
- */
- public function getRealm() {
-
- return $this->realm;
-
- }
-
- /**
- * Returns an HTTP 401 header, forcing login
- *
- * This should be called when username and password are incorrect, or not supplied at all
- *
- * @return void
- */
- abstract public function requireLogin();
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/HTTP/BasicAuth.php b/vendor/sabre/dav/lib/Sabre/HTTP/BasicAuth.php
deleted file mode 100644
index 659964faa..000000000
--- a/vendor/sabre/dav/lib/Sabre/HTTP/BasicAuth.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-namespace Sabre\HTTP;
-
-/**
- * HTTP Basic Authentication handler
- *
- * Use this class for easy http authentication setup
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class BasicAuth extends AbstractAuth {
-
- /**
- * Returns the supplied username and password.
- *
- * The returned array has two values:
- * * 0 - username
- * * 1 - password
- *
- * If nothing was supplied, 'false' will be returned
- *
- * @return mixed
- */
- public function getUserPass() {
-
- // Apache and mod_php
- if (($user = $this->httpRequest->getRawServerValue('PHP_AUTH_USER')) && ($pass = $this->httpRequest->getRawServerValue('PHP_AUTH_PW'))) {
-
- return array($user,$pass);
-
- }
-
- // Most other webservers
- $auth = $this->httpRequest->getHeader('Authorization');
-
- // Apache could prefix environment variables with REDIRECT_ when urls
- // are passed through mod_rewrite
- if (!$auth) {
- $auth = $this->httpRequest->getRawServerValue('REDIRECT_HTTP_AUTHORIZATION');
- }
-
- if (!$auth) return false;
-
- if (strpos(strtolower($auth),'basic')!==0) return false;
-
- return explode(':', base64_decode(substr($auth, 6)),2);
-
- }
-
- /**
- * Returns an HTTP 401 header, forcing login
- *
- * This should be called when username and password are incorrect, or not supplied at all
- *
- * @return void
- */
- public function requireLogin() {
-
- $this->httpResponse->setHeader('WWW-Authenticate','Basic realm="' . $this->realm . '"');
- $this->httpResponse->sendStatus(401);
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/HTTP/Request.php b/vendor/sabre/dav/lib/Sabre/HTTP/Request.php
deleted file mode 100644
index a71a52b42..000000000
--- a/vendor/sabre/dav/lib/Sabre/HTTP/Request.php
+++ /dev/null
@@ -1,284 +0,0 @@
-<?php
-
-namespace Sabre\HTTP;
-
-/**
- * HTTP Request information
- *
- * This object can be used to easily access information about an HTTP request.
- * It can additionally be used to create 'mock' requests.
- *
- * This class mostly operates independent, but because of the nature of a single
- * request per run it can operate as a singleton. For more information check out
- * the behaviour around 'defaultInputStream'.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Request {
-
- /**
- * PHP's $_SERVER data
- *
- * @var array
- */
- protected $_SERVER;
-
- /**
- * PHP's $_POST data
- *
- * @var array
- */
- protected $_POST;
-
- /**
- * The request body, if any.
- *
- * This is stored in the form of a stream resource.
- *
- * @var resource
- */
- protected $body = null;
-
- /**
- * This will be set as the 'default' inputStream for a specific HTTP request
- * We sometimes need to retain, or rebuild this if we need multiple runs
- * of parsing the original HTTP request.
- *
- * @var resource
- */
- static $defaultInputStream=null;
-
- /**
- * Sets up the object
- *
- * The serverData and postData array can be used to override usage of PHP's
- * global _SERVER and _POST variable respectively.
- *
- * @param array $serverData
- * @param array $postData
- */
- public function __construct(array $serverData = null, array $postData = null) {
-
- if ($serverData) $this->_SERVER = $serverData;
- else $this->_SERVER =& $_SERVER;
-
- if ($postData) $this->_POST = $postData;
- else $this->_POST =& $_POST;
-
- }
-
- /**
- * Returns the value for a specific http header.
- *
- * This method returns null if the header did not exist.
- *
- * @param string $name
- * @return string
- */
- public function getHeader($name) {
-
- $name = strtoupper(str_replace(array('-'),array('_'),$name));
- if (isset($this->_SERVER['HTTP_' . $name])) {
- return $this->_SERVER['HTTP_' . $name];
- }
-
- // There's a few headers that seem to end up in the top-level
- // server array.
- switch($name) {
- case 'CONTENT_TYPE' :
- case 'CONTENT_LENGTH' :
- if (isset($this->_SERVER[$name])) {
- return $this->_SERVER[$name];
- }
- break;
-
- }
- return;
-
- }
-
- /**
- * Returns all (known) HTTP headers.
- *
- * All headers are converted to lower-case, and additionally all underscores
- * are automatically converted to dashes
- *
- * @return array
- */
- public function getHeaders() {
-
- $hdrs = array();
- foreach($this->_SERVER as $key=>$value) {
-
- switch($key) {
- case 'CONTENT_LENGTH' :
- case 'CONTENT_TYPE' :
- $hdrs[strtolower(str_replace('_','-',$key))] = $value;
- break;
- default :
- if (strpos($key,'HTTP_')===0) {
- $hdrs[substr(strtolower(str_replace('_','-',$key)),5)] = $value;
- }
- break;
- }
-
- }
-
- return $hdrs;
-
- }
-
- /**
- * Returns the HTTP request method
- *
- * This is for example POST or GET
- *
- * @return string
- */
- public function getMethod() {
-
- return $this->_SERVER['REQUEST_METHOD'];
-
- }
-
- /**
- * Returns the requested uri
- *
- * @return string
- */
- public function getUri() {
-
- return $this->_SERVER['REQUEST_URI'];
-
- }
-
- /**
- * Will return protocol + the hostname + the uri
- *
- * @return string
- */
- public function getAbsoluteUri() {
-
- // Checking if the request was made through HTTPS. The last in line is for IIS
- $protocol = isset($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']!='off');
- return ($protocol?'https':'http') . '://' . $this->getHeader('Host') . $this->getUri();
-
- }
-
- /**
- * Returns everything after the ? from the current url
- *
- * @return string
- */
- public function getQueryString() {
-
- return isset($this->_SERVER['QUERY_STRING'])?$this->_SERVER['QUERY_STRING']:'';
-
- }
-
- /**
- * Returns the HTTP request body body
- *
- * This method returns a readable stream resource.
- * If the asString parameter is set to true, a string is sent instead.
- *
- * @param bool $asString
- * @return resource
- */
- public function getBody($asString = false) {
-
- if (is_null($this->body)) {
- if (!is_null(self::$defaultInputStream)) {
- $this->body = self::$defaultInputStream;
- } else {
- $this->body = fopen('php://input','r');
- self::$defaultInputStream = $this->body;
- }
- }
- if ($asString) {
- $body = stream_get_contents($this->body);
- return $body;
- } else {
- return $this->body;
- }
-
- }
-
- /**
- * Sets the contents of the HTTP request body
- *
- * This method can either accept a string, or a readable stream resource.
- *
- * If the setAsDefaultInputStream is set to true, it means for this run of the
- * script the supplied body will be used instead of php://input.
- *
- * @param mixed $body
- * @param bool $setAsDefaultInputStream
- * @return void
- */
- public function setBody($body,$setAsDefaultInputStream = false) {
-
- if(is_resource($body)) {
- $this->body = $body;
- } else {
-
- $stream = fopen('php://temp','r+');
- fputs($stream,$body);
- rewind($stream);
- // String is assumed
- $this->body = $stream;
- }
- if ($setAsDefaultInputStream) {
- self::$defaultInputStream = $this->body;
- }
-
- }
-
- /**
- * Returns PHP's _POST variable.
- *
- * The reason this is in a method is so it can be subclassed and
- * overridden.
- *
- * @return array
- */
- public function getPostVars() {
-
- return $this->_POST;
-
- }
-
- /**
- * Returns a specific item from the _SERVER array.
- *
- * Do not rely on this feature, it is for internal use only.
- *
- * @param string $field
- * @return string
- */
- public function getRawServerValue($field) {
-
- return isset($this->_SERVER[$field])?$this->_SERVER[$field]:null;
-
- }
-
- /**
- * Returns the HTTP version specified within the request.
- *
- * @return string
- */
- public function getHTTPVersion() {
-
- $protocol = $this->getRawServerValue('SERVER_PROTOCOL');
- if ($protocol==='HTTP/1.0') {
- return '1.0';
- } else {
- return '1.1';
- }
-
- }
-
-}
-
diff --git a/vendor/sabre/dav/lib/Sabre/HTTP/Response.php b/vendor/sabre/dav/lib/Sabre/HTTP/Response.php
deleted file mode 100644
index a7fc0da12..000000000
--- a/vendor/sabre/dav/lib/Sabre/HTTP/Response.php
+++ /dev/null
@@ -1,175 +0,0 @@
-<?php
-
-namespace Sabre\HTTP;
-
-/**
- * This class represents a HTTP response.
- *
- * It contains the HTTP status code, response headers and a message body.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Response {
-
- /**
- * The HTTP version to return in the header() line.
- *
- * By default you will definitely want this to be HTTP/1.1, but in some
- * edge cases (most notably pre 1.2 nginx servers acting as a proxy), you
- * want to force this to 1.0.
- *
- * @var string
- */
- public $defaultHttpVersion = '1.1';
-
- /**
- * Returns a full HTTP status message for an HTTP status code
- *
- * @param int $code
- * @return string
- */
- public function getStatusMessage($code, $httpVersion = '1.1') {
-
- $msg = array(
- 100 => 'Continue',
- 101 => 'Switching Protocols',
- 102 => 'Processing',
- 200 => 'OK',
- 201 => 'Created',
- 202 => 'Accepted',
- 203 => 'Non-Authorative Information',
- 204 => 'No Content',
- 205 => 'Reset Content',
- 206 => 'Partial Content',
- 207 => 'Multi-Status', // RFC 4918
- 208 => 'Already Reported', // RFC 5842
- 226 => 'IM Used', // RFC 3229
- 300 => 'Multiple Choices',
- 301 => 'Moved Permanently',
- 302 => 'Found',
- 303 => 'See Other',
- 304 => 'Not Modified',
- 305 => 'Use Proxy',
- 306 => 'Reserved',
- 307 => 'Temporary Redirect',
- 400 => 'Bad request',
- 401 => 'Unauthorized',
- 402 => 'Payment Required',
- 403 => 'Forbidden',
- 404 => 'Not Found',
- 405 => 'Method Not Allowed',
- 406 => 'Not Acceptable',
- 407 => 'Proxy Authentication Required',
- 408 => 'Request Timeout',
- 409 => 'Conflict',
- 410 => 'Gone',
- 411 => 'Length Required',
- 412 => 'Precondition failed',
- 413 => 'Request Entity Too Large',
- 414 => 'Request-URI Too Long',
- 415 => 'Unsupported Media Type',
- 416 => 'Requested Range Not Satisfiable',
- 417 => 'Expectation Failed',
- 418 => 'I\'m a teapot', // RFC 2324
- 422 => 'Unprocessable Entity', // RFC 4918
- 423 => 'Locked', // RFC 4918
- 424 => 'Failed Dependency', // RFC 4918
- 426 => 'Upgrade required',
- 428 => 'Precondition required', // draft-nottingham-http-new-status
- 429 => 'Too Many Requests', // draft-nottingham-http-new-status
- 431 => 'Request Header Fields Too Large', // draft-nottingham-http-new-status
- 500 => 'Internal Server Error',
- 501 => 'Not Implemented',
- 502 => 'Bad Gateway',
- 503 => 'Service Unavailable',
- 504 => 'Gateway Timeout',
- 505 => 'HTTP Version not supported',
- 506 => 'Variant Also Negotiates',
- 507 => 'Insufficient Storage', // RFC 4918
- 508 => 'Loop Detected', // RFC 5842
- 509 => 'Bandwidth Limit Exceeded', // non-standard
- 510 => 'Not extended',
- 511 => 'Network Authentication Required', // draft-nottingham-http-new-status
- );
-
- return 'HTTP/' . $httpVersion . ' ' . $code . ' ' . $msg[$code];
-
- }
-
- // @codeCoverageIgnoreStart
- // We cannot reasonably test header() related methods.
-
- /**
- * Sends an HTTP status header to the client.
- *
- * @param int $code HTTP status code
- * @return bool
- */
- public function sendStatus($code) {
-
- if (!headers_sent())
- return header($this->getStatusMessage($code, $this->defaultHttpVersion));
- else return false;
-
- }
-
- /**
- * Sets an HTTP header for the response
- *
- * @param string $name
- * @param string $value
- * @param bool $replace
- * @return bool
- */
- public function setHeader($name, $value, $replace = true) {
-
- $value = str_replace(array("\r","\n"),array('\r','\n'),$value);
- if (!headers_sent())
- return header($name . ': ' . $value, $replace);
- else return false;
-
-
- }
- // @codeCoverageIgnoreEnd
-
- /**
- * Sets a bunch of HTTP Headers
- *
- * headersnames are specified as keys, value in the array value
- *
- * @param array $headers
- * @return void
- */
- public function setHeaders(array $headers) {
-
- foreach($headers as $key=>$value)
- $this->setHeader($key, $value);
-
- }
-
- /**
- * Sends the entire response body
- *
- * This method can accept either an open filestream, or a string.
- *
- * @param mixed $body
- * @return void
- */
- public function sendBody($body) {
-
- if (is_resource($body)) {
-
- file_put_contents('php://output', $body);
-
- } else {
-
- // We assume a string
- echo $body;
-
- }
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/HTTP/Util.php b/vendor/sabre/dav/lib/Sabre/HTTP/Util.php
deleted file mode 100644
index 147246253..000000000
--- a/vendor/sabre/dav/lib/Sabre/HTTP/Util.php
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-
-namespace Sabre\HTTP;
-
-/**
- * HTTP utility methods
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @author Paul Voegler
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Util {
-
- /**
- * Parses a RFC2616-compatible date string
- *
- * This method returns false if the date is invalid
- *
- * @param string $dateHeader
- * @return bool|DateTime
- */
- static function parseHTTPDate($dateHeader) {
-
- //RFC 2616 section 3.3.1 Full Date
- //Only the format is checked, valid ranges are checked by strtotime below
- $month = '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)';
- $weekday = '(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)';
- $wkday = '(Mon|Tue|Wed|Thu|Fri|Sat|Sun)';
- $time = '[0-2]\d(\:[0-5]\d){2}';
- $date3 = $month . ' ([1-3]\d| \d)';
- $date2 = '[0-3]\d\-' . $month . '\-\d\d';
- //4-digit year cannot begin with 0 - unix timestamp begins in 1970
- $date1 = '[0-3]\d ' . $month . ' [1-9]\d{3}';
-
- //ANSI C's asctime() format
- //4-digit year cannot begin with 0 - unix timestamp begins in 1970
- $asctime_date = $wkday . ' ' . $date3 . ' ' . $time . ' [1-9]\d{3}';
- //RFC 850, obsoleted by RFC 1036
- $rfc850_date = $weekday . ', ' . $date2 . ' ' . $time . ' GMT';
- //RFC 822, updated by RFC 1123
- $rfc1123_date = $wkday . ', ' . $date1 . ' ' . $time . ' GMT';
- //allowed date formats by RFC 2616
- $HTTP_date = "($rfc1123_date|$rfc850_date|$asctime_date)";
-
- //allow for space around the string and strip it
- $dateHeader = trim($dateHeader, ' ');
- if (!preg_match('/^' . $HTTP_date . '$/', $dateHeader))
- return false;
-
- //append implicit GMT timezone to ANSI C time format
- if (strpos($dateHeader, ' GMT') === false)
- $dateHeader .= ' GMT';
-
-
- $realDate = strtotime($dateHeader);
- //strtotime can return -1 or false in case of error
- if ($realDate !== false && $realDate >= 0)
- return new \DateTime('@' . $realDate, new \DateTimeZone('UTC'));
-
- }
-
- /**
- * Transforms a DateTime object to HTTP's most common date format.
- *
- * We're serializing it as the RFC 1123 date, which, for HTTP must be
- * specified as GMT.
- *
- * @param \DateTime $dateTime
- * @return string
- */
- static function toHTTPDate(\DateTime $dateTime) {
-
- // We need to clone it, as we don't want to affect the existing
- // DateTime.
- $dateTime = clone $dateTime;
- $dateTime->setTimeZone(new \DateTimeZone('GMT'));
- return $dateTime->format('D, d M Y H:i:s \G\M\T');
-
- }
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/HTTP/Version.php b/vendor/sabre/dav/lib/Sabre/HTTP/Version.php
deleted file mode 100644
index 0e913835c..000000000
--- a/vendor/sabre/dav/lib/Sabre/HTTP/Version.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-namespace Sabre\HTTP;
-
-/**
- * This class contains the Sabre\HTTP version constants.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Version {
-
- /**
- * Full version number
- */
- const VERSION = '1.8.9';
-
- /**
- * Stability : alpha, beta, stable
- */
- const STABILITY = 'stable';
-
-}
diff --git a/vendor/sabre/dav/lib/Sabre/autoload.php b/vendor/sabre/dav/lib/Sabre/autoload.php
deleted file mode 100644
index c5945ee1d..000000000
--- a/vendor/sabre/dav/lib/Sabre/autoload.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-/**
- * SabreDAV's autoloader
- *
- * This file is kept for backwards compatibility purposes.
- * SabreDAV now uses the composer autoloader.
- *
- * You should stop including this file, and include 'vendor/autoload.php'
- * instead.
- *
- * @deprecated Will be removed in a future version!
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-
-/**
- * We are assuming that the composer autoloader is just 2 directories up.
- *
- * This is not the case when sabredav is installed as a dependency. But, in
- * those cases it's not expected that people will look for this file anyway.
- */
-
-require __DIR__ . '/../../vendor/autoload.php';
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractPDOTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractPDOTest.php
index 2224f0b63..25f8b1794 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractPDOTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractPDOTest.php
@@ -1,8 +1,10 @@
<?php
namespace Sabre\CalDAV\Backend;
+
use Sabre\CalDAV;
use Sabre\DAV;
+use Sabre\DAV\PropPatch;
abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
@@ -22,7 +24,7 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
$backend = new PDO($this->pdo);
$calendars = $backend->getCalendarsForUser('principals/user2');
- $this->assertEquals(array(),$calendars);
+ $this->assertEquals([], $calendars);
}
@@ -32,28 +34,28 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
function testCreateCalendarAndFetch() {
$backend = new PDO($this->pdo);
- $returnedId = $backend->createCalendar('principals/user2','somerandomid',array(
- '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new CalDAV\Property\SupportedCalendarComponentSet(array('VEVENT')),
- '{DAV:}displayname' => 'Hello!',
- '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Property\ScheduleCalendarTransp('transparent'),
- ));
+ $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', [
+ '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VEVENT']),
+ '{DAV:}displayname' => 'Hello!',
+ '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp('transparent'),
+ ]);
$calendars = $backend->getCalendarsForUser('principals/user2');
- $elementCheck = array(
- 'id' => $returnedId,
- 'uri' => 'somerandomid',
- '{DAV:}displayname' => 'Hello!',
- '{urn:ietf:params:xml:ns:caldav}calendar-description' => '',
- '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Property\ScheduleCalendarTransp('transparent'),
- );
+ $elementCheck = [
+ 'id' => $returnedId,
+ 'uri' => 'somerandomid',
+ '{DAV:}displayname' => 'Hello!',
+ '{urn:ietf:params:xml:ns:caldav}calendar-description' => '',
+ '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp('transparent'),
+ ];
- $this->assertInternalType('array',$calendars);
- $this->assertEquals(1,count($calendars));
+ $this->assertInternalType('array', $calendars);
+ $this->assertEquals(1, count($calendars));
- foreach($elementCheck as $name=>$value) {
+ foreach ($elementCheck as $name => $value) {
$this->assertArrayHasKey($name, $calendars[0]);
- $this->assertEquals($value,$calendars[0][$name]);
+ $this->assertEquals($value, $calendars[0][$name]);
}
@@ -67,38 +69,41 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
$backend = new PDO($this->pdo);
//Creating a new calendar
- $newId = $backend->createCalendar('principals/user2','somerandomid',array());
+ $newId = $backend->createCalendar('principals/user2', 'somerandomid', []);
+
+ $propPatch = new PropPatch([
+ '{DAV:}displayname' => 'myCalendar',
+ '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp('transparent'),
+ ]);
// Updating the calendar
- $result = $backend->updateCalendar($newId,array(
- '{DAV:}displayname' => 'myCalendar',
- '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Property\ScheduleCalendarTransp('transparent'),
- ));
+ $backend->updateCalendar($newId, $propPatch);
+ $result = $propPatch->commit();
// Verifying the result of the update
- $this->assertEquals(true, $result);
+ $this->assertTrue($result);
// Fetching all calendars from this user
$calendars = $backend->getCalendarsForUser('principals/user2');
// Checking if all the information is still correct
- $elementCheck = array(
- 'id' => $newId,
- 'uri' => 'somerandomid',
- '{DAV:}displayname' => 'myCalendar',
- '{urn:ietf:params:xml:ns:caldav}calendar-description' => '',
- '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => '',
- '{http://calendarserver.org/ns/}getctag' => '2',
- '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Property\ScheduleCalendarTransp('transparent'),
- );
+ $elementCheck = [
+ 'id' => $newId,
+ 'uri' => 'somerandomid',
+ '{DAV:}displayname' => 'myCalendar',
+ '{urn:ietf:params:xml:ns:caldav}calendar-description' => '',
+ '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => '',
+ '{http://calendarserver.org/ns/}getctag' => 'http://sabre.io/ns/sync/2',
+ '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp('transparent'),
+ ];
- $this->assertInternalType('array',$calendars);
- $this->assertEquals(1,count($calendars));
+ $this->assertInternalType('array', $calendars);
+ $this->assertEquals(1, count($calendars));
- foreach($elementCheck as $name=>$value) {
+ foreach ($elementCheck as $name => $value) {
$this->assertArrayHasKey($name, $calendars[0]);
- $this->assertEquals($value,$calendars[0][$name]);
+ $this->assertEquals($value, $calendars[0][$name]);
}
@@ -112,19 +117,22 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
$backend = new PDO($this->pdo);
//Creating a new calendar
- $newId = $backend->createCalendar('principals/user2','somerandomid',array());
+ $newId = $backend->createCalendar('principals/user2', 'somerandomid', []);
- // Updating the calendar
- $result = $backend->updateCalendar($newId,array(
+ $propPatch = new PropPatch([
'{DAV:}displayname' => 'myCalendar',
'{DAV:}yourmom' => 'wittycomment',
- ));
+ ]);
+
+ // Updating the calendar
+ $backend->updateCalendar($newId, $propPatch);
+ $propPatch->commit();
// Verifying the result of the update
- $this->assertEquals(array(
- '403' => array('{DAV:}yourmom' => null),
- '424' => array('{DAV:}displayname' => null),
- ), $result);
+ $this->assertEquals([
+ '{DAV:}yourmom' => 403,
+ '{DAV:}displayname' => 424,
+ ], $propPatch->getResult());
}
@@ -134,15 +142,15 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
function testDeleteCalendar() {
$backend = new PDO($this->pdo);
- $returnedId = $backend->createCalendar('principals/user2','somerandomid',array(
- '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new CalDAV\Property\SupportedCalendarComponentSet(array('VEVENT')),
- '{DAV:}displayname' => 'Hello!',
- ));
+ $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', [
+ '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VEVENT']),
+ '{DAV:}displayname' => 'Hello!',
+ ]);
$backend->deleteCalendar($returnedId);
$calendars = $backend->getCalendarsForUser('principals/user2');
- $this->assertEquals(array(),$calendars);
+ $this->assertEquals([], $calendars);
}
@@ -155,30 +163,78 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
$backend = new PDO($this->pdo);
//Creating a new calendar
- $newId = $backend->createCalendar('principals/user2','somerandomid',array(
+ $newId = $backend->createCalendar('principals/user2', 'somerandomid', [
'{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => 'blabla',
- ));
+ ]);
}
function testCreateCalendarObject() {
$backend = new PDO($this->pdo);
- $returnedId = $backend->createCalendar('principals/user2','somerandomid',array());
+ $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []);
$object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
$backend->createCalendarObject($returnedId, 'random-id', $object);
$result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"');
- $this->assertEquals(array(
- 'etag' => md5($object),
- 'size' => strlen($object),
- 'calendardata' => $object,
+ $this->assertEquals([
+ 'etag' => md5($object),
+ 'size' => strlen($object),
+ 'calendardata' => $object,
'firstoccurence' => strtotime('20120101'),
- 'lastoccurence' => strtotime('20120101')+(3600*24),
- 'componenttype' => 'VEVENT',
- ), $result->fetch(\PDO::FETCH_ASSOC));
+ 'lastoccurence' => strtotime('20120101') + (3600 * 24),
+ 'componenttype' => 'VEVENT',
+ ], $result->fetch(\PDO::FETCH_ASSOC));
+
+ }
+ function testGetMultipleObjects() {
+
+ $backend = new PDO($this->pdo);
+ $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []);
+
+ $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
+
+ $backend->createCalendarObject($returnedId, 'id-1', $object);
+ $backend->createCalendarObject($returnedId, 'id-2', $object);
+
+ $check = [
+ [
+ 'id' => 1,
+ 'etag' => '"' . md5($object) . '"',
+ 'uri' => 'id-1',
+ 'size' => strlen($object),
+ 'calendardata' => $object,
+ 'lastmodified' => null,
+ 'calendarid' => $returnedId,
+ ],
+ [
+ 'id' => 2,
+ 'etag' => '"' . md5($object) . '"',
+ 'uri' => 'id-2',
+ 'size' => strlen($object),
+ 'calendardata' => $object,
+ 'lastmodified' => null,
+ 'calendarid' => $returnedId,
+ ],
+ ];
+
+ $result = $backend->getMultipleCalendarObjects($returnedId, [ 'id-1', 'id-2' ]);
+
+ foreach ($check as $index => $props) {
+
+ foreach ($props as $key => $value) {
+
+ if ($key !== 'lastmodified') {
+ $this->assertEquals($value, $result[$index][$key]);
+ } else {
+ $this->assertTrue(isset($result[$index][$key]));
+ }
+
+ }
+
+ }
}
@@ -189,7 +245,7 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
function testCreateCalendarObjectNoComponent() {
$backend = new PDO($this->pdo);
- $returnedId = $backend->createCalendar('principals/user2','somerandomid',array());
+ $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []);
$object = "BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nEND:VCALENDAR\r\n";
@@ -203,21 +259,21 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
function testCreateCalendarObjectDuration() {
$backend = new PDO($this->pdo);
- $returnedId = $backend->createCalendar('principals/user2','somerandomid',array());
+ $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []);
$object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nDURATION:P2D\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
$backend->createCalendarObject($returnedId, 'random-id', $object);
$result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"');
- $this->assertEquals(array(
- 'etag' => md5($object),
- 'size' => strlen($object),
- 'calendardata' => $object,
+ $this->assertEquals([
+ 'etag' => md5($object),
+ 'size' => strlen($object),
+ 'calendardata' => $object,
'firstoccurence' => strtotime('20120101'),
- 'lastoccurence' => strtotime('20120101')+(3600*48),
- 'componenttype' => 'VEVENT',
- ), $result->fetch(\PDO::FETCH_ASSOC));
+ 'lastoccurence' => strtotime('20120101') + (3600 * 48),
+ 'componenttype' => 'VEVENT',
+ ], $result->fetch(\PDO::FETCH_ASSOC));
}
@@ -227,21 +283,45 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
function testCreateCalendarObjectNoDTEND() {
$backend = new PDO($this->pdo);
- $returnedId = $backend->createCalendar('principals/user2','somerandomid',array());
+ $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []);
$object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
$backend->createCalendarObject($returnedId, 'random-id', $object);
$result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"');
- $this->assertEquals(array(
- 'etag' => md5($object),
- 'size' => strlen($object),
- 'calendardata' => $object,
+ $this->assertEquals([
+ 'etag' => md5($object),
+ 'size' => strlen($object),
+ 'calendardata' => $object,
'firstoccurence' => strtotime('2012-01-01 10:00:00'),
- 'lastoccurence' => strtotime('2012-01-01 10:00:00'),
- 'componenttype' => 'VEVENT',
- ), $result->fetch(\PDO::FETCH_ASSOC));
+ 'lastoccurence' => strtotime('2012-01-01 10:00:00'),
+ 'componenttype' => 'VEVENT',
+ ], $result->fetch(\PDO::FETCH_ASSOC));
+
+ }
+
+ /**
+ * @depends testCreateCalendarObject
+ */
+ function testCreateCalendarObjectWithDTEND() {
+
+ $backend = new PDO($this->pdo);
+ $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []);
+
+ $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nDTEND:20120101T110000Z\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
+
+ $backend->createCalendarObject($returnedId, 'random-id', $object);
+
+ $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"');
+ $this->assertEquals([
+ 'etag' => md5($object),
+ 'size' => strlen($object),
+ 'calendardata' => $object,
+ 'firstoccurence' => strtotime('2012-01-01 10:00:00'),
+ 'lastoccurence' => strtotime('2012-01-01 11:00:00'),
+ 'componenttype' => 'VEVENT',
+ ], $result->fetch(\PDO::FETCH_ASSOC));
}
@@ -251,21 +331,21 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
function testCreateCalendarObjectInfiniteReccurence() {
$backend = new PDO($this->pdo);
- $returnedId = $backend->createCalendar('principals/user2','somerandomid',array());
+ $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []);
- $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nRRULE:FREQ=DAILY\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
+ $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nRRULE:FREQ=DAILY\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
$backend->createCalendarObject($returnedId, 'random-id', $object);
$result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"');
- $this->assertEquals(array(
- 'etag' => md5($object),
- 'size' => strlen($object),
- 'calendardata' => $object,
+ $this->assertEquals([
+ 'etag' => md5($object),
+ 'size' => strlen($object),
+ 'calendardata' => $object,
'firstoccurence' => strtotime('2012-01-01 10:00:00'),
- 'lastoccurence' => strtotime(PDO::MAX_DATE),
- 'componenttype' => 'VEVENT',
- ), $result->fetch(\PDO::FETCH_ASSOC));
+ 'lastoccurence' => strtotime(PDO::MAX_DATE),
+ 'componenttype' => 'VEVENT',
+ ], $result->fetch(\PDO::FETCH_ASSOC));
}
@@ -275,21 +355,21 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
function testCreateCalendarObjectEndingReccurence() {
$backend = new PDO($this->pdo);
- $returnedId = $backend->createCalendar('principals/user2','somerandomid',array());
+ $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []);
- $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nDTEND;VALUE=DATE-TIME:20120101T110000Z\r\nRRULE:FREQ=DAILY;COUNT=1000\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
+ $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nDTEND;VALUE=DATE-TIME:20120101T110000Z\r\nUID:foo\r\nRRULE:FREQ=DAILY;COUNT=1000\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
$backend->createCalendarObject($returnedId, 'random-id', $object);
$result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"');
- $this->assertEquals(array(
- 'etag' => md5($object),
- 'size' => strlen($object),
- 'calendardata' => $object,
+ $this->assertEquals([
+ 'etag' => md5($object),
+ 'size' => strlen($object),
+ 'calendardata' => $object,
'firstoccurence' => strtotime('2012-01-01 10:00:00'),
- 'lastoccurence' => strtotime('2012-01-01 11:00:00') + (3600 * 24 * 999),
- 'componenttype' => 'VEVENT',
- ), $result->fetch(\PDO::FETCH_ASSOC));
+ 'lastoccurence' => strtotime('2012-01-01 11:00:00') + (3600 * 24 * 999),
+ 'componenttype' => 'VEVENT',
+ ], $result->fetch(\PDO::FETCH_ASSOC));
}
@@ -299,21 +379,21 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
function testCreateCalendarObjectTask() {
$backend = new PDO($this->pdo);
- $returnedId = $backend->createCalendar('principals/user2','somerandomid',array());
+ $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []);
$object = "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nDUE;VALUE=DATE-TIME:20120101T100000Z\r\nEND:VTODO\r\nEND:VCALENDAR\r\n";
$backend->createCalendarObject($returnedId, 'random-id', $object);
$result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"');
- $this->assertEquals(array(
- 'etag' => md5($object),
- 'size' => strlen($object),
- 'calendardata' => $object,
+ $this->assertEquals([
+ 'etag' => md5($object),
+ 'size' => strlen($object),
+ 'calendardata' => $object,
'firstoccurence' => null,
- 'lastoccurence' => null,
- 'componenttype' => 'VTODO',
- ), $result->fetch(\PDO::FETCH_ASSOC));
+ 'lastoccurence' => null,
+ 'componenttype' => 'VTODO',
+ ], $result->fetch(\PDO::FETCH_ASSOC));
}
@@ -323,22 +403,42 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
function testGetCalendarObjects() {
$backend = new PDO($this->pdo);
- $returnedId = $backend->createCalendar('principals/user2','somerandomid',array());
+ $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []);
$object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
$backend->createCalendarObject($returnedId, 'random-id', $object);
- $data = $backend->getCalendarObjects($returnedId,'random-id');
+ $data = $backend->getCalendarObjects($returnedId, 'random-id');
$this->assertEquals(1, count($data));
$data = $data[0];
$this->assertEquals($returnedId, $data['calendarid']);
$this->assertEquals('random-id', $data['uri']);
- $this->assertEquals(strlen($object),$data['size']);
+ $this->assertEquals(strlen($object), $data['size']);
}
+ /**
+ * @depends testCreateCalendarObject
+ */
+ function testGetCalendarObjectByUID() {
+
+ $backend = new PDO($this->pdo);
+ $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []);
+
+ $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
+ $backend->createCalendarObject($returnedId, 'random-id', $object);
+
+ $this->assertNull(
+ $backend->getCalendarObjectByUID('principals/user2', 'bar')
+ );
+ $this->assertEquals(
+ 'somerandomid/random-id',
+ $backend->getCalendarObjectByUID('principals/user2', 'foo')
+ );
+
+ }
/**
* @depends testCreateCalendarObject
@@ -346,14 +446,14 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
function testUpdateCalendarObject() {
$backend = new PDO($this->pdo);
- $returnedId = $backend->createCalendar('principals/user2','somerandomid',array());
+ $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []);
$object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
$object2 = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20130101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
$backend->createCalendarObject($returnedId, 'random-id', $object);
$backend->updateCalendarObject($returnedId, 'random-id', $object2);
- $data = $backend->getCalendarObject($returnedId,'random-id');
+ $data = $backend->getCalendarObject($returnedId, 'random-id');
$this->assertEquals($object2, $data['calendardata']);
$this->assertEquals($returnedId, $data['calendarid']);
@@ -368,13 +468,13 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
function testDeleteCalendarObject() {
$backend = new PDO($this->pdo);
- $returnedId = $backend->createCalendar('principals/user2','somerandomid',array());
+ $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []);
$object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
$backend->createCalendarObject($returnedId, 'random-id', $object);
$backend->deleteCalendarObject($returnedId, 'random-id');
- $data = $backend->getCalendarObject($returnedId,'random-id');
+ $data = $backend->getCalendarObject($returnedId, 'random-id');
$this->assertNull($data);
}
@@ -382,24 +482,24 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
function testCalendarQueryNoResult() {
$abstract = new PDO($this->pdo);
- $filters = array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(
- array(
- 'name' => 'VJOURNAL',
- 'comp-filters' => array(),
- 'prop-filters' => array(),
+ $filters = [
+ 'name' => 'VCALENDAR',
+ 'comp-filters' => [
+ [
+ 'name' => 'VJOURNAL',
+ 'comp-filters' => [],
+ 'prop-filters' => [],
'is-not-defined' => false,
- 'time-range' => null,
- ),
- ),
- 'prop-filters' => array(),
+ 'time-range' => null,
+ ],
+ ],
+ 'prop-filters' => [],
'is-not-defined' => false,
- 'time-range' => null,
- );
+ 'time-range' => null,
+ ];
- $this->assertEquals(array(
- ), $abstract->calendarQuery(1, $filters));
+ $this->assertEquals([
+ ], $abstract->calendarQuery(1, $filters));
}
@@ -409,25 +509,25 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
$backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n");
$backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n");
- $filters = array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(
- array(
- 'name' => 'VTODO',
- 'comp-filters' => array(),
- 'prop-filters' => array(),
+ $filters = [
+ 'name' => 'VCALENDAR',
+ 'comp-filters' => [
+ [
+ 'name' => 'VTODO',
+ 'comp-filters' => [],
+ 'prop-filters' => [],
'is-not-defined' => false,
- 'time-range' => null,
- ),
- ),
- 'prop-filters' => array(),
+ 'time-range' => null,
+ ],
+ ],
+ 'prop-filters' => [],
'is-not-defined' => false,
- 'time-range' => null,
- );
+ 'time-range' => null,
+ ];
- $this->assertEquals(array(
+ $this->assertEquals([
"todo",
- ), $backend->calendarQuery(1, $filters));
+ ], $backend->calendarQuery(1, $filters));
}
function testCalendarQueryTodoNotMatch() {
@@ -436,32 +536,32 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
$backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n");
$backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n");
- $filters = array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(
- array(
- 'name' => 'VTODO',
- 'comp-filters' => array(),
- 'prop-filters' => array(
- array(
- 'name' => 'summary',
- 'text-match' => null,
- 'time-range' => null,
- 'param-filters' => array(),
+ $filters = [
+ 'name' => 'VCALENDAR',
+ 'comp-filters' => [
+ [
+ 'name' => 'VTODO',
+ 'comp-filters' => [],
+ 'prop-filters' => [
+ [
+ 'name' => 'summary',
+ 'text-match' => null,
+ 'time-range' => null,
+ 'param-filters' => [],
'is-not-defined' => false,
- ),
- ),
+ ],
+ ],
'is-not-defined' => false,
- 'time-range' => null,
- ),
- ),
- 'prop-filters' => array(),
+ 'time-range' => null,
+ ],
+ ],
+ 'prop-filters' => [],
'is-not-defined' => false,
- 'time-range' => null,
- );
+ 'time-range' => null,
+ ];
- $this->assertEquals(array(
- ), $backend->calendarQuery(1, $filters));
+ $this->assertEquals([
+ ], $backend->calendarQuery(1, $filters));
}
@@ -471,13 +571,13 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
$backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n");
$backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n");
- $filters = array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(),
- 'prop-filters' => array(),
+ $filters = [
+ 'name' => 'VCALENDAR',
+ 'comp-filters' => [],
+ 'prop-filters' => [],
'is-not-defined' => false,
- 'time-range' => null,
- );
+ 'time-range' => null,
+ ];
$result = $backend->calendarQuery(1, $filters);
$this->assertTrue(in_array('todo', $result));
@@ -492,28 +592,28 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
$backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n");
$backend->createCalendarObject(1, "event2", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120103\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n");
- $filters = array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(
- array(
- 'name' => 'VEVENT',
- 'comp-filters' => array(),
- 'prop-filters' => array(),
+ $filters = [
+ 'name' => 'VCALENDAR',
+ 'comp-filters' => [
+ [
+ 'name' => 'VEVENT',
+ 'comp-filters' => [],
+ 'prop-filters' => [],
'is-not-defined' => false,
- 'time-range' => array(
+ 'time-range' => [
'start' => new \DateTime('20120103'),
'end' => new \DateTime('20120104'),
- ),
- ),
- ),
- 'prop-filters' => array(),
+ ],
+ ],
+ ],
+ 'prop-filters' => [],
'is-not-defined' => false,
- 'time-range' => null,
- );
+ 'time-range' => null,
+ ];
- $this->assertEquals(array(
+ $this->assertEquals([
"event2",
- ), $backend->calendarQuery(1, $filters));
+ ], $backend->calendarQuery(1, $filters));
}
function testCalendarQueryTimeRangeNoEnd() {
@@ -523,28 +623,261 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
$backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n");
$backend->createCalendarObject(1, "event2", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120103\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n");
- $filters = array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(
- array(
- 'name' => 'VEVENT',
- 'comp-filters' => array(),
- 'prop-filters' => array(),
+ $filters = [
+ 'name' => 'VCALENDAR',
+ 'comp-filters' => [
+ [
+ 'name' => 'VEVENT',
+ 'comp-filters' => [],
+ 'prop-filters' => [],
'is-not-defined' => false,
- 'time-range' => array(
+ 'time-range' => [
'start' => new \DateTime('20120102'),
- 'end' => null,
- ),
- ),
- ),
- 'prop-filters' => array(),
+ 'end' => null,
+ ],
+ ],
+ ],
+ 'prop-filters' => [],
'is-not-defined' => false,
- 'time-range' => null,
- );
+ 'time-range' => null,
+ ];
- $this->assertEquals(array(
+ $this->assertEquals([
"event2",
- ), $backend->calendarQuery(1, $filters));
+ ], $backend->calendarQuery(1, $filters));
}
+
+ function testGetChanges() {
+
+ $backend = new PDO($this->pdo);
+ $id = $backend->createCalendar(
+ 'principals/user1',
+ 'bla',
+ []
+ );
+ $result = $backend->getChangesForCalendar($id, null, 1);
+
+ $this->assertEquals([
+ 'syncToken' => 1,
+ 'modified' => [],
+ 'deleted' => [],
+ 'added' => [],
+ ], $result);
+
+ $currentToken = $result['syncToken'];
+
+ $dummyTodo = "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n";
+
+ $backend->createCalendarObject($id, "todo1.ics", $dummyTodo);
+ $backend->createCalendarObject($id, "todo2.ics", $dummyTodo);
+ $backend->createCalendarObject($id, "todo3.ics", $dummyTodo);
+ $backend->updateCalendarObject($id, "todo1.ics", $dummyTodo);
+ $backend->deleteCalendarObject($id, "todo2.ics");
+
+ $result = $backend->getChangesForCalendar($id, $currentToken, 1);
+
+ $this->assertEquals([
+ 'syncToken' => 6,
+ 'modified' => ["todo1.ics"],
+ 'deleted' => ["todo2.ics"],
+ 'added' => ["todo3.ics"],
+ ], $result);
+
+ $result = $backend->getChangesForCalendar($id, null, 1);
+
+ $this->assertEquals([
+ 'syncToken' => 6,
+ 'modified' => [],
+ 'deleted' => [],
+ 'added' => ["todo1.ics", "todo3.ics"],
+ ], $result);
+ }
+
+ function testCreateSubscriptions() {
+
+ $props = [
+ '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/cal.ics', false),
+ '{DAV:}displayname' => 'cal',
+ '{http://apple.com/ns/ical/}refreshrate' => 'P1W',
+ '{http://apple.com/ns/ical/}calendar-color' => '#FF00FFFF',
+ '{http://calendarserver.org/ns/}subscribed-strip-todos' => true,
+ //'{http://calendarserver.org/ns/}subscribed-strip-alarms' => true,
+ '{http://calendarserver.org/ns/}subscribed-strip-attachments' => true,
+ ];
+
+ $backend = new PDO($this->pdo);
+ $backend->createSubscription('principals/user1', 'sub1', $props);
+
+ $subs = $backend->getSubscriptionsForUser('principals/user1');
+
+ $expected = $props;
+ $expected['id'] = 1;
+ $expected['uri'] = 'sub1';
+ $expected['principaluri'] = 'principals/user1';
+
+ unset($expected['{http://calendarserver.org/ns/}source']);
+ $expected['source'] = 'http://example.org/cal.ics';
+
+ $this->assertEquals(1, count($subs));
+ foreach ($expected as $k => $v) {
+ $this->assertEquals($subs[0][$k], $expected[$k]);
+ }
+
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ function testCreateSubscriptionFail() {
+
+ $props = [
+ ];
+
+ $backend = new PDO($this->pdo);
+ $backend->createSubscription('principals/user1', 'sub1', $props);
+
+ }
+
+ function testUpdateSubscriptions() {
+
+ $props = [
+ '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/cal.ics', false),
+ '{DAV:}displayname' => 'cal',
+ '{http://apple.com/ns/ical/}refreshrate' => 'P1W',
+ '{http://apple.com/ns/ical/}calendar-color' => '#FF00FFFF',
+ '{http://calendarserver.org/ns/}subscribed-strip-todos' => true,
+ //'{http://calendarserver.org/ns/}subscribed-strip-alarms' => true,
+ '{http://calendarserver.org/ns/}subscribed-strip-attachments' => true,
+ ];
+
+ $backend = new PDO($this->pdo);
+ $backend->createSubscription('principals/user1', 'sub1', $props);
+
+ $newProps = [
+ '{DAV:}displayname' => 'new displayname',
+ '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/cal2.ics', false),
+ ];
+
+ $propPatch = new DAV\PropPatch($newProps);
+ $backend->updateSubscription(1, $propPatch);
+ $result = $propPatch->commit();
+
+ $this->assertTrue($result);
+
+ $subs = $backend->getSubscriptionsForUser('principals/user1');
+
+ $expected = array_merge($props, $newProps);
+ $expected['id'] = 1;
+ $expected['uri'] = 'sub1';
+ $expected['principaluri'] = 'principals/user1';
+
+ unset($expected['{http://calendarserver.org/ns/}source']);
+ $expected['source'] = 'http://example.org/cal2.ics';
+
+ $this->assertEquals(1, count($subs));
+ foreach ($expected as $k => $v) {
+ $this->assertEquals($subs[0][$k], $expected[$k]);
+ }
+
+ }
+
+ function testUpdateSubscriptionsFail() {
+
+ $props = [
+ '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/cal.ics', false),
+ '{DAV:}displayname' => 'cal',
+ '{http://apple.com/ns/ical/}refreshrate' => 'P1W',
+ '{http://apple.com/ns/ical/}calendar-color' => '#FF00FFFF',
+ '{http://calendarserver.org/ns/}subscribed-strip-todos' => true,
+ //'{http://calendarserver.org/ns/}subscribed-strip-alarms' => true,
+ '{http://calendarserver.org/ns/}subscribed-strip-attachments' => true,
+ ];
+
+ $backend = new PDO($this->pdo);
+ $backend->createSubscription('principals/user1', 'sub1', $props);
+
+ $propPatch = new DAV\PropPatch([
+ '{DAV:}displayname' => 'new displayname',
+ '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/cal2.ics', false),
+ '{DAV:}unknown' => 'foo',
+ ]);
+
+ $backend->updateSubscription(1, $propPatch);
+ $propPatch->commit();
+
+ $this->assertEquals([
+ '{DAV:}unknown' => 403,
+ '{DAV:}displayname' => 424,
+ '{http://calendarserver.org/ns/}source' => 424,
+ ], $propPatch->getResult());
+
+ }
+
+ function testDeleteSubscriptions() {
+
+ $props = [
+ '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/cal.ics', false),
+ '{DAV:}displayname' => 'cal',
+ '{http://apple.com/ns/ical/}refreshrate' => 'P1W',
+ '{http://apple.com/ns/ical/}calendar-color' => '#FF00FFFF',
+ '{http://calendarserver.org/ns/}subscribed-strip-todos' => true,
+ //'{http://calendarserver.org/ns/}subscribed-strip-alarms' => true,
+ '{http://calendarserver.org/ns/}subscribed-strip-attachments' => true,
+ ];
+
+ $backend = new PDO($this->pdo);
+ $backend->createSubscription('principals/user1', 'sub1', $props);
+
+ $newProps = [
+ '{DAV:}displayname' => 'new displayname',
+ '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/cal2.ics', false),
+ ];
+
+ $backend->deleteSubscription(1);
+
+ $subs = $backend->getSubscriptionsForUser('principals/user1');
+ $this->assertEquals(0, count($subs));
+ }
+
+ function testSchedulingMethods() {
+
+ $backend = new PDO($this->pdo);
+
+ $calData = "BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n";
+
+ $backend->createSchedulingObject(
+ 'principals/user1',
+ 'schedule1.ics',
+ $calData
+ );
+
+ $expected = [
+ 'calendardata' => $calData,
+ 'uri' => 'schedule1.ics',
+ 'etag' => '"' . md5($calData) . '"',
+ 'size' => strlen($calData)
+ ];
+
+ $result = $backend->getSchedulingObject('principals/user1', 'schedule1.ics');
+ foreach ($expected as $k => $v) {
+ $this->assertArrayHasKey($k, $result);
+ $this->assertEquals($v, $result[$k]);
+ }
+
+ $results = $backend->getSchedulingObjects('principals/user1');
+
+ $this->assertEquals(1, count($results));
+ $result = $results[0];
+ foreach ($expected as $k => $v) {
+ $this->assertEquals($v, $result[$k]);
+ }
+
+ $backend->deleteSchedulingObject('principals/user1', 'schedule1.ics');
+ $result = $backend->getSchedulingObject('principals/user1', 'schedule1.ics');
+
+ $this->assertNull($result);
+
+ }
+
}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractTest.php
index 04fb16df5..7f642efc9 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractTest.php
@@ -2,37 +2,92 @@
namespace Sabre\CalDAV\Backend;
+use
+ Sabre\DAV\PropPatch;
+
class AbstractTest extends \PHPUnit_Framework_TestCase {
function testUpdateCalendar() {
$abstract = new AbstractMock();
- $this->assertEquals(false, $abstract->updateCalendar('randomid', array('{DAV:}displayname' => 'anything')));
+ $propPatch = new PropPatch(['{DAV:}displayname' => 'anything']);
+
+ $abstract->updateCalendar('randomid', $propPatch);
+ $result = $propPatch->commit();
+
+ $this->assertFalse($result);
}
function testCalendarQuery() {
$abstract = new AbstractMock();
- $filters = array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(
- array(
- 'name' => 'VEVENT',
- 'comp-filters' => array(),
- 'prop-filters' => array(),
+ $filters = [
+ 'name' => 'VCALENDAR',
+ 'comp-filters' => [
+ [
+ 'name' => 'VEVENT',
+ 'comp-filters' => [],
+ 'prop-filters' => [],
'is-not-defined' => false,
- 'time-range' => null,
- ),
- ),
- 'prop-filters' => array(),
+ 'time-range' => null,
+ ],
+ ],
+ 'prop-filters' => [],
'is-not-defined' => false,
- 'time-range' => null,
+ 'time-range' => null,
+ ];
+
+ $this->assertEquals([
+ 'event1.ics',
+ ], $abstract->calendarQuery(1, $filters));
+
+ }
+
+ function testGetCalendarObjectByUID() {
+
+ $abstract = new AbstractMock();
+ $this->assertNull(
+ $abstract->getCalendarObjectByUID('principal1', 'zim')
+ );
+ $this->assertEquals(
+ 'cal1/event1.ics',
+ $abstract->getCalendarObjectByUID('principal1', 'foo')
+ );
+ $this->assertNull(
+ $abstract->getCalendarObjectByUID('principal3', 'foo')
);
+ $this->assertNull(
+ $abstract->getCalendarObjectByUID('principal1', 'shared')
+ );
+
+ }
- $this->assertEquals(array(
+ function testGetMultipleCalendarObjects() {
+
+ $abstract = new AbstractMock();
+ $result = $abstract->getMultipleCalendarObjects(1, [
'event1.ics',
- ), $abstract->calendarQuery(1, $filters));
+ 'task1.ics',
+ ]);
+
+ $expected = [
+ [
+ 'id' => 1,
+ 'calendarid' => 1,
+ 'uri' => 'event1.ics',
+ 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
+ ],
+ [
+ 'id' => 2,
+ 'calendarid' => 1,
+ 'uri' => 'task1.ics',
+ 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n",
+ ],
+ ];
+
+ $this->assertEquals($expected, $result);
+
}
@@ -40,49 +95,84 @@ class AbstractTest extends \PHPUnit_Framework_TestCase {
class AbstractMock extends AbstractBackend {
- function getCalendarsForUser($principalUri) { }
- function createCalendar($principalUri,$calendarUri,array $properties) { }
+ function getCalendarsForUser($principalUri) {
+
+ return [
+ [
+ 'id' => 1,
+ 'principaluri' => 'principal1',
+ 'uri' => 'cal1',
+ ],
+ [
+ 'id' => 2,
+ 'principaluri' => 'principal1',
+ '{http://sabredav.org/ns}owner-principal' => 'principal2',
+ 'uri' => 'cal1',
+ ],
+ ];
+
+ }
+ function createCalendar($principalUri, $calendarUri, array $properties) { }
function deleteCalendar($calendarId) { }
- function getCalendarObjects($calendarId) {
-
- return array(
- array(
- 'id' => 1,
- 'calendarid' => 1,
- 'uri' => 'event1.ics',
- ),
- array(
- 'id' => 2,
- 'calendarid' => 1,
- 'uri' => 'task1.ics',
- ),
- );
+ function getCalendarObjects($calendarId) {
+
+ switch ($calendarId) {
+ case 1:
+ return [
+ [
+ 'id' => 1,
+ 'calendarid' => 1,
+ 'uri' => 'event1.ics',
+ ],
+ [
+ 'id' => 2,
+ 'calendarid' => 1,
+ 'uri' => 'task1.ics',
+ ],
+ ];
+ case 2:
+ return [
+ [
+ 'id' => 3,
+ 'calendarid' => 2,
+ 'uri' => 'shared-event.ics',
+ ]
+ ];
+ }
}
- function getCalendarObject($calendarId,$objectUri) {
- switch($objectUri) {
+ function getCalendarObject($calendarId, $objectUri) {
+
+ switch ($objectUri) {
case 'event1.ics' :
- return array(
- 'id' => 1,
- 'calendarid' => 1,
- 'uri' => 'event1.ics',
- 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
- );
+ return [
+ 'id' => 1,
+ 'calendarid' => 1,
+ 'uri' => 'event1.ics',
+ 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
+ ];
case 'task1.ics' :
- return array(
- 'id' => 1,
- 'calendarid' => 1,
- 'uri' => 'event1.ics',
+ return [
+ 'id' => 2,
+ 'calendarid' => 1,
+ 'uri' => 'task1.ics',
'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n",
- );
+ ];
+ case 'shared-event.ics' :
+ return [
+ 'id' => 3,
+ 'calendarid' => 2,
+ 'uri' => 'event1.ics',
+ 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:shared\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
+ ];
}
}
- function createCalendarObject($calendarId,$objectUri,$calendarData) { }
- function updateCalendarObject($calendarId,$objectUri,$calendarData) { }
- function deleteCalendarObject($calendarId,$objectUri) { }
+ function createCalendarObject($calendarId, $objectUri, $calendarData) { }
+ function updateCalendarObject($calendarId, $objectUri, $calendarData) { }
+ function deleteCalendarObject($calendarId, $objectUri) { }
}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/Mock.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/Mock.php
index d196297f7..4412e5531 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/Mock.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/Mock.php
@@ -1,21 +1,25 @@
<?php
namespace Sabre\CalDAV\Backend;
+
use Sabre\DAV;
use Sabre\CalDAV;
-class Mock extends AbstractBackend implements NotificationSupport, SharingSupport {
+class Mock extends AbstractBackend {
+
+ protected $calendarData;
+ protected $calendars;
- private $calendarData;
- private $calendars;
- private $notifications;
- private $shares = array();
+ function __construct(array $calendars = [], array $calendarData = []) {
- function __construct(array $calendars, array $calendarData, array $notifications = array()) {
+ foreach ($calendars as &$calendar) {
+ if (!isset($calendar['id'])) {
+ $calendar['id'] = DAV\UUIDUtil::getUUID();
+ }
+ }
$this->calendars = $calendars;
$this->calendarData = $calendarData;
- $this->notifications = $notifications;
}
@@ -38,8 +42,8 @@ class Mock extends AbstractBackend implements NotificationSupport, SharingSuppor
*/
function getCalendarsForUser($principalUri) {
- $r = array();
- foreach($this->calendars as $row) {
+ $r = [];
+ foreach ($this->calendars as $row) {
if ($row['principaluri'] == $principalUri) {
$r[] = $row;
}
@@ -63,71 +67,29 @@ class Mock extends AbstractBackend implements NotificationSupport, SharingSuppor
* @param array $properties
* @return string|int
*/
- function createCalendar($principalUri,$calendarUri,array $properties) {
+ function createCalendar($principalUri, $calendarUri, array $properties) {
$id = DAV\UUIDUtil::getUUID();
- $this->calendars[] = array_merge(array(
- 'id' => $id,
- 'principaluri' => $principalUri,
- 'uri' => $calendarUri,
- '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Property\SupportedCalendarComponentSet(array('VEVENT','VTODO')),
- ), $properties);
+ $this->calendars[] = array_merge([
+ 'id' => $id,
+ 'principaluri' => $principalUri,
+ 'uri' => $calendarUri,
+ '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VEVENT', 'VTODO']),
+ ], $properties);
return $id;
}
/**
- * Updates properties on this node,
- *
- * The properties array uses the propertyName in clark-notation as key,
- * and the array value for the property value. In the case a property
- * should be deleted, the property value will be null.
- *
- * This method must be atomic. If one property cannot be changed, the
- * entire operation must fail.
- *
- * If the operation was successful, true can be returned.
- * If the operation failed, false can be returned.
- *
- * Deletion of a non-existent property is always successful.
- *
- * Lastly, it is optional to return detailed information about any
- * failures. In this case an array should be returned with the following
- * structure:
- *
- * array(
- * 403 => array(
- * '{DAV:}displayname' => null,
- * ),
- * 424 => array(
- * '{DAV:}owner' => null,
- * )
- * )
- *
- * In this example it was forbidden to update {DAV:}displayname.
- * (403 Forbidden), which in turn also caused {DAV:}owner to fail
- * (424 Failed Dependency) because the request needs to be atomic.
- *
- * @param string $calendarId
- * @param array $properties
- * @return bool|array
- */
- public function updateCalendar($calendarId, array $properties) {
-
- return false;
-
- }
-
- /**
* Delete a calendar and all it's objects
*
* @param string $calendarId
* @return void
*/
- public function deleteCalendar($calendarId) {
+ function deleteCalendar($calendarId) {
- foreach($this->calendars as $k=>$calendar) {
+ foreach ($this->calendars as $k => $calendar) {
if ($calendar['id'] === $calendarId) {
unset($this->calendars[$k]);
}
@@ -157,17 +119,17 @@ class Mock extends AbstractBackend implements NotificationSupport, SharingSuppor
* @param string $calendarId
* @return array
*/
- public function getCalendarObjects($calendarId) {
+ function getCalendarObjects($calendarId) {
if (!isset($this->calendarData[$calendarId]))
- return array();
+ return [];
$objects = $this->calendarData[$calendarId];
- foreach($objects as $uri => &$object) {
+ foreach ($objects as $uri => &$object) {
$object['calendarid'] = $calendarId;
$object['uri'] = $uri;
-
+ $object['lastmodified'] = null;
}
return $objects;
@@ -185,7 +147,7 @@ class Mock extends AbstractBackend implements NotificationSupport, SharingSuppor
* @param string $objectUri
* @return array
*/
- function getCalendarObject($calendarId,$objectUri) {
+ function getCalendarObject($calendarId, $objectUri) {
if (!isset($this->calendarData[$calendarId][$objectUri])) {
throw new DAV\Exception\NotFound('Object could not be found');
@@ -193,6 +155,7 @@ class Mock extends AbstractBackend implements NotificationSupport, SharingSuppor
$object = $this->calendarData[$calendarId][$objectUri];
$object['calendarid'] = $calendarId;
$object['uri'] = $objectUri;
+ $object['lastmodified'] = null;
return $object;
}
@@ -205,13 +168,14 @@ class Mock extends AbstractBackend implements NotificationSupport, SharingSuppor
* @param string $calendarData
* @return void
*/
- function createCalendarObject($calendarId,$objectUri,$calendarData) {
+ function createCalendarObject($calendarId, $objectUri, $calendarData) {
- $this->calendarData[$calendarId][$objectUri] = array(
+ $this->calendarData[$calendarId][$objectUri] = [
'calendardata' => $calendarData,
- 'calendarid' => $calendarId,
- 'uri' => $objectUri,
- );
+ 'calendarid' => $calendarId,
+ 'uri' => $objectUri,
+ ];
+ return '"' . md5($calendarData) . '"';
}
@@ -223,13 +187,14 @@ class Mock extends AbstractBackend implements NotificationSupport, SharingSuppor
* @param string $calendarData
* @return void
*/
- function updateCalendarObject($calendarId,$objectUri,$calendarData) {
+ function updateCalendarObject($calendarId, $objectUri, $calendarData) {
- $this->calendarData[$calendarId][$objectUri] = array(
+ $this->calendarData[$calendarId][$objectUri] = [
'calendardata' => $calendarData,
- 'calendarid' => $calendarId,
- 'uri' => $objectUri,
- );
+ 'calendarid' => $calendarId,
+ 'uri' => $objectUri,
+ ];
+ return '"' . md5($calendarData) . '"';
}
@@ -240,161 +205,11 @@ class Mock extends AbstractBackend implements NotificationSupport, SharingSuppor
* @param string $objectUri
* @return void
*/
- function deleteCalendarObject($calendarId,$objectUri) {
+ function deleteCalendarObject($calendarId, $objectUri) {
throw new Exception('Not implemented');
}
- /**
- * Returns a list of notifications for a given principal url.
- *
- * The returned array should only consist of implementations of
- * Sabre\CalDAV\Notifications\INotificationType.
- *
- * @param string $principalUri
- * @return array
- */
- public function getNotificationsForPrincipal($principalUri) {
-
- if (isset($this->notifications[$principalUri])) {
- return $this->notifications[$principalUri];
- }
- return array();
-
- }
-
- /**
- * This deletes a specific notifcation.
- *
- * This may be called by a client once it deems a notification handled.
- *
- * @param string $principalUri
- * @param Sabre\CalDAV\Notifications\INotificationType $notification
- * @return void
- */
- public function deleteNotification($principalUri, CalDAV\Notifications\INotificationType $notification) {
-
- foreach($this->notifications[$principalUri] as $key=>$value) {
- if ($notification === $value) {
- unset($this->notifications[$principalUri][$key]);
- }
- }
-
- }
-
- /**
- * Updates the list of shares.
- *
- * The first array is a list of people that are to be added to the
- * calendar.
- *
- * Every element in the add array has the following properties:
- * * href - A url. Usually a mailto: address
- * * commonName - Usually a first and last name, or false
- * * summary - A description of the share, can also be false
- * * readOnly - A boolean value
- *
- * Every element in the remove array is just the address string.
- *
- * Note that if the calendar is currently marked as 'not shared' by and
- * this method is called, the calendar should be 'upgraded' to a shared
- * calendar.
- *
- * @param mixed $calendarId
- * @param array $add
- * @param array $remove
- * @return void
- */
- public function updateShares($calendarId, array $add, array $remove) {
-
- if (!isset($this->shares[$calendarId])) {
- $this->shares[$calendarId] = array();
- }
-
- foreach($add as $val) {
- $val['status'] = CalDAV\SharingPlugin::STATUS_NORESPONSE;
- $this->shares[$calendarId][] = $val;
- }
-
- foreach($this->shares[$calendarId] as $k=>$share) {
-
- if (in_array($share['href'], $remove)) {
- unset($this->shares[$calendarId][$k]);
- }
-
- }
-
- // Re-numbering keys
- $this->shares[$calendarId] = array_values($this->shares[$calendarId]);
-
- }
-
- /**
- * Returns the list of people whom this calendar is shared with.
- *
- * Every element in this array should have the following properties:
- * * href - Often a mailto: address
- * * commonName - Optional, for example a first + last name
- * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants.
- * * readOnly - boolean
- * * summary - Optional, a description for the share
- *
- * @param mixed $calendarId
- * @return array
- */
- public function getShares($calendarId) {
-
- if (!isset($this->shares[$calendarId])) {
- return array();
- }
-
- return $this->shares[$calendarId];
-
- }
-
- /**
- * This method is called when a user replied to a request to share.
- *
- * @param string href The sharee who is replying (often a mailto: address)
- * @param int status One of the SharingPlugin::STATUS_* constants
- * @param string $calendarUri The url to the calendar thats being shared
- * @param string $inReplyTo The unique id this message is a response to
- * @param string $summary A description of the reply
- * @return void
- */
- public function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null) {
-
- // This operation basically doesn't do anything yet
- if ($status === CalDAV\SharingPlugin::STATUS_ACCEPTED) {
- return 'calendars/blabla/calendar';
- }
-
- }
-
- /**
- * Publishes a calendar
- *
- * @param mixed $calendarId
- * @param bool $value
- * @return void
- */
- public function setPublishStatus($calendarId, $value) {
-
- foreach($this->calendars as $k=>$cal) {
- if ($cal['id'] === $calendarId) {
- if (!$value) {
- unset($cal['{http://calendarserver.org/ns/}publish-url']);
- } else {
- $cal['{http://calendarserver.org/ns/}publish-url'] = 'http://example.org/public/ ' . $calendarId . '.ics';
- }
- return;
- }
- }
-
- throw new DAV\Exception('Calendar with id "' . $calendarId . '" not found');
-
- }
-
}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/PDOMySQLTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/PDOMySQLTest.php
index 15c1d91fd..c215ca171 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/PDOMySQLTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/PDOMySQLTest.php
@@ -14,15 +14,15 @@ class PDOMySQLTest extends AbstractPDOTest {
$pdo = \Sabre\TestUtil::getMySQLDB();
if (!$pdo) $this->markTestSkipped('Could not connect to mysql database');
- $pdo->query('DROP TABLE IF EXISTS calendarobjects, calendars');
+ $pdo->query('DROP TABLE IF EXISTS calendarobjects, calendars, calendarchanges, calendarsubscriptions, schedulingobjects');
$queries = explode(
';',
file_get_contents(__DIR__ . '/../../../../examples/sql/mysql.calendars.sql')
);
- foreach($queries as $query) {
- $query = trim($query," \r\n\t");
+ foreach ($queries as $query) {
+ $query = trim($query, " \r\n\t");
if ($query)
$pdo->exec($query);
}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/PDOSqliteTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/PDOSqliteTest.php
index c50f06986..4074259f2 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/PDOSqliteTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/Backend/PDOSqliteTest.php
@@ -6,12 +6,24 @@ use Sabre\CalDAV;
require_once 'Sabre/CalDAV/Backend/AbstractPDOTest.php';
-class PDOSQLiteTest extends AbstractPDOTest {
+class PDOSqliteTest extends AbstractPDOTest {
function setup() {
if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available');
- $this->pdo = CalDAV\TestUtil::getSQLiteDB();
+
+ if (file_exists(SABRE_TEMPDIR . '/testdb.sqlite'))
+ unlink(SABRE_TEMPDIR . '/testdb.sqlite');
+
+ $pdo = new \PDO('sqlite:' . SABRE_TEMPDIR . '/testdb.sqlite');
+ $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+
+ // Yup this is definitely not 'fool proof', but good enough for now.
+ $queries = explode(';', file_get_contents(__DIR__ . '/../../../../examples/sql/sqlite.calendars.sql'));
+ foreach ($queries as $query) {
+ $pdo->exec($query);
+ }
+ $this->pdo = $pdo;
}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/CalendarObjectTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/CalendarObjectTest.php
index eab10eae7..9fc1eee4c 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/CalendarObjectTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/CalendarObjectTest.php
@@ -43,7 +43,7 @@ class CalendarObjectTest extends \PHPUnit_Framework_TestCase {
$this->assertInternalType('string',$children[0]->getName());
$this->assertInternalType('string',$children[0]->get());
$this->assertInternalType('string',$children[0]->getETag());
- $this->assertEquals('text/calendar; charset=utf-8', $children[0]->getContentType());
+ $this->assertEquals('text/calendar; charset=utf-8; component=vevent', $children[0]->getContentType());
}
@@ -180,6 +180,46 @@ class CalendarObjectTest extends \PHPUnit_Framework_TestCase {
'protected' => true,
),
array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/user1/calendar-proxy-write',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/user1/calendar-proxy-read',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}write',
+ 'principal' => 'principals/user1',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}write',
+ 'principal' => 'principals/user1/calendar-proxy-write',
+ 'protected' => true,
+ ),
+ );
+
+ $children = $this->calendar->getChildren();
+ $this->assertTrue($children[0] instanceof CalendarObject);
+
+ $obj = $children[0];
+ $this->assertEquals($expected, $obj->getACL());
+
+ }
+
+ function testDefaultACL() {
+
+ $backend = new Backend\Mock([], []);
+ $calendarObject = new CalendarObject($backend, ['principaluri' => 'principals/user1'], ['calendarid' => 1, 'uri' => 'foo']);
+ $expected = array(
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/user1',
+ 'protected' => true,
+ ),
+ array(
'privilege' => '{DAV:}write',
'principal' => 'principals/user1',
'protected' => true,
@@ -200,12 +240,8 @@ class CalendarObjectTest extends \PHPUnit_Framework_TestCase {
'protected' => true,
),
);
+ $this->assertEquals($expected, $calendarObject->getACL());
- $children = $this->calendar->getChildren();
- $this->assertTrue($children[0] instanceof CalendarObject);
-
- $obj = $children[0];
- $this->assertEquals($expected, $obj->getACL());
}
@@ -278,7 +314,7 @@ END:VCALENDAR";
),
)
));
- $obj = new CalendarObject($backend, array(), array('calendarid' => 1, 'uri' => 'foo'));
+ $obj = new CalendarObject($backend, array('id' => 1), array('uri' => 'foo'));
$this->assertEquals('foo', $obj->get());
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/CalendarQueryParserTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/CalendarQueryParserTest.php
deleted file mode 100644
index fdfe4de89..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/CalendarQueryParserTest.php
+++ /dev/null
@@ -1,540 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV;
-use Sabre\DAV;
-
-class CalendarQueryParserTest extends \PHPUnit_Framework_TestCase {
-
- function parse($xml) {
-
- $xml =
-'<?xml version="1.0"?>
-<c:calendar-query xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:d="DAV:">
-' . implode("\n", $xml) . '
-</c:calendar-query>';
-
- $dom = DAV\XMLUtil::loadDOMDocument($xml);
-
- $q = new CalendarQueryParser($dom);
- $q->parse();
- return $q->filters;
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testNoFilter() {
-
- $xml = array();
- $this->parse($xml);
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testTwoCompFilter() {
-
- $xml = array(
- '<c:filter>',
- ' <c:comp-filter name="VEVENT" />',
- ' <c:comp-filter name="VEVENT" />',
- '</c:filter>'
- );
- $this->parse($xml);
-
- }
-
- function testBasicFilter() {
-
- $xml = array(
- '<c:filter>',
- ' <c:comp-filter name="VCALENDAR" />',
- '</c:filter>'
- );
- $result = $this->parse($xml);
-
- $expected = array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(),
- 'prop-filters' => array(),
- 'is-not-defined' => false,
- 'time-range' => false
- );
-
- $this->assertEquals(
- $expected,
- $result
- );
-
- }
-
- function testCompIsNotDefined() {
-
- $xml = array(
- '<c:filter>',
- ' <c:comp-filter name="VCALENDAR">',
- ' <c:comp-filter name="VEVENT">',
- ' <c:is-not-defined/>',
- ' </c:comp-filter>',
- ' </c:comp-filter>',
- '</c:filter>'
- );
- $result = $this->parse($xml);
-
- $expected = array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(
- array(
- 'name' => 'VEVENT',
- 'comp-filters' => array(),
- 'prop-filters' => array(),
- 'is-not-defined' => true,
- 'time-range' => false
- ),
- ),
- 'prop-filters' => array(),
- 'is-not-defined' => false,
- 'time-range' => false
- );
-
- $this->assertEquals(
- $expected,
- $result
- );
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testCompTimeRangeOnVCALENDAR() {
-
- $xml = array(
- '<c:filter>',
- ' <c:comp-filter name="VCALENDAR">',
- ' <c:time-range start="20110101T000000Z" end="20111231T235959Z" />',
- ' </c:comp-filter>',
- '</c:filter>'
- );
- $result = $this->parse($xml);
-
- }
-
- function testCompTimeRange() {
-
- $xml = array(
- '<c:filter>',
- ' <c:comp-filter name="VCALENDAR">',
- ' <c:comp-filter name="VEVENT">',
- ' <c:time-range start="20110101T000000Z" end="20111231T235959Z" />',
- ' </c:comp-filter>',
- ' <c:comp-filter name="VTODO">',
- ' <c:time-range start="20110101T000000Z" />',
- ' </c:comp-filter>',
- ' <c:comp-filter name="VJOURNAL">',
- ' <c:time-range end="20111231T235959Z" />',
- ' </c:comp-filter>',
- ' </c:comp-filter>',
- '</c:filter>'
- );
- $result = $this->parse($xml);
-
- $expected = array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(
- array(
- 'name' => 'VEVENT',
- 'comp-filters' => array(),
- 'prop-filters' => array(),
- 'is-not-defined' => false,
- 'time-range' => array(
- 'start' => new \DateTime('2011-01-01 00:00:00', new \DateTimeZone('GMT')),
- 'end' => new \DateTime('2011-12-31 23:59:59', new \DateTimeZone('GMT')),
- ),
- ),
- array(
- 'name' => 'VTODO',
- 'comp-filters' => array(),
- 'prop-filters' => array(),
- 'is-not-defined' => false,
- 'time-range' => array(
- 'start' => new \DateTime('2011-01-01 00:00:00', new \DateTimeZone('GMT')),
- 'end' => null,
- ),
- ),
- array(
- 'name' => 'VJOURNAL',
- 'comp-filters' => array(),
- 'prop-filters' => array(),
- 'is-not-defined' => false,
- 'time-range' => array(
- 'start' => null,
- 'end' => new \DateTime('2011-12-31 23:59:59', new \DateTimeZone('GMT')),
- ),
- ),
- ),
- 'prop-filters' => array(),
- 'is-not-defined' => false,
- 'time-range' => false
- );
-
- $this->assertEquals(
- $expected,
- $result
- );
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testCompTimeRangeBadRange() {
-
- $xml = array(
- '<c:filter>',
- ' <c:comp-filter name="VCALENDAR">',
- ' <c:comp-filter name="VEVENT">',
- ' <c:time-range start="20110101T000000Z" end="20100101T000000Z" />',
- ' </c:comp-filter>',
- ' </c:comp-filter>',
- '</c:filter>'
- );
- $this->parse($xml);
-
- }
-
- function testProp() {
-
- $xml = array(
- '<c:filter>',
- ' <c:comp-filter name="VCALENDAR">',
- ' <c:comp-filter name="VEVENT">',
- ' <c:prop-filter name="SUMMARY">',
- ' <c:text-match>vacation</c:text-match>',
- ' </c:prop-filter>',
- ' </c:comp-filter>',
- ' </c:comp-filter>',
- '</c:filter>'
- );
- $result = $this->parse($xml);
-
- $expected = array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(
- array(
- 'name' => 'VEVENT',
- 'is-not-defined' => false,
- 'comp-filters' => array(),
- 'prop-filters' => array(
- array(
- 'name' => 'SUMMARY',
- 'is-not-defined' => false,
- 'param-filters' => array(),
- 'text-match' => array(
- 'negate-condition' => false,
- 'collation' => 'i;ascii-casemap',
- 'value' => 'vacation',
- ),
- 'time-range' => null,
- ),
- ),
- 'time-range' => null,
- ),
- ),
- 'prop-filters' => array(),
- 'is-not-defined' => false,
- 'time-range' => false
- );
-
- $this->assertEquals(
- $expected,
- $result
- );
-
- }
-
- function testComplex() {
-
- $xml = array(
- '<c:filter>',
- ' <c:comp-filter name="VCALENDAR">',
- ' <c:comp-filter name="VEVENT">',
- ' <c:prop-filter name="SUMMARY">',
- ' <c:text-match collation="i;unicode-casemap">vacation</c:text-match>',
- ' </c:prop-filter>',
- ' <c:prop-filter name="DTSTAMP">',
- ' <c:time-range start="20110704T000000Z" />',
- ' </c:prop-filter>',
- ' <c:prop-filter name="ORGANIZER">',
- ' <c:is-not-defined />',
- ' </c:prop-filter>',
- ' <c:prop-filter name="DTSTART">',
- ' <c:param-filter name="VALUE">',
- ' <c:text-match negate-condition="yes">DATE</c:text-match>',
- ' </c:param-filter>',
- ' </c:prop-filter>',
- ' </c:comp-filter>',
- ' </c:comp-filter>',
- '</c:filter>'
- );
- $result = $this->parse($xml);
-
- $expected = array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(
- array(
- 'name' => 'VEVENT',
- 'is-not-defined' => false,
- 'comp-filters' => array(),
- 'prop-filters' => array(
- array(
- 'name' => 'SUMMARY',
- 'is-not-defined' => false,
- 'param-filters' => array(),
- 'text-match' => array(
- 'negate-condition' => false,
- 'collation' => 'i;unicode-casemap',
- 'value' => 'vacation',
- ),
- 'time-range' => null,
- ),
- array(
- 'name' => 'DTSTAMP',
- 'is-not-defined' => false,
- 'param-filters' => array(),
- 'text-match' => null,
- 'time-range' => array(
- 'start' => new \DateTime('2011-07-04 00:00:00', new \DateTimeZone('GMT')),
- 'end' => null,
- ),
- ),
- array(
- 'name' => 'ORGANIZER',
- 'is-not-defined' => true,
- 'param-filters' => array(),
- 'text-match' => null,
- 'time-range' => null,
- ),
- array(
- 'name' => 'DTSTART',
- 'is-not-defined' => false,
- 'param-filters' => array(
- array(
- 'name' => 'VALUE',
- 'is-not-defined' => false,
- 'text-match' => array(
- 'negate-condition' => true,
- 'value' => 'DATE',
- 'collation' => 'i;ascii-casemap',
- ),
- ),
- ),
- 'text-match' => null,
- 'time-range' => null,
- ),
- ),
- 'time-range' => null,
- ),
- ),
- 'prop-filters' => array(),
- 'is-not-defined' => false,
- 'time-range' => false
- );
-
- $this->assertEquals(
- $expected,
- $result
- );
-
- }
-
- function testOther1() {
-
- // This body was exactly sent to us from the sabredav mailing list. Checking if this parses correctly.
-
- $body = <<<BLA
-<?xml version="1.0" encoding="utf-8" ?>
-<C:calendar-query xmlns:D="DAV:"
-xmlns:C="urn:ietf:params:xml:ns:caldav">
- <D:prop>
- <C:calendar-data/>
- <D:getetag/>
- </D:prop>
- <C:filter>
- <C:comp-filter name="VCALENDAR">
- <C:comp-filter name="VEVENT">
- <C:time-range start="20090101T000000Z" end="20121202T000000Z"/>
- </C:comp-filter>
- </C:comp-filter>
- </C:filter>
-</C:calendar-query>
-BLA;
-
- $dom = DAV\XMLUtil::loadDOMDocument($body);
-
- $q = new CalendarQueryParser($dom);
- $q->parse();
-
- $this->assertEquals(array(
- '{urn:ietf:params:xml:ns:caldav}calendar-data',
- '{DAV:}getetag',
- ), $q->requestedProperties);
-
- $expectedFilters = array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(
- array(
- 'name' => 'VEVENT',
- 'comp-filters' => array(),
- 'prop-filters' => array(),
- 'time-range' => array(
- 'start' => new \DateTime('2009-01-01 00:00:00', new \DateTimeZone('UTC')),
- 'end' => new \DateTime('2012-12-02 00:00:00', new \DateTimeZone('UTC')),
- ),
- 'is-not-defined' => false,
- ),
- ),
- 'prop-filters' => array(),
- 'time-range' => null,
- 'is-not-defined' => false,
- );
-
- $this->assertEquals($expectedFilters, $q->filters);
-
- }
-
- function testExpand() {
-
- $xml = array(
- '<d:prop>',
- ' <c:calendar-data>',
- ' <c:expand start="20110101T000000Z" end="20120101T000000Z"/>',
- ' </c:calendar-data>',
- '</d:prop>',
- '<c:filter>',
- ' <c:comp-filter name="VCALENDAR" />',
- '</c:filter>'
- );
-
- $xml =
-'<?xml version="1.0"?>
-<c:calendar-query xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:d="DAV:">
-' . implode("\n", $xml) . '
-</c:calendar-query>';
-
- $dom = DAV\XMLUtil::loadDOMDocument($xml);
- $q = new CalendarQueryParser($dom);
- $q->parse();
-
-
- $expected = array(
- 'name' => 'VCALENDAR',
- 'comp-filters' => array(),
- 'prop-filters' => array(),
- 'is-not-defined' => false,
- 'time-range' => false
- );
-
- $this->assertEquals(
- $expected,
- $q->filters
- );
-
- $this->assertEquals(array(
- '{urn:ietf:params:xml:ns:caldav}calendar-data',
- ), $q->requestedProperties);
-
- $this->assertEquals(
- array(
- 'start' => new \DateTime('2011-01-01 00:00:00', new \DateTimeZone('UTC')),
- 'end' => new \DateTime('2012-01-01 00:00:00', new \DateTimeZone('UTC')),
- ),
- $q->expand
- );
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testExpandNoStart() {
-
- $xml = array(
- '<d:prop>',
- ' <c:calendar-data>',
- ' <c:expand end="20120101T000000Z"/>',
- ' </c:calendar-data>',
- '</d:prop>',
- '<c:filter>',
- ' <c:comp-filter name="VCALENDAR" />',
- '</c:filter>'
- );
-
- $xml =
-'<?xml version="1.0"?>
-<c:calendar-query xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:d="DAV:">
-' . implode("\n", $xml) . '
-</c:calendar-query>';
-
- $dom = DAV\XMLUtil::loadDOMDocument($xml);
- $q = new CalendarQueryParser($dom);
- $q->parse();
-
- }
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testExpandNoEnd() {
-
- $xml = array(
- '<d:prop>',
- ' <c:calendar-data>',
- ' <c:expand start="20120101T000000Z"/>',
- ' </c:calendar-data>',
- '</d:prop>',
- '<c:filter>',
- ' <c:comp-filter name="VCALENDAR" />',
- '</c:filter>'
- );
-
- $xml =
-'<?xml version="1.0"?>
-<c:calendar-query xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:d="DAV:">
-' . implode("\n", $xml) . '
-</c:calendar-query>';
-
- $dom = DAV\XMLUtil::loadDOMDocument($xml);
- $q = new CalendarQueryParser($dom);
- $q->parse();
-
- }
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testExpandBadTimes() {
-
- $xml = array(
- '<d:prop>',
- ' <c:calendar-data>',
- ' <c:expand start="20120101T000000Z" end="19980101T000000Z"/>',
- ' </c:calendar-data>',
- '</d:prop>',
- '<c:filter>',
- ' <c:comp-filter name="VCALENDAR" />',
- '</c:filter>'
- );
-
- $xml =
-'<?xml version="1.0"?>
-<c:calendar-query xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:d="DAV:">
-' . implode("\n", $xml) . '
-</c:calendar-query>';
-
- $dom = DAV\XMLUtil::loadDOMDocument($xml);
- $q = new CalendarQueryParser($dom);
- $q->parse();
-
- }
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/CalendarQueryValidatorTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/CalendarQueryValidatorTest.php
index deb70d205..9822e82e2 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/CalendarQueryValidatorTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/CalendarQueryValidatorTest.php
@@ -6,6 +6,20 @@ use Sabre\DAV;
class CalendarQueryValidatorTest extends \PHPUnit_Framework_TestCase {
+ function testTopLevelFail() {
+
+ $validator = new CalendarQueryValidator();
+ $vcal = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+END:VEVENT
+END:VCALENDAR
+ICS;
+ $vcal = VObject\Reader::read($vcal);
+ $this->assertFalse($validator->validate($vcal, ['name' => 'VFOO']));
+
+ }
+
/**
* @dataProvider provider
*/
@@ -35,10 +49,14 @@ class CalendarQueryValidatorTest extends \PHPUnit_Framework_TestCase {
case -1 :
try {
$validator->validate($vObject, $filters);
- } catch (DAV\Exception $e) {
- // Success
- } catch (\LogicException $e) {
- // Success
+ $this->fail('This test was supposed to fail');
+ } catch (\Exception $e) {
+ // We need to test something to be valid for phpunit strict
+ // mode.
+ $this->assertTrue(true);
+ } catch (\Throwable $e) {
+ // PHP7
+ $this->assertTrue(true);
}
break;
@@ -334,6 +352,7 @@ yow;
$blob31 = <<<yow
BEGIN:VCALENDAR
BEGIN:VEVENT
+UID:foobar
DTSTART:20080101T120000Z
DURATION:PT1H
RRULE:FREQ=YEARLY
@@ -344,6 +363,7 @@ yow;
$blob32 = <<<yow
BEGIN:VCALENDAR
BEGIN:VEVENT
+UID:foobar
DTSTART:20080102T120000Z
DURATION:PT1H
RRULE:FREQ=YEARLY
@@ -353,6 +373,7 @@ yow;
$blob33 = <<<yow
BEGIN:VCALENDAR
BEGIN:VEVENT
+UID:foobar
DTSTART;VALUE=DATE:20120628
RRULE:FREQ=DAILY
END:VEVENT
@@ -361,6 +382,7 @@ yow;
$blob34 = <<<yow
BEGIN:VCALENDAR
BEGIN:VEVENT
+UID:foobar
DTSTART;VALUE=DATE:20120628
RRULE:FREQ=DAILY
BEGIN:VALARM
@@ -667,38 +689,38 @@ yow;
array($blob1, $filter3, 0),
array($blob1, $filter4, 1),
- // Subcomponent check
+ // Subcomponent check (4)
array($blob1, $filter5, 0),
array($blob2, $filter5, 1),
- // Property check
+ // Property checki (6)
array($blob1, $filter6, 1),
array($blob1, $filter7, 0),
array($blob1, $filter8, 0),
array($blob1, $filter9, 1),
- // Subcomponent + property
+ // Subcomponent + property (10)
array($blob2, $filter10, 1),
- // Param filter
+ // Param filter (11)
array($blob3, $filter11, 1),
array($blob3, $filter12, 0),
array($blob3, $filter13, 0),
array($blob3, $filter14, 1),
- // Param + text
+ // Param + text (15)
array($blob3, $filter15, 1),
array($blob3, $filter16, 0),
array($blob3, $filter17, 0),
array($blob3, $filter18, 1),
- // Prop + text
+ // Prop + text (19)
array($blob2, $filter19, 1),
- // Incorrect object (vcard)
+ // Incorrect object (vcard) (20)
array($blob4, $filter1, -1),
- // Time-range for event
+ // Time-range for event (21)
array($blob5, $filter20, 1),
array($blob6, $filter20, 1),
array($blob7, $filter20, 1),
@@ -712,7 +734,7 @@ yow;
array($blob7, $filter23, 0),
array($blob8, $filter23, 0),
- // Time-range for todo
+ // Time-range for todo (31)
array($blob9, $filter24, 1),
array($blob9, $filter25, 0),
array($blob9, $filter26, 1),
@@ -744,7 +766,7 @@ yow;
array($blob16, $filter25, 1),
array($blob16, $filter26, 1),
- // Time-range for journals
+ // Time-range for journals (55)
array($blob17, $filter27, 0),
array($blob17, $filter28, 0),
array($blob18, $filter27, 0),
@@ -752,15 +774,15 @@ yow;
array($blob19, $filter27, 1),
array($blob19, $filter28, 1),
- // Time-range for free-busy
+ // Time-range for free-busy (61)
array($blob20, $filter29, -1),
- // Time-range on property
+ // Time-range on property (62)
array($blob5, $filter30, 1),
array($blob3, $filter37, -1),
array($blob3, $filter30, 0),
- // Time-range on alarm in vevent
+ // Time-range on alarm in vevent (65)
array($blob21, $filter31, 1),
array($blob21, $filter32, 0),
array($blob22, $filter31, 1),
@@ -774,28 +796,28 @@ yow;
array($blob26, $filter31, 1),
array($blob26, $filter32, 0),
- // Time-range on alarm for vtodo
+ // Time-range on alarm for vtodo (77)
array($blob27, $filter33, 1),
array($blob27, $filter34, 0),
- // Time-range on alarm for vjournal
+ // Time-range on alarm for vjournal (79)
array($blob28, $filter35, -1),
array($blob28, $filter36, -1),
- // Time-range on alarm with duration
+ // Time-range on alarm with duration (81)
array($blob29, $filter31, 1),
array($blob29, $filter32, 0),
array($blob30, $filter31, 0),
array($blob30, $filter32, 0),
- // Time-range with RRULE
+ // Time-range with RRULE (85)
array($blob31, $filter20, 1),
array($blob32, $filter20, 0),
- // Bug reported on mailing list, related to all-day events.
- array($blob33, $filter38, 1),
+ // Bug reported on mailing list, related to all-day events (87)
+ //array($blob33, $filter38, 1),
- // Event in timerange, but filtered alarm is in the far future.
+ // Event in timerange, but filtered alarm is in the far future (88).
array($blob34, $filter39, 0),
);
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/CalendarTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/CalendarTest.php
index 2b2690d42..ea744d2cf 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/CalendarTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/CalendarTest.php
@@ -1,6 +1,8 @@
<?php
namespace Sabre\CalDAV;
+
+use Sabre\DAV\PropPatch;
use Sabre\DAVACL;
require_once 'Sabre/CalDAV/TestUtil.php';
@@ -8,7 +10,7 @@ require_once 'Sabre/CalDAV/TestUtil.php';
class CalendarTest extends \PHPUnit_Framework_TestCase {
/**
- * @var Sabre\CalDAV\Backend_PDO
+ * @var Sabre\CalDAV\Backend\PDO
*/
protected $backend;
protected $principalBackend;
@@ -51,9 +53,12 @@ class CalendarTest extends \PHPUnit_Framework_TestCase {
*/
function testUpdateProperties() {
- $result = $this->calendar->updateProperties(array(
+ $propPatch = new PropPatch([
'{DAV:}displayname' => 'NewName',
- ));
+ ]);
+
+ $result = $this->calendar->propPatch($propPatch);
+ $result = $propPatch->commit();
$this->assertEquals(true, $result);
@@ -69,9 +74,6 @@ class CalendarTest extends \PHPUnit_Framework_TestCase {
$question = array(
'{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set',
- '{urn:ietf:params:xml:ns:caldav}supported-calendar-data',
- '{urn:ietf:params:xml:ns:caldav}supported-collation-set',
- '{DAV:}owner',
);
$result = $this->calendar->getProperties($question);
@@ -80,11 +82,6 @@ class CalendarTest extends \PHPUnit_Framework_TestCase {
$this->assertEquals(array('VEVENT','VTODO'), $result['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']->getValue());
- $this->assertTrue($result['{urn:ietf:params:xml:ns:caldav}supported-collation-set'] instanceof Property\SupportedCollationSet);
-
- $this->assertTrue($result['{DAV:}owner'] instanceof DAVACL\Property\Principal);
- $this->assertEquals('principals/user1', $result['{DAV:}owner']->getHref());
-
}
/**
@@ -202,28 +199,28 @@ class CalendarTest extends \PHPUnit_Framework_TestCase {
'protected' => true,
),
array(
- 'privilege' => '{DAV:}write',
- 'principal' => 'principals/user1',
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/user1/calendar-proxy-write',
'protected' => true,
),
array(
'privilege' => '{DAV:}read',
- 'principal' => 'principals/user1/calendar-proxy-write',
+ 'principal' => 'principals/user1/calendar-proxy-read',
'protected' => true,
),
array(
- 'privilege' => '{DAV:}write',
- 'principal' => 'principals/user1/calendar-proxy-write',
+ 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy',
+ 'principal' => '{DAV:}authenticated',
'protected' => true,
),
array(
- 'privilege' => '{DAV:}read',
- 'principal' => 'principals/user1/calendar-proxy-read',
+ 'privilege' => '{DAV:}write',
+ 'principal' => 'principals/user1',
'protected' => true,
),
array(
- 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy',
- 'principal' => '{DAV:}authenticated',
+ 'privilege' => '{DAV:}write',
+ 'principal' => 'principals/user1/calendar-proxy-write',
'protected' => true,
),
);
@@ -251,5 +248,42 @@ class CalendarTest extends \PHPUnit_Framework_TestCase {
}
+ function testGetSyncToken() {
+
+ $this->assertEquals(2, $this->calendar->getSyncToken());
+
+ }
+ function testGetSyncToken2() {
+
+ $calendar = new Calendar(new Backend\Mock([],[]), [
+ '{DAV:}sync-token' => 2
+ ]);
+ $this->assertEquals(2, $this->calendar->getSyncToken());
+
+ }
+
+ function testGetSyncTokenNoSyncSupport() {
+
+ $calendar = new Calendar(new Backend\Mock([],[]), []);
+ $this->assertNull($calendar->getSyncToken());
+ }
+
+ function testGetChanges() {
+
+ $this->assertEquals([
+ 'syncToken' => 2,
+ 'modified' => [],
+ 'deleted' => [],
+ 'added' => ['UUID-2345'],
+ ], $this->calendar->getChanges(1, 1));
+
+ }
+
+ function testGetChangesNoSyncSupport() {
+
+ $calendar = new Calendar(new Backend\Mock([],[]), []);
+ $this->assertNull($calendar->getChanges(1,null));
+
+ }
}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDTest.php
index 2767b5f8d..9a3d47828 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDTest.php
@@ -1,6 +1,7 @@
<?php
namespace Sabre\CalDAV;
+
use Sabre\HTTP;
use Sabre\VObject;
@@ -8,7 +9,7 @@ use Sabre\VObject;
* This unittests is created to find out why recurring events have wrong DTSTART value
*
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -16,18 +17,18 @@ class ExpandEventsDTSTARTandDTENDTest extends \Sabre\DAVServerTest {
protected $setupCalDAV = true;
- protected $caldavCalendars = array(
- array(
- 'id' => 1,
- 'name' => 'Calendar',
+ protected $caldavCalendars = [
+ [
+ 'id' => 1,
+ 'name' => 'Calendar',
'principaluri' => 'principals/user1',
- 'uri' => 'calendar1',
- )
- );
+ 'uri' => 'calendar1',
+ ]
+ ];
- protected $caldavCalendarObjects = array(
- 1 => array(
- 'event.ics' => array(
+ protected $caldavCalendarObjects = [
+ 1 => [
+ 'event.ics' => [
'calendardata' => 'BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
@@ -47,18 +48,18 @@ RECURRENCE-ID;TZID=Europe/Berlin:20120208T181500
END:VEVENT
END:VCALENDAR
',
- ),
- ),
- );
+ ],
+ ],
+ ];
function testExpand() {
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
+ $request = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_METHOD' => 'REPORT',
'HTTP_CONTENT_TYPE' => 'application/xml',
- 'REQUEST_URI' => '/calendars/user1/calendar1',
- 'HTTP_DEPTH' => '1',
- ));
+ 'REQUEST_URI' => '/calendars/user1/calendar1',
+ 'HTTP_DEPTH' => '1',
+ ]);
$request->setBody('<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
@@ -85,26 +86,28 @@ END:VCALENDAR
$start = strpos($response->body, 'BEGIN:VCALENDAR'),
strpos($response->body, 'END:VCALENDAR') - $start + 13
);
- $body = str_replace('&#13;','',$body);
+ $body = str_replace('&#13;', '', $body);
- $vObject = VObject\Reader::read($body);
+ try {
+ $vObject = VObject\Reader::read($body);
+ } catch (VObject\ParseException $e) {
+ $this->fail('Could not parse object. Error:' . $e->getMessage() . ' full object: ' . $response->getBodyAsString());
+ }
// check if DTSTARTs and DTENDs are correct
foreach ($vObject->VEVENT as $vevent) {
/** @var $vevent Sabre\VObject\Component\VEvent */
- foreach ($vevent->children as $child) {
+ foreach ($vevent->children() as $child) {
/** @var $child Sabre\VObject\Property */
-
if ($child->name == 'DTSTART') {
// DTSTART has to be one of three valid values
- $this->assertContains($child->getValue(), array('20120207T171500Z', '20120208T171500Z', '20120209T171500Z'), 'DTSTART is not a valid value: '.$child->getValue());
+ $this->assertContains($child->getValue(), ['20120207T171500Z', '20120208T171500Z', '20120209T171500Z'], 'DTSTART is not a valid value: ' . $child->getValue());
} elseif ($child->name == 'DTEND') {
// DTEND has to be one of three valid values
- $this->assertContains($child->getValue(), array('20120207T181500Z', '20120208T181500Z', '20120209T181500Z'), 'DTEND is not a valid value: '.$child->getValue());
+ $this->assertContains($child->getValue(), ['20120207T181500Z', '20120208T181500Z', '20120209T181500Z'], 'DTEND is not a valid value: ' . $child->getValue());
}
}
}
}
}
-
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDbyDayTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDbyDayTest.php
index 3793cadc7..efc49673f 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDbyDayTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDbyDayTest.php
@@ -1,13 +1,14 @@
<?php
namespace Sabre\CalDAV;
+
use Sabre\HTTP;
use Sabre\VObject;
/**
* This unittests is created to find out why recurring events have wrong DTSTART value
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -15,18 +16,18 @@ class ExpandEventsDTSTARTandDTENDbyDayTest extends \Sabre\DAVServerTest {
protected $setupCalDAV = true;
- protected $caldavCalendars = array(
- array(
- 'id' => 1,
- 'name' => 'Calendar',
+ protected $caldavCalendars = [
+ [
+ 'id' => 1,
+ 'name' => 'Calendar',
'principaluri' => 'principals/user1',
- 'uri' => 'calendar1',
- )
- );
+ 'uri' => 'calendar1',
+ ]
+ ];
- protected $caldavCalendarObjects = array(
- 1 => array(
- 'event.ics' => array(
+ protected $caldavCalendarObjects = [
+ 1 => [
+ 'event.ics' => [
'calendardata' => 'BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
@@ -38,18 +39,18 @@ DTSTART;TZID=Europe/Berlin:20120207T181500
END:VEVENT
END:VCALENDAR
',
- ),
- ),
- );
+ ],
+ ],
+ ];
function testExpandRecurringByDayEvent() {
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
+ $request = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_METHOD' => 'REPORT',
'HTTP_CONTENT_TYPE' => 'application/xml',
- 'REQUEST_URI' => '/calendars/user1/calendar1',
- 'HTTP_DEPTH' => '1',
- ));
+ 'REQUEST_URI' => '/calendars/user1/calendar1',
+ 'HTTP_DEPTH' => '1',
+ ]);
$request->setBody('<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
@@ -76,7 +77,7 @@ END:VCALENDAR
$start = strpos($response->body, 'BEGIN:VCALENDAR'),
strpos($response->body, 'END:VCALENDAR') - $start + 13
);
- $body = str_replace('&#13;','',$body);
+ $body = str_replace('&#13;', '', $body);
$vObject = VObject\Reader::read($body);
@@ -85,19 +86,17 @@ END:VCALENDAR
// check if DTSTARTs and DTENDs are correct
foreach ($vObject->VEVENT as $vevent) {
/** @var $vevent Sabre\VObject\Component\VEvent */
- foreach ($vevent->children as $child) {
+ foreach ($vevent->children() as $child) {
/** @var $child Sabre\VObject\Property */
-
if ($child->name == 'DTSTART') {
// DTSTART has to be one of two valid values
- $this->assertContains($child->getValue(), array('20120214T171500Z', '20120216T171500Z'), 'DTSTART is not a valid value: '.$child->getValue());
+ $this->assertContains($child->getValue(), ['20120214T171500Z', '20120216T171500Z'], 'DTSTART is not a valid value: ' . $child->getValue());
} elseif ($child->name == 'DTEND') {
// DTEND has to be one of two valid values
- $this->assertContains($child->getValue(), array('20120214T181500Z', '20120216T181500Z'), 'DTEND is not a valid value: '.$child->getValue());
+ $this->assertContains($child->getValue(), ['20120214T181500Z', '20120216T181500Z'], 'DTEND is not a valid value: ' . $child->getValue());
}
}
}
}
}
-
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDoubleEventsTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDoubleEventsTest.php
index 09eea5276..b64fb122a 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDoubleEventsTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDoubleEventsTest.php
@@ -11,7 +11,7 @@ use Sabre\VObject;
* Hopefully, by the time I'm done with this, I've both found the problem, and
* fixed it :)
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -56,12 +56,12 @@ END:VCALENDAR
function testExpand() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray([
'REQUEST_METHOD' => 'REPORT',
'HTTP_CONTENT_TYPE' => 'application/xml',
'REQUEST_URI' => '/calendars/user1/calendar1',
'HTTP_DEPTH' => '1',
- ));
+ ]);
$request->setBody('<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/FreeBusyReportTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/FreeBusyReportTest.php
index 93eca9ee9..84f05f3c0 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/FreeBusyReportTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/FreeBusyReportTest.php
@@ -30,6 +30,7 @@ DURATION:PT1H
END:VEVENT
END:VCALENDAR
ics;
+
$obj2 = fopen('php://memory','r+');
fwrite($obj2,<<<ics
BEGIN:VCALENDAR
@@ -42,41 +43,55 @@ ics
);
rewind($obj2);
- $calendarData = array(
- 1 => array(
- 'obj1' => array(
+ $obj3 = <<<ics
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+DTSTART:20111006T120000
+DURATION:PT1H
+END:VEVENT
+END:VCALENDAR
+ics;
+
+ $calendarData = [
+ 1 => [
+ 'obj1' => [
'calendarid' => 1,
'uri' => 'event1.ics',
'calendardata' => $obj1,
- ),
- 'obj2' => array(
+ ],
+ 'obj2' => [
'calendarid' => 1,
'uri' => 'event2.ics',
'calendardata' => $obj2
- )
- ),
- );
+ ],
+ 'obj3' => [
+ 'calendarid' => 1,
+ 'uri' => 'event3.ics',
+ 'calendardata' => $obj3
+ ]
+ ],
+ ];
- $caldavBackend = new Backend\Mock(array(), $calendarData);
+ $caldavBackend = new Backend\Mock([], $calendarData);
- $calendar = new Calendar($caldavBackend, array(
+ $calendar = new Calendar($caldavBackend, [
'id' => 1,
'uri' => 'calendar',
'principaluri' => 'principals/user1',
- ));
+ '{' . Plugin::NS_CALDAV . '}calendar-timezone' => "BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nEND:VTIMEZONE\r\nEND:VCALENDAR",
+ ]);
- $this->server = new DAV\Server(array($calendar));
+ $this->server = new DAV\Server([$calendar]);
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray([
'REQUEST_URI' => '/calendar',
- ));
+ ]);
$this->server->httpRequest = $request;
$this->server->httpResponse = new HTTP\ResponseMock();
$this->plugin = new Plugin();
$this->server->addPlugin($this->plugin);
- $this->server->addPlugin(new DAVACL\Plugin());
}
@@ -89,12 +104,14 @@ ics
</c:free-busy-query>
XML;
- $dom = DAV\XMLUtil::loadDOMDocument($reportXML);
- $this->plugin->report('{urn:ietf:params:xml:ns:caldav}free-busy-query', $dom);
+ $report = $this->server->xml->parse($reportXML, null, $rootElem);
+ $this->plugin->report($rootElem, $report);
- $this->assertEquals('HTTP/1.1 200 OK', $this->server->httpResponse->status);
- $this->assertEquals('text/calendar', $this->server->httpResponse->headers['Content-Type']);
- $this->assertTrue(strpos($this->server->httpResponse->body,'BEGIN:VFREEBUSY')!==false);
+ $this->assertEquals(200, $this->server->httpResponse->status);
+ $this->assertEquals('text/calendar', $this->server->httpResponse->getHeader('Content-Type'));
+ $this->assertTrue(strpos($this->server->httpResponse->body, 'BEGIN:VFREEBUSY')!==false);
+ $this->assertTrue(strpos($this->server->httpResponse->body, '20111005T120000Z/20111005T130000Z')!==false);
+ $this->assertTrue(strpos($this->server->httpResponse->body, '20111006T100000Z/20111006T110000Z')!==false);
}
@@ -109,8 +126,8 @@ XML;
</c:free-busy-query>
XML;
- $dom = DAV\XMLUtil::loadDOMDocument($reportXML);
- $this->plugin->report('{urn:ietf:params:xml:ns:caldav}free-busy-query', $dom);
+ $report = $this->server->xml->parse($reportXML, null, $rootElem);
+ $this->plugin->report($rootElem, $report);
}
@@ -119,7 +136,7 @@ XML;
*/
function testFreeBusyReportWrongNode() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_URI' => '/',
));
$this->server->httpRequest = $request;
@@ -131,8 +148,8 @@ XML;
</c:free-busy-query>
XML;
- $dom = DAV\XMLUtil::loadDOMDocument($reportXML);
- $this->plugin->report('{urn:ietf:params:xml:ns:caldav}free-busy-query', $dom);
+ $report = $this->server->xml->parse($reportXML, null, $rootElem);
+ $this->plugin->report($rootElem, $report);
}
@@ -152,8 +169,8 @@ XML;
</c:free-busy-query>
XML;
- $dom = DAV\XMLUtil::loadDOMDocument($reportXML);
- $this->plugin->report('{urn:ietf:params:xml:ns:caldav}free-busy-query', $dom);
+ $report = $this->server->xml->parse($reportXML, null, $rootElem);
+ $this->plugin->report($rootElem, $report);
}
}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/FreeBusyRequestTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/FreeBusyRequestTest.php
deleted file mode 100644
index 62252e6a1..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/FreeBusyRequestTest.php
+++ /dev/null
@@ -1,282 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV;
-
-use Sabre\DAV;
-use Sabre\DAVACL;
-use Sabre\HTTP;
-
-require_once 'Sabre/HTTP/ResponseMock.php';
-
-class FreeBusyRequestTest extends \PHPUnit_Framework_TestCase {
-
- protected $plugin;
- protected $server;
- protected $aclPlugin;
- protected $request;
- protected $authPlugin;
-
- function setUp() {
-
- $calendars = array(
- array(
- 'principaluri' => 'principals/user2',
- 'id' => 1,
- 'uri' => 'calendar1',
- ),
- );
- $calendarobjects = array(
- 1 => array( '1.ics' => array(
- 'uri' => '1.ics',
- 'calendardata' => 'BEGIN:VCALENDAR
-BEGIN:VEVENT
-DTSTART:20110101T130000
-DURATION:PT1H
-END:VEVENT
-END:VCALENDAR',
- 'calendarid' => 1,
- ))
-
- );
-
- $principalBackend = new DAVACL\PrincipalBackend\Mock();
- $caldavBackend = new Backend\Mock($calendars, $calendarobjects);
-
- $tree = array(
- new DAVACL\PrincipalCollection($principalBackend),
- new CalendarRootNode($principalBackend, $caldavBackend),
- );
-
- $this->request = new HTTP\Request(array(
- 'CONTENT_TYPE' => 'text/calendar',
- ));
- $this->response = new HTTP\ResponseMock();
-
- $this->server = new DAV\Server($tree);
- $this->server->httpRequest = $this->request;
- $this->server->httpResponse = $this->response;
-
- $this->aclPlugin = new DAVACL\Plugin();
- $this->server->addPlugin($this->aclPlugin);
-
- $authBackend = new DAV\Auth\Backend\Mock();
- $authBackend->setCurrentUser('user1');
- $this->authPlugin = new DAV\Auth\Plugin($authBackend,'SabreDAV');
- $this->server->addPlugin($this->authPlugin);
-
- $this->plugin = new Plugin();
- $this->server->addPlugin($this->plugin);
-
- }
-
- function testWrongMethod() {
-
- $this->assertNull(
- $this->plugin->unknownMethod('PUT','calendars/user1/outbox')
- );
-
- }
-
- function testWrongContentType() {
-
- $this->server->httpRequest = new HTTP\Request(array(
- 'CONTENT_TYPE' => 'text/plain',
- ));
-
- $this->assertNull(
- $this->plugin->unknownMethod('POST','calendars/user1/outbox')
- );
-
- }
-
- function testNotFound() {
-
- $this->assertNull(
- $this->plugin->unknownMethod('POST','calendars/user1/blabla')
- );
-
- }
-
- function testNotOutbox() {
-
- $this->assertNull(
- $this->plugin->unknownMethod('POST','calendars/user1/inbox')
- );
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testNoItipMethod() {
-
- $body = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VFREEBUSY
-END:VFREEBUSY
-END:VCALENDAR
-ICS;
-
- $this->request->setBody($body);
- $this->plugin->unknownMethod('POST','calendars/user1/outbox');
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testNoVFreeBusy() {
-
- $body = <<<ICS
-BEGIN:VCALENDAR
-METHOD:REQUEST
-BEGIN:VEVENT
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- $this->request->setBody($body);
- $this->plugin->unknownMethod('POST','calendars/user1/outbox');
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\Forbidden
- */
- function testIncorrectOrganizer() {
-
- $body = <<<ICS
-BEGIN:VCALENDAR
-METHOD:REQUEST
-BEGIN:VFREEBUSY
-ORGANIZER:mailto:john@wayne.org
-END:VFREEBUSY
-END:VCALENDAR
-ICS;
-
- $this->request->setBody($body);
- $this->plugin->unknownMethod('POST','calendars/user1/outbox');
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testNoAttendees() {
-
- $body = <<<ICS
-BEGIN:VCALENDAR
-METHOD:REQUEST
-BEGIN:VFREEBUSY
-ORGANIZER:mailto:user1.sabredav@sabredav.org
-END:VFREEBUSY
-END:VCALENDAR
-ICS;
-
- $this->request->setBody($body);
- $this->plugin->unknownMethod('POST','calendars/user1/outbox');
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testNoDTStart() {
-
- $body = <<<ICS
-BEGIN:VCALENDAR
-METHOD:REQUEST
-BEGIN:VFREEBUSY
-ORGANIZER:mailto:user1.sabredav@sabredav.org
-ATTENDEE:mailto:user2.sabredav@sabredav.org
-END:VFREEBUSY
-END:VCALENDAR
-ICS;
-
- $this->request->setBody($body);
- $this->plugin->unknownMethod('POST','calendars/user1/outbox');
-
- }
-
- function testSucceed() {
-
- $body = <<<ICS
-BEGIN:VCALENDAR
-METHOD:REQUEST
-BEGIN:VFREEBUSY
-ORGANIZER:mailto:user1.sabredav@sabredav.org
-ATTENDEE:mailto:user2.sabredav@sabredav.org
-ATTENDEE:mailto:user3.sabredav@sabredav.org
-DTSTART:20110101T080000Z
-DTEND:20110101T180000Z
-END:VFREEBUSY
-END:VCALENDAR
-ICS;
-
- // Lazily making the current principal an admin.
- $this->aclPlugin->adminPrincipals[] = 'principals/user1';
-
- $this->request->setBody($body);
- $this->assertFalse($this->plugin->unknownMethod('POST','calendars/user1/outbox'));
-
- $this->assertEquals('HTTP/1.1 200 OK' , $this->response->status);
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml',
- ), $this->response->headers);
-
- $strings = array(
- '<d:href>mailto:user2.sabredav@sabredav.org</d:href>',
- '<d:href>mailto:user3.sabredav@sabredav.org</d:href>',
- '<cal:request-status>2.0;Success</cal:request-status>',
- '<cal:request-status>3.7;Could not find principal</cal:request-status>',
- 'FREEBUSY;FBTYPE=BUSY:20110101T130000Z/20110101T140000Z',
- );
-
- foreach($strings as $string) {
- $this->assertTrue(
- strpos($this->response->body, $string)!==false,
- 'The response body did not contain: ' . $string .'Full response: ' . $this->response->body
- );
- }
-
-
- }
-
- function testNoPrivilege() {
-
- $body = <<<ICS
-BEGIN:VCALENDAR
-METHOD:REQUEST
-BEGIN:VFREEBUSY
-ORGANIZER:mailto:user1.sabredav@sabredav.org
-ATTENDEE:mailto:user2.sabredav@sabredav.org
-DTSTART:20110101T080000Z
-DTEND:20110101T180000Z
-END:VFREEBUSY
-END:VCALENDAR
-ICS;
-
- $this->request->setBody($body);
- $this->assertFalse($this->plugin->unknownMethod('POST','calendars/user1/outbox'));
-
- $this->assertEquals('HTTP/1.1 200 OK' , $this->response->status);
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml',
- ), $this->response->headers);
-
- $strings = array(
- '<d:href>mailto:user2.sabredav@sabredav.org</d:href>',
- '<cal:request-status>3.7;No calendar-home-set property found</cal:request-status>',
- );
-
- foreach($strings as $string) {
- $this->assertTrue(
- strpos($this->response->body, $string)!==false,
- 'The response body did not contain: ' . $string .'Full response: ' . $this->response->body
- );
- }
-
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/GetEventsByTimerangeTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/GetEventsByTimerangeTest.php
index 6c9a09905..c3c97e8f6 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/GetEventsByTimerangeTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/GetEventsByTimerangeTest.php
@@ -1,13 +1,13 @@
<?php
namespace Sabre\CalDAV;
+
use Sabre\HTTP;
-use Sabre\VObject;
/**
* This unittest is created to check if queries for time-range include the start timestamp or not
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -15,24 +15,24 @@ class GetEventsByTimerangeTest extends \Sabre\DAVServerTest {
protected $setupCalDAV = true;
- protected $caldavCalendars = array(
- array(
- 'id' => 1,
- 'name' => 'Calendar',
+ protected $caldavCalendars = [
+ [
+ 'id' => 1,
+ 'name' => 'Calendar',
'principaluri' => 'principals/user1',
- 'uri' => 'calendar1',
- )
- );
+ 'uri' => 'calendar1',
+ ]
+ ];
- protected $caldavCalendarObjects = array(
- 1 => array(
- 'event.ics' => array(
+ protected $caldavCalendarObjects = [
+ 1 => [
+ 'event.ics' => [
'calendardata' => 'BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
CREATED:20120313T142342Z
UID:171EBEFC-C951-499D-B234-7BA7D677B45D
-DTEND;TZID=Europe/Berlin:20120227T000000
+DTEND;TZID=Europe/Berlin:20120227T010000
TRANSP:OPAQUE
SUMMARY:Monday 0h
DTSTART;TZID=Europe/Berlin:20120227T000000
@@ -41,31 +41,33 @@ SEQUENCE:4
END:VEVENT
END:VCALENDAR
',
- ),
- ),
- );
+ ],
+ ],
+ ];
function testQueryTimerange() {
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
- 'HTTP_CONTENT_TYPE' => 'application/xml',
- 'REQUEST_URI' => '/calendars/user1/calendar1',
- 'HTTP_DEPTH' => '1',
- ));
+ $request = new HTTP\Request(
+ 'REPORT',
+ '/calendars/user1/calendar1',
+ [
+ 'Content-Type' => 'application/xml',
+ 'Depth' => '1',
+ ]
+ );
$request->setBody('<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop>
<C:calendar-data>
- <C:expand start="20120226T230000Z" end="20120228T225959Z"/>
+ <C:expand start="20120226T220000Z" end="20120228T225959Z"/>
</C:calendar-data>
<D:getetag/>
</D:prop>
<C:filter>
<C:comp-filter name="VCALENDAR">
<C:comp-filter name="VEVENT">
- <C:time-range start="20120226T230000Z" end="20120228T225959Z"/>
+ <C:time-range start="20120226T220000Z" end="20120228T225959Z"/>
</C:comp-filter>
</C:comp-filter>
</C:filter>
@@ -73,24 +75,8 @@ END:VCALENDAR
$response = $this->request($request);
- if (strpos($response->body, 'BEGIN:VCALENDAR') === false) {
- $this->fail('Got no events instead of 1. Output: '.$response->body);
- }
-
- // Everts super awesome xml parser.
- $body = substr(
- $response->body,
- $start = strpos($response->body, 'BEGIN:VCALENDAR'),
- strpos($response->body, 'END:VCALENDAR') - $start + 13
- );
- $body = str_replace('&#13;','',$body);
-
- $vObject = VObject\Reader::read($body);
-
- // We expect 1 event
- $this->assertEquals(1, count($vObject->VEVENT), 'We got 0 events instead of 1. Output: ' . $body);
+ $this->assertTrue(strpos($response->body, 'BEGIN:VCALENDAR')!==false);
}
}
-
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/ICSExportPluginTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/ICSExportPluginTest.php
index be21796dd..c123bd0c1 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/ICSExportPluginTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/ICSExportPluginTest.php
@@ -12,27 +12,36 @@ require_once 'Sabre/HTTP/ResponseMock.php';
class ICSExportPluginTest extends \PHPUnit_Framework_TestCase {
+ function setUp() {
+
+ if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available');
+
+ }
+
function testInit() {
$p = new ICSExportPlugin();
$s = new DAV\Server();
$s->addPlugin($p);
+ $this->assertEquals($p, $s->getPlugin('ics-export'));
+ $this->assertEquals('ics-export', $p->getPluginInfo()['name']);
}
function testBeforeMethod() {
- if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available');
$cbackend = TestUtil::getBackend();
- $props = array(
+ $props = [
'uri'=>'UUID-123467',
'principaluri' => 'admin',
'id' => 1,
- );
- $tree = array(
+ '{DAV:}displayname' => 'Hello!',
+ '{http://apple.com/ns/ical/}calendar-color' => '#AA0000FF',
+ ];
+ $tree = [
new Calendar($cbackend,$props),
- );
+ ];
$p = new ICSExportPlugin();
@@ -40,29 +49,32 @@ class ICSExportPluginTest extends \PHPUnit_Framework_TestCase {
$s->addPlugin($p);
$s->addPlugin(new Plugin());
- $h = new HTTP\Request(array(
- 'QUERY_STRING' => 'export',
- ));
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467?export',
+ 'REQUEST_METHOD' => 'GET',
+ ]);
$s->httpRequest = $h;
$s->httpResponse = new HTTP\ResponseMock();
- $this->assertFalse($p->beforeMethod('GET','UUID-123467?export'));
+ $this->assertFalse($p->httpGet($h, $s->httpResponse));
- $this->assertEquals('HTTP/1.1 200 OK',$s->httpResponse->status);
- $this->assertEquals(array(
- 'Content-Type' => 'text/calendar',
- ), $s->httpResponse->headers);
+ $this->assertEquals(200, $s->httpResponse->status);
+ $this->assertEquals([
+ 'Content-Type' => ['text/calendar'],
+ ], $s->httpResponse->getHeaders());
$obj = VObject\Reader::read($s->httpResponse->body);
- $this->assertEquals(5,count($obj->children()));
+ $this->assertEquals(7,count($obj->children()));
$this->assertEquals(1,count($obj->VERSION));
$this->assertEquals(1,count($obj->CALSCALE));
$this->assertEquals(1,count($obj->PRODID));
$this->assertTrue(strpos((string)$obj->PRODID, DAV\Version::VERSION)!==false);
$this->assertEquals(1,count($obj->VTIMEZONE));
$this->assertEquals(1,count($obj->VEVENT));
+ $this->assertEquals("Hello!", $obj->{"X-WR-CALNAME"});
+ $this->assertEquals("#AA0000FF", $obj->{"X-APPLE-CALENDAR-COLOR"});
}
function testBeforeMethodNoVersion() {
@@ -70,14 +82,14 @@ class ICSExportPluginTest extends \PHPUnit_Framework_TestCase {
if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available');
$cbackend = TestUtil::getBackend();
- $props = array(
+ $props = [
'uri'=>'UUID-123467',
'principaluri' => 'admin',
'id' => 1,
- );
- $tree = array(
+ ];
+ $tree = [
new Calendar($cbackend,$props),
- );
+ ];
$p = new ICSExportPlugin();
@@ -86,21 +98,22 @@ class ICSExportPluginTest extends \PHPUnit_Framework_TestCase {
$s->addPlugin($p);
$s->addPlugin(new Plugin());
- $h = new HTTP\Request(array(
- 'QUERY_STRING' => 'export',
- ));
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467?export',
+ 'REQUEST_METHOD' => 'GET',
+ ]);
$s->httpRequest = $h;
$s->httpResponse = new HTTP\ResponseMock();
DAV\Server::$exposeVersion = false;
- $this->assertFalse($p->beforeMethod('GET','UUID-123467?export'));
+ $this->assertFalse($p->httpGet($h, $s->httpResponse));
DAV\Server::$exposeVersion = true;
- $this->assertEquals('HTTP/1.1 200 OK',$s->httpResponse->status);
- $this->assertEquals(array(
- 'Content-Type' => 'text/calendar',
- ), $s->httpResponse->headers);
+ $this->assertEquals(200, $s->httpResponse->status);
+ $this->assertEquals([
+ 'Content-Type' => ['text/calendar'],
+ ], $s->httpResponse->getHeaders());
$obj = VObject\Reader::read($s->httpResponse->body);
@@ -114,17 +127,6 @@ class ICSExportPluginTest extends \PHPUnit_Framework_TestCase {
}
- function testBeforeMethodNoGET() {
-
- $p = new ICSExportPlugin();
-
- $s = new DAV\Server();
- $s->addPlugin($p);
-
- $this->assertNull($p->beforeMethod('POST','UUID-123467?export'));
-
- }
-
function testBeforeMethodNoExport() {
$p = new ICSExportPlugin();
@@ -132,16 +134,16 @@ class ICSExportPluginTest extends \PHPUnit_Framework_TestCase {
$s = new DAV\Server();
$s->addPlugin($p);
- $this->assertNull($p->beforeMethod('GET','UUID-123467'));
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467',
+ 'REQUEST_METHOD' => 'GET',
+ ]);
+ $this->assertNull($p->httpGet($h, $s->httpResponse));
}
- /**
- * @expectedException Sabre\DAVACL\Exception\NeedPrivileges
- */
function testACLIntegrationBlocked() {
- if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available');
$cbackend = TestUtil::getBackend();
$props = array(
@@ -160,20 +162,24 @@ class ICSExportPluginTest extends \PHPUnit_Framework_TestCase {
$s->addPlugin(new Plugin());
$s->addPlugin(new DAVACL\Plugin());
- $h = new HTTP\Request(array(
- 'QUERY_STRING' => 'export',
- ));
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467?export',
+ ]);
$s->httpRequest = $h;
$s->httpResponse = new HTTP\ResponseMock();
- $p->beforeMethod('GET','UUID-123467?export');
+ $p->httpGet($h, $s->httpResponse);
+
+ // If the ACL system blocked this request, the effect will be that
+ // there's no response, because the calendar information could not be
+ // fetched.
+ $this->assertNull($s->httpResponse->getStatus());
}
function testACLIntegrationNotBlocked() {
- if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available');
$cbackend = TestUtil::getBackend();
$pbackend = new DAVACL\PrincipalBackend\Mock();
@@ -190,6 +196,7 @@ class ICSExportPluginTest extends \PHPUnit_Framework_TestCase {
$p = new ICSExportPlugin();
$s = new DAV\Server($tree);
+ $s->sapi = new HTTP\SapiMock();
$s->addPlugin($p);
$s->addPlugin(new Plugin());
$s->addPlugin(new DAVACL\Plugin());
@@ -198,21 +205,22 @@ class ICSExportPluginTest extends \PHPUnit_Framework_TestCase {
// Forcing login
$s->getPlugin('acl')->adminPrincipals = array('principals/admin');
- $h = new HTTP\Request(array(
- 'QUERY_STRING' => 'export',
- 'REQUEST_URI' => '/UUID-123467',
+
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467?export',
'REQUEST_METHOD' => 'GET',
- ));
+ ]);
$s->httpRequest = $h;
$s->httpResponse = new HTTP\ResponseMock();
$s->exec();
- $this->assertEquals('HTTP/1.1 200 OK',$s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body);
+ $this->assertEquals(200, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body);
$this->assertEquals(array(
- 'Content-Type' => 'text/calendar',
- ), $s->httpResponse->headers);
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['text/calendar'],
+ ), $s->httpResponse->getHeaders());
$obj = VObject\Reader::read($s->httpResponse->body);
@@ -224,4 +232,432 @@ class ICSExportPluginTest extends \PHPUnit_Framework_TestCase {
$this->assertEquals(1,count($obj->VEVENT));
}
+
+ function testBadStartParam() {
+
+ $cbackend = TestUtil::getBackend();
+ $pbackend = new DAVACL\PrincipalBackend\Mock();
+
+ $props = array(
+ 'uri'=>'UUID-123467',
+ 'principaluri' => 'admin',
+ 'id' => 1,
+ );
+ $tree = array(
+ new Calendar($cbackend,$props),
+ new DAVACL\PrincipalCollection($pbackend),
+ );
+
+ $p = new ICSExportPlugin();
+
+ $s = new DAV\Server($tree);
+ $s->sapi = new HTTP\SapiMock();
+ $s->addPlugin($p);
+ $s->addPlugin(new Plugin());
+
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467?export&start=foo',
+ 'REQUEST_METHOD' => 'GET',
+ ]);
+
+ $s->httpRequest = $h;
+ $s->httpResponse = new HTTP\ResponseMock();
+
+ $s->exec();
+
+ $this->assertEquals(400, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body);
+
+ }
+
+ function testBadEndParam() {
+
+ $cbackend = TestUtil::getBackend();
+ $pbackend = new DAVACL\PrincipalBackend\Mock();
+
+ $props = array(
+ 'uri'=>'UUID-123467',
+ 'principaluri' => 'admin',
+ 'id' => 1,
+ );
+ $tree = array(
+ new Calendar($cbackend,$props),
+ new DAVACL\PrincipalCollection($pbackend),
+ );
+
+ $p = new ICSExportPlugin();
+
+ $s = new DAV\Server($tree);
+ $s->sapi = new HTTP\SapiMock();
+ $s->addPlugin($p);
+ $s->addPlugin(new Plugin());
+
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467?export&end=foo',
+ 'REQUEST_METHOD' => 'GET',
+ ]);
+
+ $s->httpRequest = $h;
+ $s->httpResponse = new HTTP\ResponseMock();
+
+ $s->exec();
+
+ $this->assertEquals(400, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body);
+
+ }
+
+ function testFilterStartEnd() {
+
+ $cbackend = TestUtil::getBackend();
+ $pbackend = new DAVACL\PrincipalBackend\Mock();
+
+ $props = array(
+ 'uri'=>'UUID-123467',
+ 'principaluri' => 'admin',
+ 'id' => 1,
+ );
+ $tree = array(
+ new Calendar($cbackend,$props),
+ new DAVACL\PrincipalCollection($pbackend),
+ );
+
+ $p = new ICSExportPlugin();
+
+ $s = new DAV\Server($tree);
+ $s->sapi = new HTTP\SapiMock();
+ $s->addPlugin($p);
+ $s->addPlugin(new Plugin());
+
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467?export&start=1&end=2',
+ 'REQUEST_METHOD' => 'GET',
+ ]);
+
+ $s->httpRequest = $h;
+ $s->httpResponse = new HTTP\ResponseMock();
+
+ $s->exec();
+
+ $this->assertEquals(200, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body);
+ $obj = VObject\Reader::read($s->httpResponse->body);
+
+ $this->assertEquals(0,count($obj->VTIMEZONE));
+ $this->assertEquals(0,count($obj->VEVENT));
+
+ }
+
+ function testExpandNoStart() {
+
+ $cbackend = TestUtil::getBackend();
+ $pbackend = new DAVACL\PrincipalBackend\Mock();
+
+ $props = array(
+ 'uri'=>'UUID-123467',
+ 'principaluri' => 'admin',
+ 'id' => 1,
+ );
+ $tree = array(
+ new Calendar($cbackend,$props),
+ new DAVACL\PrincipalCollection($pbackend),
+ );
+
+ $p = new ICSExportPlugin();
+
+ $s = new DAV\Server($tree);
+ $s->sapi = new HTTP\SapiMock();
+ $s->addPlugin($p);
+ $s->addPlugin(new Plugin());
+
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467?export&expand=1&end=1',
+ 'REQUEST_METHOD' => 'GET',
+ ]);
+
+ $s->httpRequest = $h;
+ $s->httpResponse = new HTTP\ResponseMock();
+
+ $s->exec();
+
+ $this->assertEquals(400, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body);
+
+ }
+
+ function testExpand() {
+
+ $cbackend = TestUtil::getBackend();
+ $pbackend = new DAVACL\PrincipalBackend\Mock();
+
+ $props = array(
+ 'uri'=>'UUID-123467',
+ 'principaluri' => 'admin',
+ 'id' => 1,
+ );
+ $tree = array(
+ new Calendar($cbackend,$props),
+ new DAVACL\PrincipalCollection($pbackend),
+ );
+
+ $p = new ICSExportPlugin();
+
+ $s = new DAV\Server($tree);
+ $s->sapi = new HTTP\SapiMock();
+ $s->addPlugin($p);
+ $s->addPlugin(new Plugin());
+
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467?export&start=1&end=2000000000&expand=1',
+ 'REQUEST_METHOD' => 'GET',
+ ]);
+
+ $s->httpRequest = $h;
+ $s->httpResponse = new HTTP\ResponseMock();
+
+ $s->exec();
+
+ $this->assertEquals(200, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body);
+ $obj = VObject\Reader::read($s->httpResponse->body);
+
+ $this->assertEquals(0,count($obj->VTIMEZONE));
+ $this->assertEquals(1,count($obj->VEVENT));
+
+ }
+
+ function testJCal() {
+
+ $cbackend = TestUtil::getBackend();
+ $pbackend = new DAVACL\PrincipalBackend\Mock();
+
+ $props = array(
+ 'uri'=>'UUID-123467',
+ 'principaluri' => 'admin',
+ 'id' => 1,
+ );
+ $tree = array(
+ new Calendar($cbackend,$props),
+ new DAVACL\PrincipalCollection($pbackend),
+ );
+
+ $p = new ICSExportPlugin();
+
+ $s = new DAV\Server($tree);
+ $s->sapi = new HTTP\SapiMock();
+ $s->addPlugin($p);
+ $s->addPlugin(new Plugin());
+
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467?export',
+ 'REQUEST_METHOD' => 'GET',
+ 'HTTP_ACCEPT' => 'application/calendar+json',
+ ]);
+
+ $s->httpRequest = $h;
+ $s->httpResponse = new HTTP\ResponseMock();
+
+ $s->exec();
+
+ $this->assertEquals(200, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body);
+ $this->assertEquals('application/calendar+json', $s->httpResponse->getHeader('Content-Type'));
+
+ }
+
+ function testJCalInUrl() {
+
+ $cbackend = TestUtil::getBackend();
+ $pbackend = new DAVACL\PrincipalBackend\Mock();
+
+ $props = array(
+ 'uri'=>'UUID-123467',
+ 'principaluri' => 'admin',
+ 'id' => 1,
+ );
+ $tree = array(
+ new Calendar($cbackend,$props),
+ new DAVACL\PrincipalCollection($pbackend),
+ );
+
+ $p = new ICSExportPlugin();
+
+ $s = new DAV\Server($tree);
+ $s->sapi = new HTTP\SapiMock();
+ $s->addPlugin($p);
+ $s->addPlugin(new Plugin());
+
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467?export&accept=jcal',
+ 'REQUEST_METHOD' => 'GET',
+ ]);
+
+ $s->httpRequest = $h;
+ $s->httpResponse = new HTTP\ResponseMock();
+
+ $s->exec();
+
+ $this->assertEquals(200, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body);
+ $this->assertEquals('application/calendar+json', $s->httpResponse->getHeader('Content-Type'));
+
+ }
+
+ function testNegotiateDefault() {
+
+ $cbackend = TestUtil::getBackend();
+ $pbackend = new DAVACL\PrincipalBackend\Mock();
+
+ $props = array(
+ 'uri'=>'UUID-123467',
+ 'principaluri' => 'admin',
+ 'id' => 1,
+ );
+ $tree = array(
+ new Calendar($cbackend,$props),
+ new DAVACL\PrincipalCollection($pbackend),
+ );
+
+ $p = new ICSExportPlugin();
+
+ $s = new DAV\Server($tree);
+ $s->sapi = new HTTP\SapiMock();
+ $s->addPlugin($p);
+ $s->addPlugin(new Plugin());
+
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467?export',
+ 'REQUEST_METHOD' => 'GET',
+ 'HTTP_ACCEPT' => 'text/plain',
+ ]);
+
+ $s->httpRequest = $h;
+ $s->httpResponse = new HTTP\ResponseMock();
+
+ $s->exec();
+
+ $this->assertEquals(200, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body);
+ $this->assertEquals('text/calendar', $s->httpResponse->getHeader('Content-Type'));
+
+ }
+
+ function testFilterComponentVEVENT() {
+
+ $cbackend = TestUtil::getBackend();
+ $pbackend = new DAVACL\PrincipalBackend\Mock();
+
+ $props = array(
+ 'uri'=>'UUID-123467',
+ 'principaluri' => 'admin',
+ 'id' => 1,
+ );
+ // add a todo to the calendar (see /tests/Sabre/TestUtil)
+ $cbackend->createCalendarObject(1, 'UUID-3456', TestUtil::getTestTODO());
+
+ $tree = array(
+ new Calendar($cbackend,$props),
+ new DAVACL\PrincipalCollection($pbackend),
+ );
+
+ $p = new ICSExportPlugin();
+
+ $s = new DAV\Server($tree);
+ $s->sapi = new HTTP\SapiMock();
+ $s->addPlugin($p);
+ $s->addPlugin(new Plugin());
+
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467?export&componentType=VEVENT',
+ 'REQUEST_METHOD' => 'GET',
+ ]);
+
+ $s->httpRequest = $h;
+ $s->httpResponse = new HTTP\ResponseMock();
+
+ $s->exec();
+
+ $this->assertEquals(200, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body);
+ $obj = VObject\Reader::read($s->httpResponse->body);
+
+ $this->assertEquals(1,count($obj->VTIMEZONE));
+ $this->assertEquals(1,count($obj->VEVENT));
+ $this->assertEquals(0,count($obj->VTODO));
+
+ }
+
+ function testFilterComponentVTODO() {
+
+ $cbackend = TestUtil::getBackend();
+ $pbackend = new DAVACL\PrincipalBackend\Mock();
+
+ $props = [
+ 'uri'=>'UUID-123467',
+ 'principaluri' => 'admin',
+ 'id' => 1,
+ ];
+ // add a todo to the calendar (see /tests/Sabre/TestUtil)
+ $cbackend->createCalendarObject(1, 'UUID-3456', TestUtil::getTestTODO());
+
+ $tree = [
+ new Calendar($cbackend,$props),
+ new DAVACL\PrincipalCollection($pbackend),
+ ];
+
+ $p = new ICSExportPlugin();
+
+ $s = new DAV\Server($tree);
+ $s->sapi = new HTTP\SapiMock();
+ $s->addPlugin($p);
+ $s->addPlugin(new Plugin());
+
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467?export&componentType=VTODO',
+ 'REQUEST_METHOD' => 'GET',
+ ]);
+
+ $s->httpRequest = $h;
+ $s->httpResponse = new HTTP\ResponseMock();
+
+ $s->exec();
+
+ $this->assertEquals(200, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body);
+ $obj = VObject\Reader::read($s->httpResponse->body);
+
+ $this->assertEquals(0,count($obj->VTIMEZONE));
+ $this->assertEquals(0,count($obj->VEVENT));
+ $this->assertEquals(1,count($obj->VTODO));
+
+ }
+
+ function testFilterComponentBadComponent() {
+
+ $cbackend = TestUtil::getBackend();
+ $pbackend = new DAVACL\PrincipalBackend\Mock();
+
+ $props = [
+ 'uri'=>'UUID-123467',
+ 'principaluri' => 'admin',
+ 'id' => 1,
+ ];
+ // add a todo to the calendar (see /tests/Sabre/TestUtil)
+ $cbackend->createCalendarObject(1, 'UUID-3456', TestUtil::getTestTODO());
+
+ $tree = [
+ new Calendar($cbackend,$props),
+ new DAVACL\PrincipalCollection($pbackend),
+ ];
+
+ $p = new ICSExportPlugin();
+
+ $s = new DAV\Server($tree);
+ $s->sapi = new HTTP\SapiMock();
+ $s->addPlugin($p);
+ $s->addPlugin(new Plugin());
+
+ $h = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/UUID-123467?export&componentType=VVOODOO',
+ 'REQUEST_METHOD' => 'GET',
+ ]);
+
+ $s->httpRequest = $h;
+ $s->httpResponse = new HTTP\ResponseMock();
+
+ $s->exec();
+
+ $this->assertEquals(400, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body);
+
+ }
}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Issue203Test.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Issue203Test.php
index 21ee2f550..369e9a70c 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Issue203Test.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/Issue203Test.php
@@ -1,14 +1,14 @@
<?php
namespace Sabre\CalDAV;
+
use Sabre\HTTP;
use Sabre\VObject;
/**
* This unittest is created to find out why an overwritten DAILY event has wrong DTSTART, DTEND, SUMMARY and RECURRENCEID
*
- *
- * @copyright Copyright (C) 2007-2014 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -16,18 +16,18 @@ class Issue203Test extends \Sabre\DAVServerTest {
protected $setupCalDAV = true;
- protected $caldavCalendars = array(
- array(
- 'id' => 1,
- 'name' => 'Calendar',
+ protected $caldavCalendars = [
+ [
+ 'id' => 1,
+ 'name' => 'Calendar',
'principaluri' => 'principals/user1',
- 'uri' => 'calendar1',
- )
- );
+ 'uri' => 'calendar1',
+ ]
+ ];
- protected $caldavCalendarObjects = array(
- 1 => array(
- 'event.ics' => array(
+ protected $caldavCalendarObjects = [
+ 1 => [
+ 'event.ics' => [
'calendardata' => 'BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
@@ -52,18 +52,18 @@ TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
',
- ),
- ),
- );
+ ],
+ ],
+ ];
function testIssue203() {
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
+ $request = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_METHOD' => 'REPORT',
'HTTP_CONTENT_TYPE' => 'application/xml',
- 'REQUEST_URI' => '/calendars/user1/calendar1',
- 'HTTP_DEPTH' => '1',
- ));
+ 'REQUEST_URI' => '/calendars/user1/calendar1',
+ 'HTTP_DEPTH' => '1',
+ ]);
$request->setBody('<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
@@ -90,26 +90,26 @@ END:VCALENDAR
$start = strpos($response->body, 'BEGIN:VCALENDAR'),
strpos($response->body, 'END:VCALENDAR') - $start + 13
);
- $body = str_replace('&#13;','',$body);
+ $body = str_replace('&#13;', '', $body);
$vObject = VObject\Reader::read($body);
$this->assertEquals(2, count($vObject->VEVENT));
- $expectedEvents = array(
- array(
+ $expectedEvents = [
+ [
'DTSTART' => '20120326T135200Z',
'DTEND' => '20120326T145200Z',
'SUMMARY' => 'original summary',
- ),
- array(
+ ],
+ [
'DTSTART' => '20120328T135200Z',
'DTEND' => '20120328T145200Z',
'SUMMARY' => 'overwritten summary',
'RECURRENCE-ID' => '20120327T135200Z',
- )
- );
+ ]
+ ];
// try to match agains $expectedEvents array
foreach ($expectedEvents as $expectedEvent) {
@@ -118,10 +118,8 @@ END:VCALENDAR
foreach ($vObject->VEVENT as $vevent) {
/** @var $vevent Sabre\VObject\Component\VEvent */
-
- foreach ($vevent->children as $child) {
+ foreach ($vevent->children() as $child) {
/** @var $child Sabre\VObject\Property */
-
if (isset($expectedEvent[$child->name])) {
if ($expectedEvent[$child->name] != $child->getValue()) {
continue 2;
@@ -133,7 +131,7 @@ END:VCALENDAR
break;
}
- $this->assertTrue($matching, 'Did not find the following event in the response: '.var_export($expectedEvent, true));
+ $this->assertTrue($matching, 'Did not find the following event in the response: ' . var_export($expectedEvent, true));
}
}
}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Issue205Test.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Issue205Test.php
index cd6820b57..4a53fcbe2 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Issue205Test.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/Issue205Test.php
@@ -7,7 +7,7 @@ use Sabre\VObject;
/**
* This unittest is created to check if a VALARM TRIGGER of PT0S is supported
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -50,7 +50,7 @@ END:VCALENDAR
function testIssue205() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'REPORT',
'HTTP_CONTENT_TYPE' => 'application/xml',
'REQUEST_URI' => '/calendars/user1/calendar1',
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Issue211Test.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Issue211Test.php
index cc700e50d..f291e5e57 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Issue211Test.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/Issue211Test.php
@@ -7,7 +7,7 @@ use Sabre\VObject;
/**
* This unittest is created to check for an endless loop in Sabre\CalDAV\CalendarQueryValidator
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -55,7 +55,7 @@ END:VCALENDAR
function testIssue211() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'REPORT',
'HTTP_CONTENT_TYPE' => 'application/xml',
'REQUEST_URI' => '/calendars/user1/calendar1',
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Issue220Test.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Issue220Test.php
index ce66b6a5f..7b5dbfe63 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Issue220Test.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/Issue220Test.php
@@ -7,7 +7,7 @@ use Sabre\HTTP;
/**
* This unittest is created to check for an endless loop in CalendarQueryValidator
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -65,7 +65,7 @@ END:VCALENDAR
function testIssue220() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'REPORT',
'HTTP_CONTENT_TYPE' => 'application/xml',
'REQUEST_URI' => '/calendars/user1/calendar1',
@@ -94,6 +94,6 @@ END:VCALENDAR
$this->assertFalse(strpos($response->body, '<s:exception>PHPUnit_Framework_Error_Warning</s:exception>'), 'Error Warning occurred: ' . $response->body);
$this->assertFalse(strpos($response->body, 'Invalid argument supplied for foreach()'), 'Invalid argument supplied for foreach(): ' . $response->body);
- $this->assertEquals('HTTP/1.1 207 Multi-Status', $response->status);
+ $this->assertEquals(207, $response->status);
}
}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Issue228Test.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Issue228Test.php
index 23371a054..ccc6b303a 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Issue228Test.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/Issue228Test.php
@@ -6,7 +6,7 @@ use Sabre\HTTP;
/**
* This unittest is created to check if the time-range filter is working correctly with all-day-events
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -44,7 +44,7 @@ END:VCALENDAR
function testIssue228() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'REPORT',
'HTTP_CONTENT_TYPE' => 'application/xml',
'REQUEST_URI' => '/calendars/user1/calendar1',
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/CollectionTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/CollectionTest.php
index eaed4f503..68035184f 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/CollectionTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/CollectionTest.php
@@ -14,9 +14,9 @@ class CollectionTest extends \PHPUnit_Framework_TestCase {
$this->principalUri = 'principals/user1';
- $this->notification = new Notification\SystemStatus(1,'"1"');
+ $this->notification = new CalDAV\Xml\Notification\SystemStatus(1,'"1"');
- $this->caldavBackend = new CalDAV\Backend\Mock(array(),array(), array(
+ $this->caldavBackend = new CalDAV\Backend\MockSharing(array(),array(), array(
'principals/user1' => array(
$this->notification
)
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/NodeTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/NodeTest.php
index 28e43ce08..d546116fc 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/NodeTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/NodeTest.php
@@ -13,13 +13,13 @@ class NodeTest extends \PHPUnit_Framework_TestCase {
$principalUri = 'principals/user1';
- $this->systemStatus = new Notification\SystemStatus(1,'"1"');
+ $this->systemStatus = new CalDAV\Xml\Notification\SystemStatus(1,'"1"');
- $this->caldavBackend = new CalDAV\Backend\Mock(array(),array(), array(
- 'principals/user1' => array(
+ $this->caldavBackend = new CalDAV\Backend\MockSharing([], [], [
+ 'principals/user1' => [
$this->systemStatus
- )
- ));
+ ]
+ ]);
$node = new Node($this->caldavBackend, 'principals/user1', $this->systemStatus);
return $node;
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/Notification/InviteReplyTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/Notification/InviteReplyTest.php
deleted file mode 100644
index c53f68cee..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/Notification/InviteReplyTest.php
+++ /dev/null
@@ -1,134 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Notifications\Notification;
-
-use Sabre\CalDAV;
-use Sabre\DAV;
-
-class InviteReplyTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @dataProvider dataProvider
- */
- function testSerializers($notification, $expected) {
-
- $notification = new InviteReply($notification);
-
- $this->assertEquals('foo', $notification->getId());
- $this->assertEquals('"1"', $notification->getETag());
-
- $simpleExpected = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<cs:root xmlns:cs="http://calendarserver.org/ns/"><cs:invite-reply/></cs:root>' . "\n";
-
- $dom = new \DOMDocument('1.0','UTF-8');
- $elem = $dom->createElement('cs:root');
- $elem->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER);
- $dom->appendChild($elem);
- $notification->serialize(new DAV\Server(), $elem);
- $this->assertEquals($simpleExpected, $dom->saveXML());
-
- $dom = new \DOMDocument('1.0','UTF-8');
- $dom->formatOutput = true;
- $elem = $dom->createElement('cs:root');
- $elem->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER);
- $elem->setAttribute('xmlns:d','DAV:');
- $dom->appendChild($elem);
- $notification->serializeBody(new DAV\Server(), $elem);
- $this->assertEquals($expected, $dom->saveXML());
-
-
- }
-
- function dataProvider() {
-
- $dtStamp = new \DateTime('2012-01-01 00:00:00 GMT');
- return array(
- array(
- array(
- 'id' => 'foo',
- 'dtStamp' => $dtStamp,
- 'etag' => '"1"',
- 'inReplyTo' => 'bar',
- 'href' => 'mailto:foo@example.org',
- 'type' => CalDAV\SharingPlugin::STATUS_ACCEPTED,
- 'hostUrl' => 'calendar'
- ),
-<<<FOO
-<?xml version="1.0" encoding="UTF-8"?>
-<cs:root xmlns:cs="http://calendarserver.org/ns/" xmlns:d="DAV:">
- <cs:dtstamp>20120101T000000Z</cs:dtstamp>
- <cs:invite-reply>
- <cs:uid>foo</cs:uid>
- <cs:in-reply-to>bar</cs:in-reply-to>
- <d:href>mailto:foo@example.org</d:href>
- <cs:invite-accepted/>
- <cs:hosturl>
- <d:href>/calendar</d:href>
- </cs:hosturl>
- </cs:invite-reply>
-</cs:root>
-
-FOO
- ),
- array(
- array(
- 'id' => 'foo',
- 'dtStamp' => $dtStamp,
- 'etag' => '"1"',
- 'inReplyTo' => 'bar',
- 'href' => 'mailto:foo@example.org',
- 'type' => CalDAV\SharingPlugin::STATUS_DECLINED,
- 'hostUrl' => 'calendar',
- 'summary' => 'Summary!'
- ),
-<<<FOO
-<?xml version="1.0" encoding="UTF-8"?>
-<cs:root xmlns:cs="http://calendarserver.org/ns/" xmlns:d="DAV:">
- <cs:dtstamp>20120101T000000Z</cs:dtstamp>
- <cs:invite-reply>
- <cs:uid>foo</cs:uid>
- <cs:in-reply-to>bar</cs:in-reply-to>
- <d:href>mailto:foo@example.org</d:href>
- <cs:invite-declined/>
- <cs:hosturl>
- <d:href>/calendar</d:href>
- </cs:hosturl>
- <cs:summary>Summary!</cs:summary>
- </cs:invite-reply>
-</cs:root>
-
-FOO
- ),
-
- );
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testMissingArg() {
-
- new InviteReply(array());
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testUnknownArg() {
-
- new InviteReply(array(
- 'foo-i-will-break' => true,
-
- 'id' => 1,
- 'etag' => '"bla"',
- 'href' => 'abc',
- 'dtStamp' => 'def',
- 'inReplyTo' => 'qrs',
- 'type' => 'ghi',
- 'hostUrl' => 'jkl',
- ));
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/Notification/InviteTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/Notification/InviteTest.php
deleted file mode 100644
index d2c114f4c..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/Notification/InviteTest.php
+++ /dev/null
@@ -1,230 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Notifications\Notification;
-
-use Sabre\CalDAV;
-use Sabre\DAV;
-
-class InviteTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @dataProvider dataProvider
- */
- function testSerializers($notification, $expected) {
-
- $notification = new Invite($notification);
-
- $this->assertEquals('foo', $notification->getId());
- $this->assertEquals('"1"', $notification->getETag());
-
- $simpleExpected = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<cs:root xmlns:cs="http://calendarserver.org/ns/"><cs:invite-notification/></cs:root>' . "\n";
-
- $dom = new \DOMDocument('1.0','UTF-8');
- $elem = $dom->createElement('cs:root');
- $elem->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER);
- $dom->appendChild($elem);
- $notification->serialize(new DAV\Server(), $elem);
- $this->assertEquals($simpleExpected, $dom->saveXML());
-
- $dom = new \DOMDocument('1.0','UTF-8');
- $dom->formatOutput = true;
- $elem = $dom->createElement('cs:root');
- $elem->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER);
- $elem->setAttribute('xmlns:d','DAV:');
- $elem->setAttribute('xmlns:cal',CalDAV\Plugin::NS_CALDAV);
- $dom->appendChild($elem);
- $notification->serializeBody(new DAV\Server(), $elem);
- $this->assertEquals($expected, $dom->saveXML());
-
-
- }
-
- function dataProvider() {
-
- $dtStamp = new \DateTime('2012-01-01 00:00:00', new \DateTimeZone('GMT'));
- return array(
- array(
- array(
- 'id' => 'foo',
- 'dtStamp' => $dtStamp,
- 'etag' => '"1"',
- 'href' => 'mailto:foo@example.org',
- 'type' => CalDAV\SharingPlugin::STATUS_ACCEPTED,
- 'readOnly' => true,
- 'hostUrl' => 'calendar',
- 'organizer' => 'principal/user1',
- 'commonName' => 'John Doe',
- 'summary' => 'Awesome stuff!'
- ),
-<<<FOO
-<?xml version="1.0" encoding="UTF-8"?>
-<cs:root xmlns:cs="http://calendarserver.org/ns/" xmlns:d="DAV:" xmlns:cal="urn:ietf:params:xml:ns:caldav">
- <cs:dtstamp>20120101T000000Z</cs:dtstamp>
- <cs:invite-notification>
- <cs:uid>foo</cs:uid>
- <d:href>mailto:foo@example.org</d:href>
- <cs:invite-accepted/>
- <cs:hosturl>
- <d:href>/calendar</d:href>
- </cs:hosturl>
- <cs:access>
- <cs:read/>
- </cs:access>
- <cs:organizer-cn>John Doe</cs:organizer-cn>
- <cs:organizer>
- <d:href>/principal/user1</d:href>
- <cs:common-name>John Doe</cs:common-name>
- </cs:organizer>
- <cs:summary>Awesome stuff!</cs:summary>
- </cs:invite-notification>
-</cs:root>
-
-FOO
- ),
- array(
- array(
- 'id' => 'foo',
- 'dtStamp' => $dtStamp,
- 'etag' => '"1"',
- 'href' => 'mailto:foo@example.org',
- 'type' => CalDAV\SharingPlugin::STATUS_DECLINED,
- 'readOnly' => true,
- 'hostUrl' => 'calendar',
- 'organizer' => 'principal/user1',
- 'commonName' => 'John Doe',
- ),
-<<<FOO
-<?xml version="1.0" encoding="UTF-8"?>
-<cs:root xmlns:cs="http://calendarserver.org/ns/" xmlns:d="DAV:" xmlns:cal="urn:ietf:params:xml:ns:caldav">
- <cs:dtstamp>20120101T000000Z</cs:dtstamp>
- <cs:invite-notification>
- <cs:uid>foo</cs:uid>
- <d:href>mailto:foo@example.org</d:href>
- <cs:invite-declined/>
- <cs:hosturl>
- <d:href>/calendar</d:href>
- </cs:hosturl>
- <cs:access>
- <cs:read/>
- </cs:access>
- <cs:organizer-cn>John Doe</cs:organizer-cn>
- <cs:organizer>
- <d:href>/principal/user1</d:href>
- <cs:common-name>John Doe</cs:common-name>
- </cs:organizer>
- </cs:invite-notification>
-</cs:root>
-
-FOO
- ),
- array(
- array(
- 'id' => 'foo',
- 'dtStamp' => $dtStamp,
- 'etag' => '"1"',
- 'href' => 'mailto:foo@example.org',
- 'type' => CalDAV\SharingPlugin::STATUS_NORESPONSE,
- 'readOnly' => true,
- 'hostUrl' => 'calendar',
- 'organizer' => 'principal/user1',
- 'firstName' => 'Foo',
- 'lastName' => 'Bar',
- ),
-<<<FOO
-<?xml version="1.0" encoding="UTF-8"?>
-<cs:root xmlns:cs="http://calendarserver.org/ns/" xmlns:d="DAV:" xmlns:cal="urn:ietf:params:xml:ns:caldav">
- <cs:dtstamp>20120101T000000Z</cs:dtstamp>
- <cs:invite-notification>
- <cs:uid>foo</cs:uid>
- <d:href>mailto:foo@example.org</d:href>
- <cs:invite-noresponse/>
- <cs:hosturl>
- <d:href>/calendar</d:href>
- </cs:hosturl>
- <cs:access>
- <cs:read/>
- </cs:access>
- <cs:organizer-first>Foo</cs:organizer-first>
- <cs:organizer-last>Bar</cs:organizer-last>
- <cs:organizer>
- <d:href>/principal/user1</d:href>
- <cs:first-name>Foo</cs:first-name>
- <cs:last-name>Bar</cs:last-name>
- </cs:organizer>
- </cs:invite-notification>
-</cs:root>
-
-FOO
- ),
- array(
- array(
- 'id' => 'foo',
- 'dtStamp' => $dtStamp,
- 'etag' => '"1"',
- 'href' => 'mailto:foo@example.org',
- 'type' => CalDAV\SharingPlugin::STATUS_DELETED,
- 'readOnly' => false,
- 'hostUrl' => 'calendar',
- 'organizer' => 'mailto:user1@fruux.com',
- 'supportedComponents' => new CalDAV\Property\SupportedCalendarComponentSet(array('VEVENT','VTODO')),
- ),
-<<<FOO
-<?xml version="1.0" encoding="UTF-8"?>
-<cs:root xmlns:cs="http://calendarserver.org/ns/" xmlns:d="DAV:" xmlns:cal="urn:ietf:params:xml:ns:caldav">
- <cs:dtstamp>20120101T000000Z</cs:dtstamp>
- <cs:invite-notification>
- <cs:uid>foo</cs:uid>
- <d:href>mailto:foo@example.org</d:href>
- <cs:invite-deleted/>
- <cs:hosturl>
- <d:href>/calendar</d:href>
- </cs:hosturl>
- <cs:access>
- <cs:read-write/>
- </cs:access>
- <cs:organizer>
- <d:href>mailto:user1@fruux.com</d:href>
- </cs:organizer>
- <cal:supported-calendar-component-set>
- <cal:comp name="VEVENT"/>
- <cal:comp name="VTODO"/>
- </cal:supported-calendar-component-set>
- </cs:invite-notification>
-</cs:root>
-
-FOO
- ),
-
- );
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testMissingArg() {
-
- new Invite(array());
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testUnknownArg() {
-
- new Invite(array(
- 'foo-i-will-break' => true,
-
- 'id' => 1,
- 'etag' => '"bla"',
- 'href' => 'abc',
- 'dtStamp' => 'def',
- 'type' => 'ghi',
- 'readOnly' => true,
- 'hostUrl' => 'jkl',
- 'organizer' => 'mno',
- ));
-
- }
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/Notification/SystemStatusTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/Notification/SystemStatusTest.php
deleted file mode 100644
index 8dc494932..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Notifications/Notification/SystemStatusTest.php
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Notifications\Notification;
-
-use Sabre\CalDAV;
-use Sabre\DAV;
-
-class SystemStatusTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @dataProvider dataProvider
- */
- function testSerializers($notification, $expected1, $expected2) {
-
- $this->assertEquals('foo', $notification->getId());
- $this->assertEquals('"1"', $notification->getETag());
-
-
- $dom = new \DOMDocument('1.0','UTF-8');
- $elem = $dom->createElement('cs:root');
- $elem->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER);
- $dom->appendChild($elem);
- $notification->serialize(new DAV\Server(), $elem);
- $this->assertEquals($expected1, $dom->saveXML());
-
- $dom = new \DOMDocument('1.0','UTF-8');
- $elem = $dom->createElement('cs:root');
- $elem->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER);
- $dom->appendChild($elem);
- $notification->serializeBody(new DAV\Server(), $elem);
- $this->assertEquals($expected2, $dom->saveXML());
-
-
- }
-
- function dataProvider() {
-
- return array(
-
- array(
- new SystemStatus('foo', '"1"'),
- '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<cs:root xmlns:cs="http://calendarserver.org/ns/"><cs:systemstatus type="high"/></cs:root>' . "\n",
- '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<cs:root xmlns:cs="http://calendarserver.org/ns/"><cs:systemstatus type="high"/></cs:root>' . "\n",
- ),
-
- array(
- new SystemStatus('foo', '"1"', SystemStatus::TYPE_MEDIUM,'bar'),
- '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<cs:root xmlns:cs="http://calendarserver.org/ns/"><cs:systemstatus type="medium"/></cs:root>' . "\n",
- '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<cs:root xmlns:cs="http://calendarserver.org/ns/"><cs:systemstatus type="medium"><cs:description>bar</cs:description></cs:systemstatus></cs:root>' . "\n",
- ),
-
- array(
- new SystemStatus('foo', '"1"', SystemStatus::TYPE_LOW,null,'http://example.org/'),
- '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<cs:root xmlns:cs="http://calendarserver.org/ns/"><cs:systemstatus type="low"/></cs:root>' . "\n",
- '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<cs:root xmlns:cs="http://calendarserver.org/ns/"><cs:systemstatus type="low"><d:href>http://example.org/</d:href></cs:systemstatus></cs:root>' . "\n",
- )
- );
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/OutboxPostTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/OutboxPostTest.php
deleted file mode 100644
index 5a5a4e75c..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/OutboxPostTest.php
+++ /dev/null
@@ -1,545 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV;
-use Sabre\HTTP;
-use Sabre\VObject;
-use Sabre\DAV;
-
-require_once 'Sabre/DAVServerTest.php';
-require_once 'Sabre/CalDAV/Schedule/IMip/Mock.php';
-
-class OutboxPostTest extends \Sabre\DAVServerTest {
-
- protected $setupCalDAV = true;
-
- function testPostPassThruNotFound() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/notfound',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
-
- $this->assertHTTPStatus(501, $req);
-
- }
-
- function testPostPassThruNotTextCalendar() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/admin/outbox',
- ));
-
- $this->assertHTTPStatus(501, $req);
-
- }
-
- function testPostPassThruNoOutBox() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
-
- $this->assertHTTPStatus(501, $req);
-
- }
-
- function testNoOriginator() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/admin/outbox',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
- $body = array(
- 'BEGIN:VCALENDAR',
- 'METHOD:REQUEST',
- 'BEGIN:VEVENT',
- 'END:VEVENT',
- 'END:VCALENDAR',
- );
- $req->setBody(implode("\r\n",$body));
-
- $this->assertHTTPStatus(400, $req);
-
- }
-
- function testNoRecipient() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/admin/outbox',
- 'HTTP_ORIGINATOR' => 'mailto:orig@example.org',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
- $body = array(
- 'BEGIN:VCALENDAR',
- 'METHOD:REQUEST',
- 'BEGIN:VEVENT',
- 'END:VEVENT',
- 'END:VCALENDAR',
- );
- $req->setBody(implode("\r\n",$body));
-
- $this->assertHTTPStatus(400, $req);
-
- }
-
- function testBadOriginator() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/admin/outbox',
- 'HTTP_ORIGINATOR' => 'nomailto:orig@example.org',
- 'HTTP_RECIPIENT' => 'mailto:user1@example.org',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
- $body = array(
- 'BEGIN:VCALENDAR',
- 'METHOD:REQUEST',
- 'BEGIN:VEVENT',
- 'END:VEVENT',
- 'END:VCALENDAR',
- );
- $req->setBody(implode("\r\n",$body));
-
- $this->assertHTTPStatus(403, $req);
-
- }
-
- function testBadRecipient() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/admin/outbox',
- 'HTTP_ORIGINATOR' => 'mailto:orig@example.org',
- 'HTTP_RECIPIENT' => 'http://user1@example.org, mailto:user2@example.org',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
- $body = array(
- 'BEGIN:VCALENDAR',
- 'METHOD:REQUEST',
- 'BEGIN:VEVENT',
- 'END:VEVENT',
- 'END:VCALENDAR',
- );
- $req->setBody(implode("\r\n",$body));
-
- $this->assertHTTPStatus(400, $req);
-
- }
-
- function testIncorrectOriginator() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/admin/outbox',
- 'HTTP_ORIGINATOR' => 'mailto:orig@example.org',
- 'HTTP_RECIPIENT' => 'mailto:user1@example.org, mailto:user2@example.org',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
- $body = array(
- 'BEGIN:VCALENDAR',
- 'METHOD:REQUEST',
- 'BEGIN:VEVENT',
- 'END:VEVENT',
- 'END:VCALENDAR',
- );
- $req->setBody(implode("\r\n",$body));
-
- $this->assertHTTPStatus(403, $req);
-
- }
-
- function testInvalidIcalBody() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/user1/outbox',
- 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org',
- 'HTTP_RECIPIENT' => 'mailto:user2@example.org',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
- $req->setBody('foo');
-
- $this->assertHTTPStatus(400, $req);
-
- }
-
- function testNoVEVENT() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/user1/outbox',
- 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org',
- 'HTTP_RECIPIENT' => 'mailto:user2@example.org',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
-
- $body = array(
- 'BEGIN:VCALENDAR',
- 'BEGIN:VTIMEZONE',
- 'END:VTIMEZONE',
- 'END:VCALENDAR',
- );
-
- $req->setBody(implode("\r\n",$body));
-
- $this->assertHTTPStatus(400, $req);
-
- }
-
- function testNoMETHOD() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/user1/outbox',
- 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org',
- 'HTTP_RECIPIENT' => 'mailto:user2@example.org',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
-
- $body = array(
- 'BEGIN:VCALENDAR',
- 'BEGIN:VEVENT',
- 'END:VEVENT',
- 'END:VCALENDAR',
- );
-
- $req->setBody(implode("\r\n",$body));
-
- $this->assertHTTPStatus(400, $req);
-
- }
-
- function testUnsupportedMethod() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/user1/outbox',
- 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org',
- 'HTTP_RECIPIENT' => 'mailto:user2@example.org',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
-
- $body = array(
- 'BEGIN:VCALENDAR',
- 'METHOD:PUBLISH',
- 'BEGIN:VEVENT',
- 'END:VEVENT',
- 'END:VCALENDAR',
- );
-
- $req->setBody(implode("\r\n",$body));
-
- $this->assertHTTPStatus(501, $req);
-
- }
-
- function testNoIMIPHandler() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/user1/outbox',
- 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org',
- 'HTTP_RECIPIENT' => 'mailto:user2@example.org',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
-
- $body = array(
- 'BEGIN:VCALENDAR',
- 'METHOD:REQUEST',
- 'BEGIN:VEVENT',
- 'END:VEVENT',
- 'END:VCALENDAR',
- );
-
- $req->setBody(implode("\r\n",$body));
-
-
- $response = $this->request($req);
- $this->assertEquals('HTTP/1.1 200 OK', $response->status);
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml',
- ), $response->headers);
-
- // Lazily checking the body for a few expected values.
- $this->assertTrue(strpos($response->body, '5.2;')!==false);
- $this->assertTrue(strpos($response->body,'user2@example.org')!==false);
-
-
- }
-
- function testSuccessRequest() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/user1/outbox',
- 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org',
- 'HTTP_RECIPIENT' => 'mailto:user2@example.org',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
-
- $body = array(
- 'BEGIN:VCALENDAR',
- 'METHOD:REQUEST',
- 'BEGIN:VEVENT',
- 'SUMMARY:An invitation',
- 'END:VEVENT',
- 'END:VCALENDAR',
- );
-
- $req->setBody(implode("\r\n",$body));
-
- $handler = new Schedule\IMip\Mock('server@example.org');
-
- $this->caldavPlugin->setIMIPhandler($handler);
-
- $response = $this->request($req);
- $this->assertEquals('HTTP/1.1 200 OK', $response->status);
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml',
- ), $response->headers);
-
- // Lazily checking the body for a few expected values.
- $this->assertTrue(strpos($response->body, '2.0;')!==false);
- $this->assertTrue(strpos($response->body,'user2@example.org')!==false);
-
- $this->assertEquals(array(
- array(
- 'to' => 'user2@example.org',
- 'subject' => 'Invitation for: An invitation',
- 'body' => implode("\r\n", $body) . "\r\n",
- 'headers' => array(
- 'Reply-To: user1.sabredav@sabredav.org',
- 'From: server@example.org',
- 'Content-Type: text/calendar; method=REQUEST; charset=utf-8',
- 'X-Sabre-Version: ' . DAV\Version::VERSION . '-' . DAV\Version::STABILITY,
- ),
- )
- ), $handler->getSentEmails());
-
- }
-
- function testSuccessRequestUseRelativePrincipal() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/user1/outbox',
- 'HTTP_ORIGINATOR' => '/principals/user1/',
- 'HTTP_RECIPIENT' => 'mailto:user2@example.org',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
-
- $body = array(
- 'BEGIN:VCALENDAR',
- 'METHOD:REQUEST',
- 'BEGIN:VEVENT',
- 'SUMMARY:An invitation',
- 'ORGANIZER:mailto:user1.sabredav@sabredav.org',
- 'END:VEVENT',
- 'END:VCALENDAR',
- );
-
- $req->setBody(implode("\r\n",$body));
-
- $handler = new Schedule\IMip\Mock('server@example.org');
-
- $this->caldavPlugin->setIMIPhandler($handler);
-
- $response = $this->request($req);
- $this->assertEquals('HTTP/1.1 200 OK', $response->status, 'Full body: ' . $response->body);
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml',
- ), $response->headers);
-
- // Lazily checking the body for a few expected values.
- $this->assertTrue(strpos($response->body, '2.0;')!==false);
- $this->assertTrue(strpos($response->body,'user2@example.org')!==false);
-
- $this->assertEquals(array(
- array(
- 'to' => 'user2@example.org',
- 'subject' => 'Invitation for: An invitation',
- 'body' => implode("\r\n", $body) . "\r\n",
- 'headers' => array(
- 'Reply-To: user1.sabredav@sabredav.org',
- 'From: server@example.org',
- 'Content-Type: text/calendar; method=REQUEST; charset=utf-8',
- 'X-Sabre-Version: ' . DAV\Version::VERSION . '-' . DAV\Version::STABILITY,
- ),
- )
- ), $handler->getSentEmails());
-
- }
-
- function testSuccessRequestUpperCased() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/user1/outbox',
- 'HTTP_ORIGINATOR' => 'MAILTO:user1.sabredav@sabredav.org',
- 'HTTP_RECIPIENT' => 'MAILTO:user2@example.org',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
-
- $body = array(
- 'BEGIN:VCALENDAR',
- 'METHOD:REQUEST',
- 'BEGIN:VEVENT',
- 'SUMMARY:An invitation',
- 'END:VEVENT',
- 'END:VCALENDAR',
- );
-
- $req->setBody(implode("\r\n",$body));
-
- $handler = new Schedule\IMip\Mock('server@example.org');
-
- $this->caldavPlugin->setIMIPhandler($handler);
-
- $response = $this->request($req);
- $this->assertEquals('HTTP/1.1 200 OK', $response->status);
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml',
- ), $response->headers);
-
- // Lazily checking the body for a few expected values.
- $this->assertTrue(strpos($response->body, '2.0;')!==false);
- $this->assertTrue(strpos($response->body,'user2@example.org')!==false);
-
- $this->assertEquals(array(
- array(
- 'to' => 'user2@example.org',
- 'subject' => 'Invitation for: An invitation',
- 'body' => implode("\r\n", $body) . "\r\n",
- 'headers' => array(
- 'Reply-To: user1.sabredav@sabredav.org',
- 'From: server@example.org',
- 'Content-Type: text/calendar; method=REQUEST; charset=utf-8',
- 'X-Sabre-Version: ' . DAV\Version::VERSION . '-' . DAV\Version::STABILITY,
- ),
- )
- ), $handler->getSentEmails());
-
- }
-
- function testSuccessReply() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/user1/outbox',
- 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org',
- 'HTTP_RECIPIENT' => 'mailto:user2@example.org',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
-
- $body = array(
- 'BEGIN:VCALENDAR',
- 'METHOD:REPLY',
- 'BEGIN:VEVENT',
- 'SUMMARY:An invitation',
- 'END:VEVENT',
- 'END:VCALENDAR',
- );
-
- $req->setBody(implode("\r\n",$body));
-
- $handler = new Schedule\IMip\Mock('server@example.org');
-
- $this->caldavPlugin->setIMIPhandler($handler);
- $this->assertHTTPStatus(200, $req);
-
- $this->assertEquals(array(
- array(
- 'to' => 'user2@example.org',
- 'subject' => 'Response for: An invitation',
- 'body' => implode("\r\n", $body) . "\r\n",
- 'headers' => array(
- 'Reply-To: user1.sabredav@sabredav.org',
- 'From: server@example.org',
- 'Content-Type: text/calendar; method=REPLY; charset=utf-8',
- 'X-Sabre-Version: ' . DAV\Version::VERSION . '-' . DAV\Version::STABILITY,
- ),
- )
- ), $handler->getSentEmails());
-
- }
-
- function testSuccessCancel() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/user1/outbox',
- 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org',
- 'HTTP_RECIPIENT' => 'mailto:user2@example.org',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
-
- $body = array(
- 'BEGIN:VCALENDAR',
- 'METHOD:CANCEL',
- 'BEGIN:VEVENT',
- 'SUMMARY:An invitation',
- 'END:VEVENT',
- 'END:VCALENDAR',
- );
-
- $req->setBody(implode("\r\n",$body));
-
- $handler = new Schedule\IMip\Mock('server@example.org');
-
- $this->caldavPlugin->setIMIPhandler($handler);
- $this->assertHTTPStatus(200, $req);
-
- $this->assertEquals(array(
- array(
- 'to' => 'user2@example.org',
- 'subject' => 'Cancelled event: An invitation',
- 'body' => implode("\r\n", $body) . "\r\n",
- 'headers' => array(
- 'Reply-To: user1.sabredav@sabredav.org',
- 'From: server@example.org',
- 'Content-Type: text/calendar; method=CANCEL; charset=utf-8',
- 'X-Sabre-Version: ' . DAV\Version::VERSION . '-' . DAV\Version::STABILITY,
- ),
- )
- ), $handler->getSentEmails());
-
-
- }
-
- function testUseRelativePrincipalNoFallback() {
-
- $req = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'REQUEST_URI' => '/calendars/user1/outbox',
- 'HTTP_ORIGINATOR' => '/principals/user1/',
- 'HTTP_RECIPIENT' => 'mailto:user2@example.org',
- 'HTTP_CONTENT_TYPE' => 'text/calendar',
- ));
-
- $body = array(
- 'BEGIN:VCALENDAR',
- 'METHOD:REQUEST',
- 'BEGIN:VEVENT',
- 'SUMMARY:An invitation',
- 'ORGANIZER:rrrrrr',
- 'END:VEVENT',
- 'END:VCALENDAR',
- );
-
- $req->setBody(implode("\r\n",$body));
-
- $handler = new Schedule\IMip\Mock('server@example.org');
-
- $this->caldavPlugin->setIMIPhandler($handler);
-
- $response = $this->request($req);
- $this->assertEquals('HTTP/1.1 403 Forbidden', $response->status, 'Full body: ' . $response->body);
-
- }
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/PluginTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/PluginTest.php
index fb7dc316a..138012ffa 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/PluginTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/PluginTest.php
@@ -1,78 +1,80 @@
<?php
namespace Sabre\CalDAV;
+
use Sabre\DAVACL;
use Sabre\DAV;
use Sabre\HTTP;
-
-require_once 'Sabre/HTTP/ResponseMock.php';
-require_once 'Sabre/CalDAV/TestUtil.php';
+use DateTime;
+use DateTimeZone;
class PluginTest extends \PHPUnit_Framework_TestCase {
/**
- * @var Sabre\DAV\Server
+ * @var DAV\Server
*/
protected $server;
/**
- * @var Sabre\CalDAV\Plugin
+ * @var Plugin
*/
protected $plugin;
protected $response;
/**
- * @var Sabre\CalDAV\Backend\PDO
+ * @var Backend\PDO
*/
protected $caldavBackend;
function setup() {
- $this->caldavBackend = new Backend\Mock(array(
- array(
- 'id' => 1,
- 'uri' => 'UUID-123467',
- 'principaluri' => 'principals/user1',
- '{DAV:}displayname' => 'user1 calendar',
- '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar description',
- '{http://apple.com/ns/ical/}calendar-order' => '1',
- '{http://apple.com/ns/ical/}calendar-color' => '#FF0000',
- '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Property\SupportedCalendarComponentSet(array('VEVENT','VTODO')),
- ),
- array(
- 'id' => 2,
- 'uri' => 'UUID-123468',
- 'principaluri' => 'principals/user1',
- '{DAV:}displayname' => 'user1 calendar2',
- '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar description',
- '{http://apple.com/ns/ical/}calendar-order' => '1',
- '{http://apple.com/ns/ical/}calendar-color' => '#FF0000',
- '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Property\SupportedCalendarComponentSet(array('VEVENT','VTODO')),
- )
- ), array(
- 1 => array(
- 'UUID-2345' => array(
+ $caldavNS = '{urn:ietf:params:xml:ns:caldav}';
+
+ $this->caldavBackend = new Backend\Mock([
+ [
+ 'id' => 1,
+ 'uri' => 'UUID-123467',
+ 'principaluri' => 'principals/user1',
+ '{DAV:}displayname' => 'user1 calendar',
+ $caldavNS . 'calendar-description' => 'Calendar description',
+ '{http://apple.com/ns/ical/}calendar-order' => '1',
+ '{http://apple.com/ns/ical/}calendar-color' => '#FF0000',
+ $caldavNS . 'supported-calendar-component-set' => new Xml\Property\SupportedCalendarComponentSet(['VEVENT', 'VTODO']),
+ ],
+ [
+ 'id' => 2,
+ 'uri' => 'UUID-123468',
+ 'principaluri' => 'principals/user1',
+ '{DAV:}displayname' => 'user1 calendar2',
+ $caldavNS . 'calendar-description' => 'Calendar description',
+ '{http://apple.com/ns/ical/}calendar-order' => '1',
+ '{http://apple.com/ns/ical/}calendar-color' => '#FF0000',
+ $caldavNS . 'supported-calendar-component-set' => new Xml\Property\SupportedCalendarComponentSet(['VEVENT', 'VTODO']),
+ ]
+ ], [
+ 1 => [
+ 'UUID-2345' => [
'calendardata' => TestUtil::getTestCalendarData(),
- )
- )
- ));
+ ]
+ ]
+ ]);
$principalBackend = new DAVACL\PrincipalBackend\Mock();
- $principalBackend->setGroupMemberSet('principals/admin/calendar-proxy-read',array('principals/user1'));
- $principalBackend->setGroupMemberSet('principals/admin/calendar-proxy-write',array('principals/user1'));
- $principalBackend->addPrincipal(array(
+ $principalBackend->setGroupMemberSet('principals/admin/calendar-proxy-read', ['principals/user1']);
+ $principalBackend->setGroupMemberSet('principals/admin/calendar-proxy-write', ['principals/user1']);
+ $principalBackend->addPrincipal([
'uri' => 'principals/admin/calendar-proxy-read',
- ));
- $principalBackend->addPrincipal(array(
+ ]);
+ $principalBackend->addPrincipal([
'uri' => 'principals/admin/calendar-proxy-write',
- ));
+ ]);
- $calendars = new CalendarRootNode($principalBackend,$this->caldavBackend);
+ $calendars = new CalendarRoot($principalBackend, $this->caldavBackend);
$principals = new Principal\Collection($principalBackend);
$root = new DAV\SimpleCollection('root');
$root->addChild($calendars);
$root->addChild($principals);
- $objectTree = new DAV\ObjectTree($root);
- $this->server = new DAV\Server($objectTree);
+ $this->server = new DAV\Server($root);
+ $this->server->sapi = new HTTP\SapiMock();
$this->server->debugExceptions = true;
$this->server->setBaseUri('/');
$this->plugin = new Plugin();
@@ -83,11 +85,13 @@ class PluginTest extends \PHPUnit_Framework_TestCase {
// Adding Auth plugin, and ensuring that we are logged in.
$authBackend = new DAV\Auth\Backend\Mock();
- $authBackend->defaultUser = 'user1';
+ $authBackend->setPrincipal('principals/user1');
$authPlugin = new DAV\Auth\Plugin($authBackend, 'SabreDAV');
+ $authPlugin->beforeMethod(new \Sabre\HTTP\Request(), new \Sabre\HTTP\Response());
$this->server->addPlugin($authPlugin);
- $authPlugin->beforeMethod('GET', '/');
+ // This forces a login
+ $authPlugin->beforeMethod(new HTTP\Request(), new HTTP\Response());
$this->response = new HTTP\ResponseMock();
$this->server->httpResponse = $this->response;
@@ -96,48 +100,41 @@ class PluginTest extends \PHPUnit_Framework_TestCase {
function testSimple() {
- $this->assertEquals(array('MKCALENDAR'), $this->plugin->getHTTPMethods('calendars/user1/randomnewcalendar'));
- $this->assertEquals(array('calendar-access','calendar-proxy'), $this->plugin->getFeatures());
- $this->assertArrayHasKey('urn:ietf:params:xml:ns:caldav', $this->server->xmlNamespaces);
+ $this->assertEquals(['MKCALENDAR'], $this->plugin->getHTTPMethods('calendars/user1/randomnewcalendar'));
+ $this->assertEquals(['calendar-access', 'calendar-proxy'], $this->plugin->getFeatures());
+ $this->assertEquals(
+ 'caldav',
+ $this->plugin->getPluginInfo()['name']
+ );
}
function testUnknownMethodPassThrough() {
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'MKBREAKFAST',
- 'REQUEST_URI' => '/',
- ));
+ $request = new HTTP\Request('MKBREAKFAST', '/');
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 501 Not Implemented', $this->response->status,'Incorrect status returned. Full response body:' . $this->response->body);
+ $this->assertEquals(501, $this->response->status, 'Incorrect status returned. Full response body:' . $this->response->body);
}
function testReportPassThrough() {
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
- 'HTTP_CONTENT_TYPE' => 'application/xml',
- 'REQUEST_URI' => '/',
- ));
+ $request = new HTTP\Request('REPORT', '/', ['Content-Type' => 'application/xml']);
$request->setBody('<?xml version="1.0"?><s:somereport xmlns:s="http://www.rooftopsolutions.nl/NS/example" />');
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 403 Forbidden', $this->response->status);
+ $this->assertEquals(415, $this->response->status);
}
function testMkCalendarBadLocation() {
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'MKCALENDAR',
- 'REQUEST_URI' => '/blabla',
- ));
+ $request = new HTTP\Request('MKCALENDAR', '/blabla');
$body = '<?xml version="1.0" encoding="utf-8" ?>
<C:mkcalendar xmlns:D="DAV:"
@@ -181,16 +178,13 @@ class PluginTest extends \PHPUnit_Framework_TestCase {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 403 Forbidden', $this->response->status);
+ $this->assertEquals(403, $this->response->status);
}
function testMkCalendarNoParentNode() {
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'MKCALENDAR',
- 'REQUEST_URI' => '/doesntexist/calendar',
- ));
+ $request = new HTTP\Request('MKCALENDAR', '/doesntexist/calendar');
$body = '<?xml version="1.0" encoding="utf-8" ?>
<C:mkcalendar xmlns:D="DAV:"
@@ -234,16 +228,16 @@ class PluginTest extends \PHPUnit_Framework_TestCase {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 409 Conflict', $this->response->status);
+ $this->assertEquals(409, $this->response->status);
}
function testMkCalendarExistingCalendar() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray([
'REQUEST_METHOD' => 'MKCALENDAR',
'REQUEST_URI' => '/calendars/user1/UUID-123467',
- ));
+ ]);
$body = '<?xml version="1.0" encoding="utf-8" ?>
<C:mkcalendar xmlns:D="DAV:"
@@ -287,16 +281,13 @@ class PluginTest extends \PHPUnit_Framework_TestCase {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 405 Method Not Allowed', $this->response->status);
+ $this->assertEquals(405, $this->response->status);
}
function testMkCalendarSucceed() {
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'MKCALENDAR',
- 'REQUEST_URI' => '/calendars/user1/NEWCALENDAR',
- ));
+ $request = new HTTP\Request('MKCALENDAR', '/calendars/user1/NEWCALENDAR');
$timezone = 'BEGIN:VCALENDAR
PRODID:-//Example Corp.//CalDAV Client//EN
@@ -341,31 +332,31 @@ END:VCALENDAR';
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 201 Created', $this->response->status,'Invalid response code received. Full response body: ' .$this->response->body);
+ $this->assertEquals(201, $this->response->status, 'Invalid response code received. Full response body: ' . $this->response->body);
$calendars = $this->caldavBackend->getCalendarsForUser('principals/user1');
$this->assertEquals(3, count($calendars));
$newCalendar = null;
- foreach($calendars as $calendar) {
+ foreach ($calendars as $calendar) {
if ($calendar['uri'] === 'NEWCALENDAR') {
$newCalendar = $calendar;
break;
}
}
- $this->assertInternalType('array',$newCalendar);
+ $this->assertInternalType('array', $newCalendar);
- $keys = array(
- 'uri' => 'NEWCALENDAR',
- 'id' => null,
- '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar restricted to events.',
- '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => $timezone,
- '{DAV:}displayname' => 'Lisa\'s Events',
+ $keys = [
+ 'uri' => 'NEWCALENDAR',
+ 'id' => null,
+ '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar restricted to events.',
+ '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => $timezone,
+ '{DAV:}displayname' => 'Lisa\'s Events',
'{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => null,
- );
+ ];
- foreach($keys as $key=>$value) {
+ foreach ($keys as $key => $value) {
$this->assertArrayHasKey($key, $newCalendar);
@@ -374,44 +365,41 @@ END:VCALENDAR';
}
$sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
- $this->assertTrue($newCalendar[$sccs] instanceof Property\SupportedCalendarComponentSet);
- $this->assertEquals(array('VEVENT'),$newCalendar[$sccs]->getValue());
+ $this->assertTrue($newCalendar[$sccs] instanceof Xml\Property\SupportedCalendarComponentSet);
+ $this->assertEquals(['VEVENT'], $newCalendar[$sccs]->getValue());
}
function testMkCalendarEmptyBodySucceed() {
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'MKCALENDAR',
- 'REQUEST_URI' => '/calendars/user1/NEWCALENDAR',
- ));
+ $request = new HTTP\Request('MKCALENDAR', '/calendars/user1/NEWCALENDAR');
$request->setBody('');
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 201 Created', $this->response->status,'Invalid response code received. Full response body: ' .$this->response->body);
+ $this->assertEquals(201, $this->response->status, 'Invalid response code received. Full response body: ' . $this->response->body);
$calendars = $this->caldavBackend->getCalendarsForUser('principals/user1');
$this->assertEquals(3, count($calendars));
$newCalendar = null;
- foreach($calendars as $calendar) {
+ foreach ($calendars as $calendar) {
if ($calendar['uri'] === 'NEWCALENDAR') {
$newCalendar = $calendar;
break;
}
}
- $this->assertInternalType('array',$newCalendar);
+ $this->assertInternalType('array', $newCalendar);
- $keys = array(
- 'uri' => 'NEWCALENDAR',
- 'id' => null,
+ $keys = [
+ 'uri' => 'NEWCALENDAR',
+ 'id' => null,
'{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => null,
- );
+ ];
- foreach($keys as $key=>$value) {
+ foreach ($keys as $key => $value) {
$this->assertArrayHasKey($key, $newCalendar);
@@ -420,84 +408,82 @@ END:VCALENDAR';
}
$sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
- $this->assertTrue($newCalendar[$sccs] instanceof Property\SupportedCalendarComponentSet);
- $this->assertEquals(array('VEVENT','VTODO'),$newCalendar[$sccs]->getValue());
+ $this->assertTrue($newCalendar[$sccs] instanceof Xml\Property\SupportedCalendarComponentSet);
+ $this->assertEquals(['VEVENT', 'VTODO'], $newCalendar[$sccs]->getValue());
+
+ }
+
+ function testMkCalendarBadXml() {
+
+ $request = new HTTP\Request('MKCALENDAR', '/blabla');
+ $body = 'This is not xml';
+
+ $request->setBody($body);
+ $this->server->httpRequest = $request;
+ $this->server->exec();
+
+ $this->assertEquals(400, $this->response->status);
}
function testPrincipalProperties() {
- $httpRequest = new HTTP\Request(array(
- 'HTTP_HOST' => 'sabredav.org',
- ));
+ $httpRequest = new HTTP\Request('FOO', '/blabla', ['Host' => 'sabredav.org']);
$this->server->httpRequest = $httpRequest;
- $props = $this->server->getPropertiesForPath('/principals/user1',array(
- '{urn:ietf:params:xml:ns:caldav}calendar-home-set',
- '{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL',
- '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set',
+ $props = $this->server->getPropertiesForPath('/principals/user1', [
+ '{' . Plugin::NS_CALDAV . '}calendar-home-set',
'{' . Plugin::NS_CALENDARSERVER . '}calendar-proxy-read-for',
'{' . Plugin::NS_CALENDARSERVER . '}calendar-proxy-write-for',
'{' . Plugin::NS_CALENDARSERVER . '}notification-URL',
- ));
+ '{' . Plugin::NS_CALENDARSERVER . '}email-address-set',
+ ]);
- $this->assertArrayHasKey(0,$props);
- $this->assertArrayHasKey(200,$props[0]);
+ $this->assertArrayHasKey(0, $props);
+ $this->assertArrayHasKey(200, $props[0]);
- $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-home-set',$props[0][200]);
+ $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-home-set', $props[0][200]);
$prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-home-set'];
- $this->assertTrue($prop instanceof DAV\Property\Href);
- $this->assertEquals('calendars/user1/',$prop->getHref());
-
- $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL',$props[0][200]);
- $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL'];
- $this->assertTrue($prop instanceof DAV\Property\Href);
- $this->assertEquals('calendars/user1/outbox',$prop->getHref());
-
- $this->assertArrayHasKey('{'.Plugin::NS_CALENDARSERVER .'}notification-URL',$props[0][200]);
- $prop = $props[0][200]['{'.Plugin::NS_CALENDARSERVER .'}notification-URL'];
- $this->assertTrue($prop instanceof DAV\Property\Href);
- $this->assertEquals('calendars/user1/notifications/',$prop->getHref());
-
-
- $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-user-address-set',$props[0][200]);
- $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set'];
- $this->assertTrue($prop instanceof DAV\Property\HrefList);
- $this->assertEquals(array('mailto:user1.sabredav@sabredav.org','/principals/user1/'),$prop->getHrefs());
+ $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $prop);
+ $this->assertEquals('calendars/user1/', $prop->getHref());
$this->assertArrayHasKey('{http://calendarserver.org/ns/}calendar-proxy-read-for', $props[0][200]);
$prop = $props[0][200]['{http://calendarserver.org/ns/}calendar-proxy-read-for'];
- $this->assertInstanceOf('Sabre\\DAV\\Property\\HrefList', $prop);
- $this->assertEquals(array('principals/admin'), $prop->getHrefs());
+ $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $prop);
+ $this->assertEquals(['principals/admin/'], $prop->getHrefs());
$this->assertArrayHasKey('{http://calendarserver.org/ns/}calendar-proxy-write-for', $props[0][200]);
$prop = $props[0][200]['{http://calendarserver.org/ns/}calendar-proxy-write-for'];
- $this->assertInstanceOf('Sabre\\DAV\\Property\\HrefList', $prop);
- $this->assertEquals(array('principals/admin'), $prop->getHrefs());
+ $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $prop);
+ $this->assertEquals(['principals/admin/'], $prop->getHrefs());
+ $this->assertArrayHasKey('{' . Plugin::NS_CALENDARSERVER . '}email-address-set', $props[0][200]);
+ $prop = $props[0][200]['{' . Plugin::NS_CALENDARSERVER . '}email-address-set'];
+ $this->assertInstanceOf('Sabre\\CalDAV\\Xml\\Property\\EmailAddressSet', $prop);
+ $this->assertEquals(['user1.sabredav@sabredav.org'], $prop->getValue());
}
function testSupportedReportSetPropertyNonCalendar() {
- $props = $this->server->getPropertiesForPath('/calendars/user1',array(
+ $props = $this->server->getPropertiesForPath('/calendars/user1', [
'{DAV:}supported-report-set',
- ));
+ ]);
- $this->assertArrayHasKey(0,$props);
- $this->assertArrayHasKey(200,$props[0]);
- $this->assertArrayHasKey('{DAV:}supported-report-set',$props[0][200]);
+ $this->assertArrayHasKey(0, $props);
+ $this->assertArrayHasKey(200, $props[0]);
+ $this->assertArrayHasKey('{DAV:}supported-report-set', $props[0][200]);
$prop = $props[0][200]['{DAV:}supported-report-set'];
- $this->assertInstanceOf('\\Sabre\\DAV\\Property\\SupportedReportSet', $prop);
- $value = array(
+ $this->assertInstanceOf('\\Sabre\\DAV\\Xml\\Property\\SupportedReportSet', $prop);
+ $value = [
'{DAV:}expand-property',
'{DAV:}principal-property-search',
'{DAV:}principal-search-property-set'
- );
- $this->assertEquals($value,$prop->getValue());
+ ];
+ $this->assertEquals($value, $prop->getValue());
}
@@ -506,26 +492,51 @@ END:VCALENDAR';
*/
function testSupportedReportSetProperty() {
- $props = $this->server->getPropertiesForPath('/calendars/user1/UUID-123467',array(
+ $props = $this->server->getPropertiesForPath('/calendars/user1/UUID-123467', [
'{DAV:}supported-report-set',
- ));
+ ]);
- $this->assertArrayHasKey(0,$props);
- $this->assertArrayHasKey(200,$props[0]);
- $this->assertArrayHasKey('{DAV:}supported-report-set',$props[0][200]);
+ $this->assertArrayHasKey(0, $props);
+ $this->assertArrayHasKey(200, $props[0]);
+ $this->assertArrayHasKey('{DAV:}supported-report-set', $props[0][200]);
$prop = $props[0][200]['{DAV:}supported-report-set'];
- $this->assertTrue($prop instanceof \Sabre\DAV\Property\SupportedReportSet);
- $value = array(
+ $this->assertInstanceOf('\\Sabre\\DAV\\Xml\\Property\\SupportedReportSet', $prop);
+ $value = [
'{urn:ietf:params:xml:ns:caldav}calendar-multiget',
'{urn:ietf:params:xml:ns:caldav}calendar-query',
'{urn:ietf:params:xml:ns:caldav}free-busy-query',
'{DAV:}expand-property',
'{DAV:}principal-property-search',
'{DAV:}principal-search-property-set'
- );
- $this->assertEquals($value,$prop->getValue());
+ ];
+ $this->assertEquals($value, $prop->getValue());
+
+ }
+
+ function testSupportedReportSetUserCalendars() {
+
+ $this->server->addPlugin(new \Sabre\DAV\Sync\Plugin());
+
+ $props = $this->server->getPropertiesForPath('/calendars/user1', [
+ '{DAV:}supported-report-set',
+ ]);
+
+ $this->assertArrayHasKey(0, $props);
+ $this->assertArrayHasKey(200, $props[0]);
+ $this->assertArrayHasKey('{DAV:}supported-report-set', $props[0][200]);
+
+ $prop = $props[0][200]['{DAV:}supported-report-set'];
+
+ $this->assertInstanceOf('\\Sabre\\DAV\\Xml\\Property\\SupportedReportSet', $prop);
+ $value = [
+ '{DAV:}sync-collection',
+ '{DAV:}expand-property',
+ '{DAV:}principal-property-search',
+ '{DAV:}principal-search-property-set',
+ ];
+ $this->assertEquals($value, $prop->getValue());
}
@@ -544,48 +555,33 @@ END:VCALENDAR';
'<d:href>/calendars/user1/UUID-123467/UUID-2345</d:href>' .
'</c:calendar-multiget>';
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
- 'REQUEST_URI' => '/calendars/user1',
- 'HTTP_DEPTH' => '1',
- ));
+ $request = new HTTP\Request('REPORT', '/calendars/user1', ['Depth' => '1']);
$request->setBody($body);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body);
-
- $xml = simplexml_load_string(DAV\XMLUtil::convertDAVNamespace($this->response->body));
+ $this->assertEquals(207, $this->response->status, 'Invalid HTTP status received. Full response body');
- $xml->registerXPathNamespace('d','urn:DAV');
- $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav');
+ $expectedIcal = TestUtil::getTestCalendarData();
- $check = array(
- '/d:multistatus',
- '/d:multistatus/d:response',
- '/d:multistatus/d:response/d:href',
- '/d:multistatus/d:response/d:propstat',
- '/d:multistatus/d:response/d:propstat/d:prop',
- '/d:multistatus/d:response/d:propstat/d:prop/d:getetag',
- '/d:multistatus/d:response/d:propstat/d:prop/c:calendar-data',
- '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK',
- );
+ $expected = <<<XML
+<?xml version="1.0"?>
+<d:multistatus xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
+<d:response>
+ <d:href>/calendars/user1/UUID-123467/UUID-2345</d:href>
+ <d:propstat>
+ <d:prop>
+ <cal:calendar-data>$expectedIcal</cal:calendar-data>
+ <d:getetag>"e207e33c10e5fb9c12cfb35b5d9116e1"</d:getetag>
+ </d:prop>
+ <d:status>HTTP/1.1 200 OK</d:status>
+ </d:propstat>
+</d:response>
+</d:multistatus>
+XML;
- foreach($check as $v1=>$v2) {
-
- $xpath = is_int($v1)?$v2:$v1;
-
- $result = $xml->xpath($xpath);
- $this->assertEquals(1,count($result));
-
- if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]);
-
- }
-
- // The response object should have a reference to the Asia/Seoul
- // timezone.
- $this->assertTrue(strpos($this->response->body,'Asia/Seoul')!==false);
+ $this->assertXmlStringEqualsXmlString($expected, $this->response->getBodyAsString());
}
@@ -606,46 +602,39 @@ END:VCALENDAR';
'<d:href>/calendars/user1/UUID-123467/UUID-2345</d:href>' .
'</c:calendar-multiget>';
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
- 'REQUEST_URI' => '/calendars/user1',
- 'HTTP_DEPTH' => '1',
- ));
+ $request = new HTTP\Request('REPORT', '/calendars/user1', ['Depth' => '1']);
$request->setBody($body);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body);
-
- $xml = simplexml_load_string(DAV\XMLUtil::convertDAVNamespace($this->response->body));
-
- $xml->registerXPathNamespace('d','urn:DAV');
- $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav');
+ $this->assertEquals(207, $this->response->status, 'Invalid HTTP status received. Full response body: ' . $this->response->body);
- $check = array(
- '/d:multistatus',
- '/d:multistatus/d:response',
- '/d:multistatus/d:response/d:href',
- '/d:multistatus/d:response/d:propstat',
- '/d:multistatus/d:response/d:propstat/d:prop',
- '/d:multistatus/d:response/d:propstat/d:prop/d:getetag',
- '/d:multistatus/d:response/d:propstat/d:prop/c:calendar-data',
- '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK',
+ $expectedIcal = TestUtil::getTestCalendarData();
+ $expectedIcal = \Sabre\VObject\Reader::read($expectedIcal);
+ $expectedIcal = $expectedIcal->expand(
+ new DateTime('2011-01-01 00:00:00', new DateTimeZone('UTC')),
+ new DateTime('2011-12-31 23:59:59', new DateTimeZone('UTC'))
);
-
- foreach($check as $v1=>$v2) {
-
- $xpath = is_int($v1)?$v2:$v1;
-
- $result = $xml->xpath($xpath);
- $this->assertEquals(1,count($result));
-
- if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]);
-
- }
- // The response object should no longer hold references to timezones.
- $this->assertTrue(strpos($this->response->body,'Asia/Seoul')===false);
+ $expectedIcal = str_replace("\r\n", "&#xD;\n", $expectedIcal->serialize());
+
+ $expected = <<<XML
+<?xml version="1.0"?>
+<d:multistatus xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
+<d:response>
+ <d:href>/calendars/user1/UUID-123467/UUID-2345</d:href>
+ <d:propstat>
+ <d:prop>
+ <cal:calendar-data>$expectedIcal</cal:calendar-data>
+ <d:getetag>"e207e33c10e5fb9c12cfb35b5d9116e1"</d:getetag>
+ </d:prop>
+ <d:status>HTTP/1.1 200 OK</d:status>
+ </d:propstat>
+</d:response>
+</d:multistatus>
+XML;
+
+ $this->assertXmlStringEqualsXmlString($expected, $this->response->getBodyAsString());
}
@@ -671,56 +660,117 @@ END:VCALENDAR';
'</c:filter>' .
'</c:calendar-query>';
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
- 'REQUEST_URI' => '/calendars/user1/UUID-123467',
- 'HTTP_DEPTH' => '1',
- ));
+ $request = new HTTP\Request('REPORT', '/calendars/user1/UUID-123467', ['Depth' => '1']);
$request->setBody($body);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body);
+ $this->assertEquals(207, $this->response->status, 'Received an unexpected status. Full response body: ' . $this->response->body);
- $xml = simplexml_load_string(DAV\XMLUtil::convertDAVNamespace($this->response->body));
+ $expectedIcal = TestUtil::getTestCalendarData();
+ $expectedIcal = \Sabre\VObject\Reader::read($expectedIcal);
+ $expectedIcal = $expectedIcal->expand(
+ new DateTime('2000-01-01 00:00:00', new DateTimeZone('UTC')),
+ new DateTime('2010-12-31 23:59:59', new DateTimeZone('UTC'))
+ );
+ $expectedIcal = str_replace("\r\n", "&#xD;\n", $expectedIcal->serialize());
+
+ $expected = <<<XML
+<?xml version="1.0"?>
+<d:multistatus xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
+<d:response>
+ <d:href>/calendars/user1/UUID-123467/UUID-2345</d:href>
+ <d:propstat>
+ <d:prop>
+ <cal:calendar-data>$expectedIcal</cal:calendar-data>
+ <d:getetag>"e207e33c10e5fb9c12cfb35b5d9116e1"</d:getetag>
+ </d:prop>
+ <d:status>HTTP/1.1 200 OK</d:status>
+ </d:propstat>
+</d:response>
+</d:multistatus>
+XML;
+
+ $this->assertXmlStringEqualsXmlString($expected, $this->response->getBodyAsString());
- $xml->registerXPathNamespace('d','urn:DAV');
- $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav');
+ }
- $check = array(
- '/d:multistatus',
- '/d:multistatus/d:response',
- '/d:multistatus/d:response/d:href',
- '/d:multistatus/d:response/d:propstat',
- '/d:multistatus/d:response/d:propstat/d:prop',
- '/d:multistatus/d:response/d:propstat/d:prop/d:getetag',
- '/d:multistatus/d:response/d:propstat/d:prop/c:calendar-data',
- '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK',
- );
+ /**
+ * @depends testSupportedReportSetProperty
+ * @depends testCalendarMultiGetReport
+ */
+ function testCalendarQueryReportWindowsPhone() {
- foreach($check as $v1=>$v2) {
+ $body =
+ '<?xml version="1.0"?>' .
+ '<c:calendar-query xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:d="DAV:">' .
+ '<d:prop>' .
+ ' <c:calendar-data>' .
+ ' <c:expand start="20000101T000000Z" end="20101231T235959Z" />' .
+ ' </c:calendar-data>' .
+ ' <d:getetag />' .
+ '</d:prop>' .
+ '<c:filter>' .
+ ' <c:comp-filter name="VCALENDAR">' .
+ ' <c:comp-filter name="VEVENT" />' .
+ ' </c:comp-filter>' .
+ '</c:filter>' .
+ '</c:calendar-query>';
- $xpath = is_int($v1)?$v2:$v1;
+ $request = new HTTP\Request('REPORT', '/calendars/user1/UUID-123467', [
+ 'Depth' => '0',
+ 'User-Agent' => 'MSFT-WP/8.10.14219 (gzip)',
+ ]);
- $result = $xml->xpath($xpath);
- $this->assertEquals(1,count($result), 'We expected 1 ' . $xpath . ' elements. We\'ve found ' . count($result) . '. Full result: ' . $this->response->body);
+ $request->setBody($body);
- if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]);
+ $this->server->httpRequest = $request;
+ $this->server->exec();
- }
+ $this->assertEquals(207, $this->response->status, 'Received an unexpected status. Full response body: ' . $this->response->body);
+
+ $expectedIcal = TestUtil::getTestCalendarData();
+ $expectedIcal = \Sabre\VObject\Reader::read($expectedIcal);
+ $expectedIcal = $expectedIcal->expand(
+ new DateTime('2000-01-01 00:00:00', new DateTimeZone('UTC')),
+ new DateTime('2010-12-31 23:59:59', new DateTimeZone('UTC'))
+ );
+ $expectedIcal = str_replace("\r\n", "&#xD;\n", $expectedIcal->serialize());
+
+ $expected = <<<XML
+<?xml version="1.0"?>
+<d:multistatus xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
+<d:response>
+ <d:href>/calendars/user1/UUID-123467/UUID-2345</d:href>
+ <d:propstat>
+ <d:prop>
+ <cal:calendar-data>$expectedIcal</cal:calendar-data>
+ <d:getetag>"e207e33c10e5fb9c12cfb35b5d9116e1"</d:getetag>
+ </d:prop>
+ <d:status>HTTP/1.1 200 OK</d:status>
+ </d:propstat>
+</d:response>
+</d:multistatus>
+XML;
+
+ $this->assertXmlStringEqualsXmlString($expected, $this->response->getBodyAsString());
}
/**
- * @depends testCalendarQueryReport
+ * @depends testSupportedReportSetProperty
+ * @depends testCalendarMultiGetReport
*/
- function testCalendarQueryReportNoCalData() {
+ function testCalendarQueryReportBadDepth() {
$body =
'<?xml version="1.0"?>' .
'<c:calendar-query xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:d="DAV:">' .
'<d:prop>' .
+ ' <c:calendar-data>' .
+ ' <c:expand start="20000101T000000Z" end="20101231T235959Z" />' .
+ ' </c:calendar-data>' .
' <d:getetag />' .
'</d:prop>' .
'<c:filter>' .
@@ -730,43 +780,62 @@ END:VCALENDAR';
'</c:filter>' .
'</c:calendar-query>';
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
- 'REQUEST_URI' => '/calendars/user1//UUID-123467',
- 'HTTP_DEPTH' => '1',
- ));
+ $request = new HTTP\Request('REPORT', '/calendars/user1/UUID-123467', [
+ 'Depth' => '0',
+ ]);
$request->setBody($body);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body);
-
- $xml = simplexml_load_string(DAV\XMLUtil::convertDAVNamespace($this->response->body));
+ $this->assertEquals(400, $this->response->status, 'Received an unexpected status. Full response body: ' . $this->response->body);
- $xml->registerXPathNamespace('d','urn:DAV');
- $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav');
-
- $check = array(
- '/d:multistatus',
- '/d:multistatus/d:response',
- '/d:multistatus/d:response/d:href',
- '/d:multistatus/d:response/d:propstat',
- '/d:multistatus/d:response/d:propstat/d:prop',
- '/d:multistatus/d:response/d:propstat/d:prop/d:getetag',
- '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK',
- );
+ }
- foreach($check as $v1=>$v2) {
+ /**
+ * @depends testCalendarQueryReport
+ */
+ function testCalendarQueryReportNoCalData() {
- $xpath = is_int($v1)?$v2:$v1;
+ $body =
+ '<?xml version="1.0"?>' .
+ '<c:calendar-query xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:d="DAV:">' .
+ '<d:prop>' .
+ ' <d:getetag />' .
+ '</d:prop>' .
+ '<c:filter>' .
+ ' <c:comp-filter name="VCALENDAR">' .
+ ' <c:comp-filter name="VEVENT" />' .
+ ' </c:comp-filter>' .
+ '</c:filter>' .
+ '</c:calendar-query>';
- $result = $xml->xpath($xpath);
- $this->assertEquals(1,count($result), 'We expected 1 ' . $xpath . ' elements. We\'ve found ' . count($result) . '. Full result: ' . $this->response->body);
+ $request = new HTTP\Request('REPORT', '/calendars/user1/UUID-123467', [
+ 'Depth' => '1',
+ ]);
+ $request->setBody($body);
- if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]);
+ $this->server->httpRequest = $request;
+ $this->server->exec();
- }
+ $this->assertEquals(207, $this->response->status, 'Received an unexpected status. Full response body: ' . $this->response->body);
+
+ $expected = <<<XML
+<?xml version="1.0"?>
+<d:multistatus xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
+<d:response>
+ <d:href>/calendars/user1/UUID-123467/UUID-2345</d:href>
+ <d:propstat>
+ <d:prop>
+ <d:getetag>"e207e33c10e5fb9c12cfb35b5d9116e1"</d:getetag>
+ </d:prop>
+ <d:status>HTTP/1.1 200 OK</d:status>
+ </d:propstat>
+</d:response>
+</d:multistatus>
+XML;
+
+ $this->assertXmlStringEqualsXmlString($expected, $this->response->getBodyAsString());
}
@@ -784,16 +853,13 @@ END:VCALENDAR';
'</d:prop>' .
'</c:calendar-query>';
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
- 'REQUEST_URI' => '/calendars/user1//UUID-123467',
- ));
+ $request = new HTTP\Request('REPORT', '/calendars/user1/UUID-123467');
$request->setBody($body);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body);
+ $this->assertEquals(400, $this->response->status, 'Received an unexpected status. Full response body: ' . $this->response->body);
}
@@ -819,44 +885,39 @@ END:VCALENDAR';
'</c:filter>' .
'</c:calendar-query>';
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
- 'REQUEST_URI' => '/calendars/user1/UUID-123467/UUID-2345',
- 'HTTP_DEPTH' => '0',
- ));
+ $request = new HTTP\Request('REPORT', '/calendars/user1/UUID-123467/UUID-2345', ['Depth' => '0']);
$request->setBody($body);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body);
-
- $xml = simplexml_load_string(DAV\XMLUtil::convertDAVNamespace($this->response->body));
-
- $xml->registerXPathNamespace('d','urn:DAV');
- $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav');
+ $this->assertEquals(207, $this->response->status, 'Received an unexpected status. Full response body: ' . $this->response->body);
- $check = array(
- '/d:multistatus',
- '/d:multistatus/d:response',
- '/d:multistatus/d:response/d:href',
- '/d:multistatus/d:response/d:propstat',
- '/d:multistatus/d:response/d:propstat/d:prop',
- '/d:multistatus/d:response/d:propstat/d:prop/d:getetag',
- '/d:multistatus/d:response/d:propstat/d:prop/c:calendar-data',
- '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK',
+ $expectedIcal = TestUtil::getTestCalendarData();
+ $expectedIcal = \Sabre\VObject\Reader::read($expectedIcal);
+ $expectedIcal = $expectedIcal->expand(
+ new DateTime('2000-01-01 00:00:00', new DateTimeZone('UTC')),
+ new DateTime('2010-12-31 23:59:59', new DateTimeZone('UTC'))
);
-
- foreach($check as $v1=>$v2) {
-
- $xpath = is_int($v1)?$v2:$v1;
-
- $result = $xml->xpath($xpath);
- $this->assertEquals(1,count($result), 'We expected 1 ' . $xpath . ' elements. We\'ve found ' . count($result) . '. Full result: ' . $this->response->body);
-
- if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]);
-
- }
+ $expectedIcal = str_replace("\r\n", "&#xD;\n", $expectedIcal->serialize());
+
+ $expected = <<<XML
+<?xml version="1.0"?>
+<d:multistatus xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
+<d:response>
+ <d:href>/calendars/user1/UUID-123467/UUID-2345</d:href>
+ <d:propstat>
+ <d:prop>
+ <cal:calendar-data>$expectedIcal</cal:calendar-data>
+ <d:getetag>"e207e33c10e5fb9c12cfb35b5d9116e1"</d:getetag>
+ </d:prop>
+ <d:status>HTTP/1.1 200 OK</d:status>
+ </d:propstat>
+</d:response>
+</d:multistatus>
+XML;
+
+ $this->assertXmlStringEqualsXmlString($expected, $this->response->getBodyAsString());
}
@@ -879,77 +940,40 @@ END:VCALENDAR';
'</c:filter>' .
'</c:calendar-query>';
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
- 'REQUEST_URI' => '/calendars/user1/UUID-123467/UUID-2345',
- 'HTTP_DEPTH' => '0',
- ));
+ $request = new HTTP\Request('REPORT', '/calendars/user1/UUID-123467/UUID-2345', ['Depth' => '0']);
$request->setBody($body);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body);
-
- $xml = simplexml_load_string(DAV\XMLUtil::convertDAVNamespace($this->response->body));
-
- $xml->registerXPathNamespace('d','urn:DAV');
- $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav');
-
- $check = array(
- '/d:multistatus',
- '/d:multistatus/d:response',
- '/d:multistatus/d:response/d:href',
- '/d:multistatus/d:response/d:propstat',
- '/d:multistatus/d:response/d:propstat/d:prop',
- '/d:multistatus/d:response/d:propstat/d:prop/d:getetag',
- '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK',
- );
-
- foreach($check as $v1=>$v2) {
-
- $xpath = is_int($v1)?$v2:$v1;
-
- $result = $xml->xpath($xpath);
- $this->assertEquals(1,count($result), 'We expected 1 ' . $xpath . ' elements. We\'ve found ' . count($result) . '. Full result: ' . $this->response->body);
-
- if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]);
-
- }
+ $this->assertEquals(207, $this->response->status, 'Received an unexpected status. Full response body: ' . $this->response->body);
+
+ $expected = <<<XML
+<?xml version="1.0"?>
+<d:multistatus xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
+<d:response>
+ <d:href>/calendars/user1/UUID-123467/UUID-2345</d:href>
+ <d:propstat>
+ <d:prop>
+ <d:getetag>"e207e33c10e5fb9c12cfb35b5d9116e1"</d:getetag>
+ </d:prop>
+ <d:status>HTTP/1.1 200 OK</d:status>
+ </d:propstat>
+</d:response>
+</d:multistatus>
+XML;
+
+ $this->assertXmlStringEqualsXmlString($expected, $this->response->getBodyAsString());
}
function testHTMLActionsPanel() {
$output = '';
- $r = $this->server->broadcastEvent('onHTMLActionsPanel', array($this->server->tree->getNodeForPath('calendars/user1'), &$output));
+ $r = $this->server->emit('onHTMLActionsPanel', [$this->server->tree->getNodeForPath('calendars/user1'), &$output]);
$this->assertFalse($r);
- $this->assertTrue(!!strpos($output,'Display name'));
-
- }
-
- function testBrowserPostAction() {
-
- $r = $this->server->broadcastEvent('onBrowserPostAction', array('calendars/user1', 'mkcalendar', array(
- 'name' => 'NEWCALENDAR',
- '{DAV:}displayname' => 'foo',
- )));
- $this->assertFalse($r);
-
- $calendars = $this->caldavBackend->getCalendarsForUser('principals/user1');
- $this->assertEquals(3, count($calendars));
-
- $newCalendar = null;
- foreach($calendars as $calendar) {
- if ($calendar['uri'] === 'NEWCALENDAR') {
- $newCalendar = $calendar;
- break;
- }
- }
- if (!$newCalendar)
- $this->fail('Could not find newly created calendar');
-
+ $this->assertTrue(!!strpos($output, 'Display name'));
}
@@ -970,17 +994,13 @@ END:VCALENDAR';
'<d:href>/calendars/user1/UUID-123467/UUID-2345</d:href>' .
'</c:calendar-multiget>';
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
- 'REQUEST_URI' => '/calendars/user1',
- 'HTTP_DEPTH' => '1',
- ));
+ $request = new HTTP\Request('REPORT', '/calendars/user1', ['Depth' => '1']);
$request->setBody($body);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body);
+ $this->assertEquals(400, $this->response->status, 'Invalid HTTP status received. Full response body: ' . $this->response->body);
}
@@ -1001,17 +1021,13 @@ END:VCALENDAR';
'<d:href>/calendars/user1/UUID-123467/UUID-2345</d:href>' .
'</c:calendar-multiget>';
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
- 'REQUEST_URI' => '/calendars/user1',
- 'HTTP_DEPTH' => '1',
- ));
+ $request = new HTTP\Request('REPORT', '/calendars/user1', ['Depth' => '1']);
$request->setBody($body);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body);
+ $this->assertEquals(400, $this->response->status, 'Invalid HTTP status received. Full response body: ' . $this->response->body);
}
@@ -1032,95 +1048,34 @@ END:VCALENDAR';
'<d:href>/calendars/user1/UUID-123467/UUID-2345</d:href>' .
'</c:calendar-multiget>';
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
- 'REQUEST_URI' => '/calendars/user1',
- 'HTTP_DEPTH' => '1',
- ));
+ $request = new HTTP\Request('REPORT', '/calendars/user1', ['Depth' => '1']);
$request->setBody($body);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body);
+ $this->assertEquals(400, $this->response->status, 'Invalid HTTP status received. Full response body: ' . $this->response->body);
}
- function testNotificationProperties() {
-
- $request = array(
- '{' . Plugin::NS_CALENDARSERVER . '}notificationtype',
- );
- $result = array();
- $notification = new Notifications\Node(
- $this->caldavBackend,
- 'principals/user1',
- new Notifications\Notification\SystemStatus('foo','"1"')
- );
- $this->plugin->beforeGetProperties('foo', $notification, $request, $result);
-
- $this->assertEquals(
- array(
- 200 => array(
- '{' . Plugin::NS_CALENDARSERVER . '}notificationtype' => $notification->getNotificationType()
- )
- ), $result);
-
- }
-
- function testNotificationGet() {
-
- $notification = new Notifications\Node(
- $this->caldavBackend,
- 'principals/user1',
- new Notifications\Notification\SystemStatus('foo','"1"')
- );
-
- $server = new DAV\Server(array($notification));
- $caldav = new Plugin();
-
- $server->httpRequest = new HTTP\Request(array(
- 'REQUEST_URI' => '/foo.xml',
- ));
- $httpResponse = new HTTP\ResponseMock();
- $server->httpResponse = $httpResponse;
-
- $server->addPlugin($caldav);
-
- $caldav->beforeMethod('GET','foo.xml');
-
- $this->assertEquals('HTTP/1.1 200 OK', $httpResponse->status);
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml',
- 'ETag' => '"1"',
- ), $httpResponse->headers);
-
- $expected =
-'<?xml version="1.0" encoding="UTF-8"?>
-<cs:notification xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/">
- <cs:systemstatus type="high"/>
-</cs:notification>
-';
-
- $this->assertEquals($expected, $httpResponse->body);
-
- }
-
- function testGETPassthrough() {
-
- $server = new DAV\Server();
- $caldav = new Plugin();
-
- $httpResponse = new HTTP\ResponseMock();
- $server->httpResponse = $httpResponse;
-
- $server->addPlugin($caldav);
+ /**
+ * @depends testSupportedReportSetPropertyNonCalendar
+ */
+ function testCalendarProperties() {
- $caldav->beforeMethod('GET','foo');
+ $ns = '{urn:ietf:params:xml:ns:caldav}';
+ $props = $this->server->getProperties('calendars/user1/UUID-123467', [
+ $ns . 'max-resource-size',
+ $ns . 'supported-calendar-data',
+ $ns . 'supported-collation-set',
+ ]);
- $this->assertNull($caldav->beforeMethod('GET','foozz'));
+ $this->assertEquals([
+ $ns . 'max-resource-size' => 10000000,
+ $ns . 'supported-calendar-data' => new Xml\Property\SupportedCalendarData(),
+ $ns . 'supported-collation-set' => new Xml\Property\SupportedCollationSet(),
+ ], $props);
}
-
}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Principal/UserTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Principal/UserTest.php
index d41692f2a..37b5eae97 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Principal/UserTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/Principal/UserTest.php
@@ -103,7 +103,7 @@ class UserTest extends \PHPUnit_Framework_TestCase {
$expected = array(
array(
'privilege' => '{DAV:}read',
- 'principal' => 'principals/user',
+ 'principal' => '{DAV:}authenticated',
'protected' => true,
),
array(
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Property/AllowedSharingModesTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Property/AllowedSharingModesTest.php
deleted file mode 100644
index 733ea1108..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Property/AllowedSharingModesTest.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Property;
-
-use Sabre\CalDAV;
-use Sabre\DAV;
-
-class AllowedSharingModesTest extends \PHPUnit_Framework_TestCase {
-
- function testSimple() {
-
- $sccs = new AllowedSharingModes(true,true);
-
- }
-
- /**
- * @depends testSimple
- */
- function testSerialize() {
-
- $property = new AllowedSharingModes(true,true);
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:root');
- $root->setAttribute('xmlns:d','DAV:');
- $root->setAttribute('xmlns:cal',CalDAV\Plugin::NS_CALDAV);
- $root->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER);
-
- $doc->appendChild($root);
- $server = new DAV\Server();
-
- $property->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:root xmlns:d="DAV:" xmlns:cal="' . CalDAV\Plugin::NS_CALDAV . '" xmlns:cs="' . CalDAV\Plugin::NS_CALENDARSERVER . '">' .
-'<cs:can-be-shared/>' .
-'<cs:can-be-published/>' .
-'</d:root>
-', $xml);
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Property/InviteTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Property/InviteTest.php
deleted file mode 100644
index 349a6e080..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Property/InviteTest.php
+++ /dev/null
@@ -1,196 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Property;
-
-use Sabre\CalDAV;
-use Sabre\DAV;
-
-class InviteTest extends \PHPUnit_Framework_TestCase {
-
- function testSimple() {
-
- $sccs = new Invite(array());
-
- }
-
- /**
- * @depends testSimple
- */
- function testSerialize() {
-
- $property = new Invite(array(
- array(
- 'href' => 'mailto:user1@example.org',
- 'status' => CalDAV\SharingPlugin::STATUS_ACCEPTED,
- 'readOnly' => false,
- ),
- array(
- 'href' => 'mailto:user2@example.org',
- 'commonName' => 'John Doe',
- 'status' => CalDAV\SharingPlugin::STATUS_DECLINED,
- 'readOnly' => true,
- ),
- array(
- 'href' => 'mailto:user3@example.org',
- 'commonName' => 'Joe Shmoe',
- 'status' => CalDAV\SharingPlugin::STATUS_NORESPONSE,
- 'readOnly' => true,
- 'summary' => 'Something, something',
- ),
- array(
- 'href' => 'mailto:user4@example.org',
- 'commonName' => 'Hoe Boe',
- 'status' => CalDAV\SharingPlugin::STATUS_INVALID,
- 'readOnly' => true,
- ),
- ), array(
- 'href' => 'mailto:thedoctor@example.org',
- 'commonName' => 'The Doctor',
- 'firstName' => 'The',
- 'lastName' => 'Doctor',
- ));
-
- $doc = new \DOMDocument();
- $doc->formatOutput = true;
- $root = $doc->createElement('d:root');
- $root->setAttribute('xmlns:d','DAV:');
- $root->setAttribute('xmlns:cal',CalDAV\Plugin::NS_CALDAV);
- $root->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER);
-
- $doc->appendChild($root);
- $server = new DAV\Server();
-
- $property->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:root xmlns:d="DAV:" xmlns:cal="' . CalDAV\Plugin::NS_CALDAV . '" xmlns:cs="' . CalDAV\Plugin::NS_CALENDARSERVER . '">
- <cs:organizer>
- <d:href>mailto:thedoctor@example.org</d:href>
- <cs:common-name>The Doctor</cs:common-name>
- <cs:first-name>The</cs:first-name>
- <cs:last-name>Doctor</cs:last-name>
- </cs:organizer>
- <cs:user>
- <d:href>mailto:user1@example.org</d:href>
- <cs:invite-accepted/>
- <cs:access>
- <cs:read-write/>
- </cs:access>
- </cs:user>
- <cs:user>
- <d:href>mailto:user2@example.org</d:href>
- <cs:common-name>John Doe</cs:common-name>
- <cs:invite-declined/>
- <cs:access>
- <cs:read/>
- </cs:access>
- </cs:user>
- <cs:user>
- <d:href>mailto:user3@example.org</d:href>
- <cs:common-name>Joe Shmoe</cs:common-name>
- <cs:invite-noresponse/>
- <cs:access>
- <cs:read/>
- </cs:access>
- <cs:summary>Something, something</cs:summary>
- </cs:user>
- <cs:user>
- <d:href>mailto:user4@example.org</d:href>
- <cs:common-name>Hoe Boe</cs:common-name>
- <cs:invite-invalid/>
- <cs:access>
- <cs:read/>
- </cs:access>
- </cs:user>
-</d:root>
-', $xml);
-
- }
-
- /**
- * @depends testSerialize
- */
- public function testUnserialize() {
-
- $input = array(
- array(
- 'href' => 'mailto:user1@example.org',
- 'status' => CalDAV\SharingPlugin::STATUS_ACCEPTED,
- 'readOnly' => false,
- 'commonName' => '',
- 'summary' => '',
- ),
- array(
- 'href' => 'mailto:user2@example.org',
- 'commonName' => 'John Doe',
- 'status' => CalDAV\SharingPlugin::STATUS_DECLINED,
- 'readOnly' => true,
- 'summary' => '',
- ),
- array(
- 'href' => 'mailto:user3@example.org',
- 'commonName' => 'Joe Shmoe',
- 'status' => CalDAV\SharingPlugin::STATUS_NORESPONSE,
- 'readOnly' => true,
- 'summary' => 'Something, something',
- ),
- array(
- 'href' => 'mailto:user4@example.org',
- 'commonName' => 'Hoe Boe',
- 'status' => CalDAV\SharingPlugin::STATUS_INVALID,
- 'readOnly' => true,
- 'summary' => '',
- ),
- );
-
- // Creating the xml
- $doc = new \DOMDocument();
- $doc->formatOutput = true;
- $root = $doc->createElement('d:root');
- $root->setAttribute('xmlns:d','DAV:');
- $root->setAttribute('xmlns:cal',CalDAV\Plugin::NS_CALDAV);
- $root->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER);
-
- $doc->appendChild($root);
- $server = new DAV\Server();
-
- $inputProperty = new Invite($input);
- $inputProperty->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- // Parsing it again
-
- $doc2 = DAV\XMLUtil::loadDOMDocument($xml);
-
- $outputProperty = Invite::unserialize($doc2->firstChild);
-
- $this->assertEquals($input, $outputProperty->getValue());
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception
- */
- function testUnserializeNoStatus() {
-
-$xml = '<?xml version="1.0"?>
-<d:root xmlns:d="DAV:" xmlns:cal="' . CalDAV\Plugin::NS_CALDAV . '" xmlns:cs="' . CalDAV\Plugin::NS_CALENDARSERVER . '">
- <cs:user>
- <d:href>mailto:user1@example.org</d:href>
- <!-- <cs:invite-accepted/> -->
- <cs:access>
- <cs:read-write/>
- </cs:access>
- </cs:user>
-</d:root>';
-
- $doc2 = DAV\XMLUtil::loadDOMDocument($xml);
- $outputProperty = Invite::unserialize($doc2->firstChild);
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Property/ScheduleCalendarTranspTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Property/ScheduleCalendarTranspTest.php
deleted file mode 100644
index 1ace0b18f..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Property/ScheduleCalendarTranspTest.php
+++ /dev/null
@@ -1,99 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Property;
-
-use Sabre\CalDAV;
-use Sabre\DAV;
-
-class ScheduleCalendarTranspTest extends \PHPUnit_Framework_TestCase {
-
- function testSimple() {
-
- $sccs = new ScheduleCalendarTransp('transparent');
- $this->assertEquals('transparent', $sccs->getValue());
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testBadArg() {
-
- $sccs = new ScheduleCalendarTransp('foo');
-
- }
-
- function values() {
-
- return array(
- array('transparent'),
- array('opaque'),
- );
-
- }
-
- /**
- * @depends testSimple
- * @dataProvider values
- */
- function testSerialize($value) {
-
- $property = new ScheduleCalendarTransp($value);
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:root');
- $root->setAttribute('xmlns:d','DAV:');
- $root->setAttribute('xmlns:cal',CalDAV\Plugin::NS_CALDAV);
-
- $doc->appendChild($root);
- $server = new DAV\Server();
-
- $property->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:root xmlns:d="DAV:" xmlns:cal="' . CalDAV\Plugin::NS_CALDAV . '">' .
-'<cal:' . $value . '/>' .
-'</d:root>
-', $xml);
-
- }
-
- /**
- * @depends testSimple
- * @dataProvider values
- */
- function testUnserializer($value) {
-
- $xml = '<?xml version="1.0"?>
-<d:root xmlns:d="DAV:" xmlns:cal="' . CalDAV\Plugin::NS_CALDAV . '">' .
-'<cal:'.$value.'/>' .
-'</d:root>';
-
- $dom = DAV\XMLUtil::loadDOMDocument($xml);
-
- $property = ScheduleCalendarTransp::unserialize($dom->firstChild);
-
- $this->assertTrue($property instanceof ScheduleCalendarTransp);
- $this->assertEquals($value, $property->getValue());
-
- }
-
- /**
- * @depends testSimple
- */
- function testUnserializerBadData() {
-
- $xml = '<?xml version="1.0"?>
-<d:root xmlns:d="DAV:" xmlns:cal="' . CalDAV\Plugin::NS_CALDAV . '">' .
-'<cal:foo/>' .
-'</d:root>';
-
- $dom = DAV\XMLUtil::loadDOMDocument($xml);
-
- $this->assertNull(ScheduleCalendarTransp::unserialize($dom->firstChild));
-
- }
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Property/SupportedCalendarComponentSetTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Property/SupportedCalendarComponentSetTest.php
deleted file mode 100644
index 3e5d5f5fc..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Property/SupportedCalendarComponentSetTest.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Property;
-
-class SupportedCalendarComponentSetTest extends \PHPUnit_Framework_TestCase {
-
- function testSimple() {
-
- $sccs = new SupportedCalendarComponentSet(array('VEVENT'));
- $this->assertEquals(array('VEVENT'), $sccs->getValue());
-
- }
-
- /**
- * @depends testSimple
- */
- function testSerialize() {
-
- $property = new SupportedCalendarComponentSet(array('VEVENT','VJOURNAL'));
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:root');
- $root->setAttribute('xmlns:d','DAV:');
- $root->setAttribute('xmlns:cal',\Sabre\CalDAV\Plugin::NS_CALDAV);
-
- $doc->appendChild($root);
- $server = new \Sabre\DAV\Server();
-
- $property->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:root xmlns:d="DAV:" xmlns:cal="' . \Sabre\CalDAV\Plugin::NS_CALDAV . '">' .
-'<cal:comp name="VEVENT"/>' .
-'<cal:comp name="VJOURNAL"/>' .
-'</d:root>
-', $xml);
-
- }
-
- /**
- * @depends testSimple
- */
- function testUnserializer() {
-
- $xml = '<?xml version="1.0"?>
-<d:root xmlns:d="DAV:" xmlns:cal="' . \Sabre\CalDAV\Plugin::NS_CALDAV . '">' .
-'<cal:comp name="VEVENT"/>' .
-'<cal:comp name="VJOURNAL"/>' .
-'</d:root>';
-
- $dom = \Sabre\DAV\XMLUtil::loadDOMDocument($xml);
-
- $property = SupportedCalendarComponentSet::unserialize($dom->firstChild);
-
- $this->assertTrue($property instanceof SupportedCalendarComponentSet);
- $this->assertEquals(array(
- 'VEVENT',
- 'VJOURNAL',
- ),
- $property->getValue());
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Property/SupportedCalendarDataTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Property/SupportedCalendarDataTest.php
deleted file mode 100644
index 3e016368c..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Property/SupportedCalendarDataTest.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Property;
-
-use Sabre\CalDAV;
-use Sabre\DAV;
-
-class SupportedCalendarDataTest extends \PHPUnit_Framework_TestCase {
-
- function testSimple() {
-
- $sccs = new SupportedCalendarData();
-
- }
-
- /**
- * @depends testSimple
- */
- function testSerialize() {
-
- $property = new SupportedCalendarData();
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:root');
- $root->setAttribute('xmlns:d','DAV:');
- $root->setAttribute('xmlns:cal',CalDAV\Plugin::NS_CALDAV);
-
- $doc->appendChild($root);
- $server = new DAV\Server();
-
- $property->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:root xmlns:d="DAV:" xmlns:cal="' . CalDAV\Plugin::NS_CALDAV . '">' .
-'<cal:calendar-data content-type="text/calendar" version="2.0"/>' .
-'</d:root>
-', $xml);
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Property/SupportedCollationSetTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Property/SupportedCollationSetTest.php
deleted file mode 100644
index 669e31553..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Property/SupportedCollationSetTest.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Property;
-
-use Sabre\CalDAV;
-use Sabre\DAV;
-
-class SupportedCollationSetTest extends \PHPUnit_Framework_TestCase {
-
- function testSimple() {
-
- $scs = new SupportedCollationSet();
-
- }
-
- /**
- * @depends testSimple
- */
- function testSerialize() {
-
- $property = new SupportedCollationSet();
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:root');
- $root->setAttribute('xmlns:d','DAV:');
- $root->setAttribute('xmlns:cal',CalDAV\Plugin::NS_CALDAV);
-
- $doc->appendChild($root);
- $server = new DAV\Server();
-
- $property->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:root xmlns:d="DAV:" xmlns:cal="' . CalDAV\Plugin::NS_CALDAV . '">' .
-'<cal:supported-collation>i;ascii-casemap</cal:supported-collation>' .
-'<cal:supported-collation>i;octet</cal:supported-collation>' .
-'<cal:supported-collation>i;unicode-casemap</cal:supported-collation>' .
-'</d:root>
-', $xml);
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Schedule/IMip/Mock.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Schedule/IMip/Mock.php
deleted file mode 100644
index ce0946dc8..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Schedule/IMip/Mock.php
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV\Schedule\IMip;
-
-/**
- * iMIP handler.
- *
- * This class is responsible for sending out iMIP messages. iMIP is the
- * email-based transport for iTIP. iTIP deals with scheduling operations for
- * iCalendar objects.
- *
- * If you want to customize the email that gets sent out, you can do so by
- * extending this class and overriding the sendMessage method.
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-class Mock extends \Sabre\CalDAV\Schedule\IMip {
-
- protected $emails = array();
-
- /**
- * This function is reponsible for sending the actual email.
- *
- * @param string $to Recipient email address
- * @param string $subject Subject of the email
- * @param string $body iCalendar body
- * @param array $headers List of headers
- * @return void
- */
- protected function mail($to, $subject, $body, array $headers) {
-
- $this->emails[] = array(
- 'to' => $to,
- 'subject' => $subject,
- 'body' => $body,
- 'headers' => $headers,
- );
-
- }
-
- public function getSentEmails() {
-
- return $this->emails;
-
- }
-
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/Schedule/OutboxTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/Schedule/OutboxTest.php
index 60ce9a2ad..933c7157b 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/Schedule/OutboxTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/Schedule/OutboxTest.php
@@ -20,6 +20,7 @@ class OutboxTest extends \PHPUnit_Framework_TestCase {
'principal' => 'principals/user1',
'protected' => true,
),
+
array(
'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-post-vevent',
'principal' => 'principals/user1',
@@ -30,6 +31,26 @@ class OutboxTest extends \PHPUnit_Framework_TestCase {
'principal' => 'principals/user1',
'protected' => true,
),
+ array(
+ 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-query-freebusy',
+ 'principal' => 'principals/user1/calendar-proxy-write',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-post-vevent',
+ 'principal' => 'principals/user1/calendar-proxy-write',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/user1/calendar-proxy-read',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/user1/calendar-proxy-write',
+ 'protected' => true,
+ ),
), $outbox->getACL());
$ok = false;
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/ShareableCalendarTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/ShareableCalendarTest.php
index 2f79351f1..15b869d50 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/ShareableCalendarTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/ShareableCalendarTest.php
@@ -15,10 +15,8 @@ class ShareableCalendarTest extends \PHPUnit_Framework_TestCase {
'id' => 1,
);
- $this->backend = new Backend\Mock(
- array($props),
- array(),
- array()
+ $this->backend = new Backend\MockSharing(
+ array($props)
);
$this->backend->updateShares(1, array(
array(
@@ -55,8 +53,8 @@ class ShareableCalendarTest extends \PHPUnit_Framework_TestCase {
function testPublish() {
- $this->instance->setPublishStatus(true);
- $this->instance->setPublishStatus(false);
+ $this->assertNull($this->instance->setPublishStatus(true));
+ $this->assertNull($this->instance->setPublishStatus(false));
}
}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/SharedCalendarTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/SharedCalendarTest.php
index 955831917..337b658f4 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/SharedCalendarTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/SharedCalendarTest.php
@@ -20,7 +20,7 @@ class SharedCalendarTest extends \PHPUnit_Framework_TestCase {
);
}
- $this->backend = new Backend\Mock(
+ $this->backend = new Backend\MockSharing(
array($props),
array(),
array()
@@ -64,18 +64,58 @@ class SharedCalendarTest extends \PHPUnit_Framework_TestCase {
'principal' => 'principals/owner',
'protected' => true,
),
+
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/owner/calendar-proxy-write',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/owner/calendar-proxy-read',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy',
+ 'principal' => '{DAV:}authenticated',
+ 'protected' => true,
+ ),
array(
'privilege' => '{DAV:}write',
'principal' => 'principals/owner',
'protected' => true,
),
array(
- 'privilege' => '{DAV:}read',
+ 'privilege' => '{DAV:}write',
'principal' => 'principals/owner/calendar-proxy-write',
'protected' => true,
),
array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/sharee',
+ 'protected' => true,
+ ),
+ array(
'privilege' => '{DAV:}write',
+ 'principal' => 'principals/sharee',
+ 'protected' => true,
+ ),
+ );
+
+ $this->assertEquals($expected, $this->getInstance()->getACL());
+
+ }
+
+ function testGetChildACL() {
+
+ $expected = array(
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/owner',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
'principal' => 'principals/owner/calendar-proxy-write',
'protected' => true,
),
@@ -85,8 +125,13 @@ class SharedCalendarTest extends \PHPUnit_Framework_TestCase {
'protected' => true,
),
array(
- 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy',
- 'principal' => '{DAV:}authenticated',
+ 'privilege' => '{DAV:}write',
+ 'principal' => 'principals/owner',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}write',
+ 'principal' => 'principals/owner/calendar-proxy-write',
'protected' => true,
),
array(
@@ -101,7 +146,43 @@ class SharedCalendarTest extends \PHPUnit_Framework_TestCase {
),
);
- $this->assertEquals($expected, $this->getInstance()->getACL());
+ $this->assertEquals($expected, $this->getInstance()->getChildACL());
+
+ }
+
+ function testGetChildACLReadOnly() {
+
+ $expected = array(
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/owner',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/owner/calendar-proxy-write',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/owner/calendar-proxy-read',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/sharee',
+ 'protected' => true,
+ ),
+ );
+
+ $props = array(
+ 'id' => 1,
+ '{http://calendarserver.org/ns/}shared-url' => 'calendars/owner/original',
+ '{http://sabredav.org/ns}owner-principal' => 'principals/owner',
+ '{http://sabredav.org/ns}read-only' => true,
+ 'principaluri' => 'principals/sharee',
+ );
+ $this->assertEquals($expected, $this->getInstance($props)->getChildACL());
}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/SharingPluginTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/SharingPluginTest.php
index 60a71fd7f..b4270da7d 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/SharingPluginTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/SharingPluginTest.php
@@ -33,7 +33,7 @@ class SharingPluginTest extends \Sabre\DAVServerTest {
'id' => 3,
'uri' => 'cal3',
),
- );
+ );
parent::setUp();
@@ -46,6 +46,10 @@ class SharingPluginTest extends \Sabre\DAVServerTest {
function testSimple() {
$this->assertInstanceOf('Sabre\\CalDAV\\SharingPlugin', $this->server->getPlugin('caldav-sharing'));
+ $this->assertEquals(
+ 'caldav-sharing',
+ $this->caldavSharingPlugin->getPluginInfo()['name']
+ );
}
@@ -58,14 +62,14 @@ class SharingPluginTest extends \Sabre\DAVServerTest {
function testBeforeGetShareableCalendar() {
// Forcing the server to authenticate:
- $this->authPlugin->beforeMethod('GET','');
+ $this->authPlugin->beforeMethod(new HTTP\Request(), new HTTP\Response());
$props = $this->server->getProperties('calendars/user1/cal1', array(
'{' . Plugin::NS_CALENDARSERVER . '}invite',
'{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes',
));
- $this->assertInstanceOf('Sabre\\CalDAV\\Property\\Invite', $props['{' . Plugin::NS_CALENDARSERVER . '}invite']);
- $this->assertInstanceOf('Sabre\\CalDAV\\Property\\AllowedSharingModes', $props['{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes']);
+ $this->assertInstanceOf('Sabre\\CalDAV\\Xml\\Property\\Invite', $props['{' . Plugin::NS_CALENDARSERVER . '}invite']);
+ $this->assertInstanceOf('Sabre\\CalDAV\\Xml\\Property\\AllowedSharingModes', $props['{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes']);
}
@@ -76,8 +80,8 @@ class SharingPluginTest extends \Sabre\DAVServerTest {
'{' . Plugin::NS_CALENDARSERVER . '}invite',
));
- $this->assertInstanceOf('Sabre\\CalDAV\\Property\\Invite', $props['{' . Plugin::NS_CALENDARSERVER . '}invite']);
- $this->assertInstanceOf('Sabre\\DAV\\Property\\IHref', $props['{' . Plugin::NS_CALENDARSERVER . '}shared-url']);
+ $this->assertInstanceOf('Sabre\\CalDAV\\Xml\\Property\\Invite', $props['{' . Plugin::NS_CALENDARSERVER . '}invite']);
+ $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $props['{' . Plugin::NS_CALENDARSERVER . '}shared-url']);
}
@@ -92,15 +96,12 @@ class SharingPluginTest extends \Sabre\DAVServerTest {
array()
);
$result = $this->server->updateProperties('calendars/user1/cal1', array(
- '{DAV:}resourcetype' => new DAV\Property\ResourceType(array('{DAV:}collection'))
+ '{DAV:}resourcetype' => new DAV\Xml\Property\ResourceType(['{DAV:}collection'])
));
- $this->assertEquals(array(
- 200 => array(
- '{DAV:}resourcetype' => null,
- ),
- 'href' => 'calendars/user1/cal1',
- ), $result);
+ $this->assertEquals([
+ '{DAV:}resourcetype' => 200
+ ], $result);
$this->assertEquals(0, count($this->caldavBackend->getShares(1)));
@@ -113,30 +114,27 @@ class SharingPluginTest extends \Sabre\DAVServerTest {
));
$this->assertEquals(array(
- 403 => array(
- '{DAV:}foo' => null,
- ),
- 'href' => 'calendars/user1/cal3',
+ '{DAV:}foo' => 403,
), $result);
}
function testUnknownMethodNoPOST() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PATCH',
'REQUEST_URI' => '/',
));
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 501 Not Implemented', $response->status, $response->body);
+ $this->assertEquals(501, $response->status, $response->body);
}
function testUnknownMethodNoXML() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/',
'CONTENT_TYPE' => 'text/plain',
@@ -144,13 +142,13 @@ class SharingPluginTest extends \Sabre\DAVServerTest {
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 501 Not Implemented', $response->status, $response->body);
+ $this->assertEquals(501, $response->status, $response->body);
}
function testUnknownMethodNoNode() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/foo',
'CONTENT_TYPE' => 'text/xml',
@@ -158,13 +156,13 @@ class SharingPluginTest extends \Sabre\DAVServerTest {
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 501 Not Implemented', $response->status, $response->body);
+ $this->assertEquals(501, $response->status, $response->body);
}
function testShareRequest() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/calendars/user1/cal1',
'CONTENT_TYPE' => 'text/xml',
@@ -187,7 +185,7 @@ RRR;
$request->setBody($xml);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 200 OK', $response->status, $response->body);
+ $this->assertEquals(200, $response->status, $response->body);
$this->assertEquals(array(array(
'href' => 'mailto:joe@example.org',
@@ -207,7 +205,7 @@ RRR;
function testShareRequestNoShareableCalendar() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/calendars/user1/cal2',
'CONTENT_TYPE' => 'text/xml',
@@ -229,13 +227,13 @@ RRR;
$request->setBody($xml);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 501 Not Implemented', $response->status, $response->body);
+ $this->assertEquals(501, $response->status, $response->body);
}
function testInviteReply() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/calendars/user1',
'CONTENT_TYPE' => 'text/xml',
@@ -250,13 +248,13 @@ RRR;
$request->setBody($xml);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 200 OK', $response->status, $response->body);
+ $this->assertEquals(200, $response->status, $response->body);
}
function testInviteBadXML() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/calendars/user1',
'CONTENT_TYPE' => 'text/xml',
@@ -268,13 +266,13 @@ RRR;
';
$request->setBody($xml);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 400 Bad request', $response->status, $response->body);
+ $this->assertEquals(400, $response->status, $response->body);
}
function testInviteWrongUrl() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/calendars/user1/cal1',
'CONTENT_TYPE' => 'text/xml',
@@ -287,7 +285,7 @@ RRR;
';
$request->setBody($xml);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 501 Not Implemented', $response->status, $response->body);
+ $this->assertEquals(501, $response->status, $response->body);
// If the plugin did not handle this request, it must ensure that the
// body is still accessible by other plugins.
@@ -297,7 +295,7 @@ RRR;
function testPublish() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/calendars/user1/cal1',
'CONTENT_TYPE' => 'text/xml',
@@ -310,13 +308,13 @@ RRR;
$request->setBody($xml);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 202 Accepted', $response->status, $response->body);
+ $this->assertEquals(202, $response->status, $response->body);
}
function testUnpublish() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/calendars/user1/cal1',
'CONTENT_TYPE' => 'text/xml',
@@ -329,13 +327,13 @@ RRR;
$request->setBody($xml);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 200 OK', $response->status, $response->body);
+ $this->assertEquals(200, $response->status, $response->body);
}
function testPublishWrongUrl() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/calendars/user1/cal2',
'CONTENT_TYPE' => 'text/xml',
@@ -348,13 +346,13 @@ RRR;
$request->setBody($xml);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 501 Not Implemented', $response->status, $response->body);
+ $this->assertEquals(501, $response->status, $response->body);
}
function testUnpublishWrongUrl() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/calendars/user1/cal2',
'CONTENT_TYPE' => 'text/xml',
@@ -367,13 +365,13 @@ RRR;
$request->setBody($xml);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 501 Not Implemented', $response->status, $response->body);
+ $this->assertEquals(501, $response->status, $response->body);
}
function testUnknownXmlDoc() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/calendars/user1/cal2',
'CONTENT_TYPE' => 'text/xml',
@@ -385,7 +383,7 @@ RRR;
$request->setBody($xml);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 501 Not Implemented', $response->status, $response->body);
+ $this->assertEquals(501, $response->status, $response->body);
}
}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/UserCalendarsSharedCalendarsTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/UserCalendarsSharedCalendarsTest.php
deleted file mode 100644
index 4c3bae3a4..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/UserCalendarsSharedCalendarsTest.php
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV;
-
-use Sabre\DAVACL;
-
-require_once 'Sabre/CalDAV/TestUtil.php';
-
-/**
- * @covers Sabre\CalDAV\UserCalendars
- */
-class UserCalendarsSharedCalendarsTest extends \PHPUnit_Framework_TestCase {
-
- protected $backend;
-
- function getInstance() {
-
- $calendars = array(
- array(
- 'id' => 1,
- 'principaluri' => 'principals/user1',
- ),
- array(
- 'id' => 2,
- '{http://calendarserver.org/ns/}shared-url' => 'calendars/owner/cal1',
- '{http://sabredav.org/ns}owner-principal' => 'principal/owner',
- '{http://sabredav.org/ns}read-only' => false,
- 'principaluri' => 'principals/user1',
- ),
- );
-
- $this->backend = new Backend\Mock(
- $calendars,
- array(),
- array()
- );
-
- return new UserCalendars($this->backend, array(
- 'uri' => 'principals/user1'
- ));
-
- }
-
- function testSimple() {
-
- $instance = $this->getInstance();
- $this->assertEquals('user1', $instance->getName());
-
- }
-
- function testGetChildren() {
-
- $instance = $this->getInstance();
- $children = $instance->getChildren();
- $this->assertEquals(4, count($children));
-
- // Testing if we got all the objects back.
- $hasShareable = false;
- $hasShared = false;
- $hasOutbox = false;
- $hasNotifications = false;
-
- foreach($children as $child) {
-
- if ($child instanceof IShareableCalendar) {
- $hasShareable = true;
- }
- if ($child instanceof ISharedCalendar) {
- $hasShared = true;
- }
- if ($child instanceof Schedule\IOutbox) {
- $hasOutbox = true;
- }
- if ($child instanceof Notifications\ICollection) {
- $hasNotifications = true;
- }
-
- }
- if (!$hasShareable) $this->fail('Missing node!');
- if (!$hasShared) $this->fail('Missing node!');
- if (!$hasOutbox) $this->fail('Missing node!');
- if (!$hasNotifications) $this->fail('Missing node!');
-
- }
-
- function testShareReply() {
-
- $instance = $this->getInstance();
- $instance->shareReply('uri', SharingPlugin::STATUS_DECLINED, 'curi', '1');
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/UserCalendarsTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/UserCalendarsTest.php
deleted file mode 100644
index 453c872e5..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/UserCalendarsTest.php
+++ /dev/null
@@ -1,207 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV;
-use Sabre\DAVACL;
-use Sabre\DAV;
-
-require_once 'Sabre/CalDAV/TestUtil.php';
-
-/**
- * @covers Sabre\CalDAV\UserCalendars
- */
-class UserCalendarsTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @var Sabre\CalDAV\UserCalendars
- */
- protected $usercalendars;
- /**
- * @var Sabre\CalDAV\Backend\PDO
- */
- protected $backend;
-
- function setup() {
-
- if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available');
- $this->backend = TestUtil::getBackend();
- $this->usercalendars = new UserCalendars($this->backend, array(
- 'uri' => 'principals/user1'
- ));
-
- }
-
- function testSimple() {
-
- $this->assertEquals('user1',$this->usercalendars->getName());
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\NotFound
- * @depends testSimple
- */
- function testGetChildNotFound() {
-
- $this->usercalendars->getChild('randomname');
-
- }
-
- function testChildExists() {
-
- $this->assertFalse($this->usercalendars->childExists('foo'));
- $this->assertTrue($this->usercalendars->childExists('UUID-123467'));
-
- }
-
- function testGetOwner() {
-
- $this->assertEquals('principals/user1', $this->usercalendars->getOwner());
-
- }
-
- function testGetGroup() {
-
- $this->assertNull($this->usercalendars->getGroup());
-
- }
-
- function testGetACL() {
-
- $expected = array(
- array(
- 'privilege' => '{DAV:}read',
- 'principal' => 'principals/user1',
- 'protected' => true,
- ),
- array(
- 'privilege' => '{DAV:}write',
- 'principal' => 'principals/user1',
- 'protected' => true,
- ),
- array(
- 'privilege' => '{DAV:}read',
- 'principal' => 'principals/user1/calendar-proxy-write',
- 'protected' => true,
- ),
- array(
- 'privilege' => '{DAV:}write',
- 'principal' => 'principals/user1/calendar-proxy-write',
- 'protected' => true,
- ),
- array(
- 'privilege' => '{DAV:}read',
- 'principal' => 'principals/user1/calendar-proxy-read',
- 'protected' => true,
- ),
- );
- $this->assertEquals($expected, $this->usercalendars->getACL());
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\MethodNotAllowed
- */
- function testSetACL() {
-
- $this->usercalendars->setACL(array());
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\Forbidden
- * @depends testSimple
- */
- function testSetName() {
-
- $this->usercalendars->setName('bla');
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\Forbidden
- * @depends testSimple
- */
- function testDelete() {
-
- $this->usercalendars->delete();
-
- }
-
- /**
- * @depends testSimple
- */
- function testGetLastModified() {
-
- $this->assertNull($this->usercalendars->getLastModified());
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\MethodNotAllowed
- * @depends testSimple
- */
- function testCreateFile() {
-
- $this->usercalendars->createFile('bla');
-
- }
-
-
- /**
- * @expectedException Sabre\DAV\Exception\MethodNotAllowed
- * @depends testSimple
- */
- function testCreateDirectory() {
-
- $this->usercalendars->createDirectory('bla');
-
- }
-
- /**
- * @depends testSimple
- */
- function testCreateExtendedCollection() {
-
- $result = $this->usercalendars->createExtendedCollection('newcalendar', array('{DAV:}collection', '{urn:ietf:params:xml:ns:caldav}calendar'), array());
- $this->assertNull($result);
- $cals = $this->backend->getCalendarsForUser('principals/user1');
- $this->assertEquals(3,count($cals));
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\InvalidResourceType
- * @depends testSimple
- */
- function testCreateExtendedCollectionBadResourceType() {
-
- $this->usercalendars->createExtendedCollection('newcalendar', array('{DAV:}collection','{DAV:}blabla'), array());
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\InvalidResourceType
- * @depends testSimple
- */
- function testCreateExtendedCollectionNotACalendar() {
-
- $this->usercalendars->createExtendedCollection('newcalendar', array('{DAV:}collection'), array());
-
- }
-
- function testGetSupportedPrivilegesSet() {
-
- $this->assertNull($this->usercalendars->getSupportedPrivilegeSet());
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\NotImplemented
- */
- function testShareReplyFail() {
-
- $this->usercalendars->shareReply('uri', SharingPlugin::STATUS_DECLINED, 'curi', '1');
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/ValidateICalTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/ValidateICalTest.php
index 6634b9c3b..be166d9e6 100644
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/ValidateICalTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CalDAV/ValidateICalTest.php
@@ -26,24 +26,25 @@ class ValidateICalTest extends \PHPUnit_Framework_TestCase {
'id' => 'calendar1',
'principaluri' => 'principals/admin',
'uri' => 'calendar1',
- '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Property\SupportedCalendarComponentSet( array('VEVENT','VTODO','VJOURNAL') ),
+ '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Xml\Property\SupportedCalendarComponentSet( ['VEVENT','VTODO','VJOURNAL'] ),
),
array(
'id' => 'calendar2',
'principaluri' => 'principals/admin',
'uri' => 'calendar2',
- '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Property\SupportedCalendarComponentSet( array('VTODO','VJOURNAL') ),
+ '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Xml\Property\SupportedCalendarComponentSet( ['VTODO','VJOURNAL'] ),
)
);
- $this->calBackend = new Backend\Mock($calendars,array());
+ $this->calBackend = new Backend\Mock($calendars, []);
$principalBackend = new DAVACL\PrincipalBackend\Mock();
- $tree = array(
- new CalendarRootNode($principalBackend, $this->calBackend),
- );
+ $tree = [
+ new CalendarRoot($principalBackend, $this->calBackend),
+ ];
$this->server = new DAV\Server($tree);
+ $this->server->sapi = new HTTP\SapiMock();
$this->server->debugExceptions = true;
$plugin = new Plugin();
@@ -65,20 +66,20 @@ class ValidateICalTest extends \PHPUnit_Framework_TestCase {
function testCreateFile() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics',
));
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 415 Unsupported Media Type', $response->status);
+ $this->assertEquals(415, $response->status);
}
function testCreateFileValid() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics',
));
@@ -86,11 +87,18 @@ class ValidateICalTest extends \PHPUnit_Framework_TestCase {
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 201 Created', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+ $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+ $this->assertEquals(array(
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Length' => ['0'],
+ 'ETag' => ['"' . md5("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n") . '"'],
+ ), $response->getHeaders());
+
$expected = array(
'uri' => 'blabla.ics',
'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
'calendarid' => 'calendar1',
+ 'lastmodified' => null,
);
$this->assertEquals($expected, $this->calBackend->getCalendarObject('calendar1','blabla.ics'));
@@ -99,7 +107,7 @@ class ValidateICalTest extends \PHPUnit_Framework_TestCase {
function testCreateFileNoComponents() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics',
));
@@ -107,13 +115,13 @@ class ValidateICalTest extends \PHPUnit_Framework_TestCase {
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 400 Bad request', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+ $this->assertEquals(400, $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
}
function testCreateFileNoUID() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics',
));
@@ -121,13 +129,13 @@ class ValidateICalTest extends \PHPUnit_Framework_TestCase {
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 400 Bad request', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+ $this->assertEquals(400, $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
}
function testCreateFileVCard() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics',
));
@@ -135,13 +143,13 @@ class ValidateICalTest extends \PHPUnit_Framework_TestCase {
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 415 Unsupported Media Type', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+ $this->assertEquals(415, $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
}
function testCreateFile2Components() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics',
));
@@ -149,13 +157,13 @@ class ValidateICalTest extends \PHPUnit_Framework_TestCase {
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 400 Bad request', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+ $this->assertEquals(400, $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
}
function testCreateFile2UIDS() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics',
));
@@ -163,13 +171,13 @@ class ValidateICalTest extends \PHPUnit_Framework_TestCase {
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 400 Bad request', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+ $this->assertEquals(400, $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
}
function testCreateFileWrongComponent() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics',
));
@@ -177,28 +185,28 @@ class ValidateICalTest extends \PHPUnit_Framework_TestCase {
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 400 Bad request', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+ $this->assertEquals(400, $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
}
function testUpdateFile() {
$this->calBackend->createCalendarObject('calendar1','blabla.ics','foo');
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics',
));
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 415 Unsupported Media Type', $response->status);
+ $this->assertEquals(415, $response->status);
}
function testUpdateFileParsableBody() {
$this->calBackend->createCalendarObject('calendar1','blabla.ics','foo');
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics',
));
@@ -207,12 +215,13 @@ class ValidateICalTest extends \PHPUnit_Framework_TestCase {
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 204 No Content', $response->status);
+ $this->assertEquals(204, $response->status);
$expected = array(
'uri' => 'blabla.ics',
'calendardata' => $body,
'calendarid' => 'calendar1',
+ 'lastmodified' => null,
);
$this->assertEquals($expected, $this->calBackend->getCalendarObject('calendar1','blabla.ics'));
@@ -221,7 +230,7 @@ class ValidateICalTest extends \PHPUnit_Framework_TestCase {
function testCreateFileInvalidComponent() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/calendars/admin/calendar2/blabla.ics',
));
@@ -229,14 +238,14 @@ class ValidateICalTest extends \PHPUnit_Framework_TestCase {
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 403 Forbidden', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+ $this->assertEquals(403, $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
}
function testUpdateFileInvalidComponent() {
$this->calBackend->createCalendarObject('calendar2','blabla.ics','foo');
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/calendars/admin/calendar2/blabla.ics',
));
@@ -244,7 +253,29 @@ class ValidateICalTest extends \PHPUnit_Framework_TestCase {
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 403 Forbidden', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+ $this->assertEquals(403, $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+
+ }
+
+ /**
+ * What we are testing here, is if we send in a latin1 character, the
+ * server should automatically transform this into UTF-8.
+ *
+ * More importantly. If any transformation happens, the etag must no longer
+ * be returned by the server.
+ */
+ function testCreateFileModified() {
+
+ $request = HTTP\Sapi::createFromServerArray(array(
+ 'REQUEST_METHOD' => 'PUT',
+ 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics',
+ ));
+ $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nSUMMARY:Meeting in M\xfcnster\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n");
+
+ $response = $this->request($request);
+
+ $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+ $this->assertNull($response->getHeader('ETag'));
}
}
diff --git a/vendor/sabre/dav/tests/Sabre/CalDAV/VersionTest.php b/vendor/sabre/dav/tests/Sabre/CalDAV/VersionTest.php
deleted file mode 100644
index a4e093e35..000000000
--- a/vendor/sabre/dav/tests/Sabre/CalDAV/VersionTest.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-namespace Sabre\CalDAV;
-
-class VersionTest extends \PHPUnit_Framework_TestCase {
-
- function testString() {
-
- $v = Version::VERSION;
- $this->assertEquals(-1, version_compare('1.0.0',$v));
-
- $s = Version::STABILITY;
- $this->assertTrue($s == 'alpha' || $s == 'beta' || $s =='stable');
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/AbstractPluginTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/AbstractPluginTest.php
index 94081fc8b..a123099a0 100644
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/AbstractPluginTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CardDAV/AbstractPluginTest.php
@@ -4,6 +4,7 @@ namespace Sabre\CardDAV;
use Sabre\DAV;
use Sabre\DAVACL;
+use Sabre\HTTP;
abstract class AbstractPluginTest extends \PHPUnit_Framework_TestCase {
@@ -33,6 +34,7 @@ abstract class AbstractPluginTest extends \PHPUnit_Framework_TestCase {
$this->plugin = new Plugin();
$this->plugin->directories = array('directory');
$this->server = new DAV\Server($tree);
+ $this->server->sapi = new HTTP\SapiMock();
$this->server->addPlugin($this->plugin);
$this->server->debugExceptions = true;
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookQueryParserTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookQueryParserTest.php
deleted file mode 100644
index 51bea6c6c..000000000
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookQueryParserTest.php
+++ /dev/null
@@ -1,329 +0,0 @@
-<?php
-
-namespace Sabre\CardDAV;
-
-use Sabre\DAV;
-
-class AddressBookQueryParserTest extends \PHPUnit_Framework_TestCase {
-
- function parse($xml) {
-
- $xml = implode("\n", $xml);
- $dom = DAV\XMLUtil::loadDOMDocument($xml);
-
- $q = new AddressBookQueryParser($dom);
- $q->parse();
- return $q;
-
- }
-
- function testFilterBasic() {
-
- $xml = array(
- '<?xml version="1.0"?>',
- '<c:addressbook-query xmlns:c="urn:ietf:params:xml:ns:carddav" xmlns:d="DAV:">',
- ' <d:prop>',
- ' <d:foo />',
- ' </d:prop>',
- ' <c:filter>',
- ' <c:prop-filter name="NICKNAME" />',
- ' </c:filter>',
- '</c:addressbook-query>'
- );
-
- $q = $this->parse($xml);
-
- $this->assertEquals(
- array('{DAV:}foo'),
- $q->requestedProperties
- );
-
- $this->assertEquals(
- array(
- array(
- 'name' => 'NICKNAME',
- 'test' => 'anyof',
- 'is-not-defined' => false,
- 'param-filters' => array(),
- 'text-matches' => array(),
- ),
- ),
- $q->filters
- );
-
- $this->assertNull($q->limit);
- $this->assertEquals('anyof', $q->test);
-
- }
-
- function testNoFilter() {
-
- // This is non-standard, but helps working around a KDE bug
- $xml = array(
- '<?xml version="1.0"?>',
- '<c:addressbook-query xmlns:c="urn:ietf:params:xml:ns:carddav" xmlns:d="DAV:">',
- ' <d:prop>',
- ' <d:foo />',
- ' </d:prop>',
- '</c:addressbook-query>'
- );
-
- $q = $this->parse($xml);
-
- $this->assertEquals(
- array('{DAV:}foo'),
- $q->requestedProperties
- );
-
- $this->assertEquals(
- array(),
- $q->filters
- );
-
- $this->assertNull($q->limit);
- $this->assertEquals('anyof', $q->test);
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testFilterDoubleFilter() {
-
- $xml = array(
- '<?xml version="1.0"?>',
- '<c:addressbook-query xmlns:c="urn:ietf:params:xml:ns:carddav" xmlns:d="DAV:">',
- ' <d:prop>',
- ' <d:foo />',
- ' </d:prop>',
- ' <c:filter>',
- ' <c:prop-filter name="NICKNAME" />',
- ' </c:filter>',
- ' <c:filter>',
- ' <c:prop-filter name="NICKNAME" />',
- ' </c:filter>',
- '</c:addressbook-query>'
- );
-
- $q = $this->parse($xml);
-
- }
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testFilterCorruptTest() {
-
- $xml = array(
- '<?xml version="1.0"?>',
- '<c:addressbook-query xmlns:c="urn:ietf:params:xml:ns:carddav" xmlns:d="DAV:">',
- ' <d:prop>',
- ' <d:foo />',
- ' </d:prop>',
- ' <c:filter test="foo">',
- ' <c:prop-filter name="NICKNAME" />',
- ' </c:filter>',
- '</c:addressbook-query>'
- );
-
- $q = $this->parse($xml);
-
- }
-
- function testPropFilter() {
-
- $xml = array(
- '<?xml version="1.0"?>',
- '<c:addressbook-query xmlns:c="urn:ietf:params:xml:ns:carddav" xmlns:d="DAV:">',
- ' <d:prop>',
- ' <d:foo />',
- ' </d:prop>',
- ' <c:filter test="allof">',
- ' <c:prop-filter name="NICKNAME" />',
- ' <c:prop-filter name="EMAIL" test="allof" />',
- ' <c:prop-filter name="FN">',
- ' <c:is-not-defined />',
- ' </c:prop-filter>',
- ' </c:filter>',
- ' <c:limit><c:nresults>4</c:nresults></c:limit>',
- '</c:addressbook-query>'
- );
-
- $q = $this->parse($xml);
-
- $this->assertEquals(
- array(
- array(
- 'name' => 'NICKNAME',
- 'test' => 'anyof',
- 'is-not-defined' => false,
- 'param-filters' => array(),
- 'text-matches' => array(),
- ),
- array(
- 'name' => 'EMAIL',
- 'test' => 'allof',
- 'is-not-defined' => false,
- 'param-filters' => array(),
- 'text-matches' => array(),
- ),
- array(
- 'name' => 'FN',
- 'test' => 'anyof',
- 'is-not-defined' => true,
- 'param-filters' => array(),
- 'text-matches' => array(),
- ),
- ),
- $q->filters
- );
-
- $this->assertEquals(4,$q->limit);
- $this->assertEquals('allof', $q->test);
-
- }
-
- function testParamFilter() {
-
- $xml = array(
- '<?xml version="1.0"?>',
- '<c:addressbook-query xmlns:c="urn:ietf:params:xml:ns:carddav" xmlns:d="DAV:">',
- ' <d:prop>',
- ' <d:foo />',
- ' </d:prop>',
- ' <c:filter>',
- ' <c:prop-filter name="NICKNAME">',
- ' <c:param-filter name="BLA" />',
- ' <c:param-filter name="BLA2">',
- ' <c:is-not-defined />',
- ' </c:param-filter>',
- ' </c:prop-filter>',
- ' </c:filter>',
- '</c:addressbook-query>'
- );
-
- $q = $this->parse($xml);
-
- $this->assertEquals(
- array(
- array(
- 'name' => 'NICKNAME',
- 'test' => 'anyof',
- 'is-not-defined' => false,
- 'param-filters' => array(
- array(
- 'name' => 'BLA',
- 'is-not-defined' => false,
- 'text-match' => null
- ),
- array(
- 'name' => 'BLA2',
- 'is-not-defined' => true,
- 'text-match' => null
- ),
- ),
- 'text-matches' => array(),
- ),
- ),
- $q->filters
- );
-
- }
-
- function testTextMatch() {
-
- $xml = array(
- '<?xml version="1.0"?>',
- '<c:addressbook-query xmlns:c="urn:ietf:params:xml:ns:carddav" xmlns:d="DAV:">',
- ' <d:prop>',
- ' <d:foo />',
- ' </d:prop>',
- ' <c:filter>',
- ' <c:prop-filter name="NICKNAME">',
- ' <c:text-match>evert</c:text-match>',
- ' <c:text-match collation="i;octet">evert</c:text-match>',
- ' <c:text-match negate-condition="yes">rene</c:text-match>',
- ' <c:text-match match-type="starts-with">e</c:text-match>',
- ' <c:param-filter name="BLA">',
- ' <c:text-match>foo</c:text-match>',
- ' </c:param-filter>',
- ' </c:prop-filter>',
- ' </c:filter>',
- '</c:addressbook-query>'
- );
-
- $q = $this->parse($xml);
-
- $this->assertEquals(
- array(
- array(
- 'name' => 'NICKNAME',
- 'test' => 'anyof',
- 'is-not-defined' => false,
- 'param-filters' => array(
- array(
- 'name' => 'BLA',
- 'is-not-defined' => false,
- 'text-match' => array(
- 'negate-condition' => false,
- 'collation' => 'i;unicode-casemap',
- 'match-type' => 'contains',
- 'value' => 'foo',
- ),
- ),
- ),
- 'text-matches' => array(
- array(
- 'negate-condition' => false,
- 'collation' => 'i;unicode-casemap',
- 'match-type' => 'contains',
- 'value' => 'evert',
- ),
- array(
- 'negate-condition' => false,
- 'collation' => 'i;octet',
- 'match-type' => 'contains',
- 'value' => 'evert',
- ),
- array(
- 'negate-condition' => true,
- 'collation' => 'i;unicode-casemap',
- 'match-type' => 'contains',
- 'value' => 'rene',
- ),
- array(
- 'negate-condition' => false,
- 'collation' => 'i;unicode-casemap',
- 'match-type' => 'starts-with',
- 'value' => 'e',
- ),
- ),
- ),
- ),
- $q->filters
- );
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testBadTextMatch() {
-
- $xml = array(
- '<?xml version="1.0"?>',
- '<c:addressbook-query xmlns:c="urn:ietf:params:xml:ns:carddav" xmlns:d="DAV:">',
- ' <d:prop>',
- ' <d:foo />',
- ' </d:prop>',
- ' <c:filter>',
- ' <c:prop-filter name="NICKNAME">',
- ' <c:text-match match-type="foo">evert</c:text-match>',
- ' </c:prop-filter>',
- ' </c:filter>',
- '</c:addressbook-query>'
- );
-
- $q = $this->parse($xml);
-
- }
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookQueryTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookQueryTest.php
index c79f7e877..478f6beb5 100644
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookQueryTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookQueryTest.php
@@ -12,7 +12,7 @@ class AddressBookQueryTest extends AbstractPluginTest {
function testQuery() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'REPORT',
'REQUEST_URI' => '/addressbooks/user1/book1',
'HTTP_DEPTH' => '1',
@@ -37,7 +37,7 @@ class AddressBookQueryTest extends AbstractPluginTest {
$this->server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status', $response->status, 'Incorrect status code. Full response body:' . $response->body);
+ $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body);
// using the client for parsing
$client = new DAV\Client(array('baseUri'=>'/'));
@@ -62,7 +62,7 @@ class AddressBookQueryTest extends AbstractPluginTest {
function testQueryDepth0() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'REPORT',
'REQUEST_URI' => '/addressbooks/user1/book1/card1',
'HTTP_DEPTH' => '0',
@@ -87,7 +87,7 @@ class AddressBookQueryTest extends AbstractPluginTest {
$this->server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status', $response->status, 'Incorrect status code. Full response body:' . $response->body);
+ $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body);
// using the client for parsing
$client = new DAV\Client(array('baseUri'=>'/'));
@@ -107,7 +107,7 @@ class AddressBookQueryTest extends AbstractPluginTest {
function testQueryNoMatch() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'REPORT',
'REQUEST_URI' => '/addressbooks/user1/book1',
'HTTP_DEPTH' => '1',
@@ -132,7 +132,7 @@ class AddressBookQueryTest extends AbstractPluginTest {
$this->server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status', $response->status, 'Incorrect status code. Full response body:' . $response->body);
+ $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body);
// using the client for parsing
$client = new DAV\Client(array('baseUri'=>'/'));
@@ -145,7 +145,7 @@ class AddressBookQueryTest extends AbstractPluginTest {
function testQueryLimit() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'REPORT',
'REQUEST_URI' => '/addressbooks/user1/book1',
'HTTP_DEPTH' => '1',
@@ -171,7 +171,7 @@ class AddressBookQueryTest extends AbstractPluginTest {
$this->server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status', $response->status, 'Incorrect status code. Full response body:' . $response->body);
+ $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body);
// using the client for parsing
$client = new DAV\Client(array('baseUri'=>'/'));
@@ -189,4 +189,122 @@ class AddressBookQueryTest extends AbstractPluginTest {
}
+ function testJson() {
+
+ $request = new HTTP\Request(
+ 'REPORT',
+ '/addressbooks/user1/book1/card1',
+ ['Depth' => '0']
+ );
+
+ $request->setBody(
+'<?xml version="1.0"?>
+<c:addressbook-query xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:carddav">
+ <d:prop>
+ <c:address-data content-type="application/vcard+json" />
+ <d:getetag />
+ </d:prop>
+</c:addressbook-query>'
+ );
+
+ $response = new HTTP\ResponseMock();
+
+ $this->server->httpRequest = $request;
+ $this->server->httpResponse = $response;
+
+ $this->server->exec();
+
+ $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body);
+
+ // using the client for parsing
+ $client = new DAV\Client(array('baseUri'=>'/'));
+
+ $result = $client->parseMultiStatus($response->body);
+
+ $vobjVersion = \Sabre\VObject\Version::VERSION;
+
+ $this->assertEquals(array(
+ '/addressbooks/user1/book1/card1' => array(
+ 200 => array(
+ '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD"). '"',
+ '{urn:ietf:params:xml:ns:carddav}address-data' => '["vcard",[["version",{},"text","4.0"],["prodid",{},"text","-\/\/Sabre\/\/Sabre VObject ' . $vobjVersion . '\/\/EN"],["uid",{},"text","12345"]]]',
+ ),
+ ),
+ ), $result);
+
+ }
+
+ function testVCard4() {
+
+ $request = new HTTP\Request(
+ 'REPORT',
+ '/addressbooks/user1/book1/card1',
+ ['Depth' => '0']
+ );
+
+ $request->setBody(
+'<?xml version="1.0"?>
+<c:addressbook-query xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:carddav">
+ <d:prop>
+ <c:address-data content-type="text/vcard" version="4.0" />
+ <d:getetag />
+ </d:prop>
+</c:addressbook-query>'
+ );
+
+ $response = new HTTP\ResponseMock();
+
+ $this->server->httpRequest = $request;
+ $this->server->httpResponse = $response;
+
+ $this->server->exec();
+
+ $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body);
+
+ // using the client for parsing
+ $client = new DAV\Client(array('baseUri'=>'/'));
+
+ $result = $client->parseMultiStatus($response->body);
+
+ $vobjVersion = \Sabre\VObject\Version::VERSION;
+
+ $this->assertEquals(array(
+ '/addressbooks/user1/book1/card1' => array(
+ 200 => array(
+ '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD"). '"',
+ '{urn:ietf:params:xml:ns:carddav}address-data' => "BEGIN:VCARD\r\nVERSION:4.0\r\nPRODID:-//Sabre//Sabre VObject $vobjVersion//EN\r\nUID:12345\r\nEND:VCARD\r\n",
+ ),
+ ),
+ ), $result);
+
+ }
+
+ function testAddressBookDepth0() {
+
+ $request = new HTTP\Request(
+ 'REPORT',
+ '/addressbooks/user1/book1',
+ ['Depth' => '0']
+ );
+
+ $request->setBody(
+'<?xml version="1.0"?>
+<c:addressbook-query xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:carddav">
+ <d:prop>
+ <c:address-data content-type="application/vcard+json" />
+ <d:getetag />
+ </d:prop>
+</c:addressbook-query>'
+ );
+
+ $response = new HTTP\ResponseMock();
+
+ $this->server->httpRequest = $request;
+ $this->server->httpResponse = $response;
+
+ $this->server->exec();
+
+ $this->assertEquals(415, $response->status, 'Incorrect status code. Full response body:' . $response->body);
+
+ }
}
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookRootTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookRootTest.php
index 6eaff5db0..fc20480f2 100644
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookRootTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookRootTest.php
@@ -24,7 +24,7 @@ class AddressBookRootTest extends \PHPUnit_Framework_TestCase {
$children = $root->getChildren();
$this->assertEquals(3, count($children));
- $this->assertInstanceOf('Sabre\\CardDAV\\UserAddressBooks', $children[0]);
+ $this->assertInstanceOf('Sabre\\CardDAV\\AddressBookHome', $children[0]);
$this->assertEquals('user1', $children[0]->getName());
}
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookTest.php
index aac749b37..fe8ba9025 100644
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CardDAV/AddressBookTest.php
@@ -2,6 +2,7 @@
namespace Sabre\CardDAV;
+use Sabre\DAV\PropPatch;
require_once 'Sabre/CardDAV/Backend/Mock.php';
@@ -105,9 +106,11 @@ class AddressBookTest extends \PHPUnit_Framework_TestCase {
function testUpdateProperties() {
- $this->assertTrue(
- $this->ab->updateProperties(array('{DAV:}displayname' => 'barrr'))
- );
+ $propPatch = new PropPatch([
+ '{DAV:}displayname' => 'barrr',
+ ]);
+ $this->ab->propPatch($propPatch);
+ $this->assertTrue($propPatch->commit());
$this->assertEquals('barrr', $this->backend->addressBooks[0]['{DAV:}displayname']);
@@ -158,5 +161,52 @@ class AddressBookTest extends \PHPUnit_Framework_TestCase {
}
+ function testGetSyncTokenNoSyncSupport() {
+
+ $this->assertNull($this->ab->getSyncToken());
+
+ }
+ function testGetChangesNoSyncSupport() {
+
+ $this->assertNull($this->ab->getChanges(1,null));
+
+ }
+
+ function testGetSyncToken() {
+
+ if (!SABRE_HASSQLITE) {
+ $this->markTestSkipped('Sqlite is required for this test to run');
+ }
+ $ab = new AddressBook(TestUtil::getBackend(), [ 'id' => 1, '{DAV:}sync-token' => 2]);
+ $this->assertEquals(2, $ab->getSyncToken());
+ TestUtil::deleteSQLiteDB();
+ }
+
+ function testGetSyncToken2() {
+
+ if (!SABRE_HASSQLITE) {
+ $this->markTestSkipped('Sqlite is required for this test to run');
+ }
+ $ab = new AddressBook(TestUtil::getBackend(), [ 'id' => 1, '{http://sabredav.org/ns}sync-token' => 2]);
+ $this->assertEquals(2, $ab->getSyncToken());
+ TestUtil::deleteSQLiteDB();
+ }
+
+ function testGetChanges() {
+
+ if (!SABRE_HASSQLITE) {
+ $this->markTestSkipped('Sqlite is required for this test to run');
+ }
+ $ab = new AddressBook(TestUtil::getBackend(), [ 'id' => 1, '{DAV:}sync-token' => 2]);
+ $this->assertEquals([
+ 'syncToken' => 2,
+ 'modified' => [],
+ 'deleted' => [],
+ 'added' => ['UUID-2345'],
+ ], $ab->getChanges(1, 1));
+ TestUtil::deleteSQLiteDB();
+
+ }
+
}
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/AbstractPDOTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/AbstractPDOTest.php
index 623188d32..d2ec278be 100644
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/AbstractPDOTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/AbstractPDOTest.php
@@ -3,6 +3,7 @@
namespace Sabre\CardDAV\Backend;
use Sabre\CardDAV;
+use Sabre\DAV\PropPatch;
abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
@@ -19,7 +20,10 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
public function setUp() {
- $this->backend = new PDO($this->getPDO());
+ $pdo = $this->getPDO();
+ $this->backend = new PDO($pdo);
+ $pdo->exec('INSERT INTO addressbooks (principaluri, displayname, uri, description, synctoken) VALUES ("principals/user1", "book1", "book1", "addressbook 1", 1)');
+ $pdo->exec('INSERT INTO cards (addressbookid, carddata, uri, lastmodified, etag, size) VALUES (1, "card1", "card1", 0, "' . md5('card1') . '", 5)');
}
@@ -35,7 +39,7 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
'{DAV:}displayname' => 'book1',
'{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1',
'{http://calendarserver.org/ns/}getctag' => 1,
- '{' . CardDAV\Plugin::NS_CARDDAV . '}supported-address-data' => new CardDAV\Property\SupportedAddressData(),
+ '{http://sabredav.org/ns}sync-token' => 1
)
);
@@ -45,11 +49,14 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
public function testUpdateAddressBookInvalidProp() {
- $result = $this->backend->updateAddressBook(1, array(
+ $propPatch = new PropPatch([
'{DAV:}displayname' => 'updated',
'{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'updated',
'{DAV:}foo' => 'bar',
- ));
+ ]);
+
+ $this->backend->updateAddressBook(1, $propPatch);
+ $result = $propPatch->commit();
$this->assertFalse($result);
@@ -63,7 +70,7 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
'{DAV:}displayname' => 'book1',
'{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1',
'{http://calendarserver.org/ns/}getctag' => 1,
- '{' . CardDAV\Plugin::NS_CARDDAV . '}supported-address-data' => new CardDAV\Property\SupportedAddressData(),
+ '{http://sabredav.org/ns}sync-token' => 1
)
);
@@ -73,9 +80,12 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
public function testUpdateAddressBookNoProps() {
- $result = $this->backend->updateAddressBook(1, array());
+ $propPatch = new PropPatch([
+ ]);
- $this->assertFalse($result);
+ $this->backend->updateAddressBook(1, $propPatch);
+ $result = $propPatch->commit();
+ $this->assertTrue($result);
$result = $this->backend->getAddressBooksForUser('principals/user1');
@@ -87,7 +97,7 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
'{DAV:}displayname' => 'book1',
'{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1',
'{http://calendarserver.org/ns/}getctag' => 1,
- '{' . CardDAV\Plugin::NS_CARDDAV . '}supported-address-data' => new CardDAV\Property\SupportedAddressData(),
+ '{http://sabredav.org/ns}sync-token' => 1
)
);
@@ -98,10 +108,13 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
public function testUpdateAddressBookSuccess() {
- $result = $this->backend->updateAddressBook(1, array(
+ $propPatch = new PropPatch([
'{DAV:}displayname' => 'updated',
'{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'updated',
- ));
+ ]);
+
+ $this->backend->updateAddressBook(1, $propPatch);
+ $result = $propPatch->commit();
$this->assertTrue($result);
@@ -115,7 +128,7 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
'{DAV:}displayname' => 'updated',
'{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'updated',
'{http://calendarserver.org/ns/}getctag' => 2,
- '{' . CardDAV\Plugin::NS_CARDDAV . '}supported-address-data' => new CardDAV\Property\SupportedAddressData(),
+ '{http://sabredav.org/ns}sync-token' => 2
)
);
@@ -158,7 +171,7 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
'{DAV:}displayname' => 'book1',
'{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1',
'{http://calendarserver.org/ns/}getctag' => 1,
- '{' . CardDAV\Plugin::NS_CARDDAV . '}supported-address-data' => new CardDAV\Property\SupportedAddressData(),
+ '{http://sabredav.org/ns}sync-token' => 1,
),
array(
'id' => 2,
@@ -167,7 +180,7 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
'{DAV:}displayname' => 'book2',
'{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 2',
'{http://calendarserver.org/ns/}getctag' => 1,
- '{' . CardDAV\Plugin::NS_CARDDAV . '}supported-address-data' => new CardDAV\Property\SupportedAddressData(),
+ '{http://sabredav.org/ns}sync-token' => 1,
)
);
$result = $this->backend->getAddressBooksForUser('principals/user1');
@@ -183,8 +196,9 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
array(
'id' => 1,
'uri' => 'card1',
- 'carddata' => 'card1',
'lastmodified' => 0,
+ 'etag' => '"' . md5('card1') . '"',
+ 'size' => 5
)
);
@@ -201,6 +215,8 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
'uri' => 'card1',
'carddata' => 'card1',
'lastmodified' => 0,
+ 'etag' => '"' . md5('card1') . '"',
+ 'size' => 5
);
$this->assertEquals($expected, $result);
@@ -222,6 +238,53 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
}
/**
+ * @depends testCreateCard
+ */
+ public function testGetMultiple() {
+
+ $result = $this->backend->createCard(1, 'card2', 'data2');
+ $result = $this->backend->createCard(1, 'card3', 'data3');
+ $check = [
+ [
+ 'id' => 1,
+ 'uri' => 'card1',
+ 'carddata' => 'card1',
+ 'lastmodified' => 0,
+ ],
+ [
+ 'id' => 2,
+ 'uri' => 'card2',
+ 'carddata' => 'data2',
+ 'lastmodified' => time(),
+ ],
+ [
+ 'id' => 3,
+ 'uri' => 'card3',
+ 'carddata' => 'data3',
+ 'lastmodified' => time(),
+ ],
+ ];
+
+ $result = $this->backend->getMultipleCards(1, ['card1','card2','card3']);
+
+ foreach($check as $index=>$node) {
+
+ foreach($node as $k=>$v) {
+
+ if ($k!=='lastmodified') {
+ $this->assertEquals($v, $result[$index][$k]);
+ } else {
+ $this->assertTrue(isset($result[$index][$k]));
+ }
+
+ }
+
+ }
+
+
+ }
+
+ /**
* @depends testGetCard
*/
public function testUpdateCard() {
@@ -245,5 +308,43 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
$this->assertFalse($result);
}
+
+ function testGetChanges() {
+
+ $backend = $this->backend;
+ $id = $backend->createAddressBook(
+ 'principals/user1',
+ 'bla',
+ []
+ );
+ $result = $backend->getChangesForAddressBook($id, null, 1);
+
+ $this->assertEquals([
+ 'syncToken' => 1,
+ "added" => [],
+ 'modified' => [],
+ 'deleted' => [],
+ ], $result);
+
+ $currentToken = $result['syncToken'];
+
+ $dummyCard = "BEGIN:VCARD\r\nEND:VCARD\r\n";
+
+ $backend->createCard($id, "card1.ics", $dummyCard);
+ $backend->createCard($id, "card2.ics", $dummyCard);
+ $backend->createCard($id, "card3.ics", $dummyCard);
+ $backend->updateCard($id, "card1.ics", $dummyCard);
+ $backend->deleteCard($id, "card2.ics");
+
+ $result = $backend->getChangesForAddressBook($id, $currentToken, 1);
+
+ $this->assertEquals([
+ 'syncToken' => 6,
+ 'modified' => ["card1.ics"],
+ 'deleted' => ["card2.ics"],
+ "added" => ["card3.ics"],
+ ], $result);
+
+ }
}
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/Mock.php b/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/Mock.php
index ab7ac4e6a..3f96d3c5d 100644
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/Mock.php
+++ b/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/Mock.php
@@ -48,18 +48,36 @@ class Mock extends AbstractBackend {
}
- function updateAddressBook($addressBookId, array $mutations) {
+ /**
+ * Updates properties for an address book.
+ *
+ * The list of mutations is stored in a Sabre\DAV\PropPatch object.
+ * To do the actual updates, you must tell this object which properties
+ * you're going to process with the handle() method.
+ *
+ * Calling the handle method is like telling the PropPatch object "I
+ * promise I can handle updating this property".
+ *
+ * Read the PropPatch documenation for more info and examples.
+ *
+ * @param string $addressBookId
+ * @param \Sabre\DAV\PropPatch $propPatch
+ * @return void
+ */
+ public function updateAddressBook($addressBookId, \Sabre\DAV\PropPatch $propPatch) {
foreach($this->addressBooks as &$book) {
if ($book['id'] !== $addressBookId)
continue;
- foreach($mutations as $key=>$value) {
- $book[$key] = $value;
- }
- return true;
+ $propPatch->handleRemaining(function($mutations) use (&$book) {
+ foreach($mutations as $key=>$value) {
+ $book[$key] = $value;
+ }
+ return true;
+ });
+
}
- return false;
}
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/PDOMySQLTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/PDOMySQLTest.php
index b2f871f6e..38cb655d0 100644
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/PDOMySQLTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/PDOMySQLTest.php
@@ -16,42 +16,18 @@ class PDOMySQLTest extends AbstractPDOTest {
$pdo = \Sabre\TestUtil::getMySQLDB();
if (!$pdo) $this->markTestSkipped('Could not connect to MySQL database');
- $pdo->query("DROP TABLE IF EXISTS addressbooks");
- $pdo->query("DROP TABLE IF EXISTS cards");
- $pdo->query("
-CREATE TABLE addressbooks (
- id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
- principaluri VARCHAR(255),
- displayname VARCHAR(255),
- uri VARCHAR(100),
- description TEXT,
- ctag INT(11) UNSIGNED NOT NULL DEFAULT '1'
-);
-");
-
- $pdo->query("
-INSERT INTO addressbooks
- (principaluri, displayname, uri, description, ctag)
-VALUES
- ('principals/user1', 'book1', 'book1', 'addressbook 1', 1);
-");
-
- $pdo->query("
-CREATE TABLE cards (
- id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
- addressbookid INT(11) UNSIGNED NOT NULL,
- carddata TEXT,
- uri VARCHAR(100),
- lastmodified INT(11) UNSIGNED
-);
-");
-
- $pdo->query("
-INSERT INTO cards
- (addressbookid, carddata, uri, lastmodified)
-VALUES
- (1, 'card1', 'card1', 0);
-");
+ $pdo->query("DROP TABLE IF EXISTS addressbooks, cards, addressbookchanges");
+
+ $queries = explode(
+ ';',
+ file_get_contents(__DIR__ . '/../../../../examples/sql/mysql.addressbook.sql')
+ );
+
+ foreach($queries as $query) {
+ $query = trim($query," \r\n\t");
+ if ($query)
+ $pdo->exec($query);
+ }
return $pdo;
}
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/PDOSqliteTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/PDOSqliteTest.php
index a9bbb0bd1..5a4a7a327 100644
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/PDOSqliteTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CardDAV/Backend/PDOSqliteTest.php
@@ -18,48 +18,33 @@ class PDOSqliteTest extends AbstractPDOTest {
*/
function getPDO() {
+ return self::getSQLite();
+
+ }
+
+ /**
+ * @return PDO
+ */
+ static function getSQLite() {
+
if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available');
$pdo = new \PDO('sqlite:'.SABRE_TEMPDIR.'/pdobackend');
$pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION);
$pdo->query("DROP TABLE IF EXISTS addressbooks");
+ $pdo->query("DROP TABLE IF EXISTS addressbookchanges");
$pdo->query("DROP TABLE IF EXISTS cards");
- $pdo->query("
-CREATE TABLE addressbooks (
- id integer primary key asc,
- principaluri text,
- displayname text,
- uri text,
- description text,
- ctag integer
-);
-
-");
-
- $pdo->query("
-INSERT INTO addressbooks
- (principaluri, displayname, uri, description, ctag)
-VALUES
- ('principals/user1', 'book1', 'book1', 'addressbook 1', 1);
-");
-
- $pdo->query("
-
-CREATE TABLE cards (
- id integer primary key asc,
- addressbookid integer,
- carddata text,
- uri text,
- lastmodified integer
-);
-
-");
- $pdo->query("
-INSERT INTO cards
- (addressbookid, carddata, uri, lastmodified)
-VALUES
- (1, 'card1', 'card1', 0);
-");
+
+ $queries = explode(
+ ';',
+ file_get_contents(__DIR__ . '/../../../../examples/sql/sqlite.addressbooks.sql')
+ );
+
+ foreach($queries as $query) {
+ $query = trim($query," \r\n\t");
+ if ($query)
+ $pdo->exec($query);
+ }
return $pdo;
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/CardTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/CardTest.php
index 438bd2ea5..cf8dbab0c 100644
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/CardTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CardDAV/CardTest.php
@@ -82,7 +82,7 @@ class CardTest extends \PHPUnit_Framework_TestCase {
function testGetContentType() {
- $this->assertEquals('text/x-vcard; charset=utf-8', $this->card->getContentType());
+ $this->assertEquals('text/vcard; charset=utf-8', $this->card->getContentType());
}
@@ -163,6 +163,37 @@ class CardTest extends \PHPUnit_Framework_TestCase {
), $this->card->getACL());
}
+ function testOverrideACL() {
+
+ $card = new Card(
+ $this->backend,
+ array(
+ 'uri' => 'book1',
+ 'id' => 'foo',
+ 'principaluri' => 'principals/user1',
+ ),
+ array(
+ 'uri' => 'card1',
+ 'addressbookid' => 'foo',
+ 'carddata' => 'card',
+ 'acl' => array(
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/user1',
+ 'protected' => true,
+ ),
+ ),
+ )
+ );
+ $this->assertEquals(array(
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => 'principals/user1',
+ 'protected' => true,
+ ),
+ ), $card->getACL());
+
+ }
/**
* @expectedException Sabre\DAV\Exception\MethodNotAllowed
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/MultiGetTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/MultiGetTest.php
index 12922c6fd..b0ee45880 100644
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/MultiGetTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CardDAV/MultiGetTest.php
@@ -11,7 +11,7 @@ class MultiGetTest extends AbstractPluginTest {
function testMultiGet() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'REPORT',
'REQUEST_URI' => '/addressbooks/user1/book1',
));
@@ -34,7 +34,7 @@ class MultiGetTest extends AbstractPluginTest {
$this->server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status', $response->status, 'Incorrect status code. Full response body:' . $response->body);
+ $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body);
// using the client for parsing
$client = new DAV\Client(array('baseUri'=>'/'));
@@ -45,11 +45,55 @@ class MultiGetTest extends AbstractPluginTest {
'/addressbooks/user1/book1/card1' => array(
200 => array(
'{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"',
- '{urn:ietf:params:xml:ns:carddav}address-data' => "BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD",
+ '{urn:ietf:params:xml:ns:carddav}address-data' => "BEGIN:VCARD\r\nVERSION:3.0\r\nUID:12345\r\nEND:VCARD\r\n",
)
)
), $result);
}
+ function testMultiGetVCard4() {
+
+ $request = HTTP\Sapi::createFromServerArray(array(
+ 'REQUEST_METHOD' => 'REPORT',
+ 'REQUEST_URI' => '/addressbooks/user1/book1',
+ ));
+
+ $request->setBody(
+'<?xml version="1.0"?>
+<c:addressbook-multiget xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:carddav">
+ <d:prop>
+ <d:getetag />
+ <c:address-data content-type="text/vcard" version="4.0" />
+ </d:prop>
+ <d:href>/addressbooks/user1/book1/card1</d:href>
+</c:addressbook-multiget>'
+ );
+
+ $response = new HTTP\ResponseMock();
+
+ $this->server->httpRequest = $request;
+ $this->server->httpResponse = $response;
+
+ $this->server->exec();
+
+ $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body);
+
+ // using the client for parsing
+ $client = new DAV\Client(array('baseUri'=>'/'));
+
+ $result = $client->parseMultiStatus($response->body);
+
+ $prodId = "PRODID:-//Sabre//Sabre VObject " . \Sabre\VObject\Version::VERSION . "//EN";
+
+ $this->assertEquals(array(
+ '/addressbooks/user1/book1/card1' => array(
+ 200 => array(
+ '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"',
+ '{urn:ietf:params:xml:ns:carddav}address-data' => "BEGIN:VCARD\r\nVERSION:4.0\r\n$prodId\r\nUID:12345\r\nEND:VCARD\r\n",
+ )
+ )
+ ), $result);
+
+ }
}
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/PluginTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/PluginTest.php
index 297ebf496..9c916350e 100644
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/PluginTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CardDAV/PluginTest.php
@@ -3,17 +3,16 @@
namespace Sabre\CardDAV;
use Sabre\DAV;
-
-require_once 'Sabre/CardDAV/AbstractPluginTest.php';
+use Sabre\DAV\Xml\Property\Href;
class PluginTest extends AbstractPluginTest {
function testConstruct() {
- $this->assertEquals('card', $this->server->xmlNamespaces[Plugin::NS_CARDDAV]);
$this->assertEquals('{' . Plugin::NS_CARDDAV . '}addressbook', $this->server->resourceTypeMapping['Sabre\\CardDAV\\IAddressBook']);
$this->assertTrue(in_array('addressbook', $this->plugin->getFeatures()));
+ $this->assertEquals('carddav', $this->plugin->getPluginInfo()['name']);
}
@@ -43,25 +42,6 @@ class PluginTest extends AbstractPluginTest {
}
- function testMeCardTest() {
-
- $result = $this->server->getProperties(
- 'addressbooks/user1',
- array(
- '{http://calendarserver.org/ns/}me-card',
- )
- );
-
- $this->assertEquals(
- array(
- '{http://calendarserver.org/ns/}me-card' =>
- new DAV\Property\Href('addressbooks/user1/book1/vcard1.vcf')
- ),
- $result
- );
-
- }
-
function testDirectoryGateway() {
$result = $this->server->getProperties('principals/user1', array('{' . Plugin::NS_CARDDAV . '}directory-gateway'));
@@ -74,76 +54,50 @@ class PluginTest extends AbstractPluginTest {
function testReportPassThrough() {
- $this->assertNull($this->plugin->report('{DAV:}foo', new \DomDocument()));
+ $this->assertNull($this->plugin->report('{DAV:}foo', new \DomDocument(), ''));
}
function testHTMLActionsPanel() {
$output = '';
- $r = $this->server->broadcastEvent('onHTMLActionsPanel', array($this->server->tree->getNodeForPath('addressbooks/user1'), &$output));
+ $r = $this->server->emit('onHTMLActionsPanel', [$this->server->tree->getNodeForPath('addressbooks/user1'), &$output]);
$this->assertFalse($r);
$this->assertTrue(!!strpos($output,'Display name'));
}
- function testBrowserPostAction() {
+ function testAddressbookPluginProperties() {
- $r = $this->server->broadcastEvent('onBrowserPostAction', array('addressbooks/user1', 'mkaddressbook', array(
- 'name' => 'NEWADDRESSBOOK',
- '{DAV:}displayname' => 'foo',
- )));
- $this->assertFalse($r);
+ $ns = '{' . Plugin::NS_CARDDAV . '}';
+ $propFind = new DAV\PropFind('addressbooks/user1/book1', [
+ $ns . 'supported-address-data',
+ $ns . 'supported-collation-set',
+ ]);
+ $node = $this->server->tree->getNodeForPath('addressbooks/user1/book1');
+ $this->plugin->propFindEarly($propFind, $node);
- $addressbooks = $this->backend->getAddressBooksforUser('principals/user1');
- $this->assertEquals(2, count($addressbooks));
+ $this->assertInstanceOf(
+ 'Sabre\\CardDAV\\Xml\\Property\\SupportedAddressData',
+ $propFind->get($ns . 'supported-address-data')
+ );
+ $this->assertInstanceOf(
+ 'Sabre\\CardDAV\\Xml\\Property\\SupportedCollationSet',
+ $propFind->get($ns . 'supported-collation-set')
+ );
- $newAddressBook = null;
- foreach($addressbooks as $addressbook) {
- if ($addressbook['uri'] === 'NEWADDRESSBOOK') {
- $newAddressBook = $addressbook;
- break;
- }
- }
- if (!$newAddressBook)
- $this->fail('Could not find newly created addressbook');
}
- function testUpdatePropertiesMeCard() {
+ function testGetTransform() {
- $result = $this->server->updateProperties('addressbooks/user1', array(
- '{http://calendarserver.org/ns/}me-card' => new DAV\Property\Href('/addressbooks/user1/book1/vcard2',true),
- ));
+ $request = new \Sabre\HTTP\Request('GET', '/addressbooks/user1/book1/card1', ['Accept: application/vcard+json']);
+ $response = new \Sabre\HTTP\ResponseMock();
+ $this->server->invokeMethod($request, $response);
- $this->assertEquals(
- array(
- 'href' => 'addressbooks/user1',
- 200 => array(
- '{http://calendarserver.org/ns/}me-card' => null,
- ),
- ),
- $result
- );
+ $this->assertEquals(200, $response->getStatus());
}
- function testUpdatePropertiesMeCardBadValue() {
-
- $result = $this->server->updateProperties('addressbooks/user1', array(
- '{http://calendarserver.org/ns/}me-card' => new DAV\Property\HrefList(array()),
- ));
-
- $this->assertEquals(
- array(
- 'href' => 'addressbooks/user1',
- 400 => array(
- '{http://calendarserver.org/ns/}me-card' => null,
- ),
- ),
- $result
- );
-
- }
}
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/Property/SupportedAddressDataTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/Property/SupportedAddressDataTest.php
deleted file mode 100644
index a0e4130d5..000000000
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/Property/SupportedAddressDataTest.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-namespace Sabre\CardDAV\Property;
-
-use Sabre\CardDAV;
-use Sabre\DAV;
-
-class SupportedAddressDataDataTest extends \PHPUnit_Framework_TestCase {
-
- function testSimple() {
-
- $property = new SupportedAddressData();
-
- }
-
- /**
- * @depends testSimple
- */
- function testSerialize() {
-
- $property = new SupportedAddressData();
-
- $doc = new \DOMDocument();
- $root = $doc->createElementNS(CardDAV\Plugin::NS_CARDDAV, 'card:root');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $server = new DAV\Server();
-
- $property->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<card:root xmlns:card="' . CardDAV\Plugin::NS_CARDDAV . '" xmlns:d="DAV:">' .
-'<card:address-data-type content-type="text/vcard" version="3.0"/>' .
-//'<card:address-data-type content-type="text/vcard" version="4.0"/>' .
-'</card:root>
-', $xml);
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/SogoStripContentTypeTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/SogoStripContentTypeTest.php
index 2a62bd2f9..f828cc25b 100644
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/SogoStripContentTypeTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CardDAV/SogoStripContentTypeTest.php
@@ -3,6 +3,7 @@
namespace Sabre\CardDAV;
use Sabre\HTTP;
+use Sabre\DAV\PropFind;
class SogoStripContentType extends \Sabre\DAVServerTest {
@@ -24,13 +25,13 @@ class SogoStripContentType extends \Sabre\DAVServerTest {
$result = $this->server->getProperties('addressbooks/user1/book1/card1.vcf',array('{DAV:}getcontenttype'));
$this->assertEquals(array(
- '{DAV:}getcontenttype' => 'text/x-vcard; charset=utf-8'
+ '{DAV:}getcontenttype' => 'text/vcard; charset=utf-8'
), $result);
}
function testStrip() {
- $this->server->httpRequest = new HTTP\Request(array(
+ $this->server->httpRequest = HTTP\Sapi::createFromServerArray(array(
'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:10.0.2) Gecko/20120216 Thunderbird/10.0.2 Lightning/1.2.1',
));
$result = $this->server->getProperties('addressbooks/user1/book1/card1.vcf',array('{DAV:}getcontenttype'));
@@ -39,5 +40,17 @@ class SogoStripContentType extends \Sabre\DAVServerTest {
), $result);
}
+ function testDontTouchOtherMimeTypes() {
+
+ $this->server->httpRequest = new HTTP\Request('GET','/addressbooks/user1/book1/card1.vcf', [
+ 'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:10.0.2) Gecko/20120216 Thunderbird/10.0.2 Lightning/1.2.1',
+ ]);
+
+ $propFind = new PropFind('hello', ['{DAV:}getcontenttype']);
+ $propFind->set('{DAV:}getcontenttype', 'text/plain');
+ $this->carddavPlugin->propFindLate($propFind, new \Sabre\DAV\SimpleCollection('foo'));
+ $this->assertEquals('text/plain', $propFind->get('{DAV:}getcontenttype'));
+
+ }
}
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/TestUtil.php b/vendor/sabre/dav/tests/Sabre/CardDAV/TestUtil.php
index 9f84566af..c9cc10d35 100644
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/TestUtil.php
+++ b/vendor/sabre/dav/tests/Sabre/CardDAV/TestUtil.php
@@ -15,17 +15,8 @@ class TestUtil {
static function getSQLiteDB() {
- if (file_exists(SABRE_TEMPDIR . '/testdb.sqlite'))
- unlink(SABRE_TEMPDIR . '/testdb.sqlite');
+ $pdo = Backend\PDOSqliteTest::getSQLite();
- $pdo = new PDO('sqlite:' . SABRE_TEMPDIR . '/testdb.sqlite');
- $pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
-
- // Yup this is definitely not 'fool proof', but good enough for now.
- $queries = explode(';', file_get_contents(__DIR__ . '/../../../examples/sql/sqlite.addressbooks.sql'));
- foreach($queries as $query) {
- $pdo->exec($query);
- }
// Inserting events through a backend class.
$backend = new Backend\PDO($pdo);
$addressbookId = $backend->createAddressBook(
@@ -49,7 +40,12 @@ class TestUtil {
}
- static function getTestCardData($type = 1) {
+ static function deleteSQLiteDB() {
+ $sqliteTest = new Backend\PDOSqliteTest();
+ $pdo = $sqliteTest->tearDown();
+ }
+
+ static function getTestCardData() {
$addressbookData = 'BEGIN:VCARD
VERSION:3.0
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/UserAddressBooksTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/UserAddressBooksTest.php
deleted file mode 100644
index a6ecf3e47..000000000
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/UserAddressBooksTest.php
+++ /dev/null
@@ -1,162 +0,0 @@
-<?php
-
-namespace Sabre\CardDAV;
-
-class UserAddressBooksTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @var Sabre\CardDAV\UserAddressBooks
- */
- protected $s;
- protected $backend;
-
- function setUp() {
-
- $this->backend = new Backend\Mock();
- $this->s = new UserAddressBooks(
- $this->backend,
- 'principals/user1'
- );
-
- }
-
- function testGetName() {
-
- $this->assertEquals('user1', $this->s->getName());
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\MethodNotAllowed
- */
- function testSetName() {
-
- $this->s->setName('user2');
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\MethodNotAllowed
- */
- function testDelete() {
-
- $this->s->delete();
-
- }
-
- function testGetLastModified() {
-
- $this->assertNull($this->s->getLastModified());
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\MethodNotAllowed
- */
- function testCreateFile() {
-
- $this->s->createFile('bla');
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\MethodNotAllowed
- */
- function testCreateDirectory() {
-
- $this->s->createDirectory('bla');
-
- }
-
- function testGetChild() {
-
- $child = $this->s->getChild('book1');
- $this->assertInstanceOf('Sabre\\CardDAV\\AddressBook', $child);
- $this->assertEquals('book1', $child->getName());
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\NotFound
- */
- function testGetChild404() {
-
- $this->s->getChild('book2');
-
- }
-
- function testGetChildren() {
-
- $children = $this->s->getChildren();
- $this->assertEquals(1, count($children));
- $this->assertInstanceOf('Sabre\\CardDAV\\AddressBook', $children[0]);
- $this->assertEquals('book1', $children[0]->getName());
-
- }
-
- function testCreateExtendedCollection() {
-
- $resourceType = array(
- '{' . Plugin::NS_CARDDAV . '}addressbook',
- '{DAV:}collection',
- );
- $this->s->createExtendedCollection('book2', $resourceType, array('{DAV:}displayname' => 'a-book 2'));
-
- $this->assertEquals(array(
- 'id' => 'book2',
- 'uri' => 'book2',
- '{DAV:}displayname' => 'a-book 2',
- 'principaluri' => 'principals/user1',
- ), $this->backend->addressBooks[1]);
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\InvalidResourceType
- */
- function testCreateExtendedCollectionInvalid() {
-
- $resourceType = array(
- '{DAV:}collection',
- );
- $this->s->createExtendedCollection('book2', $resourceType, array('{DAV:}displayname' => 'a-book 2'));
-
- }
-
-
- function testACLMethods() {
-
- $this->assertEquals('principals/user1', $this->s->getOwner());
- $this->assertNull($this->s->getGroup());
- $this->assertEquals(array(
- array(
- 'privilege' => '{DAV:}read',
- 'principal' => 'principals/user1',
- 'protected' => true,
- ),
- array(
- 'privilege' => '{DAV:}write',
- 'principal' => 'principals/user1',
- 'protected' => true,
- ),
- ), $this->s->getACL());
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\MethodNotAllowed
- */
- function testSetACL() {
-
- $this->s->setACL(array());
-
- }
-
- function testGetSupportedPrivilegeSet() {
-
- $this->assertNull(
- $this->s->getSupportedPrivilegeSet()
- );
-
- }
-}
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/VCFExportTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/VCFExportTest.php
index 84da59311..71fde719d 100644
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/VCFExportTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CardDAV/VCFExportTest.php
@@ -29,28 +29,35 @@ class VCFExportTest extends \Sabre\DAVServerTest {
function setUp() {
parent::setUp();
+ $plugin = new VCFExportPlugin();
$this->server->addPlugin(
- new VCFExportPlugin()
+ $plugin
);
}
function testSimple() {
- $this->assertInstanceOf('Sabre\\CardDAV\\VCFExportPlugin', $this->server->getPlugin('Sabre\\CardDAV\\VCFExportPlugin'));
+ $plugin = $this->server->getPlugin('vcf-export');
+ $this->assertInstanceOf('Sabre\\CardDAV\\VCFExportPlugin', $plugin);
+
+ $this->assertEquals(
+ 'vcf-export',
+ $plugin->getPluginInfo()['name']
+ );
}
function testExport() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_URI' => '/addressbooks/user1/book1?export',
'QUERY_STRING' => 'export',
'REQUEST_METHOD' => 'GET',
));
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 200 OK', $response->status, $response->body);
+ $this->assertEquals(200, $response->status, $response->body);
$expected = "BEGIN:VCARD
FN:Person1
@@ -72,4 +79,14 @@ END:VCARD
}
+ function testBrowserIntegration() {
+
+ $plugin = $this->server->getPlugin('vcf-export');
+ $actions = '';
+ $addressbook = new AddressBook($this->carddavBackend, []);
+ $this->server->emit('browserButtonActions', ['/foo', $addressbook, &$actions]);
+ $this->assertContains('/foo?export', $actions);
+
+ }
+
}
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/ValidateVCardTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/ValidateVCardTest.php
index 1f52f30a7..ad8495c13 100644
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/ValidateVCardTest.php
+++ b/vendor/sabre/dav/tests/Sabre/CardDAV/ValidateVCardTest.php
@@ -31,6 +31,7 @@ class ValidateVCardTest extends \PHPUnit_Framework_TestCase {
);
$this->server = new DAV\Server($tree);
+ $this->server->sapi = new HTTP\SapiMock();
$this->server->debugExceptions = true;
$plugin = new Plugin();
@@ -52,20 +53,20 @@ class ValidateVCardTest extends \PHPUnit_Framework_TestCase {
function testCreateFile() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf',
));
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 415 Unsupported Media Type', $response->status);
+ $this->assertEquals(415, $response->status);
}
function testCreateFileValid() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf',
));
@@ -73,7 +74,7 @@ class ValidateVCardTest extends \PHPUnit_Framework_TestCase {
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 201 Created', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+ $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
$expected = array(
'uri' => 'blabla.vcf',
'carddata' => "BEGIN:VCARD\r\nUID:foo\r\nEND:VCARD\r\n",
@@ -85,24 +86,40 @@ class ValidateVCardTest extends \PHPUnit_Framework_TestCase {
function testCreateFileNoUID() {
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'PUT',
- 'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf',
- ));
+ $request = new HTTP\Request(
+ 'PUT',
+ '/addressbooks/admin/addressbook1/blabla.vcf'
+ );
$request->setBody("BEGIN:VCARD\r\nEND:VCARD\r\n");
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 201 Created', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+ $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
$foo = $this->cardBackend->getCard('addressbook1','blabla.vcf');
$this->assertTrue(strpos($foo['carddata'],'UID')!==false);
}
+ function testCreateFileJson() {
+
+ $request = new HTTP\Request(
+ 'PUT',
+ '/addressbooks/admin/addressbook1/blabla.vcf'
+ );
+ $request->setBody('[ "vcard" , [ [ "UID" , {}, "text", "foo" ] ] ]');
+
+ $response = $this->request($request);
+
+ $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+
+ $foo = $this->cardBackend->getCard('addressbook1','blabla.vcf');
+ $this->assertEquals("BEGIN:VCARD\r\nUID:foo\r\nEND:VCARD\r\n", $foo['carddata']);
+
+ }
function testCreateFileVCalendar() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf',
));
@@ -110,28 +127,28 @@ class ValidateVCardTest extends \PHPUnit_Framework_TestCase {
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 415 Unsupported Media Type', $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
+ $this->assertEquals(415, $response->status, 'Incorrect status returned! Full response body: ' . $response->body);
}
function testUpdateFile() {
$this->cardBackend->createCard('addressbook1','blabla.vcf','foo');
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf',
));
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 415 Unsupported Media Type', $response->status);
+ $this->assertEquals(415, $response->status);
}
function testUpdateFileParsableBody() {
$this->cardBackend->createCard('addressbook1','blabla.vcf','foo');
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf',
));
@@ -140,7 +157,7 @@ class ValidateVCardTest extends \PHPUnit_Framework_TestCase {
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 204 No Content', $response->status);
+ $this->assertEquals(204, $response->status);
$expected = array(
'uri' => 'blabla.vcf',
diff --git a/vendor/sabre/dav/tests/Sabre/CardDAV/VersionTest.php b/vendor/sabre/dav/tests/Sabre/CardDAV/VersionTest.php
deleted file mode 100644
index 02943b2d3..000000000
--- a/vendor/sabre/dav/tests/Sabre/CardDAV/VersionTest.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-namespace Sabre\CardDAV;
-
-class VersionTest extends \PHPUnit_Framework_TestCase {
-
- function testString() {
-
- $v = Version::VERSION;
- $this->assertEquals(-1, version_compare('0.1',$v));
-
- $s = Version::STABILITY;
- $this->assertTrue($s == 'alpha' || $s == 'beta' || $s =='stable');
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/AbstractServer.php b/vendor/sabre/dav/tests/Sabre/DAV/AbstractServer.php
index 4bf5b343e..b5b8d64ee 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/AbstractServer.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/AbstractServer.php
@@ -4,8 +4,6 @@ namespace Sabre\DAV;
use Sabre\HTTP;
-require_once 'Sabre/HTTP/ResponseMock.php';
-
abstract class AbstractServer extends \PHPUnit_Framework_TestCase {
/**
@@ -23,6 +21,7 @@ abstract class AbstractServer extends \PHPUnit_Framework_TestCase {
$this->response = new HTTP\ResponseMock();
$this->server = new Server($this->getRootNode());
+ $this->server->sapi = new HTTP\SapiMock();
$this->server->httpResponse = $this->response;
$this->server->debugExceptions = true;
$this->deleteTree(SABRE_TEMPDIR,false);
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractBasicTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractBasicTest.php
index 36d23c5c0..7d7a59898 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractBasicTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractBasicTest.php
@@ -5,67 +5,68 @@ namespace Sabre\DAV\Auth\Backend;
use Sabre\DAV;
use Sabre\HTTP;
-require_once 'Sabre/HTTP/ResponseMock.php';
-
class AbstractBasicTest extends \PHPUnit_Framework_TestCase {
- /**
- * @expectedException Sabre\DAV\Exception\NotAuthenticated
- */
- public function testAuthenticateNoHeaders() {
+ function testCheckNoHeaders() {
- $response = new HTTP\ResponseMock();
- $server = new DAV\Server();
- $server->httpResponse = $response;
+ $request = new HTTP\Request();
+ $response = new HTTP\Response();
$backend = new AbstractBasicMock();
- $backend->authenticate($server,'myRealm');
- }
+ $this->assertFalse(
+ $backend->check($request, $response)[0]
+ );
- /**
- * @expectedException Sabre\DAV\Exception\NotAuthenticated
- */
- public function testAuthenticateUnknownUser() {
+ }
- $response = new HTTP\ResponseMock();
- $tree = new DAV\ObjectTree(new DAV\SimpleCollection('bla'));
- $server = new DAV\Server($tree);
- $server->httpResponse = $response;
+ function testCheckUnknownUser() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'PHP_AUTH_USER' => 'username',
'PHP_AUTH_PW' => 'wrongpassword',
));
- $server->httpRequest = $request;
+ $response = new HTTP\Response();
$backend = new AbstractBasicMock();
- $backend->authenticate($server,'myRealm');
- }
+ $this->assertFalse(
+ $backend->check($request, $response)[0]
+ );
- public function testAuthenticate() {
+ }
- $response = new HTTP\ResponseMock();
- $tree = new DAV\ObjectTree(new DAV\SimpleCollection('bla'));
- $server = new DAV\Server($tree);
- $server->httpResponse = $response;
+ function testCheckSuccess() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'PHP_AUTH_USER' => 'username',
'PHP_AUTH_PW' => 'password',
));
- $server->httpRequest = $request;
+ $response = new HTTP\Response();
$backend = new AbstractBasicMock();
- $this->assertTrue($backend->authenticate($server,'myRealm'));
+ $this->assertEquals(
+ [true, 'principals/username'],
+ $backend->check($request, $response)
+ );
- $result = $backend->getCurrentUser();
+ }
- $this->assertEquals('username', $result);
+ function testRequireAuth() {
- }
+ $request = new HTTP\Request();
+ $response = new HTTP\Response();
+ $backend = new AbstractBasicMock();
+ $backend->setRealm('writing unittests on a saturday night');
+ $backend->challenge($request, $response);
+
+ $this->assertEquals(
+ 'Basic realm="writing unittests on a saturday night"',
+ $response->getHeader('WWW-Authenticate')
+ );
+
+ }
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractDigestTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractDigestTest.php
index 495690c4e..8ef416c37 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractDigestTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractDigestTest.php
@@ -5,130 +5,120 @@ namespace Sabre\DAV\Auth\Backend;
use Sabre\DAV;
use Sabre\HTTP;
-require_once 'Sabre/HTTP/ResponseMock.php';
-
class AbstractDigestTest extends \PHPUnit_Framework_TestCase {
- /**
- * @expectedException Sabre\DAV\Exception\NotAuthenticated
- */
- public function testAuthenticateNoHeaders() {
+ function testCheckNoHeaders() {
- $response = new HTTP\ResponseMock();
- $server = new DAV\Server();
- $server->httpResponse = $response;
+ $request = new HTTP\Request();
+ $response = new HTTP\Response();
$backend = new AbstractDigestMock();
- $backend->authenticate($server,'myRealm');
+ $this->assertFalse(
+ $backend->check($request, $response)[0]
+ );
}
- /**
- * @expectedException Sabre\DAV\Exception
- */
- public function testAuthenticateBadGetUserInfoResponse() {
-
- $response = new HTTP\ResponseMock();
- $server = new DAV\Server();
- $server->httpResponse = $response;
+ function testCheckBadGetUserInfoResponse() {
$header = 'username=null, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1';
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray([
'PHP_AUTH_DIGEST' => $header,
- ));
- $server->httpRequest = $request;
+ ]);
+ $response = new HTTP\Response();
$backend = new AbstractDigestMock();
- $backend->authenticate($server,'myRealm');
+ $this->assertFalse(
+ $backend->check($request, $response)[0]
+ );
}
/**
* @expectedException Sabre\DAV\Exception
*/
- public function testAuthenticateBadGetUserInfoResponse2() {
-
- $response = new HTTP\ResponseMock();
- $server = new DAV\Server();
- $server->httpResponse = $response;
+ function testCheckBadGetUserInfoResponse2() {
$header = 'username=array, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1';
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray([
'PHP_AUTH_DIGEST' => $header,
- ));
- $server->httpRequest = $request;
+ ]);
+
+ $response = new HTTP\Response();
$backend = new AbstractDigestMock();
- $backend->authenticate($server,'myRealm');
+ $backend->check($request, $response);
}
- /**
- * @expectedException Sabre\DAV\Exception\NotAuthenticated
- */
- public function testAuthenticateUnknownUser() {
-
- $response = new HTTP\ResponseMock();
- $server = new DAV\Server();
- $server->httpResponse = $response;
+ function testCheckUnknownUser() {
$header = 'username=false, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1';
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray([
'PHP_AUTH_DIGEST' => $header,
- ));
- $server->httpRequest = $request;
+ ]);
+
+ $response = new HTTP\Response();
$backend = new AbstractDigestMock();
- $backend->authenticate($server,'myRealm');
+ $this->assertFalse(
+ $backend->check($request, $response)[0]
+ );
}
- /**
- * @expectedException Sabre\DAV\Exception\NotAuthenticated
- */
- public function testAuthenticateBadPassword() {
-
- $response = new HTTP\ResponseMock();
- $server = new DAV\Server();
- $server->httpResponse = $response;
+ function testCheckBadPassword() {
$header = 'username=user, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1';
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray([
'PHP_AUTH_DIGEST' => $header,
'REQUEST_METHOD' => 'PUT',
- ));
- $server->httpRequest = $request;
+ ]);
+
+ $response = new HTTP\Response();
$backend = new AbstractDigestMock();
- $backend->authenticate($server,'myRealm');
+ $this->assertFalse(
+ $backend->check($request, $response)[0]
+ );
}
- public function testAuthenticate() {
-
- $response = new HTTP\ResponseMock();
- $server = new DAV\Server();
- $server->httpResponse = $response;
+ function testCheck() {
$digestHash = md5('HELLO:12345:1:1:auth:' . md5('GET:/'));
$header = 'username=user, realm=myRealm, nonce=12345, uri=/, response='.$digestHash.', opaque=1, qop=auth, nc=1, cnonce=1';
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'GET',
'PHP_AUTH_DIGEST' => $header,
'REQUEST_URI' => '/',
));
- $server->httpRequest = $request;
+
+ $response = new HTTP\Response();
$backend = new AbstractDigestMock();
- $this->assertTrue($backend->authenticate($server,'myRealm'));
+ $this->assertEquals(
+ [true, 'principals/user'],
+ $backend->check($request, $response)
+ );
- $result = $backend->getCurrentUser();
+ }
- $this->assertEquals('user', $result);
- $this->assertEquals('HELLO', $backend->getDigestHash('myRealm', $result));
+ function testRequireAuth() {
- }
+ $request = new HTTP\Request();
+ $response = new HTTP\Response();
+ $backend = new AbstractDigestMock();
+ $backend->setRealm('writing unittests on a saturday night');
+ $backend->challenge($request, $response);
+
+ $this->assertStringStartsWith(
+ 'Digest realm="writing unittests on a saturday night"',
+ $response->getHeader('WWW-Authenticate')
+ );
+
+ }
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/ApacheTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/ApacheTest.php
index b1ed555d4..697b593db 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/ApacheTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/ApacheTest.php
@@ -10,36 +10,63 @@ class ApacheTest extends \PHPUnit_Framework_TestCase {
function testConstruct() {
$backend = new Apache();
+ $this->assertInstanceOf('Sabre\DAV\Auth\Backend\Apache', $backend);
}
- /**
- * @expectedException Sabre\DAV\Exception
- */
function testNoHeader() {
- $server = new DAV\Server();
+ $request = new HTTP\Request();
+ $response = new HTTP\Response();
$backend = new Apache();
- $backend->authenticate($server,'Realm');
+
+ $this->assertFalse(
+ $backend->check($request, $response)[0]
+ );
}
function testRemoteUser() {
+ $request = HTTP\Sapi::createFromServerArray([
+ 'REMOTE_USER' => 'username',
+ ]);
+ $response = new HTTP\Response();
$backend = new Apache();
- $server = new DAV\Server();
- $request = new HTTP\Request(array(
- 'REMOTE_USER' => 'username',
- ));
- $server->httpRequest = $request;
+ $this->assertEquals(
+ [true, 'principals/username'],
+ $backend->check($request, $response)
+ );
- $this->assertTrue($backend->authenticate($server, 'Realm'));
+ }
+
+ function testRedirectRemoteUser() {
- $userInfo = 'username';
+ $request = HTTP\Sapi::createFromServerArray([
+ 'REDIRECT_REMOTE_USER' => 'username',
+ ]);
+ $response = new HTTP\Response();
+ $backend = new Apache();
- $this->assertEquals($userInfo, $backend->getCurrentUser());
+ $this->assertEquals(
+ [true, 'principals/username'],
+ $backend->check($request, $response)
+ );
}
+ function testRequireAuth() {
+
+ $request = new HTTP\Request();
+ $response = new HTTP\Response();
+
+ $backend = new Apache();
+ $backend->challenge($request, $response);
+
+ $this->assertNull(
+ $response->getHeader('WWW-Authenticate')
+ );
+
+ }
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/FileTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/FileTest.php
index 72f150ab6..d2e5fe49b 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/FileTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/FileTest.php
@@ -35,7 +35,7 @@ class FileTest extends \PHPUnit_Framework_TestCase {
$file->loadFile(SABRE_TEMPDIR . '/backend');
$this->assertFalse($file->getDigestHash('realm','blabla'));
- $this->assertEquals(md5('user:realm:password'), $file->getDigesthash('realm','user'));
+ $this->assertEquals(md5('user:realm:password'), $file->getDigestHash('realm','user'));
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/Mock.php b/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/Mock.php
index fdad8a605..a782cb74d 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/Mock.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/Mock.php
@@ -2,35 +2,85 @@
namespace Sabre\DAV\Auth\Backend;
-use Sabre\DAV;
+use
+ Sabre\DAV,
+ Sabre\HTTP\RequestInterface,
+ Sabre\HTTP\ResponseInterface;
class Mock implements BackendInterface {
- protected $currentUser;
+ public $fail = false;
- public $defaultUser = 'admin';
+ public $invalidCheckResponse = false;
- /**
- * @param Sabre\DAV\Server $server
- * @param string $realm
- * @throws Sabre\DAV\Exception\NotAuthenticated
- */
- function authenticate(DAV\Server $server, $realm) {
+ public $principal = 'principals/admin';
- if ($realm=='failme') throw new DAV\Exception\NotAuthenticated('deliberate fail');
- $this->currentUser = $this->defaultUser;
+ function setPrincipal($principal) {
+
+ $this->principal = $principal;
}
- function setCurrentUser($user) {
+ /**
+ * When this method is called, the backend must check if authentication was
+ * successful.
+ *
+ * The returned value must be one of the following
+ *
+ * [true, "principals/username"]
+ * [false, "reason for failure"]
+ *
+ * If authentication was successful, it's expected that the authentication
+ * backend returns a so-called principal url.
+ *
+ * Examples of a principal url:
+ *
+ * principals/admin
+ * principals/user1
+ * principals/users/joe
+ * principals/uid/123457
+ *
+ * If you don't use WebDAV ACL (RFC3744) we recommend that you simply
+ * return a string such as:
+ *
+ * principals/users/[username]
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return array
+ */
+ function check(RequestInterface $request, ResponseInterface $response) {
- $this->currentUser = $user;
+ if ($this->invalidCheckResponse) {
+ return 'incorrect!';
+ }
+ if ($this->fail) {
+ return [false, "fail!"];
+ }
+ return [true, $this->principal];
}
- function getCurrentUser() {
-
- return $this->currentUser;
+ /**
+ * This method is called when a user could not be authenticated, and
+ * authentication was required for the current request.
+ *
+ * This gives you the oppurtunity to set authentication headers. The 401
+ * status code will already be set.
+ *
+ * In this case of Basic Auth, this would for example mean that the
+ * following header needs to be set:
+ *
+ * $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV');
+ *
+ * Keep in mind that in the case of multiple authentication backends, other
+ * WWW-Authenticate headers may already have been set, and you'll want to
+ * append your own WWW-Authenticate header instead of overwriting the
+ * existing one.
+ *
+ * @return void
+ */
+ function challenge(RequestInterface $request, ResponseInterface $response) {
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/PDOMySQLTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/PDOMySQLTest.php
index ede432de2..8de2be667 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/PDOMySQLTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/PDOMySQLTest.php
@@ -12,15 +12,17 @@ class PDOMySQLTest extends AbstractPDOTest {
$pdo = \Sabre\TestUtil::getMySQLDB();
if (!$pdo) $this->markTestSkipped('Could not connect to MySQL database');
$pdo->query("DROP TABLE IF EXISTS users");
- $pdo->query("
+ $pdo->query(<<<SQL
create table users (
- id integer unsigned not null primary key auto_increment,
- username varchar(50),
- digesta1 varchar(32),
- email varchar(80),
- displayname varchar(80),
- unique(username)
-);");
+ id integer unsigned not null primary key auto_increment,
+ username varchar(50),
+ digesta1 varchar(32),
+ email varchar(80),
+ displayname varchar(80),
+ unique(username)
+)
+SQL
+ );
$pdo->query("INSERT INTO users (username,digesta1,email,displayname) VALUES ('user','hash','user@example.org','User')");
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Auth/PluginTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Auth/PluginTest.php
index 2096a04d7..0ac9e0613 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Auth/PluginTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Auth/PluginTest.php
@@ -12,10 +12,11 @@ class PluginTest extends \PHPUnit_Framework_TestCase {
function testInit() {
$fakeServer = new DAV\Server( new DAV\SimpleCollection('bla'));
- $plugin = new Plugin(new Backend\Mock(),'realm');
+ $plugin = new Plugin(new Backend\Mock());
$this->assertTrue($plugin instanceof Plugin);
$fakeServer->addPlugin($plugin);
$this->assertEquals($plugin, $fakeServer->getPlugin('auth'));
+ $this->assertInternalType('array', $plugin->getPluginInfo());
}
@@ -25,14 +26,14 @@ class PluginTest extends \PHPUnit_Framework_TestCase {
function testAuthenticate() {
$fakeServer = new DAV\Server( new DAV\SimpleCollection('bla'));
- $plugin = new Plugin(new Backend\Mock(),'realm');
+ $plugin = new Plugin(new Backend\Mock());
$fakeServer->addPlugin($plugin);
- $fakeServer->broadCastEvent('beforeMethod',array('GET','/'));
+ $this->assertTrue(
+ $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()])
+ );
}
-
-
/**
* @depends testInit
* @expectedException Sabre\DAV\Exception\NotAuthenticated
@@ -40,42 +41,87 @@ class PluginTest extends \PHPUnit_Framework_TestCase {
function testAuthenticateFail() {
$fakeServer = new DAV\Server( new DAV\SimpleCollection('bla'));
- $plugin = new Plugin(new Backend\Mock(),'failme');
+ $backend = new Backend\Mock();
+ $backend->fail = true;
+
+ $plugin = new Plugin($backend);
$fakeServer->addPlugin($plugin);
- $fakeServer->broadCastEvent('beforeMethod',array('GET','/'));
+ $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]);
}
- function testReportPassThrough() {
+ /**
+ * @depends testAuthenticate
+ */
+ function testMultipleBackend() {
- $fakeServer = new DAV\Server(new DAV\SimpleCollection('bla'));
- $plugin = new Plugin(new Backend\Mock(),'realm');
- $fakeServer->addPlugin($plugin);
+ $fakeServer = new DAV\Server( new DAV\SimpleCollection('bla'));
+ $backend1 = new Backend\Mock();
+ $backend2 = new Backend\Mock();
+ $backend2->fail = true;
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'REPORT',
- 'HTTP_CONTENT_TYPE' => 'application/xml',
- 'REQUEST_URI' => '/',
- ));
- $request->setBody('<?xml version="1.0"?><s:somereport xmlns:s="http://www.rooftopsolutions.nl/NS/example" />');
+ $plugin = new Plugin();
+ $plugin->addBackend($backend1);
+ $plugin->addBackend($backend2);
- $fakeServer->httpRequest = $request;
- $fakeServer->httpResponse = new HTTP\ResponseMock();
- $fakeServer->exec();
+ $fakeServer->addPlugin($plugin);
+ $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]);
- $this->assertEquals('HTTP/1.1 403 Forbidden', $fakeServer->httpResponse->status);
+ $this->assertEquals('principals/admin', $plugin->getCurrentPrincipal());
}
/**
* @depends testInit
+ * @expectedException Sabre\DAV\Exception
+ */
+ function testNoAuthBackend() {
+
+ $fakeServer = new DAV\Server( new DAV\SimpleCollection('bla'));
+
+ $plugin = new Plugin();
+ $fakeServer->addPlugin($plugin);
+ $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]);
+
+ }
+ /**
+ * @depends testInit
+ * @expectedException Sabre\DAV\Exception
+ */
+ function testInvalidCheckResponse() {
+
+ $fakeServer = new DAV\Server( new DAV\SimpleCollection('bla'));
+ $backend = new Backend\Mock();
+ $backend->invalidCheckResponse = true;
+
+ $plugin = new Plugin($backend);
+ $fakeServer->addPlugin($plugin);
+ $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]);
+
+ }
+
+ /**
+ * @depends testAuthenticate
+ */
+ function testGetCurrentPrincipal() {
+
+ $fakeServer = new DAV\Server( new DAV\SimpleCollection('bla'));
+ $plugin = new Plugin(new Backend\Mock());
+ $fakeServer->addPlugin($plugin);
+ $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]);
+ $this->assertEquals('principals/admin', $plugin->getCurrentPrincipal());
+
+ }
+
+ /**
+ * @depends testAuthenticate
*/
- function testGetCurrentUserPrincipal() {
+ function testGetCurrentUser() {
$fakeServer = new DAV\Server( new DAV\SimpleCollection('bla'));
- $plugin = new Plugin(new Backend\Mock(),'realm');
+ $plugin = new Plugin(new Backend\Mock());
$fakeServer->addPlugin($plugin);
- $fakeServer->broadCastEvent('beforeMethod',array('GET','/'));
+ $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]);
$this->assertEquals('admin', $plugin->getCurrentUser());
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/BasicNodeTest.php b/vendor/sabre/dav/tests/Sabre/DAV/BasicNodeTest.php
index fdc2403db..155c785f8 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/BasicNodeTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/BasicNodeTest.php
@@ -131,6 +131,7 @@ class BasicNodeTest extends \PHPUnit_Framework_TestCase {
public function testSimpleDirectoryConstruct() {
$dir = new SimpleCollection('simpledir',array());
+ $this->assertInstanceOf('Sabre\DAV\SimpleCollection', $dir);
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Browser/GuessContentTypeTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Browser/GuessContentTypeTest.php
index 6fc65f9e8..157c2170a 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Browser/GuessContentTypeTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Browser/GuessContentTypeTest.php
@@ -10,6 +10,7 @@ class GuessContentTypeTest extends DAV\AbstractServer {
function setUp() {
parent::setUp();
+ \Sabre\TestUtil::clearTempDir();
file_put_contents(SABRE_TEMPDIR . '/somefile.jpg','blabla');
file_put_contents(SABRE_TEMPDIR . '/somefile.hoi','blabla');
@@ -17,7 +18,7 @@ class GuessContentTypeTest extends DAV\AbstractServer {
function tearDown() {
- unlink(SABRE_TEMPDIR . '/somefile.jpg');
+ \Sabre\TestUtil::clearTempDir();
parent::tearDown();
}
@@ -44,7 +45,7 @@ class GuessContentTypeTest extends DAV\AbstractServer {
);
$result = $this->server->getPropertiesForPath('/somefile.jpg',$properties);
$this->assertArrayHasKey(0,$result);
- $this->assertArrayHasKey(200,$result[0]);
+ $this->assertArrayHasKey(200,$result[0], 'We received: ' . print_r($result,true));
$this->assertArrayHasKey('{DAV:}getcontenttype',$result[0][200]);
$this->assertEquals('image/jpeg',$result[0][200]['{DAV:}getcontenttype']);
@@ -61,8 +62,9 @@ class GuessContentTypeTest extends DAV\AbstractServer {
);
$result = $this->server->getPropertiesForPath('/somefile.hoi',$properties);
$this->assertArrayHasKey(0,$result);
- $this->assertArrayHasKey(404,$result[0]);
- $this->assertArrayHasKey('{DAV:}getcontenttype',$result[0][404]);
+ $this->assertArrayHasKey(200,$result[0]);
+ $this->assertArrayHasKey('{DAV:}getcontenttype',$result[0][200]);
+ $this->assertEquals('application/octet-stream',$result[0][200]['{DAV:}getcontenttype']);
}
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Browser/MapGetToPropFindTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Browser/MapGetToPropFindTest.php
index 169675e7e..9d9fbb319 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Browser/MapGetToPropFindTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Browser/MapGetToPropFindTest.php
@@ -23,21 +23,21 @@ class MapGetToPropFindTest extends DAV\AbstractServer {
'REQUEST_METHOD' => 'GET',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('');
$this->server->httpRequest = ($request);
$this->server->exec();
+ $this->assertEquals(207, $this->response->status,'Incorrect status response received. Full response body: ' . $this->response->body);
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- 'DAV' => '1, 3, extended-mkcol',
- 'Vary' => 'Brief,Prefer',
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ 'DAV' => ['1, 3, extended-mkcol'],
+ 'Vary' => ['Brief,Prefer'],
),
- $this->response->headers
+ $this->response->getHeaders()
);
- $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Incorrect status response received. Full response body: ' . $this->response->body);
-
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Browser/PluginTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Browser/PluginTest.php
index c3c4bdebb..00beea9f2 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Browser/PluginTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Browser/PluginTest.php
@@ -9,80 +9,114 @@ require_once 'Sabre/DAV/AbstractServer.php';
class PluginTest extends DAV\AbstractServer{
+ protected $plugin;
+
function setUp() {
parent::setUp();
- $this->server->addPlugin(new Plugin());
+ $this->server->addPlugin($this->plugin = new Plugin());
+ $this->server->tree->getNodeForPath('')->createDirectory('dir2');
}
function testCollectionGet() {
- $serverVars = array(
- 'REQUEST_URI' => '/dir',
- 'REQUEST_METHOD' => 'GET',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
+ $request = new HTTP\Request('GET', '/dir');
+ $this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
- $this->assertEquals(array(
- 'Content-Type' => 'text/html; charset=utf-8',
- ),
- $this->response->headers
+ $this->assertEquals(200, $this->response->getStatus(), "Incorrect status received. Full response body: " . $this->response->getBodyAsString());
+ $this->assertEquals(
+ [
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['text/html; charset=utf-8'],
+ 'Content-Security-Policy' => ["img-src 'self'; style-src 'self';"]
+ ],
+ $this->response->getHeaders()
);
- $this->assertTrue(strpos($this->response->body, 'Index for dir/') !== false);
- $this->assertTrue(strpos($this->response->body, '<a href="/dir/child.txt"><img src="/?sabreAction=asset&assetName=icons%2Ffile.png" alt="" width="24" />')!==false);
+ $body = $this->response->getBodyAsString();
+ $this->assertTrue(strpos($body, '<title>dir') !== false, $body);
+ $this->assertTrue(strpos($body, '<a href="/dir/child.txt">')!==false);
}
- function testNotFound() {
+ /**
+ * Adding the If-None-Match should have 0 effect, but it threw an error.
+ */
+ function testCollectionGetIfNoneMatch() {
- $serverVars = array(
- 'REQUEST_URI' => '/random',
- 'REQUEST_METHOD' => 'GET',
+ $request = new HTTP\Request('GET', '/dir');
+ $request->setHeader('If-None-Match', '"foo-bar"');
+ $this->server->httpRequest = $request;
+ $this->server->exec();
+
+ $this->assertEquals(200, $this->response->getStatus(), "Incorrect status received. Full response body: " . $this->response->getBodyAsString());
+ $this->assertEquals(
+ [
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['text/html; charset=utf-8'],
+ 'Content-Security-Policy' => ["img-src 'self'; style-src 'self';"]
+ ],
+ $this->response->getHeaders()
);
- $request = new HTTP\Request($serverVars);
+ $body = $this->response->getBodyAsString();
+ $this->assertTrue(strpos($body, '<title>dir') !== false, $body);
+ $this->assertTrue(strpos($body, '<a href="/dir/child.txt">')!==false);
+
+ }
+ function testCollectionGetRoot() {
+
+ $request = new HTTP\Request('GET', '/');
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals('HTTP/1.1 404 Not Found',$this->response->status);
+ $this->assertEquals(200, $this->response->status, "Incorrect status received. Full response body: " . $this->response->getBodyAsString());
+ $this->assertEquals(
+ [
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['text/html; charset=utf-8'],
+ 'Content-Security-Policy' => ["img-src 'self'; style-src 'self';"]
+ ],
+ $this->response->getHeaders()
+ );
+
+ $body = $this->response->getBodyAsString();
+ $this->assertTrue(strpos($body, '<title>/') !== false, $body);
+ $this->assertTrue(strpos($body, '<a href="/dir/">')!==false);
+ $this->assertTrue(strpos($body, '<span class="btn disabled">')!==false);
}
- function testPostOtherContentType() {
+ function testGETPassthru() {
- $serverVars = array(
- 'REQUEST_URI' => '/',
- 'REQUEST_METHOD' => 'POST',
- 'CONTENT_TYPE' => 'text/xml',
+ $request = new HTTP\Request('GET', '/random');
+ $response = new HTTP\Response();
+ $this->assertNull(
+ $this->plugin->httpGet($request, $response)
);
- $request = new HTTP\Request($serverVars);
+
+ }
+
+ function testPostOtherContentType() {
+
+ $request = new HTTP\Request('POST', '/', ['Content-Type' => 'text/xml']);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 501 Not Implemented', $this->response->status);
+ $this->assertEquals(501, $this->response->status);
}
function testPostNoSabreAction() {
- $serverVars = array(
- 'REQUEST_URI' => '/',
- 'REQUEST_METHOD' => 'POST',
- 'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
- );
- $postVars = array();
-
- $request = new HTTP\Request($serverVars,$postVars);
+ $request = new HTTP\Request('POST', '/', ['Content-Type' => 'application/x-www-form-urlencoded']);
+ $request->setPostData([]);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 501 Not Implemented', $this->response->status);
+ $this->assertEquals(501, $this->response->status);
}
@@ -98,17 +132,55 @@ class PluginTest extends DAV\AbstractServer{
'name' => 'new_collection',
);
- $request = new HTTP\Request($serverVars,$postVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
+ $request->setPostData($postVars);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 302 Found', $this->response->status);
+ $this->assertEquals(302, $this->response->status);
$this->assertEquals(array(
- 'Location' => '/',
- ), $this->response->headers);
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Location' => ['/'],
+ ), $this->response->getHeaders());
$this->assertTrue(is_dir(SABRE_TEMPDIR . '/new_collection'));
}
+ function testGetAsset() {
+
+ $request = new HTTP\Request('GET', '/?sabreAction=asset&assetName=favicon.ico');
+ $this->server->httpRequest = $request;
+ $this->server->exec();
+
+ $this->assertEquals(200, $this->response->getStatus(), 'Error: ' . $this->response->body);
+ $this->assertEquals([
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['image/vnd.microsoft.icon'],
+ 'Content-Length' => ['4286'],
+ 'Cache-Control' => ['public, max-age=1209600'],
+ 'Content-Security-Policy' => ["img-src 'self'; style-src 'self';"]
+ ], $this->response->getHeaders());
+
+ }
+
+ function testGetAsset404() {
+
+ $request = new HTTP\Request('GET', '/?sabreAction=asset&assetName=flavicon.ico');
+ $this->server->httpRequest = $request;
+ $this->server->exec();
+
+ $this->assertEquals(404, $this->response->getStatus(), 'Error: ' . $this->response->body);
+
+ }
+
+ function testGetAssetEscapeBasePath() {
+
+ $request = new HTTP\Request('GET', '/?sabreAction=asset&assetName=./../assets/favicon.ico');
+ $this->server->httpRequest = $request;
+ $this->server->exec();
+
+ $this->assertEquals(404, $this->response->getStatus(), 'Error: ' . $this->response->body);
+
+ }
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/ClientMock.php b/vendor/sabre/dav/tests/Sabre/DAV/ClientMock.php
index 6e74e6ec0..d8b53a5a1 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/ClientMock.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/ClientMock.php
@@ -2,21 +2,16 @@
namespace Sabre\DAV;
+use Sabre\HTTP\RequestInterface;
+
class ClientMock extends Client {
+ public $request;
public $response;
public $url;
public $curlSettings;
- protected function curlRequest($url, $curlSettings) {
-
- $this->url = $url;
- $this->curlSettings = $curlSettings;
- return $this->response;
-
- }
-
/**
* Just making this method public
*
@@ -29,4 +24,11 @@ class ClientMock extends Client {
}
+ public function doRequest(RequestInterface $request) {
+
+ $this->request = $request;
+ return $this->response;
+
+ }
+
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/ClientTest.php b/vendor/sabre/dav/tests/Sabre/DAV/ClientTest.php
index 9c3532a47..4cf27dfaa 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/ClientTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/ClientTest.php
@@ -2,15 +2,27 @@
namespace Sabre\DAV;
+use Sabre\HTTP\Request;
+use Sabre\HTTP\Response;
+
require_once 'Sabre/DAV/ClientMock.php';
class ClientTest extends \PHPUnit_Framework_TestCase {
+ function setUp() {
+
+ if (!function_exists('curl_init')) {
+ $this->markTestSkipped('CURL must be installed to test the client');
+ }
+
+ }
+
function testConstruct() {
- $client = new ClientMock(array(
+ $client = new ClientMock([
'baseUri' => '/',
- ));
+ ]);
+ $this->assertInstanceOf('Sabre\DAV\ClientMock', $client);
}
@@ -19,931 +31,276 @@ class ClientTest extends \PHPUnit_Framework_TestCase {
*/
function testConstructNoBaseUri() {
- $client = new ClientMock(array());
+ $client = new ClientMock([]);
}
- function testRequest() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
-
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "Content-Type: text/plain",
- "",
- "Hello there!"
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 45,
- 'http_code' => 200,
- ),
- 0,
- ""
- );
-
- $result = $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain'));
-
- $this->assertEquals('http://example.org/foo/bar/baz', $client->url);
- $this->assertEquals(array(
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_MAXREDIRS => 5,
- CURLOPT_CUSTOMREQUEST => 'POST',
- CURLOPT_POSTFIELDS => 'sillybody',
- CURLOPT_HEADER => true,
- CURLOPT_HTTPHEADER => array('Content-Type: text/plain'),
- ), $client->curlSettings);
+ function testAuth() {
- $this->assertEquals(array(
- 'statusCode' => 200,
- 'headers' => array(
- 'content-type' => 'text/plain',
- ),
- 'body' => 'Hello there!'
- ), $result);
+ $client = new ClientMock([
+ 'baseUri' => '/',
+ 'userName' => 'foo',
+ 'password' => 'bar',
+ ]);
+ $this->assertEquals("foo:bar", $client->curlSettings[CURLOPT_USERPWD]);
+ $this->assertEquals(CURLAUTH_BASIC | CURLAUTH_DIGEST, $client->curlSettings[CURLOPT_HTTPAUTH]);
}
+ function testBasicAuth() {
- function testRequestProxy() {
+ $client = new ClientMock([
+ 'baseUri' => '/',
+ 'userName' => 'foo',
+ 'password' => 'bar',
+ 'authType' => Client::AUTH_BASIC
+ ]);
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- 'proxy' => 'http://localhost:8000/',
- ));
-
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "Content-Type: text/plain",
- "",
- "Hello there!"
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 45,
- 'http_code' => 200,
- ),
- 0,
- ""
- );
-
- $result = $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain'));
-
- $this->assertEquals('http://example.org/foo/bar/baz', $client->url);
- $this->assertEquals(array(
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_MAXREDIRS => 5,
- CURLOPT_CUSTOMREQUEST => 'POST',
- CURLOPT_POSTFIELDS => 'sillybody',
- CURLOPT_HEADER => true,
- CURLOPT_HTTPHEADER => array('Content-Type: text/plain'),
- CURLOPT_PROXY => 'http://localhost:8000/',
- ), $client->curlSettings);
-
- $this->assertEquals(array(
- 'statusCode' => 200,
- 'headers' => array(
- 'content-type' => 'text/plain',
- ),
- 'body' => 'Hello there!'
- ), $result);
+ $this->assertEquals("foo:bar", $client->curlSettings[CURLOPT_USERPWD]);
+ $this->assertEquals(CURLAUTH_BASIC, $client->curlSettings[CURLOPT_HTTPAUTH]);
}
- function testRequestCAInfo() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
+ function testDigestAuth() {
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "Content-Type: text/plain",
- "",
- "Hello there!"
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 45,
- 'http_code' => 200,
- ),
- 0,
- ""
- );
+ $client = new ClientMock([
+ 'baseUri' => '/',
+ 'userName' => 'foo',
+ 'password' => 'bar',
+ 'authType' => Client::AUTH_DIGEST
+ ]);
- $client->addTrustedCertificates('bla');
-
- $result = $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain'));
-
- $this->assertEquals('http://example.org/foo/bar/baz', $client->url);
- $this->assertEquals(array(
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_MAXREDIRS => 5,
- CURLOPT_CUSTOMREQUEST => 'POST',
- CURLOPT_POSTFIELDS => 'sillybody',
- CURLOPT_HEADER => true,
- CURLOPT_CAINFO => 'bla',
- CURLOPT_HTTPHEADER => array('Content-Type: text/plain'),
- ), $client->curlSettings);
+ $this->assertEquals("foo:bar", $client->curlSettings[CURLOPT_USERPWD]);
+ $this->assertEquals(CURLAUTH_DIGEST, $client->curlSettings[CURLOPT_HTTPAUTH]);
}
- function testRequestSslPeer() {
+ function testNTLMAuth() {
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
+ $client = new ClientMock([
+ 'baseUri' => '/',
+ 'userName' => 'foo',
+ 'password' => 'bar',
+ 'authType' => Client::AUTH_NTLM
+ ]);
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "Content-Type: text/plain",
- "",
- "Hello there!"
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 45,
- 'http_code' => 200,
- ),
- 0,
- ""
- );
-
- $client->setVerifyPeer(true);
-
- $result = $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain'));
-
- $this->assertEquals('http://example.org/foo/bar/baz', $client->url);
- $this->assertEquals(array(
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_MAXREDIRS => 5,
- CURLOPT_CUSTOMREQUEST => 'POST',
- CURLOPT_POSTFIELDS => 'sillybody',
- CURLOPT_HEADER => true,
- CURLOPT_HTTPHEADER => array('Content-Type: text/plain'),
- CURLOPT_SSL_VERIFYPEER => true
- ), $client->curlSettings);
-
- }
-
- function testRequestAuth() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- 'userName' => 'user',
- 'password' => 'password',
- ));
-
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "Content-Type: text/plain",
- "",
- "Hello there!"
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 45,
- 'http_code' => 200,
- ),
- 0,
- ""
- );
-
- $result = $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain'));
-
- $this->assertEquals('http://example.org/foo/bar/baz', $client->url);
- $this->assertEquals(array(
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_MAXREDIRS => 5,
- CURLOPT_CUSTOMREQUEST => 'POST',
- CURLOPT_POSTFIELDS => 'sillybody',
- CURLOPT_HEADER => true,
- CURLOPT_HTTPHEADER => array('Content-Type: text/plain'),
- CURLOPT_HTTPAUTH => CURLAUTH_BASIC | CURLAUTH_DIGEST,
- CURLOPT_USERPWD => 'user:password'
- ), $client->curlSettings);
-
- $this->assertEquals(array(
- 'statusCode' => 200,
- 'headers' => array(
- 'content-type' => 'text/plain',
- ),
- 'body' => 'Hello there!'
- ), $result);
-
- }
-
- function testRequestAuthBasic() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- 'userName' => 'user',
- 'password' => 'password',
- 'authType' => Client::AUTH_BASIC,
- ));
-
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "Content-Type: text/plain",
- "",
- "Hello there!"
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 45,
- 'http_code' => 200,
- ),
- 0,
- ""
- );
-
- $result = $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain'));
-
- $this->assertEquals('http://example.org/foo/bar/baz', $client->url);
- $this->assertEquals(array(
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_MAXREDIRS => 5,
- CURLOPT_CUSTOMREQUEST => 'POST',
- CURLOPT_POSTFIELDS => 'sillybody',
- CURLOPT_HEADER => true,
- CURLOPT_HTTPHEADER => array('Content-Type: text/plain'),
- CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
- CURLOPT_USERPWD => 'user:password'
- ), $client->curlSettings);
-
- $this->assertEquals(array(
- 'statusCode' => 200,
- 'headers' => array(
- 'content-type' => 'text/plain',
- ),
- 'body' => 'Hello there!'
- ), $result);
+ $this->assertEquals("foo:bar", $client->curlSettings[CURLOPT_USERPWD]);
+ $this->assertEquals(CURLAUTH_NTLM, $client->curlSettings[CURLOPT_HTTPAUTH]);
}
- function testRequestAuthDigest() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- 'userName' => 'user',
- 'password' => 'password',
- 'authType' => Client::AUTH_DIGEST,
- ));
+ function testProxy() {
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "Content-Type: text/plain",
- "",
- "Hello there!"
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 45,
- 'http_code' => 200,
- ),
- 0,
- ""
- );
-
- $result = $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain'));
-
- $this->assertEquals('http://example.org/foo/bar/baz', $client->url);
- $this->assertEquals(array(
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_MAXREDIRS => 5,
- CURLOPT_CUSTOMREQUEST => 'POST',
- CURLOPT_POSTFIELDS => 'sillybody',
- CURLOPT_HEADER => true,
- CURLOPT_HTTPHEADER => array('Content-Type: text/plain'),
- CURLOPT_HTTPAUTH => CURLAUTH_DIGEST,
- CURLOPT_USERPWD => 'user:password'
- ), $client->curlSettings);
-
- $this->assertEquals(array(
- 'statusCode' => 200,
- 'headers' => array(
- 'content-type' => 'text/plain',
- ),
- 'body' => 'Hello there!'
- ), $result);
-
- }
- function testRequestError() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
-
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "Content-Type: text/plain",
- "",
- "Hello there!"
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 45,
- 'http_code' => 200,
- ),
- CURLE_COULDNT_CONNECT,
- "Could not connect, or something"
- );
+ $client = new ClientMock([
+ 'baseUri' => '/',
+ 'proxy' => 'localhost:8888',
+ ]);
- $caught = false;
- try {
- $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain'));
- } catch (Exception $e) {
- $caught = true;
- }
- if (!$caught) {
- $this->markTestFailed('Exception was not thrown');
- }
+ $this->assertEquals("localhost:8888", $client->curlSettings[CURLOPT_PROXY]);
}
- function testRequestHTTPError() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
+ function testEncoding() {
- $responseBlob = array(
- "HTTP/1.1 400 Bad Request",
- "Content-Type: text/plain",
- "",
- "Hello there!"
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 45,
- 'http_code' => 400,
- ),
- 0,
- ""
- );
+ $client = new ClientMock([
+ 'baseUri' => '/',
+ 'encoding' => Client::ENCODING_IDENTITY | Client::ENCODING_GZIP | Client::ENCODING_DEFLATE,
+ ]);
- $caught = false;
- try {
- $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain'));
- } catch (Exception $e) {
- $caught = true;
- }
- if (!$caught) {
- $this->fail('Exception was not thrown');
- }
+ $this->assertEquals("identity,deflate,gzip", $client->curlSettings[CURLOPT_ENCODING]);
}
- function testRequestHTTP404() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
-
- $responseBlob = array(
- "HTTP/1.1 404 Not Found",
- "Content-Type: text/plain",
- "",
- "Hello there!"
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 45,
- 'http_code' => 404,
- ),
- 0,
- ""
- );
+ function testPropFind() {
- $caught = false;
- try {
- $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain'));
- } catch (Exception\NotFound $e) {
- $caught = true;
- }
- if (!$caught) {
- $this->fail('Exception was not thrown');
- }
+ $client = new ClientMock([
+ 'baseUri' => '/',
+ ]);
+
+ $responseBody = <<<XML
+<?xml version="1.0"?>
+<multistatus xmlns="DAV:">
+ <response>
+ <href>/foo</href>
+ <propstat>
+ <prop>
+ <displayname>bar</displayname>
+ </prop>
+ <status>HTTP/1.1 200 OK</status>
+ </propstat>
+ </response>
+</multistatus>
+XML;
+
+ $client->response = new Response(207, [], $responseBody);
+ $result = $client->propFind('foo', ['{DAV:}displayname', '{urn:zim}gir']);
+
+ $this->assertEquals(['{DAV:}displayname' => 'bar'], $result);
+
+ $request = $client->request;
+ $this->assertEquals('PROPFIND', $request->getMethod());
+ $this->assertEquals('/foo', $request->getUrl());
+ $this->assertEquals([
+ 'Depth' => ['0'],
+ 'Content-Type' => ['application/xml'],
+ ], $request->getHeaders());
}
/**
- * @dataProvider supportedHTTPCodes
+ * @expectedException \Sabre\HTTP\ClientHttpException
*/
- function testSpecificHTTPErrors($error) {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
-
- $responseBlob = array(
- "HTTP/1.1 $error blabla",
- "Content-Type: text/plain",
- "",
- "Hello there!"
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 42,
- 'http_code' => $error,
- ),
- 0,
- ""
- );
-
- try {
- $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain'));
- $this->fail('Exception was not thrown');
- } catch (Exception $e) {
- $this->assertEquals($e->getHTTPCode(), $error);
- }
-
-
- }
-
- public function supportedHTTPCodes() {
-
- return array(
- array(400),
- array(401),
- array(402),
- array(403),
- array(404),
- array(405),
- array(409),
- array(412),
- array(416),
- array(500),
- array(501),
- array(507),
- );
-
- }
-
- function testUnsupportedHTTPError() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
-
- $responseBlob = array(
- "HTTP/1.1 580 blabla",
- "Content-Type: text/plain",
- "",
- "Hello there!"
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 42,
- 'http_code' => "580"
- ),
- 0,
- ""
- );
-
- try {
- $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain'));
- $this->fail('Exception was not thrown');
- } catch (Exception $e) {
- $this->assertEquals(500, $e->getHTTPCode());
- }
-
-
- }
-
- function testGetAbsoluteUrl() {
+ function testPropFindError() {
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/',
- ));
-
- $this->assertEquals(
- 'http://example.org/foo/bar',
- $client->getAbsoluteUrl('bar')
- );
-
- $this->assertEquals(
- 'http://example.org/bar',
- $client->getAbsoluteUrl('/bar')
- );
+ $client = new ClientMock([
+ 'baseUri' => '/',
+ ]);
- $this->assertEquals(
- 'http://example.com/bar',
- $client->getAbsoluteUrl('http://example.com/bar')
- );
+ $client->response = new Response(405, []);
+ $client->propFind('foo', ['{DAV:}displayname', '{urn:zim}gir']);
}
- function testOptions() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
-
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "DAV: feature1, feature2",
- "",
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 40,
- 'http_code' => 200,
- ),
- 0,
- ""
- );
+ function testPropFindDepth1() {
- $result = $client->options();
- $this->assertEquals(
- array('feature1', 'feature2'),
- $result
- );
+ $client = new ClientMock([
+ 'baseUri' => '/',
+ ]);
+
+ $responseBody = <<<XML
+<?xml version="1.0"?>
+<multistatus xmlns="DAV:">
+ <response>
+ <href>/foo</href>
+ <propstat>
+ <prop>
+ <displayname>bar</displayname>
+ </prop>
+ <status>HTTP/1.1 200 OK</status>
+ </propstat>
+ </response>
+</multistatus>
+XML;
+
+ $client->response = new Response(207, [], $responseBody);
+ $result = $client->propFind('foo', ['{DAV:}displayname', '{urn:zim}gir'], 1);
+
+ $this->assertEquals([
+ '/foo' => [
+ '{DAV:}displayname' => 'bar'
+ ],
+ ], $result);
+
+ $request = $client->request;
+ $this->assertEquals('PROPFIND', $request->getMethod());
+ $this->assertEquals('/foo', $request->getUrl());
+ $this->assertEquals([
+ 'Depth' => ['1'],
+ 'Content-Type' => ['application/xml'],
+ ], $request->getHeaders());
}
- function testOptionsNoDav() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
-
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "",
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 20,
- 'http_code' => 200,
- ),
- 0,
- ""
- );
+ function testPropPatch() {
- $result = $client->options();
- $this->assertEquals(
- array(),
- $result
- );
+ $client = new ClientMock([
+ 'baseUri' => '/',
+ ]);
+
+ $responseBody = <<<XML
+<?xml version="1.0"?>
+<multistatus xmlns="DAV:">
+ <response>
+ <href>/foo</href>
+ <propstat>
+ <prop>
+ <displayname>bar</displayname>
+ </prop>
+ <status>HTTP/1.1 200 OK</status>
+ </propstat>
+ </response>
+</multistatus>
+XML;
+
+ $client->response = new Response(207, [], $responseBody);
+ $result = $client->propPatch('foo', ['{DAV:}displayname' => 'hi', '{urn:zim}gir' => null], 1);
+ $this->assertTrue($result);
+ $request = $client->request;
+ $this->assertEquals('PROPPATCH', $request->getMethod());
+ $this->assertEquals('/foo', $request->getUrl());
+ $this->assertEquals([
+ 'Content-Type' => ['application/xml'],
+ ], $request->getHeaders());
}
/**
- * @expectedException InvalidArgumentException
+ * @depends testPropPatch
+ * @expectedException \Sabre\HTTP\ClientHttpException
*/
- function testPropFindNoXML() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
-
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "",
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 20,
- 'http_code' => 200,
- ),
- 0,
- ""
- );
+ function testPropPatchHTTPError() {
- $client->propfind('', array('{DAV:}foo','{DAV:}bar'));
-
- }
-
- function testPropFind() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
-
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "",
- "<?xml version=\"1.0\"?>",
- "<d:multistatus xmlns:d=\"DAV:\">",
- " <d:response>",
- " <d:href>/foo/bar/</d:href>",
- " <d:propstat>",
- " <d:prop>",
- " <d:foo>hello</d:foo>",
- " </d:prop>",
- " <d:status>HTTP/1.1 200 OK</d:status>",
- " </d:propstat>",
- " <d:propstat>",
- " <d:prop>",
- " <d:bar />",
- " </d:prop>",
- " <d:status>HTTP/1.1 404 Not Found</d:status>",
- " </d:propstat>",
- " </d:response>",
- "</d:multistatus>",
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 19,
- 'http_code' => 200,
- ),
- 0,
- ""
- );
-
- $result = $client->propfind('', array('{DAV:}foo','{DAV:}bar'));
-
- $this->assertEquals(array(
- '{DAV:}foo' => 'hello',
- ), $result);
-
- $requestBody = array(
- '<?xml version="1.0"?>',
- '<d:propfind xmlns:d="DAV:">',
- ' <d:prop>',
- ' <d:foo />',
- ' <d:bar />',
- ' </d:prop>',
- '</d:propfind>'
- );
- $requestBody = implode("\n", $requestBody);
+ $client = new ClientMock([
+ 'baseUri' => '/',
+ ]);
- $this->assertEquals($requestBody, $client->curlSettings[CURLOPT_POSTFIELDS]);
+ $client->response = new Response(403, [], '');
+ $client->propPatch('foo', ['{DAV:}displayname' => 'hi', '{urn:zim}gir' => null], 1);
}
/**
- * This was reported in Issue 235.
- *
- * If no '200 Ok' properties are returned, the client will throw an
- * E_NOTICE.
+ * @depends testPropPatch
+ * @expectedException Sabre\HTTP\ClientException
*/
- function testPropFindNo200s() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
-
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "",
- "<?xml version=\"1.0\"?>",
- "<d:multistatus xmlns:d=\"DAV:\">",
- " <d:response>",
- " <d:href>/foo/bar/</d:href>",
- " <d:propstat>",
- " <d:prop>",
- " <d:bar />",
- " </d:prop>",
- " <d:status>HTTP/1.1 404 Not Found</d:status>",
- " </d:propstat>",
- " </d:response>",
- "</d:multistatus>",
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 19,
- 'http_code' => 200,
- ),
- 0,
- ""
- );
-
- $result = $client->propfind('', array('{DAV:}foo','{DAV:}bar'));
-
- $this->assertEquals(array(
- ), $result);
-
- }
-
- function testPropFindDepth1CustomProp() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
-
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "",
- "<?xml version=\"1.0\"?>",
- "<d:multistatus xmlns:d=\"DAV:\" xmlns:x=\"urn:custom\">",
- " <d:response>",
- " <d:href>/foo/bar/</d:href>",
- " <d:propstat>",
- " <d:prop>",
- " <d:foo>hello</d:foo>",
- " <x:bar>world</x:bar>",
- " </d:prop>",
- " <d:status>HTTP/1.1 200 OK</d:status>",
- " </d:propstat>",
- " </d:response>",
- "</d:multistatus>",
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 19,
- 'http_code' => 200,
- ),
- 0,
- ""
- );
-
- $result = $client->propfind('', array('{DAV:}foo','{urn:custom}bar'),1);
-
- $this->assertEquals(array(
- "/foo/bar/" => array(
- '{DAV:}foo' => 'hello',
- '{urn:custom}bar' => 'world',
- ),
- ), $result);
-
- $requestBody = array(
- '<?xml version="1.0"?>',
- '<d:propfind xmlns:d="DAV:">',
- ' <d:prop>',
- ' <d:foo />',
- ' <x:bar xmlns:x="urn:custom"/>',
- ' </d:prop>',
- '</d:propfind>'
- );
- $requestBody = implode("\n", $requestBody);
-
- $this->assertEquals($requestBody, $client->curlSettings[CURLOPT_POSTFIELDS]);
-
- }
-
- function testPropPatch() {
-
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
-
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "",
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 20,
- 'http_code' => 200,
- ),
- 0,
- ""
- );
-
- $client->proppatch('', array(
- '{DAV:}foo' => 'newvalue',
- '{urn:custom}foo' => 'newvalue2',
- '{DAV:}bar' => null,
- '{urn:custom}bar' => null,
- ));
-
- $requestBody = array(
- '<?xml version="1.0"?>',
- '<d:propertyupdate xmlns:d="DAV:">',
- '<d:set><d:prop>',
- ' <d:foo>newvalue</d:foo>',
- '</d:prop></d:set>',
- '<d:set><d:prop>',
- ' <x:foo xmlns:x="urn:custom">newvalue2</x:foo>',
- '</d:prop></d:set>',
- '<d:remove><d:prop>',
- ' <d:bar />',
- '</d:prop></d:remove>',
- '<d:remove><d:prop>',
- ' <x:bar xmlns:x="urn:custom"/>',
- '</d:prop></d:remove>',
- '</d:propertyupdate>'
- );
- $requestBody = implode("\n", $requestBody);
-
- $this->assertEquals($requestBody, $client->curlSettings[CURLOPT_POSTFIELDS]);
-
- }
-
- function testHEADRequest() {
+ function testPropPatchMultiStatusError() {
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
-
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "Content-Type: text/plain",
- "",
- "Hello there!"
- );
-
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 45,
- 'http_code' => 200,
- ),
- 0,
- ""
- );
-
- $result = $client->request('HEAD', 'baz');
-
- $this->assertEquals('http://example.org/foo/bar/baz', $client->url);
- $this->assertEquals(array(
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_MAXREDIRS => 5,
- CURLOPT_CUSTOMREQUEST => 'HEAD',
- CURLOPT_NOBODY => true,
- CURLOPT_HEADER => true,
- CURLOPT_HTTPHEADER => array(),
- CURLOPT_POSTFIELDS => null,
- ), $client->curlSettings);
+ $client = new ClientMock([
+ 'baseUri' => '/',
+ ]);
+
+ $responseBody = <<<XML
+<?xml version="1.0"?>
+<multistatus xmlns="DAV:">
+<response>
+ <href>/foo</href>
+ <propstat>
+ <prop>
+ <displayname />
+ </prop>
+ <status>HTTP/1.1 403 Forbidden</status>
+ </propstat>
+</response>
+</multistatus>
+XML;
+
+ $client->response = new Response(207, [], $responseBody);
+ $client->propPatch('foo', ['{DAV:}displayname' => 'hi', '{urn:zim}gir' => null], 1);
}
- function testPUTRequest() {
+ function testOPTIONS() {
- $client = new ClientMock(array(
- 'baseUri' => 'http://example.org/foo/bar/',
- ));
+ $client = new ClientMock([
+ 'baseUri' => '/',
+ ]);
- $responseBlob = array(
- "HTTP/1.1 200 OK",
- "Content-Type: text/plain",
- "",
- "Hello there!"
- );
+ $client->response = new Response(207, [
+ 'DAV' => 'calendar-access, extended-mkcol',
+ ]);
+ $result = $client->options();
- $client->response = array(
- implode("\r\n", $responseBlob),
- array(
- 'header_size' => 45,
- 'http_code' => 200,
- ),
- 0,
- ""
+ $this->assertEquals(
+ ['calendar-access', 'extended-mkcol'],
+ $result
);
- $result = $client->request('PUT', 'bar','newcontent');
-
- $this->assertEquals('http://example.org/foo/bar/bar', $client->url);
- $this->assertEquals(array(
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_MAXREDIRS => 5,
- CURLOPT_CUSTOMREQUEST => "PUT",
- CURLOPT_POSTFIELDS => 'newcontent',
- CURLOPT_HEADER => true,
- CURLOPT_HTTPHEADER => array(),
- ), $client->curlSettings);
+ $request = $client->request;
+ $this->assertEquals('OPTIONS', $request->getMethod());
+ $this->assertEquals('/', $request->getUrl());
+ $this->assertEquals([
+ ], $request->getHeaders());
}
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/FSExt/FileTest.php b/vendor/sabre/dav/tests/Sabre/DAV/FSExt/FileTest.php
index 8947c6688..3708594e0 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/FSExt/FileTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/FSExt/FileTest.php
@@ -22,73 +22,90 @@ class FileTest extends \PHPUnit_Framework_TestCase {
function testPut() {
- $file = new File(SABRE_TEMPDIR . '/file.txt');
- $result = $file->put('New contents');
-
- $this->assertEquals('New contents',file_get_contents(SABRE_TEMPDIR . '/file.txt'));
- $this->assertEquals('"' . md5('New contents') . '"', $result);
+ $filename = SABRE_TEMPDIR . '/file.txt';
+ $file = new File($filename);
+ $result = $file->put('New contents');
+
+ $this->assertEquals('New contents',file_get_contents(SABRE_TEMPDIR . '/file.txt'));
+ $this->assertEquals(
+ '"' .
+ sha1(
+ fileinode($filename) .
+ filesize($filename ) .
+ filemtime($filename)
+ ) . '"',
+ $result
+ );
}
function testRange() {
- $file = new File(SABRE_TEMPDIR . '/file.txt');
- $file->put('0000000');
- $file->patch('111', 2, 3);
+ $file = new File(SABRE_TEMPDIR . '/file.txt');
+ $file->put('0000000');
+ $file->patch('111', 2, 3);
- $this->assertEquals('0001110',file_get_contents(SABRE_TEMPDIR . '/file.txt'));
+ $this->assertEquals('0001110',file_get_contents(SABRE_TEMPDIR . '/file.txt'));
}
function testRangeStream() {
- $stream = fopen('php://memory','r+');
- fwrite($stream, "222");
- rewind($stream);
+ $stream = fopen('php://memory','r+');
+ fwrite($stream, "222");
+ rewind($stream);
- $file = new File(SABRE_TEMPDIR . '/file.txt');
- $file->put('0000000');
- $file->patch($stream, 2, 3);
+ $file = new File(SABRE_TEMPDIR . '/file.txt');
+ $file->put('0000000');
+ $file->patch($stream, 2, 3);
- $this->assertEquals('0002220',file_get_contents(SABRE_TEMPDIR . '/file.txt'));
+ $this->assertEquals('0002220',file_get_contents(SABRE_TEMPDIR . '/file.txt'));
}
function testGet() {
- $file = new File(SABRE_TEMPDIR . '/file.txt');
- $this->assertEquals('Contents',stream_get_contents($file->get()));
+ $file = new File(SABRE_TEMPDIR . '/file.txt');
+ $this->assertEquals('Contents',stream_get_contents($file->get()));
}
function testDelete() {
- $file = new File(SABRE_TEMPDIR . '/file.txt');
- $file->delete();
+ $file = new File(SABRE_TEMPDIR . '/file.txt');
+ $file->delete();
- $this->assertFalse(file_exists(SABRE_TEMPDIR . '/file.txt'));
+ $this->assertFalse(file_exists(SABRE_TEMPDIR . '/file.txt'));
}
function testGetETag() {
- $file = new File(SABRE_TEMPDIR . '/file.txt');
- $this->assertEquals('"' . md5('Contents') . '"',$file->getETag());
-
+ $filename = SABRE_TEMPDIR . '/file.txt';
+ $file = new File($filename);
+ $this->assertEquals(
+ '"' .
+ sha1(
+ fileinode($filename) .
+ filesize($filename ) .
+ filemtime($filename)
+ ) . '"',
+ $file->getETag()
+ );
}
function testGetContentType() {
- $file = new File(SABRE_TEMPDIR . '/file.txt');
- $this->assertNull($file->getContentType());
+ $file = new File(SABRE_TEMPDIR . '/file.txt');
+ $this->assertNull($file->getContentType());
}
function testGetSize() {
- $file = new File(SABRE_TEMPDIR . '/file.txt');
- $this->assertEquals(8,$file->getSize());
+ $file = new File(SABRE_TEMPDIR . '/file.txt');
+ $this->assertEquals(8,$file->getSize());
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/FSExt/NodeTest.php b/vendor/sabre/dav/tests/Sabre/DAV/FSExt/NodeTest.php
deleted file mode 100644
index 275075b4c..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAV/FSExt/NodeTest.php
+++ /dev/null
@@ -1,178 +0,0 @@
-<?php
-
-namespace Sabre\DAV\FSExt;
-use Sabre\DAV;
-
-require_once 'Sabre/TestUtil.php';
-
-class NodeTest extends \PHPUnit_Framework_TestCase {
-
- function setUp() {
-
- mkdir(SABRE_TEMPDIR . '/dir');
- file_put_contents(SABRE_TEMPDIR . '/dir/file.txt', 'Contents');
- file_put_contents(SABRE_TEMPDIR . '/dir/file2.txt', 'Contents2');
-
- }
-
- function tearDown() {
-
- \Sabre\TestUtil::clearTempDir();
-
- }
-
- function testUpdateProperties() {
-
- $file = new File(SABRE_TEMPDIR . '/dir/file.txt');
- $properties = array(
- '{http://sabredav.org/NS/2010}test1' => 'foo',
- '{http://sabredav.org/NS/2010}test2' => 'bar',
- );
-
- $result = $file->updateProperties($properties);
- $expected = true;
-
- $this->assertEquals($expected, $result);
-
- $getProperties = $file->getProperties(array_keys($properties));
-
- $this->assertEquals($properties, $getProperties);
-
- }
-
- /**
- * @depends testUpdateProperties
- */
- function testUpdatePropertiesAgain() {
-
- $file = new File(SABRE_TEMPDIR . '/dir/file.txt');
- $mutations = array(
- '{http://sabredav.org/NS/2010}test1' => 'foo',
- '{http://sabredav.org/NS/2010}test2' => 'bar',
- );
-
- $result = $file->updateProperties($mutations);
-
- $this->assertEquals(true, $result);
-
- $mutations = array(
- '{http://sabredav.org/NS/2010}test1' => 'foo',
- '{http://sabredav.org/NS/2010}test3' => 'baz',
- );
-
- $result = $file->updateProperties($mutations);
-
- $this->assertEquals(true, $result);
- }
-
- /**
- * @depends testUpdateProperties
- */
- function testUpdatePropertiesDelete() {
-
- $file = new File(SABRE_TEMPDIR . '/dir/file.txt');
-
- $mutations = array(
- '{http://sabredav.org/NS/2010}test1' => 'foo',
- '{http://sabredav.org/NS/2010}test2' => 'bar',
- );
-
- $result = $file->updateProperties($mutations);
-
- $this->assertEquals(true, $result);
-
- $mutations = array(
- '{http://sabredav.org/NS/2010}test1' => null,
- '{http://sabredav.org/NS/2010}test3' => null
- );
-
- $result = $file->updateProperties($mutations);
-
- $this->assertEquals(true, $result);
-
- $properties = $file->getProperties(array('http://sabredav.org/NS/2010}test1','{http://sabredav.org/NS/2010}test2','{http://sabredav.org/NS/2010}test3'));
-
- $this->assertEquals(array(
- '{http://sabredav.org/NS/2010}test2' => 'bar',
- ), $properties);
- }
-
- /**
- * @depends testUpdateProperties
- */
- function testUpdatePropertiesMove() {
-
- $file = new File(SABRE_TEMPDIR . '/dir/file.txt');
-
- $mutations = array(
- '{http://sabredav.org/NS/2010}test1' => 'foo',
- '{http://sabredav.org/NS/2010}test2' => 'bar',
- );
-
- $result = $file->updateProperties($mutations);
-
- $this->assertEquals(true, $result);
-
- $properties = $file->getProperties(array('{http://sabredav.org/NS/2010}test1','{http://sabredav.org/NS/2010}test2','{http://sabredav.org/NS/2010}test3'));
-
- $this->assertEquals(array(
- '{http://sabredav.org/NS/2010}test1' => 'foo',
- '{http://sabredav.org/NS/2010}test2' => 'bar',
- ), $properties);
-
- // Renaming
- $file->setName('file3.txt');
-
- $this->assertFalse(file_exists(SABRE_TEMPDIR . '/dir/file.txt'));
- $this->assertTrue(file_exists(SABRE_TEMPDIR . '/dir/file3.txt'));
- $this->assertEquals('file3.txt',$file->getName());
-
- $newFile = new File(SABRE_TEMPDIR . '/dir/file3.txt');
- $this->assertEquals('file3.txt',$newFile->getName());
-
- $properties = $newFile->getProperties(array('{http://sabredav.org/NS/2010}test1','{http://sabredav.org/NS/2010}test2','{http://sabredav.org/NS/2010}test3'));
-
- $this->assertEquals(array(
- '{http://sabredav.org/NS/2010}test1' => 'foo',
- '{http://sabredav.org/NS/2010}test2' => 'bar',
- ), $properties);
- }
-
- /**
- * @depends testUpdatePropertiesMove
- */
- function testUpdatePropertiesDeleteBleed() {
-
- $file = new File(SABRE_TEMPDIR . '/dir/file.txt');
- $mutations = array(
- '{http://sabredav.org/NS/2010}test1' => 'foo',
- '{http://sabredav.org/NS/2010}test2' => 'bar',
- );
-
- $result = $file->updateProperties($mutations);
-
- $this->assertEquals(true, $result);
-
- $properties = $file->getProperties(array('{http://sabredav.org/NS/2010}test1','{http://sabredav.org/NS/2010}test2','{http://sabredav.org/NS/2010}test3'));
-
- $this->assertEquals(array(
- '{http://sabredav.org/NS/2010}test1' => 'foo',
- '{http://sabredav.org/NS/2010}test2' => 'bar',
- ), $properties);
-
- // Deleting
- $file->delete();
-
- $this->assertFalse(file_exists(SABRE_TEMPDIR . '/dir/file.txt'));
-
- // Creating it again
- file_put_contents(SABRE_TEMPDIR . '/dir/file.txt','New Contents');
- $file = new File(SABRE_TEMPDIR . '/dir/file.txt');
-
- $properties = $file->getProperties(array('http://sabredav.org/NS/2010}test1','{http://sabredav.org/NS/2010}test2','{http://sabredav.org/NS/2010}test3'));
-
- $this->assertEquals(array(), $properties);
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/FSExt/ServerTest.php b/vendor/sabre/dav/tests/Sabre/DAV/FSExt/ServerTest.php
index 907ede40b..63d858de1 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/FSExt/ServerTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/FSExt/ServerTest.php
@@ -17,116 +17,98 @@ class ServerTest extends DAV\AbstractServer{
function testGet() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'GET',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
+ $request = new HTTP\Request('GET', '/test.txt');
+ $filename = $this->tempDir . '/test.txt';
+ $this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals(array(
- 'Content-Type' => 'application/octet-stream',
- 'Content-Length' => 13,
- 'Last-Modified' => HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt'))),
- 'ETag' => '"' .md5_file($this->tempDir . '/test.txt') . '"',
- ),
- $this->response->headers
+ $this->assertEquals(200, $this->response->getStatus(), 'Invalid status code received.');
+ $this->assertEquals([
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/octet-stream'],
+ 'Content-Length' => [13],
+ 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($filename)))],
+ 'ETag' => ['"' . sha1(fileinode($filename ) . filesize($filename) . filemtime($filename)) . '"'],
+ ],
+ $this->response->getHeaders()
);
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+
$this->assertEquals('Test contents', stream_get_contents($this->response->body));
}
function testHEAD() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'HEAD',
- );
-
- $request = new HTTP\Request($serverVars);
+ $request = new HTTP\Request('HEAD', '/test.txt');
+ $filename = $this->tempDir . '/test.txt';
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals(array(
- 'Content-Type' => 'application/octet-stream',
- 'Content-Length' => 13,
- 'Last-Modified' => HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt'))),
- 'ETag' => '"' . md5_file($this->tempDir . '/test.txt') . '"',
- ),
- $this->response->headers
+ $this->assertEquals([
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/octet-stream'],
+ 'Content-Length' => [13],
+ 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))],
+ 'ETag' => ['"' . sha1(fileinode($filename ) . filesize($filename) . filemtime($filename)) . '"'],
+ ],
+ $this->response->getHeaders()
);
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200,$this->response->status);
$this->assertEquals('', $this->response->body);
}
function testPut() {
- $serverVars = array(
- 'REQUEST_URI' => '/testput.txt',
- 'REQUEST_METHOD' => 'PUT',
- );
-
- $request = new HTTP\Request($serverVars);
+ $request = new HTTP\Request('PUT', '/testput.txt');
+ $filename = $this->tempDir . '/testput.txt';
$request->setBody('Testing new file');
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals(array(
- 'Content-Length' => 0,
- 'ETag' => '"' . md5('Testing new file') . '"',
- ), $this->response->headers);
+ $this->assertEquals([
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Length' => ['0'],
+ 'ETag' => ['"' . sha1(fileinode($filename ) . filesize($filename) . filemtime($filename)) . '"'],
+ ], $this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
+ $this->assertEquals(201, $this->response->status);
$this->assertEquals('', $this->response->body);
- $this->assertEquals('Testing new file',file_get_contents($this->tempDir . '/testput.txt'));
+ $this->assertEquals('Testing new file',file_get_contents($filename));
}
function testPutAlreadyExists() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'PUT',
- 'HTTP_IF_NONE_MATCH' => '*',
- );
-
- $request = new HTTP\Request($serverVars);
+ $request = new HTTP\Request('PUT', '/test.txt', ['If-None-Match' => '*']);
$request->setBody('Testing new file');
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),$this->response->headers);
+ $this->assertEquals([
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ],$this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 412 Precondition failed',$this->response->status);
+ $this->assertEquals(412, $this->response->status);
$this->assertNotEquals('Testing new file',file_get_contents($this->tempDir . '/test.txt'));
}
function testMkcol() {
- $serverVars = array(
- 'REQUEST_URI' => '/testcol',
- 'REQUEST_METHOD' => 'MKCOL',
- );
-
- $request = new HTTP\Request($serverVars);
- $request->setBody("");
+ $request = new HTTP\Request('MKCOL', '/testcol');
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals(array(
- 'Content-Length' => '0',
- ),$this->response->headers);
+ $this->assertEquals([
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Length' => ['0'],
+ ],$this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
+ $this->assertEquals(201, $this->response->status);
$this->assertEquals('', $this->response->body);
$this->assertTrue(is_dir($this->tempDir . '/testcol'));
@@ -134,19 +116,14 @@ class ServerTest extends DAV\AbstractServer{
function testPutUpdate() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'PUT',
- );
-
- $request = new HTTP\Request($serverVars);
+ $request = new HTTP\Request('PUT', '/test.txt');
$request->setBody('Testing updated file');
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals('0', $this->response->headers['Content-Length']);
+ $this->assertEquals('0', $this->response->getHeader('Content-Length'));
- $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status);
+ $this->assertEquals(204, $this->response->status);
$this->assertEquals('', $this->response->body);
$this->assertEquals('Testing updated file',file_get_contents($this->tempDir . '/test.txt'));
@@ -154,20 +131,16 @@ class ServerTest extends DAV\AbstractServer{
function testDelete() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'DELETE',
- );
-
- $request = new HTTP\Request($serverVars);
+ $request = new HTTP\Request('DELETE', '/test.txt');
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals(array(
- 'Content-Length' => '0',
- ),$this->response->headers);
+ $this->assertEquals([
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Length' => ['0'],
+ ],$this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status);
+ $this->assertEquals(204, $this->response->status);
$this->assertEquals('', $this->response->body);
$this->assertFalse(file_exists($this->tempDir . '/test.txt'));
@@ -175,50 +148,99 @@ class ServerTest extends DAV\AbstractServer{
function testDeleteDirectory() {
- $serverVars = array(
- 'REQUEST_URI' => '/testcol',
- 'REQUEST_METHOD' => 'DELETE',
- );
-
mkdir($this->tempDir.'/testcol');
file_put_contents($this->tempDir.'/testcol/test.txt','Hi! I\'m a file with a short lifespan');
- $request = new HTTP\Request($serverVars);
+ $request = new HTTP\Request('DELETE', '/testcol');
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals(array(
- 'Content-Length' => '0',
- ),$this->response->headers);
- $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status);
+ $this->assertEquals([
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Length' => ['0'],
+ ],$this->response->getHeaders());
+ $this->assertEquals(204, $this->response->status);
$this->assertEquals('', $this->response->body);
- $this->assertFalse(file_exists($this->tempDir . '/col'));
+ $this->assertFalse(file_exists($this->tempDir . '/testcol'));
}
function testOptions() {
- $serverVars = array(
- 'REQUEST_URI' => '/',
- 'REQUEST_METHOD' => 'OPTIONS',
- );
-
- $request = new HTTP\Request($serverVars);
+ $request = new HTTP\Request('OPTIONS', '/');
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals(array(
- 'DAV' => '1, 3, extended-mkcol',
- 'MS-Author-Via' => 'DAV',
- 'Allow' => 'OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT',
- 'Accept-Ranges' => 'bytes',
- 'Content-Length' => '0',
- 'X-Sabre-Version'=> DAV\Version::VERSION,
- ),$this->response->headers);
+ $this->assertEquals([
+ 'DAV' => ['1, 3, extended-mkcol'],
+ 'MS-Author-Via' => ['DAV'],
+ 'Allow' => ['OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT'],
+ 'Accept-Ranges' => ['bytes'],
+ 'Content-Length' => ['0'],
+ 'X-Sabre-Version'=> [DAV\Version::VERSION],
+ ], $this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200, $this->response->status);
$this->assertEquals('', $this->response->body);
}
+ function testMove() {
+
+ mkdir($this->tempDir.'/testcol');
+
+ $request = new HTTP\Request('MOVE', '/test.txt', ['Destination' => '/testcol/test2.txt']);
+ $this->server->httpRequest = ($request);
+ $this->server->exec();
+
+ $this->assertEquals(201, $this->response->status);
+ $this->assertEquals('', $this->response->body);
+
+ $this->assertEquals([
+ 'Content-Length' => ['0'],
+ 'X-Sabre-Version'=> [DAV\Version::VERSION],
+ ],$this->response->getHeaders());
+
+ $this->assertTrue(
+ is_file($this->tempDir . '/testcol/test2.txt')
+ );
+
+
+ }
+
+ /**
+ * This test checks if it's possible to move a non-FSExt collection into a
+ * FSExt collection.
+ *
+ * The moveInto function *should* ignore the object and let sabredav itself
+ * execute the slow move.
+ */
+ function testMoveOtherObject() {
+
+ mkdir($this->tempDir.'/tree1');
+ mkdir($this->tempDir.'/tree2');
+
+ $tree = new DAV\Tree(new DAV\SimpleCollection('root', [
+ new DAV\FS\Directory($this->tempDir . '/tree1'),
+ new DAV\FSExt\Directory($this->tempDir . '/tree2'),
+ ]));
+ $this->server->tree = $tree;
+
+ $request = new HTTP\Request('MOVE', '/tree1', ['Destination' => '/tree2/tree1']);
+ $this->server->httpRequest = ($request);
+ $this->server->exec();
+
+ $this->assertEquals(201, $this->response->status);
+ $this->assertEquals('', $this->response->body);
+
+ $this->assertEquals([
+ 'Content-Length' => ['0'],
+ 'X-Sabre-Version'=> [DAV\Version::VERSION],
+ ],$this->response->getHeaders());
+
+ $this->assertTrue(
+ is_dir($this->tempDir . '/tree2/tree1')
+ );
+
+ }
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/HTTPPreferParsingTest.php b/vendor/sabre/dav/tests/Sabre/DAV/HTTPPreferParsingTest.php
index 45865b2a1..cd8bee968 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/HTTPPreferParsingTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/HTTPPreferParsingTest.php
@@ -4,105 +4,96 @@ namespace Sabre\DAV;
use Sabre\HTTP;
-class HTTPPReferParsingTest extends \Sabre\DAVServerTest {
+class HTTPPreferParsingTest extends \Sabre\DAVServerTest {
function testParseSimple() {
- $httpRequest = new HTTP\Request(array(
+ $httpRequest = HTTP\Sapi::createFromServerArray([
'HTTP_PREFER' => 'return-asynch',
- ));
+ ]);
$server = new Server();
$server->httpRequest = $httpRequest;
- $this->assertEquals(array(
- 'return-asynch' => true,
- 'return-minimal' => false,
- 'return-representation' => false,
- 'strict' => false,
- 'lenient' => false,
- 'wait' => null,
- ), $server->getHTTPPrefer());
+ $this->assertEquals([
+ 'respond-async' => true,
+ 'return' => null,
+ 'handling' => null,
+ 'wait' => null,
+ ], $server->getHTTPPrefer());
}
function testParseValue() {
- $httpRequest = new HTTP\Request(array(
+ $httpRequest = HTTP\Sapi::createFromServerArray([
'HTTP_PREFER' => 'wait=10',
- ));
+ ]);
$server = new Server();
$server->httpRequest = $httpRequest;
- $this->assertEquals(array(
- 'return-asynch' => false,
- 'return-minimal' => false,
- 'return-representation' => false,
- 'strict' => false,
- 'lenient' => false,
- 'wait' => 10,
- ), $server->getHTTPPrefer());
+ $this->assertEquals([
+ 'respond-async' => false,
+ 'return' => null,
+ 'handling' => null,
+ 'wait' => '10',
+ ], $server->getHTTPPrefer());
}
function testParseMultiple() {
- $httpRequest = new HTTP\Request(array(
+ $httpRequest = HTTP\Sapi::createFromServerArray([
'HTTP_PREFER' => 'return-minimal, strict,lenient',
- ));
+ ]);
$server = new Server();
$server->httpRequest = $httpRequest;
- $this->assertEquals(array(
- 'return-asynch' => false,
- 'return-minimal' => true,
- 'return-representation' => false,
- 'strict' => true,
- 'lenient' => true,
- 'wait' => null,
- ), $server->getHTTPPrefer());
+ $this->assertEquals([
+ 'respond-async' => false,
+ 'return' => 'minimal',
+ 'handling' => 'lenient',
+ 'wait' => null,
+ ], $server->getHTTPPrefer());
}
function testParseWeirdValue() {
- $httpRequest = new HTTP\Request(array(
+ $httpRequest = HTTP\Sapi::createFromServerArray([
'HTTP_PREFER' => 'BOOOH',
- ));
+ ]);
$server = new Server();
$server->httpRequest = $httpRequest;
- $this->assertEquals(array(
- 'strict' => false,
- 'lenient' => false,
- 'wait' => null,
- 'return-asynch' => false,
- 'return-minimal' => false,
- 'return-representation' => false,
- ), $server->getHTTPPrefer());
+ $this->assertEquals([
+ 'respond-async' => false,
+ 'return' => null,
+ 'handling' => null,
+ 'wait' => null,
+ 'boooh' => true,
+ ], $server->getHTTPPrefer());
}
function testBrief() {
- $httpRequest = new HTTP\Request(array(
+ $httpRequest = HTTP\Sapi::createFromServerArray([
'HTTP_BRIEF' => 't',
- ));
+ ]);
$server = new Server();
$server->httpRequest = $httpRequest;
- $this->assertEquals(array(
- 'strict' => false,
- 'lenient' => false,
- 'wait' => null,
- 'return-asynch' => false,
- 'return-minimal' => true,
- 'return-representation' => false,
- ), $server->getHTTPPrefer());
+ $this->assertEquals([
+ 'respond-async' => false,
+ 'return' => 'minimal',
+ 'handling' => null,
+ 'wait' => null,
+ ], $server->getHTTPPrefer());
}
@@ -113,11 +104,11 @@ class HTTPPReferParsingTest extends \Sabre\DAVServerTest {
*/
function testpropfindMinimal() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray([
'REQUEST_METHOD' => 'PROPFIND',
'REQUEST_URI' => '/',
- 'HTTP_PREFER' => 'return-minimal',
- ));
+ 'HTTP_PREFER' => 'return-minimal',
+ ]);
$request->setBody(<<<BLA
<?xml version="1.0"?>
<d:propfind xmlns:d="DAV:">
@@ -131,70 +122,67 @@ BLA
$response = $this->request($request);
- $this->assertTrue(strpos($response->body, 'resourcetype')!==false);
- $this->assertTrue(strpos($response->body, 'something')===false);
+ $body = $response->getBodyAsString();
+
+ $this->assertEquals(207, $response->getStatus(), $body);
+
+ $this->assertTrue(strpos($body, 'resourcetype') !== false, $body);
+ $this->assertTrue(strpos($body, 'something') === false, $body);
}
function testproppatchMinimal() {
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'PROPPATCH',
- 'REQUEST_URI' => '/',
- 'HTTP_PREFER' => 'return-minimal',
- ));
+ $request = new HTTP\Request('PROPPATCH', '/', ['Prefer' => 'return-minimal']);
$request->setBody(<<<BLA
<?xml version="1.0"?>
-<d:proppatch xmlns:d="DAV:">
+<d:propertyupdate xmlns:d="DAV:">
<d:set>
<d:prop>
<d:something>nope!</d:something>
</d:prop>
</d:set>
-</d:proppatch>
+</d:propertyupdate>
BLA
);
- $this->server->subscribeEvent('updateProperties', function(&$props, &$result) {
+ $this->server->on('propPatch', function($path, PropPatch $propPatch) {
- if (isset($props['{DAV:}something'])) {
- unset($props['{DAV:}something']);
- $result[200]['{DAV:}something'] = null;
- }
+ $propPatch->handle('{DAV:}something', function($props) {
+ return true;
+ });
});
$response = $this->request($request);
$this->assertEquals(0, strlen($response->body), 'Expected empty body: ' . $response->body);
- $this->assertEquals('HTTP/1.1 204 No Content', $response->status);
+ $this->assertEquals(204, $response->status);
}
function testproppatchMinimalError() {
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'PROPPATCH',
- 'REQUEST_URI' => '/',
- 'HTTP_PREFER' => 'return-minimal',
- ));
+ $request = new HTTP\Request('PROPPATCH', '/', ['Prefer' => 'return-minimal']);
$request->setBody(<<<BLA
<?xml version="1.0"?>
-<d:proppatch xmlns:d="DAV:">
+<d:propertyupdate xmlns:d="DAV:">
<d:set>
<d:prop>
<d:something>nope!</d:something>
</d:prop>
</d:set>
-</d:proppatch>
+</d:propertyupdate>
BLA
);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 207 Multi-Status', $response->status);
- $this->assertTrue(strpos($response->body, 'something')!==false);
- $this->assertTrue(strpos($response->body, 'HTTP/1.1 403 Forbidden')!==false);
+ $body = $response->getBodyAsString();
+
+ $this->assertEquals(207, $response->status);
+ $this->assertTrue(strpos($body, 'something') !== false);
+ $this->assertTrue(strpos($body, '403 Forbidden') !== false, $body);
}
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/HttpDeleteTest.php b/vendor/sabre/dav/tests/Sabre/DAV/HttpDeleteTest.php
index da28b6979..6c10afa9f 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/HttpDeleteTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/HttpDeleteTest.php
@@ -8,7 +8,7 @@ use Sabre\HTTP;
/**
* Tests related to the PUT request.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH. All rights reserved.
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -21,13 +21,13 @@ class HttpDeleteTest extends DAVServerTest {
*/
public function setUpTree() {
- $this->tree = new Mock\Collection('root', array(
+ $this->tree = new Mock\Collection('root', [
'file1' => 'foo',
- 'dir' => array(
+ 'dir' => [
'subfile' => 'bar',
'subfile2' => 'baz',
- ),
- ));
+ ],
+ ]);
}
@@ -36,24 +36,22 @@ class HttpDeleteTest extends DAVServerTest {
*/
public function testDelete() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file1',
- 'REQUEST_METHOD' => 'DELETE',
- ));
+ $request = new HTTP\Request('DELETE', '/file1');
$response = $this->request($request);
$this->assertEquals(
- 'HTTP/1.1 204 No Content',
- $response->status,
- "Incorrect status code. Response body: " . $response->body
+ 204,
+ $response->getStatus(),
+ "Incorrect status code. Response body: " . $response->getBodyAsString()
);
$this->assertEquals(
- array(
- 'Content-Length' => '0',
- ),
- $response->headers
+ [
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Length' => ['0'],
+ ],
+ $response->getHeaders()
);
}
@@ -63,24 +61,22 @@ class HttpDeleteTest extends DAVServerTest {
*/
public function testDeleteDirectory() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/dir',
- 'REQUEST_METHOD' => 'DELETE',
- ));
+ $request = new HTTP\Request('DELETE', '/dir');
$response = $this->request($request);
$this->assertEquals(
- 'HTTP/1.1 204 No Content',
- $response->status,
- "Incorrect status code. Response body: " . $response->body
+ 204,
+ $response->getStatus(),
+ "Incorrect status code. Response body: " . $response->getBodyAsString()
);
$this->assertEquals(
- array(
- 'Content-Length' => '0',
- ),
- $response->headers
+ [
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Length' => ['0'],
+ ],
+ $response->getHeaders()
);
}
@@ -90,17 +86,13 @@ class HttpDeleteTest extends DAVServerTest {
*/
public function testDeleteNotFound() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file2',
- 'REQUEST_METHOD' => 'DELETE',
- ));
-
+ $request = new HTTP\Request('DELETE', '/file2');
$response = $this->request($request);
$this->assertEquals(
- 'HTTP/1.1 404 Not Found',
- $response->status,
- "Incorrect status code. Response body: " . $response->body
+ 404,
+ $response->getStatus(),
+ "Incorrect status code. Response body: " . $response->getBodyAsString()
);
}
@@ -110,18 +102,16 @@ class HttpDeleteTest extends DAVServerTest {
*/
public function testDeletePreconditions() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file1',
- 'REQUEST_METHOD' => 'DELETE',
- 'HTTP_IF_MATCH' => '"' . md5('foo') . '"',
- ));
+ $request = new HTTP\Request('DELETE', '/file1', [
+ 'If-Match' => '"' . md5('foo') . '"',
+ ]);
$response = $this->request($request);
$this->assertEquals(
- 'HTTP/1.1 204 No Content',
- $response->status,
- "Incorrect status code. Response body: " . $response->body
+ 204,
+ $response->getStatus(),
+ "Incorrect status code. Response body: " . $response->getBodyAsString()
);
}
@@ -131,18 +121,16 @@ class HttpDeleteTest extends DAVServerTest {
*/
public function testDeletePreconditionsFailed() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file1',
- 'REQUEST_METHOD' => 'DELETE',
- 'HTTP_IF_MATCH' => '"' . md5('bar') . '"',
- ));
+ $request = new HTTP\Request('DELETE', '/file1', [
+ 'If-Match' => '"' . md5('bar') . '"',
+ ]);
$response = $this->request($request);
$this->assertEquals(
- 'HTTP/1.1 412 Precondition failed',
- $response->status,
- "Incorrect status code. Response body: " . $response->body
+ 412,
+ $response->getStatus(),
+ "Incorrect status code. Response body: " . $response->getBodyAsString()
);
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/HttpPutTest.php b/vendor/sabre/dav/tests/Sabre/DAV/HttpPutTest.php
index b14554595..eddaf3f22 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/HttpPutTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/HttpPutTest.php
@@ -8,12 +8,9 @@ use Sabre\HTTP;
/**
* Tests related to the PUT request.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH. All rights reserved.
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
- * @covers Sabre\DAV\Server::httpPut
- * @covers Sabre\DAV\Server::createFile
- * @covers Sabre\DAV\Server::checkPreconditions
*/
class HttpPutTest extends DAVServerTest {
@@ -22,28 +19,24 @@ class HttpPutTest extends DAVServerTest {
*
* @return void
*/
- public function setUpTree() {
+ function setUpTree() {
- $this->tree = new Mock\Collection('root', array(
+ $this->tree = new Mock\Collection('root', [
'file1' => 'foo',
- ));
+ ]);
}
/**
* A successful PUT of a new file.
*/
- public function testPut() {
+ function testPut() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file2',
- 'REQUEST_METHOD' => 'PUT',
- ));
- $request->setBody('hello');
+ $request = new HTTP\Request('PUT', '/file2', [], 'hello');
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 201 Created', $response->status);
+ $this->assertEquals(201, $response->getStatus(), 'Incorrect status code received. Full response body:' . $response->getBodyAsString());
$this->assertEquals(
'hello',
@@ -51,11 +44,12 @@ class HttpPutTest extends DAVServerTest {
);
$this->assertEquals(
- array(
- 'Content-Length' => '0',
- 'ETag' => '"' . md5('hello') . '"'
- ),
- $response->headers
+ [
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Length' => ['0'],
+ 'ETag' => ['"' . md5('hello') . '"']
+ ],
+ $response->getHeaders()
);
}
@@ -65,17 +59,13 @@ class HttpPutTest extends DAVServerTest {
*
* @depends testPut
*/
- public function testPutExisting() {
+ function testPutExisting() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file1',
- 'REQUEST_METHOD' => 'PUT',
- ));
- $request->setBody('bar');
+ $request = new HTTP\Request('PUT', '/file1', [], 'bar');
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 204 No Content', $response->status);
+ $this->assertEquals(204, $response->getStatus());
$this->assertEquals(
'bar',
@@ -83,11 +73,12 @@ class HttpPutTest extends DAVServerTest {
);
$this->assertEquals(
- array(
- 'Content-Length' => '0',
- 'ETag' => '"' . md5('bar') . '"'
- ),
- $response->headers
+ [
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Length' => ['0'],
+ 'ETag' => ['"' . md5('bar') . '"']
+ ],
+ $response->getHeaders()
);
}
@@ -97,18 +88,18 @@ class HttpPutTest extends DAVServerTest {
*
* @depends testPutExisting
*/
- public function testPutExistingIfMatchStar() {
+ function testPutExistingIfMatchStar() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file1',
- 'REQUEST_METHOD' => 'PUT',
- 'HTTP_IF_MATCH' => '*',
- ));
- $request->setBody('hello');
+ $request = new HTTP\Request(
+ 'PUT',
+ '/file1',
+ ['If-Match' => '*'],
+ 'hello'
+ );
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 204 No Content', $response->status);
+ $this->assertEquals(204, $response->getStatus());
$this->assertEquals(
'hello',
@@ -116,11 +107,12 @@ class HttpPutTest extends DAVServerTest {
);
$this->assertEquals(
- array(
- 'Content-Length' => '0',
- 'ETag' => '"' . md5('hello') . '"'
- ),
- $response->headers
+ [
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Length' => ['0'],
+ 'ETag' => ['"' . md5('hello') . '"']
+ ],
+ $response->getHeaders()
);
}
@@ -130,18 +122,18 @@ class HttpPutTest extends DAVServerTest {
*
* @depends testPutExisting
*/
- public function testPutExistingIfMatchCorrect() {
+ function testPutExistingIfMatchCorrect() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file1',
- 'REQUEST_METHOD' => 'PUT',
- 'HTTP_IF_MATCH' => '"' . md5('foo') . '"',
- ));
- $request->setBody('hello');
+ $request = new HTTP\Request(
+ 'PUT',
+ '/file1',
+ ['If-Match' => '"' . md5('foo') . '"'],
+ 'hello'
+ );
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 204 No Content', $response->status);
+ $this->assertEquals(204, $response->status);
$this->assertEquals(
'hello',
@@ -149,11 +141,12 @@ class HttpPutTest extends DAVServerTest {
);
$this->assertEquals(
- array(
- 'Content-Length' => '0',
- 'ETag' => '"' . md5('hello') . '"'
- ),
- $response->headers
+ [
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Length' => ['0'],
+ 'ETag' => ['"' . md5('hello') . '"'],
+ ],
+ $response->getHeaders()
);
}
@@ -163,17 +156,17 @@ class HttpPutTest extends DAVServerTest {
*
* @depends testPut
*/
- public function testPutContentRange() {
+ function testPutContentRange() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file2',
- 'REQUEST_METHOD' => 'PUT',
- 'HTTP_CONTENT_RANGE' => 'bytes/100-200',
- ));
- $request->setBody('hello');
+ $request = new HTTP\Request(
+ 'PUT',
+ '/file2',
+ ['Content-Range' => 'bytes/100-200'],
+ 'hello'
+ );
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 501 Not Implemented', $response->status);
+ $this->assertEquals(400, $response->getStatus());
}
@@ -182,18 +175,18 @@ class HttpPutTest extends DAVServerTest {
*
* @depends testPut
*/
- public function testPutIfNoneMatchStar() {
+ function testPutIfNoneMatchStar() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file2',
- 'REQUEST_METHOD' => 'PUT',
- 'HTTP_IF_NONE_MATCH' => '*',
- ));
- $request->setBody('hello');
+ $request = new HTTP\Request(
+ 'PUT',
+ '/file2',
+ ['If-None-Match' => '*'],
+ 'hello'
+ );
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 201 Created', $response->status);
+ $this->assertEquals(201, $response->getStatus());
$this->assertEquals(
'hello',
@@ -201,11 +194,12 @@ class HttpPutTest extends DAVServerTest {
);
$this->assertEquals(
- array(
- 'Content-Length' => '0',
- 'ETag' => '"' . md5('hello') . '"'
- ),
- $response->headers
+ [
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Length' => ['0'],
+ 'ETag' => ['"' . md5('hello') . '"']
+ ],
+ $response->getHeaders()
);
}
@@ -215,18 +209,18 @@ class HttpPutTest extends DAVServerTest {
*
* @depends testPut
*/
- public function testPutIfMatchStar() {
+ function testPutIfMatchStar() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file2',
- 'REQUEST_METHOD' => 'PUT',
- 'HTTP_IF_MATCH' => '*',
- ));
- $request->setBody('hello');
+ $request = new HTTP\Request(
+ 'PUT',
+ '/file2',
+ ['If-Match' => '*'],
+ 'hello'
+ );
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 412 Precondition failed', $response->status);
+ $this->assertEquals(412, $response->getStatus());
}
@@ -235,18 +229,19 @@ class HttpPutTest extends DAVServerTest {
*
* @depends testPut
*/
- public function testPutExistingIfNoneMatchStar() {
+ function testPutExistingIfNoneMatchStar() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file1',
- 'REQUEST_METHOD' => 'PUT',
- 'HTTP_IF_NONE_MATCH' => '*',
- ));
+ $request = new HTTP\Request(
+ 'PUT',
+ '/file1',
+ ['If-None-Match' => '*'],
+ 'hello'
+ );
$request->setBody('hello');
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 412 Precondition failed', $response->status);
+ $this->assertEquals(412, $response->getStatus());
}
@@ -255,16 +250,17 @@ class HttpPutTest extends DAVServerTest {
*
* @depends testPut
*/
- public function testPutNoParent() {
+ function testPutNoParent() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file1/file2',
- 'REQUEST_METHOD' => 'PUT',
- ));
- $request->setBody('hello');
+ $request = new HTTP\Request(
+ 'PUT',
+ '/file1/file2',
+ [],
+ 'hello'
+ );
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 409 Conflict', $response->status);
+ $this->assertEquals(409, $response->getStatus());
}
@@ -275,18 +271,17 @@ class HttpPutTest extends DAVServerTest {
*
* @depends testPut
*/
- public function testFinderPutSuccess() {
-
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file2',
- 'REQUEST_METHOD' => 'PUT',
- 'HTTP_X_EXPECTED_ENTITY_LENGTH' => '5',
- ));
- $request->setBody('hello');
+ function testFinderPutSuccess() {
+ $request = new HTTP\Request(
+ 'PUT',
+ '/file2',
+ ['X-Expected-Entity-Length' => '5'],
+ 'hello'
+ );
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 201 Created', $response->status);
+ $this->assertEquals(201, $response->getStatus());
$this->assertEquals(
'hello',
@@ -294,11 +289,12 @@ class HttpPutTest extends DAVServerTest {
);
$this->assertEquals(
- array(
- 'Content-Length' => '0',
- 'ETag' => '"' . md5('hello') . '"'
- ),
- $response->headers
+ [
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Length' => ['0'],
+ 'ETag' => ['"' . md5('hello') . '"'],
+ ],
+ $response->getHeaders()
);
}
@@ -308,54 +304,45 @@ class HttpPutTest extends DAVServerTest {
*
* @depends testFinderPutSuccess
*/
- public function testFinderPutFail() {
+ function testFinderPutFail() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file2',
- 'REQUEST_METHOD' => 'PUT',
- 'HTTP_X_EXPECTED_ENTITY_LENGTH' => '5',
- ));
- $request->setBody('');
+ $request = new HTTP\Request(
+ 'PUT',
+ '/file2',
+ ['X-Expected-Entity-Length' => '5'],
+ ''
+ );
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 403 Forbidden', $response->status);
+ $this->assertEquals(403, $response->getStatus());
}
/**
* Plugins can intercept PUT. We need to make sure that works.
+ *
+ * @depends testPut
*/
- public function testPutIntercept() {
-
- $this->server->subscribeEvent('beforeBind', array($this, 'beforeBind'));
+ function testPutIntercept() {
- $request = new HTTP\Request(array(
- 'REQUEST_URI' => '/file2',
- 'REQUEST_METHOD' => 'PUT',
- ));
- $request->setBody('hello');
+ $this->server->on('beforeBind', function($uri) {
+ $this->server->httpResponse->setStatus(418);
+ return false;
+ });
+ $request = new HTTP\Request('PUT', '/file2', [], 'hello');
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 418 I\'m a teapot', $response->status);
+ $this->assertEquals(418, $response->getStatus(), 'Incorrect status code received. Full response body: ' .$response->getBodyAsString());
$this->assertFalse(
$this->server->tree->nodeExists('file2')
);
- $this->assertEquals(
- array(
- ),
- $response->headers
- );
-
- }
-
- public function beforeBind() {
-
- $this->server->httpResponse->sendStatus(418);
- return false;
+ $this->assertEquals([
+ 'X-Sabre-Version' => [Version::VERSION],
+ ], $response->getHeaders());
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Issue33Test.php b/vendor/sabre/dav/tests/Sabre/DAV/Issue33Test.php
index c3fba4aae..4ccb42fbb 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Issue33Test.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Issue33Test.php
@@ -28,11 +28,11 @@ class Issue33Test extends \PHPUnit_Framework_TestCase {
'HTTP_OVERWRITE' => 'F',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$server->httpRequest = $request;
- $info = $server->getCopyAndMoveInfo();
+ $info = $server->getCopyAndMoveInfo($request);
$this->assertEquals('%C3%A0fo%C3%B3', urlencode($info['destination']));
$this->assertFalse($info['destinationExists']);
@@ -47,7 +47,7 @@ class Issue33Test extends \PHPUnit_Framework_TestCase {
$dir->createDirectory('bar');
- $tree = new ObjectTree($dir);
+ $tree = new Tree($dir);
$tree->move('bar',urldecode('%C3%A0fo%C3%B3'));
$node = $tree->getNodeForPath(urldecode('%C3%A0fo%C3%B3'));
@@ -78,7 +78,7 @@ class Issue33Test extends \PHPUnit_Framework_TestCase {
'HTTP_OVERWRITE' => 'F',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('');
$response = new HTTP\ResponseMock();
@@ -89,13 +89,14 @@ class Issue33Test extends \PHPUnit_Framework_TestCase {
$dir->createDirectory('bar');
- $tree = new ObjectTree($dir);
+ $tree = new Tree($dir);
$server = new Server($tree);
$server->setBaseUri('/webdav/');
$server->httpRequest = $request;
$server->httpResponse = $response;
+ $server->sapi = new HTTP\SapiMock();
$server->exec();
$this->assertTrue(file_exists(SABRE_TEMPDIR . '/issue33/' . urldecode('%C3%A0fo%C3%B3')));
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Locks/Backend/FSTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Locks/Backend/FSTest.php
deleted file mode 100644
index 651abf786..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAV/Locks/Backend/FSTest.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Locks\Backend;
-
-require_once 'Sabre/TestUtil.php';
-
-class FSTest extends AbstractTest {
-
- function getBackend() {
-
- \Sabre\TestUtil::clearTempDir();
- mkdir(SABRE_TEMPDIR . '/locks');
- $backend = new FS(SABRE_TEMPDIR . '/locks/');
- return $backend;
-
- }
-
- function tearDown() {
-
- \Sabre\TestUtil::clearTempDir();
-
- }
-
- function testGetLocksChildren() {
-
- // We're skipping this test. This doesn't work, and it will
- // never. The class is deprecated anyway.
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Locks/GetIfConditionsTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Locks/GetIfConditionsTest.php
deleted file mode 100644
index 7b2cd0db0..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAV/Locks/GetIfConditionsTest.php
+++ /dev/null
@@ -1,375 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Locks;
-
-use Sabre\DAV;
-use Sabre\HTTP;
-
-require_once 'Sabre/HTTP/ResponseMock.php';
-require_once 'Sabre/DAV/AbstractServer.php';
-
-class GetIfConditionsTest extends DAV\AbstractServer {
-
- /**
- * @var Sabre\DAV\Locks\Plugin
- */
- protected $locksPlugin;
-
- function setUp() {
-
- parent::setUp();
- $locksPlugin = new Plugin();
- $this->server->addPlugin($locksPlugin);
- $this->locksPlugin = $locksPlugin;
-
- }
-
- function testNoConditions() {
-
- $serverVars = array(
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
-
- $conditions = $this->locksPlugin->getIfConditions();
- $this->assertEquals(array(),$conditions);
-
- }
-
- function testLockToken() {
-
- $serverVars = array(
- 'HTTP_IF' => '(<opaquelocktoken:token1>)',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
-
- $conditions = $this->locksPlugin->getIfConditions();
-
- $compare = array(
-
- array(
- 'uri' => '',
- 'tokens' => array(
- array(
- 1,
- 'opaquelocktoken:token1',
- '',
- ),
- ),
-
- ),
-
- );
-
- $this->assertEquals($compare,$conditions);
-
- }
-
- function testNotLockToken() {
-
- $serverVars = array(
- 'HTTP_IF' => '(Not <opaquelocktoken:token1>)',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
-
- $conditions = $this->locksPlugin->getIfConditions();
-
- $compare = array(
-
- array(
- 'uri' => '',
- 'tokens' => array(
- array(
- 0,
- 'opaquelocktoken:token1',
- '',
- ),
- ),
-
- ),
-
- );
- $this->assertEquals($compare,$conditions);
-
- }
-
- function testLockTokenUrl() {
-
- $serverVars = array(
- 'HTTP_IF' => '<http://www.example.com/> (<opaquelocktoken:token1>)',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
-
- $conditions = $this->locksPlugin->getIfConditions();
-
- $compare = array(
-
- array(
- 'uri' => 'http://www.example.com/',
- 'tokens' => array(
- array(
- 1,
- 'opaquelocktoken:token1',
- '',
- ),
- ),
-
- ),
-
- );
- $this->assertEquals($compare,$conditions);
-
- }
-
- function test2LockTokens() {
-
- $serverVars = array(
- 'HTTP_IF' => '(<opaquelocktoken:token1>) (Not <opaquelocktoken:token2>)',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
-
- $conditions = $this->locksPlugin->getIfConditions();
-
- $compare = array(
-
- array(
- 'uri' => '',
- 'tokens' => array(
- array(
- 1,
- 'opaquelocktoken:token1',
- '',
- ),
- array(
- 0,
- 'opaquelocktoken:token2',
- '',
- ),
- ),
-
- ),
-
- );
- $this->assertEquals($compare,$conditions);
-
- }
-
- function test2UriLockTokens() {
-
- $serverVars = array(
- 'HTTP_IF' => '<http://www.example.org/node1> (<opaquelocktoken:token1>) <http://www.example.org/node2> (Not <opaquelocktoken:token2>)',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
-
- $conditions = $this->locksPlugin->getIfConditions();
-
- $compare = array(
-
- array(
- 'uri' => 'http://www.example.org/node1',
- 'tokens' => array(
- array(
- 1,
- 'opaquelocktoken:token1',
- '',
- ),
- ),
- ),
- array(
- 'uri' => 'http://www.example.org/node2',
- 'tokens' => array(
- array(
- 0,
- 'opaquelocktoken:token2',
- '',
- ),
- ),
-
- ),
-
- );
- $this->assertEquals($compare,$conditions);
-
- }
-
- function test2UriMultiLockTokens() {
-
- $serverVars = array(
- 'HTTP_IF' => '<http://www.example.org/node1> (<opaquelocktoken:token1>) (<opaquelocktoken:token2>) <http://www.example.org/node2> (Not <opaquelocktoken:token3>)',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
-
- $conditions = $this->locksPlugin->getIfConditions();
-
- $compare = array(
-
- array(
- 'uri' => 'http://www.example.org/node1',
- 'tokens' => array(
- array(
- 1,
- 'opaquelocktoken:token1',
- '',
- ),
- array(
- 1,
- 'opaquelocktoken:token2',
- '',
- ),
- ),
- ),
- array(
- 'uri' => 'http://www.example.org/node2',
- 'tokens' => array(
- array(
- 0,
- 'opaquelocktoken:token3',
- '',
- ),
- ),
-
- ),
-
- );
- $this->assertEquals($compare,$conditions);
-
- }
-
- function testEtag() {
-
- $serverVars = array(
- 'HTTP_IF' => '([etag1])',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
-
- $conditions = $this->locksPlugin->getIfConditions();
-
- $compare = array(
-
- array(
- 'uri' => '',
- 'tokens' => array(
- array(
- 1,
- '',
- 'etag1',
- ),
- ),
- ),
-
- );
- $this->assertEquals($compare,$conditions);
-
- }
-
- function test2Etags() {
-
- $serverVars = array(
- 'HTTP_IF' => '<http://www.example.org/> ([etag1]) ([etag2])',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
-
- $conditions = $this->locksPlugin->getIfConditions();
-
- $compare = array(
-
- array(
- 'uri' => 'http://www.example.org/',
- 'tokens' => array(
- array(
- 1,
- '',
- 'etag1',
- ),
- array(
- 1,
- '',
- 'etag2',
- ),
- ),
- ),
-
- );
- $this->assertEquals($compare,$conditions);
-
- }
-
- function testComplexIf() {
-
- $serverVars = array(
- 'HTTP_IF' => '<http://www.example.org/node1> (<opaquelocktoken:token1> [etag1]) ' .
- '(Not <opaquelocktoken:token2>) ([etag2]) <http://www.example.org/node2> ' .
- '(<opaquelocktoken:token3>) (Not <opaquelocktoken:token4>) ([etag3])',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
-
- $conditions = $this->locksPlugin->getIfConditions();
-
- $compare = array(
-
- array(
- 'uri' => 'http://www.example.org/node1',
- 'tokens' => array(
- array(
- 1,
- 'opaquelocktoken:token1',
- 'etag1',
- ),
- array(
- 0,
- 'opaquelocktoken:token2',
- '',
- ),
- array(
- 1,
- '',
- 'etag2',
- ),
- ),
- ),
- array(
- 'uri' => 'http://www.example.org/node2',
- 'tokens' => array(
- array(
- 1,
- 'opaquelocktoken:token3',
- '',
- ),
- array(
- 0,
- 'opaquelocktoken:token4',
- '',
- ),
- array(
- 1,
- '',
- 'etag3',
- ),
- ),
- ),
-
- );
- $this->assertEquals($compare,$conditions);
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Locks/MSWordTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Locks/MSWordTest.php
index b3d7d447b..23f283796 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Locks/MSWordTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Locks/MSWordTest.php
@@ -10,6 +10,12 @@ require_once 'Sabre/TestUtil.php';
class MSWordTest extends \PHPUnit_Framework_TestCase {
+ function tearDown() {
+
+ \Sabre\TestUtil::clearTempDir();
+
+ }
+
function testLockEtc() {
mkdir(SABRE_TEMPDIR . '/mstest');
@@ -25,11 +31,12 @@ class MSWordTest extends \PHPUnit_Framework_TestCase {
$server->httpRequest = $this->getLockRequest();
$server->httpResponse = $response1;
+ $server->sapi = new HTTP\SapiMock();
$server->exec();
- $this->assertEquals('HTTP/1.1 201 Created', $server->httpResponse->status);
- $this->assertTrue(isset($server->httpResponse->headers['Lock-Token']));
- $lockToken = $server->httpResponse->headers['Lock-Token'];
+ $this->assertEquals(201, $server->httpResponse->getStatus(), 'Full response body:' . $response1->getBodyAsString());
+ $this->assertTrue(!!$server->httpResponse->getHeaders('Lock-Token'));
+ $lockToken = $server->httpResponse->getHeader('Lock-Token');
//sleep(10);
@@ -39,8 +46,8 @@ class MSWordTest extends \PHPUnit_Framework_TestCase {
$server->httpResponse = $response2;
$server->exec();
- $this->assertEquals('HTTP/1.1 201 Created', $server->httpResponse->status);
- $this->assertTrue(isset($server->httpResponse->headers['Lock-Token']));
+ $this->assertEquals(201, $server->httpResponse->status);
+ $this->assertTrue(!!$server->httpResponse->getHeaders('Lock-Token'));
//sleep(10);
@@ -49,19 +56,13 @@ class MSWordTest extends \PHPUnit_Framework_TestCase {
$server->httpResponse = $response3;
$server->exec();
- $this->assertEquals('HTTP/1.1 204 No Content', $server->httpResponse->status);
-
- }
-
- function tearDown() {
-
- \Sabre\TestUtil::clearTempDir();
+ $this->assertEquals(204, $server->httpResponse->status);
}
function getLockRequest() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'LOCK',
'HTTP_CONTENT_TYPE' => 'application/xml',
'HTTP_TIMEOUT' => 'Second-3600',
@@ -85,7 +86,7 @@ class MSWordTest extends \PHPUnit_Framework_TestCase {
}
function getLockRequest2() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'LOCK',
'HTTP_CONTENT_TYPE' => 'application/xml',
'HTTP_TIMEOUT' => 'Second-3600',
@@ -110,7 +111,7 @@ class MSWordTest extends \PHPUnit_Framework_TestCase {
function getPutRequest($lockToken) {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'PUT',
'REQUEST_URI' => '/Nouveau%20Microsoft%20Office%20Excel%20Worksheet.xlsx',
'HTTP_IF' => 'If: ('.$lockToken.')',
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Locks/PluginTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Locks/PluginTest.php
index caa1d0118..ef0e473ae 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Locks/PluginTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Locks/PluginTest.php
@@ -10,7 +10,7 @@ require_once 'Sabre/DAV/AbstractServer.php';
class PluginTest extends DAV\AbstractServer {
/**
- * @var Sabre\DAV\Locks\Plugin
+ * @var Plugin
*/
protected $locksPlugin;
@@ -24,62 +24,47 @@ class PluginTest extends DAV\AbstractServer {
}
- function testGetFeatures() {
-
- $this->assertEquals(array(2),$this->locksPlugin->getFeatures());
-
- }
+ function testGetInfo() {
- function testGetHTTPMethods() {
-
- $this->assertEquals(array('LOCK','UNLOCK'),$this->locksPlugin->getHTTPMethods(''));
+ $this->assertArrayHasKey(
+ 'name',
+ $this->locksPlugin->getPluginInfo()
+ );
}
- function testGetHTTPMethodsNoBackend() {
+ function testGetFeatures() {
- $locksPlugin = new Plugin();
- $this->server->addPlugin($locksPlugin);
- $this->assertEquals(array(),$locksPlugin->getHTTPMethods(''));
+ $this->assertEquals(array(2),$this->locksPlugin->getFeatures());
}
- function testUnknownMethodPassthough() {
+ function testGetHTTPMethods() {
- $this->assertNull($this->locksPlugin->unknownMethod('BLA','/'));
+ $this->assertEquals(array('LOCK','UNLOCK'),$this->locksPlugin->getHTTPMethods(''));
}
function testLockNoBody() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'LOCK',
- );
-
- $request = new HTTP\Request($serverVars);
- $request->setBody('');
- $this->server->httpRequest = ($request);
+ $request = new HTTP\Request('LOCK', '/test.txt');
+ $this->server->httpRequest = $request;
$this->server->exec();
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
),
- $this->response->headers
+ $this->response->getHeaders()
);
- $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status);
+ $this->assertEquals(400, $this->response->status);
}
function testLock() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'LOCK',
- );
-
- $request = new HTTP\Request($serverVars);
+ $request = new HTTP\Request('LOCK', '/test.txt');
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -92,10 +77,10 @@ class PluginTest extends DAV\AbstractServer {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status,'Got an incorrect status back. Response body: ' . $this->response->body);
+ $this->assertEquals(200, $this->response->status,'Got an incorrect status back. Response body: ' . $this->response->body);
$body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body);
$xml = simplexml_load_string($body);
@@ -127,7 +112,7 @@ class PluginTest extends DAV\AbstractServer {
$this->assertEquals('infinity',(string)$depth[0]);
$token = $xml->xpath('/d:prop/d:lockdiscovery/d:activelock/d:locktoken/d:href');
- $this->assertEquals($this->response->headers['Lock-Token'],'<' . (string)$token[0] . '>','Token in response body didn\'t match token in response header.');
+ $this->assertEquals($this->response->getHeader('Lock-Token'),'<' . (string)$token[0] . '>','Token in response body didn\'t match token in response header.');
}
@@ -136,12 +121,7 @@ class PluginTest extends DAV\AbstractServer {
*/
function testDoubleLock() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'LOCK',
- );
-
- $request = new HTTP\Request($serverVars);
+ $request = new HTTP\Request('LOCK', '/test.txt');
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -159,9 +139,9 @@ class PluginTest extends DAV\AbstractServer {
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
- $this->assertEquals('HTTP/1.1 423 Locked',$this->response->status);
+ $this->assertEquals(423, $this->response->status, 'Full response: ' . $this->response->body);
}
@@ -170,12 +150,7 @@ class PluginTest extends DAV\AbstractServer {
*/
function testLockRefresh() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'LOCK',
- );
-
- $request = new HTTP\Request($serverVars);
+ $request = new HTTP\Request('LOCK', '/test.txt');
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -188,25 +163,55 @@ class PluginTest extends DAV\AbstractServer {
$this->server->httpRequest = $request;
$this->server->exec();
- $lockToken = $this->response->headers['Lock-Token'];
+ $lockToken = $this->response->getHeader('Lock-Token');
$this->response = new HTTP\ResponseMock();
$this->server->httpResponse = $this->response;
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'LOCK',
- 'HTTP_IF' => '(' . $lockToken . ')',
- );
- $request = new HTTP\Request($serverVars);
+ $request = new HTTP\Request('LOCK', '/test.txt', ['If' => '(' . $lockToken . ')' ]);
$request->setBody('');
+
+ $this->server->httpRequest = $request;
+ $this->server->exec();
+
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+
+ $this->assertEquals(200, $this->response->status,'We received an incorrect status code. Full response body: ' . $this->response->getBody());
+
+ }
+
+ /**
+ * @depends testLock
+ */
+ function testLockRefreshBadToken() {
+
+ $request = new HTTP\Request('LOCK', '/test.txt');
+ $request->setBody('<?xml version="1.0"?>
+<D:lockinfo xmlns:D="DAV:">
+ <D:lockscope><D:exclusive/></D:lockscope>
+ <D:locktype><D:write/></D:locktype>
+ <D:owner>
+ <D:href>http://example.org/~ejw/contact.html</D:href>
+ </D:owner>
+</D:lockinfo>');
+
$this->server->httpRequest = $request;
+ $this->server->exec();
+
+ $lockToken = $this->response->getHeader('Lock-Token');
+ $this->response = new HTTP\ResponseMock();
+ $this->server->httpResponse = $this->response;
+
+ $request = new HTTP\Request('LOCK', '/test.txt', ['If' => '(' . $lockToken . 'foobar) (<opaquelocktoken:anotherbadtoken>)' ]);
+ $request->setBody('');
+
+ $this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status,'We received an incorrect status code. Full response body: ' . $this->response->body);
+ $this->assertEquals(423, $this->response->getStatus(),'We received an incorrect status code. Full response body: ' . $this->response->getBody());
}
@@ -215,12 +220,7 @@ class PluginTest extends DAV\AbstractServer {
*/
function testLockNoFile() {
- $serverVars = array(
- 'REQUEST_URI' => '/notfound.txt',
- 'REQUEST_METHOD' => 'LOCK',
- );
-
- $request = new HTTP\Request($serverVars);
+ $request = new HTTP\Request('LOCK', '/notfound.txt');
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -233,10 +233,10 @@ class PluginTest extends DAV\AbstractServer {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
+ $this->assertEquals(201, $this->response->status);
}
@@ -245,22 +245,18 @@ class PluginTest extends DAV\AbstractServer {
*/
function testUnlockNoToken() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'UNLOCK',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
+ $request = new HTTP\Request('UNLOCK', '/test.txt');
+ $this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),
- $this->response->headers
+ $this->assertEquals([
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ],
+ $this->response->getHeaders()
);
- $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status);
+ $this->assertEquals(400, $this->response->status);
}
@@ -269,23 +265,18 @@ class PluginTest extends DAV\AbstractServer {
*/
function testUnlockBadToken() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'UNLOCK',
- 'HTTP_LOCK_TOKEN' => '<opaquelocktoken:blablabla>',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
+ $request = new HTTP\Request('UNLOCK', '/test.txt', ['Lock-Token' => '<opaquelocktoken:blablabla>']);
+ $this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),
- $this->response->headers
+ $this->assertEquals([
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ],
+ $this->response->getHeaders()
);
- $this->assertEquals('HTTP/1.1 409 Conflict',$this->response->status,'Got an incorrect status code. Full response body: ' . $this->response->body);
+ $this->assertEquals(409, $this->response->status, 'Got an incorrect status code. Full response body: ' . $this->response->body);
}
@@ -294,12 +285,7 @@ class PluginTest extends DAV\AbstractServer {
*/
function testLockPutNoToken() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'LOCK',
- );
-
- $request = new HTTP\Request($serverVars);
+ $request = new HTTP\Request('LOCK', '/test.txt');
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -312,25 +298,20 @@ class PluginTest extends DAV\AbstractServer {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200, $this->response->status);
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'PUT',
- );
-
- $request = new HTTP\Request($serverVars);
+ $request = new HTTP\Request('PUT', '/test.txt');
$request->setBody('newbody');
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 423 Locked',$this->response->status);
+ $this->assertEquals(423, $this->response->status);
}
@@ -339,7 +320,7 @@ class PluginTest extends DAV\AbstractServer {
*/
function testUnlock() {
- $request = new HTTP\Request(array());
+ $request = new HTTP\Request('LOCK', '/test.txt');
$this->server->httpRequest = $request;
$request->setBody('<?xml version="1.0"?>
@@ -351,23 +332,20 @@ class PluginTest extends DAV\AbstractServer {
</D:owner>
</D:lockinfo>');
- $this->server->invokeMethod('LOCK','test.txt');
- $lockToken = $this->server->httpResponse->headers['Lock-Token'];
-
- $serverVars = array(
- 'HTTP_LOCK_TOKEN' => $lockToken,
- );
+ $this->server->invokeMethod($request, $this->server->httpResponse);
+ $lockToken = $this->server->httpResponse->getHeader('Lock-Token');
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
+ $request = new HTTP\Request('UNLOCK', '/test.txt', ['Lock-Token' => $lockToken]);
+ $this->server->httpRequest = $request;
$this->server->httpResponse = new HTTP\ResponseMock();
- $this->server->invokeMethod('UNLOCK', 'test.txt');
-
- $this->assertEquals('HTTP/1.1 204 No Content',$this->server->httpResponse->status,'Got an incorrect status code. Full response body: ' . $this->response->body);
- $this->assertEquals(array(
- 'Content-Length' => '0',
- ),
- $this->server->httpResponse->headers
+ $this->server->invokeMethod($request, $this->server->httpResponse);
+
+ $this->assertEquals(204,$this->server->httpResponse->status,'Got an incorrect status code. Full response body: ' . $this->response->body);
+ $this->assertEquals([
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Length' => ['0'],
+ ],
+ $this->server->httpResponse->getHeaders()
);
@@ -378,7 +356,7 @@ class PluginTest extends DAV\AbstractServer {
*/
function testUnlockWindowsBug() {
- $request = new HTTP\Request(array());
+ $request = new HTTP\Request('LOCK', '/test.txt');
$this->server->httpRequest = $request;
$request->setBody('<?xml version="1.0"?>
@@ -390,26 +368,23 @@ class PluginTest extends DAV\AbstractServer {
</D:owner>
</D:lockinfo>');
- $this->server->invokeMethod('LOCK','test.txt');
- $lockToken = $this->server->httpResponse->headers['Lock-Token'];
+ $this->server->invokeMethod($request, $this->server->httpResponse);
+ $lockToken = $this->server->httpResponse->getHeader('Lock-Token');
// See Issue 123
$lockToken = trim($lockToken,'<>');
- $serverVars = array(
- 'HTTP_LOCK_TOKEN' => $lockToken,
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
+ $request = new HTTP\Request('UNLOCK', '/test.txt', ['Lock-Token' => $lockToken]);
+ $this->server->httpRequest = $request;
$this->server->httpResponse = new HTTP\ResponseMock();
- $this->server->invokeMethod('UNLOCK', 'test.txt');
-
- $this->assertEquals('HTTP/1.1 204 No Content',$this->server->httpResponse->status,'Got an incorrect status code. Full response body: ' . $this->response->body);
- $this->assertEquals(array(
- 'Content-Length' => '0',
- ),
- $this->server->httpResponse->headers
+ $this->server->invokeMethod($request, $this->server->httpResponse);
+
+ $this->assertEquals(204, $this->server->httpResponse->status,'Got an incorrect status code. Full response body: ' . $this->response->body);
+ $this->assertEquals([
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Length' => ['0'],
+ ],
+ $this->server->httpResponse->getHeaders()
);
@@ -420,7 +395,10 @@ class PluginTest extends DAV\AbstractServer {
*/
function testLockRetainOwner() {
- $request = new HTTP\Request(array());
+ $request = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_URI' => '/test.txt',
+ 'REQUEST_METHOD' => 'LOCK',
+ ]);
$this->server->httpRequest = $request;
$request->setBody('<?xml version="1.0"?>
@@ -430,8 +408,8 @@ class PluginTest extends DAV\AbstractServer {
<D:owner>Evert</D:owner>
</D:lockinfo>');
- $this->server->invokeMethod('LOCK','test.txt');
- $lockToken = $this->server->httpResponse->headers['Lock-Token'];
+ $this->server->invokeMethod($request, $this->server->httpResponse);
+ $lockToken = $this->server->httpResponse->getHeader('Lock-Token');
$locks = $this->locksPlugin->getLocks('test.txt');
$this->assertEquals(1,count($locks));
@@ -450,7 +428,7 @@ class PluginTest extends DAV\AbstractServer {
'REQUEST_METHOD' => 'LOCK',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -463,10 +441,10 @@ class PluginTest extends DAV\AbstractServer {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200, $this->response->status);
$serverVars = array(
'REQUEST_URI' => '/test.txt',
@@ -474,15 +452,16 @@ class PluginTest extends DAV\AbstractServer {
'HTTP_IF' => '(<opaquelocktoken:token1>)',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('newbody');
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 412 Precondition failed',$this->response->status);
+ // $this->assertEquals('412 Precondition failed',$this->response->status);
+ $this->assertEquals(423, $this->response->status);
}
@@ -496,7 +475,7 @@ class PluginTest extends DAV\AbstractServer {
'REQUEST_METHOD' => 'LOCK',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -509,22 +488,22 @@ class PluginTest extends DAV\AbstractServer {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200, $this->response->status);
$serverVars = array(
'REQUEST_URI' => '/dir',
'REQUEST_METHOD' => 'DELETE',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 423 Locked',$this->response->status);
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
+ $this->assertEquals(423, $this->response->status);
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
}
/**
@@ -537,7 +516,7 @@ class PluginTest extends DAV\AbstractServer {
'REQUEST_METHOD' => 'LOCK',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -550,23 +529,23 @@ class PluginTest extends DAV\AbstractServer {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200, $this->response->status);
$serverVars = array(
'REQUEST_URI' => '/dir/child.txt',
'REQUEST_METHOD' => 'DELETE',
- 'HTTP_IF' => '(' . $this->response->headers['Lock-Token'] . ')',
+ 'HTTP_IF' => '(' . $this->response->getHeader('Lock-Token') . ')',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status);
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
+ $this->assertEquals(204, $this->response->status);
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
}
@@ -580,7 +559,7 @@ class PluginTest extends DAV\AbstractServer {
'REQUEST_METHOD' => 'LOCK',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -593,10 +572,10 @@ class PluginTest extends DAV\AbstractServer {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200, $this->response->status);
$serverVars = array(
'REQUEST_URI' => '/dir/child.txt',
@@ -604,12 +583,12 @@ class PluginTest extends DAV\AbstractServer {
'HTTP_DESTINATION' => '/dir/child2.txt',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status,'Copy must succeed if only the source is locked, but not the destination');
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
+ $this->assertEquals(201, $this->response->status,'Copy must succeed if only the source is locked, but not the destination');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
}
/**
@@ -622,7 +601,7 @@ class PluginTest extends DAV\AbstractServer {
'REQUEST_METHOD' => 'LOCK',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -635,10 +614,10 @@ class PluginTest extends DAV\AbstractServer {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
+ $this->assertEquals(201, $this->response->status);
$serverVars = array(
'REQUEST_URI' => '/dir/child.txt',
@@ -646,12 +625,12 @@ class PluginTest extends DAV\AbstractServer {
'HTTP_DESTINATION' => '/dir/child2.txt',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 423 Locked',$this->response->status,'Copy must succeed if only the source is locked, but not the destination');
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
+ $this->assertEquals(423, $this->response->status,'Copy must succeed if only the source is locked, but not the destination');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
}
@@ -665,7 +644,7 @@ class PluginTest extends DAV\AbstractServer {
'REQUEST_METHOD' => 'LOCK',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -678,10 +657,10 @@ class PluginTest extends DAV\AbstractServer {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200, $this->response->status);
$serverVars = array(
'REQUEST_URI' => '/dir/child.txt',
@@ -689,12 +668,12 @@ class PluginTest extends DAV\AbstractServer {
'HTTP_DESTINATION' => '/dir/child2.txt',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 423 Locked',$this->response->status,'Copy must succeed if only the source is locked, but not the destination');
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
+ $this->assertEquals(423, $this->response->status,'Copy must succeed if only the source is locked, but not the destination');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
}
@@ -708,7 +687,7 @@ class PluginTest extends DAV\AbstractServer {
'REQUEST_METHOD' => 'LOCK',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -721,23 +700,23 @@ class PluginTest extends DAV\AbstractServer {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200, $this->response->status);
$serverVars = array(
'REQUEST_URI' => '/dir/child.txt',
'REQUEST_METHOD' => 'MOVE',
'HTTP_DESTINATION' => '/dir/child2.txt',
- 'HTTP_IF' => '(' . $this->response->headers['Lock-Token'] . ')',
+ 'HTTP_IF' => '(' . $this->response->getHeader('Lock-Token') . ')',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status,'A valid lock-token was provided for the source, so this MOVE operation must succeed. Full response body: ' . $this->response->body);
+ $this->assertEquals(201, $this->response->status,'A valid lock-token was provided for the source, so this MOVE operation must succeed. Full response body: ' . $this->response->body);
}
@@ -751,7 +730,7 @@ class PluginTest extends DAV\AbstractServer {
'REQUEST_METHOD' => 'LOCK',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -764,10 +743,10 @@ class PluginTest extends DAV\AbstractServer {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
+ $this->assertEquals(201, $this->response->status);
$serverVars = array(
'REQUEST_URI' => '/dir/child.txt',
@@ -775,12 +754,12 @@ class PluginTest extends DAV\AbstractServer {
'HTTP_DESTINATION' => '/dir/child2.txt',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 423 Locked',$this->response->status,'Copy must succeed if only the source is locked, but not the destination');
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
+ $this->assertEquals(423, $this->response->status,'Copy must succeed if only the source is locked, but not the destination');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
}
/**
@@ -794,7 +773,7 @@ class PluginTest extends DAV\AbstractServer {
'HTTP_DEPTH' => 'infinite',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -807,24 +786,24 @@ class PluginTest extends DAV\AbstractServer {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200,$this->response->status);
$serverVars = array(
'REQUEST_URI' => '/dir/child.txt',
'REQUEST_METHOD' => 'MOVE',
'HTTP_DESTINATION' => '/dir/child2.txt',
- 'HTTP_IF' => '</dir> (' . $this->response->headers['Lock-Token'] . ')',
+ 'HTTP_IF' => '</dir> (' . $this->response->getHeader('Lock-Token') . ')',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status,'We locked the parent of both the source and destination, but the move didn\'t succeed.');
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
+ $this->assertEquals(201, $this->response->status,'We locked the parent of both the source and destination, but the move didn\'t succeed.');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
}
@@ -838,7 +817,7 @@ class PluginTest extends DAV\AbstractServer {
'REQUEST_METHOD' => 'LOCK',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -851,26 +830,65 @@ class PluginTest extends DAV\AbstractServer {
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200, $this->response->status);
$serverVars = array(
'REQUEST_URI' => '/test.txt',
'REQUEST_METHOD' => 'PUT',
- 'HTTP_IF' => '('.$this->response->headers['Lock-Token'].')',
+ 'HTTP_IF' => '('.$this->response->getHeader('Lock-Token').')',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('newbody');
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
- $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status);
+ $this->assertEquals(204, $this->response->status);
+
+ }
+
+ /**
+ * @depends testLock
+ */
+ function testLockPutUnrelatedToken() {
+
+ $request = new HTTP\Request('LOCK', '/unrelated.txt');
+ $request->setBody('<?xml version="1.0"?>
+<D:lockinfo xmlns:D="DAV:">
+ <D:lockscope><D:exclusive/></D:lockscope>
+ <D:locktype><D:write/></D:locktype>
+ <D:owner>
+ <D:href>http://example.org/~ejw/contact.html</D:href>
+ </D:owner>
+</D:lockinfo>');
+
+ $this->server->httpRequest = $request;
+ $this->server->exec();
+
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
+
+ $this->assertEquals(201, $this->response->getStatus());
+
+ $request = new HTTP\Request(
+ 'PUT',
+ '/test.txt',
+ ['If' => '</unrelated.txt> ('.$this->response->getHeader('Lock-Token').')']
+ );
+ $request->setBody('newbody');
+ $this->server->httpRequest = $request;
+ $this->server->exec();
+
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
+
+ $this->assertEquals(204, $this->response->status);
}
@@ -882,11 +900,11 @@ class PluginTest extends DAV\AbstractServer {
'HTTP_IF' => '(["etag1"])',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('newbody');
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 412 Precondition failed',$this->response->status);
+ $this->assertEquals(412, $this->response->status);
}
@@ -895,22 +913,27 @@ class PluginTest extends DAV\AbstractServer {
*/
function testPutWithCorrectETag() {
- // We need an etag-enabled file node.
- $tree = new DAV\ObjectTree(new DAV\FSExt\Directory(SABRE_TEMPDIR));
+ // We need an ETag-enabled file node.
+ $tree = new DAV\Tree(new DAV\FSExt\Directory(SABRE_TEMPDIR));
$this->server->tree = $tree;
- $etag = md5(file_get_contents(SABRE_TEMPDIR . '/test.txt'));
+ $filename = SABRE_TEMPDIR . '/test.txt';
+ $etag = sha1(
+ fileinode($filename) .
+ filesize($filename ) .
+ filemtime($filename)
+ );
$serverVars = array(
'REQUEST_URI' => '/test.txt',
'REQUEST_METHOD' => 'PUT',
'HTTP_IF' => '(["'.$etag.'"])',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('newbody');
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status, 'Incorrect status received. Full response body:' . $this->response->body);
+ $this->assertEquals(204, $this->response->status, 'Incorrect status received. Full response body:' . $this->response->body);
}
@@ -921,18 +944,17 @@ class PluginTest extends DAV\AbstractServer {
'REQUEST_METHOD' => 'DELETE',
'HTTP_IF' => '(["etag1"])',
);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
- $request = new HTTP\Request($serverVars);
- $request->setBody('newbody');
$this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 412 Precondition failed',$this->response->status);
+ $this->assertEquals(412, $this->response->status);
}
function testGetTimeoutHeader() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'HTTP_TIMEOUT' => 'second-100',
));
@@ -941,22 +963,21 @@ class PluginTest extends DAV\AbstractServer {
}
+ function testGetTimeoutHeaderTwoItems() {
- function testGetTimeoutHeaderNotSet() {
-
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
+ 'HTTP_TIMEOUT' => 'second-5, infinite',
));
$this->server->httpRequest = $request;
- $this->assertEquals(0, $this->locksPlugin->getTimeoutHeader());
+ $this->assertEquals(5, $this->locksPlugin->getTimeoutHeader());
}
-
function testGetTimeoutHeaderInfinite() {
- $request = new HTTP\Request(array(
- 'HTTP_TIMEOUT' => 'infinite',
+ $request = HTTP\Sapi::createFromServerArray(array(
+ 'HTTP_TIMEOUT' => 'infinite, second-5',
));
$this->server->httpRequest = $request;
@@ -969,7 +990,7 @@ class PluginTest extends DAV\AbstractServer {
*/
function testGetTimeoutHeaderInvalid() {
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'HTTP_TIMEOUT' => 'yourmom',
));
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Mock/Collection.php b/vendor/sabre/dav/tests/Sabre/DAV/Mock/Collection.php
index b2613ec9f..6ccab4f66 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Mock/Collection.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Mock/Collection.php
@@ -15,7 +15,7 @@ use Sabre\DAV;
* * a string, for a file
* * An instance of \Sabre\DAV\INode.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH. All rights reserved.
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -32,10 +32,20 @@ class Collection extends DAV\Collection {
* @param array $children
* @return void
*/
- public function __construct($name, array $children = array(), Collection $parent = null) {
+ function __construct($name, array $children = [], Collection $parent = null) {
$this->name = $name;
- $this->children = $children;
+ foreach ($children as $key => $value) {
+ if (is_string($value)) {
+ $this->children[] = new File($key, $value, $this);
+ } elseif (is_array($value)) {
+ $this->children[] = new self($key, $value, $this);
+ } elseif ($value instanceof \Sabre\DAV\INode) {
+ $this->children[] = $value;
+ } else {
+ throw new \InvalidArgumentException('Unknown value passed in $children');
+ }
+ }
$this->parent = $parent;
}
@@ -47,7 +57,7 @@ class Collection extends DAV\Collection {
*
* @return string
*/
- public function getName() {
+ function getName() {
return $this->name;
@@ -77,12 +87,12 @@ class Collection extends DAV\Collection {
* @param resource|string $data Initial payload
* @return null|string
*/
- public function createFile($name, $data = null) {
+ function createFile($name, $data = null) {
if (is_resource($data)) {
$data = stream_get_contents($data);
}
- $this->children[$name] = $data;
+ $this->children[] = new File($name, $data, $this);
return '"' . md5($data) . '"';
}
@@ -93,9 +103,9 @@ class Collection extends DAV\Collection {
* @param string $name
* @return void
*/
- public function createDirectory($name) {
+ function createDirectory($name) {
- $this->children[$name] = array();
+ $this->children[] = new self($name);
}
@@ -104,22 +114,18 @@ class Collection extends DAV\Collection {
*
* @return \Sabre\DAV\INode[]
*/
- public function getChildren() {
+ function getChildren() {
- $result = array();
- foreach($this->children as $key=>$value) {
+ return $this->children;
- if ($value instanceof DAV\INode) {
- $result[] = $value;
- } elseif (is_array($value)) {
- $result[] = new Collection($key, $value, $this);
- } else {
- $result[] = new File($key, $value, $this);
- }
+ }
- }
+ /**
+ * Adds an already existing node to this collection.
+ */
+ function addNode(\Sabre\DAV\INode $node) {
- return $result;
+ $this->children[] = $node;
}
@@ -129,16 +135,11 @@ class Collection extends DAV\Collection {
* @param string $name
* @return void
*/
- public function deleteChild($name) {
+ function deleteChild($name) {
- foreach($this->children as $key=>$value) {
+ foreach ($this->children as $key => $value) {
- if ($value instanceof DAV\INode) {
- if ($value->getName() == $name) {
- unset($this->children[$key]);
- return;
- }
- } elseif ($key === $name) {
+ if ($value->getName() == $name) {
unset($this->children[$key]);
return;
}
@@ -152,9 +153,9 @@ class Collection extends DAV\Collection {
*
* @return void
*/
- public function delete() {
+ function delete() {
- foreach($this->getChildren() as $child) {
+ foreach ($this->getChildren() as $child) {
$this->deleteChild($child->getName());
}
$this->parent->deleteChild($this->getName());
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Mock/File.php b/vendor/sabre/dav/tests/Sabre/DAV/Mock/File.php
index 2b25bbb88..23855e3c5 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Mock/File.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Mock/File.php
@@ -9,7 +9,7 @@ use Sabre\DAV;
*
* See the Collection in this directory for more details.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH. All rights reserved.
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -18,6 +18,7 @@ class File extends DAV\File {
protected $name;
protected $contents;
protected $parent;
+ protected $lastModified;
/**
* Creates the object
@@ -26,12 +27,18 @@ class File extends DAV\File {
* @param array $children
* @return void
*/
- public function __construct($name, $contents, Collection $parent) {
+ function __construct($name, $contents, Collection $parent = null, $lastModified = -1) {
$this->name = $name;
$this->put($contents);
$this->parent = $parent;
+ if ($lastModified === -1) {
+ $lastModified = time();
+ }
+
+ $this->lastModified = $lastModified;
+
}
/**
@@ -41,13 +48,24 @@ class File extends DAV\File {
*
* @return string
*/
- public function getName() {
+ function getName() {
return $this->name;
}
/**
+ * Changes the name of the node.
+ *
+ * @return void
+ */
+ function setName($name) {
+
+ $this->name = $name;
+
+ }
+
+ /**
* Updates the data
*
* The data argument is a readable stream resource.
@@ -67,7 +85,7 @@ class File extends DAV\File {
* @param resource $data
* @return string|null
*/
- public function put($data) {
+ function put($data) {
if (is_resource($data)) {
$data = stream_get_contents($data);
@@ -84,7 +102,7 @@ class File extends DAV\File {
*
* @return mixed
*/
- public function get() {
+ function get() {
return $this->contents;
@@ -99,7 +117,7 @@ class File extends DAV\File {
*
* @return void
*/
- public function getETag() {
+ function getETag() {
return '"' . md5($this->contents) . '"';
@@ -110,7 +128,7 @@ class File extends DAV\File {
*
* @return int
*/
- public function getSize() {
+ function getSize() {
return strlen($this->contents);
@@ -121,10 +139,22 @@ class File extends DAV\File {
*
* @return void
*/
- public function delete() {
+ function delete() {
$this->parent->deleteChild($this->name);
}
+ /**
+ * Returns the last modification time as a unix timestamp.
+ * If the information is not available, return null.
+ *
+ * @return int
+ */
+ function getLastModified() {
+
+ return $this->lastModified;
+
+ }
+
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Mount/PluginTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Mount/PluginTest.php
index e818fe043..e6415792c 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/Mount/PluginTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/Mount/PluginTest.php
@@ -23,11 +23,11 @@ class PluginTest extends DAV\AbstractServer {
'REQUEST_METHOD' => 'GET',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals('HTTP/1.1 501 Not Implemented',$this->response->status,'We expected GET to not be implemented for Directories. Response body: ' . $this->response->body);
+ $this->assertEquals(501, $this->response->status,'We expected GET to not be implemented for Directories. Response body: ' . $this->response->body);
}
@@ -40,14 +40,14 @@ class PluginTest extends DAV\AbstractServer {
'HTTP_HOST' => 'example.org',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200, $this->response->status);
$xml = simplexml_load_string($this->response->body);
- $this->assertTrue($xml==true,'Response was not a valid xml document');
+ $this->assertInstanceOf('SimpleXMLElement',$xml, 'Response was not a valid xml document. The list of errors:' . print_r(libxml_get_errors(),true) . '. xml body: ' . $this->response->body . '. What type we got: ' . gettype($xml) . ' class, if object: ' . get_class($xml));
$xml->registerXPathNamespace('dm','http://purl.org/NET/webdav/mount');
$url = $xml->xpath('//dm:url');
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/ObjectTreeTest.php b/vendor/sabre/dav/tests/Sabre/DAV/ObjectTreeTest.php
index 330058b6d..9b7eeb90c 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/ObjectTreeTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/ObjectTreeTest.php
@@ -16,7 +16,7 @@ class ObjectTreeTest extends \PHPUnit_Framework_TestCase {
file_put_contents(SABRE_TEMPDIR . '/root/file.txt','contents');
file_put_contents(SABRE_TEMPDIR . '/root/subdir/subfile.txt','subcontents');
$rootNode = new FSExt\Directory(SABRE_TEMPDIR . '/root');
- $this->tree = new ObjectTree($rootNode);
+ $this->tree = new Tree($rootNode);
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/PartialUpdate/FileMock.php b/vendor/sabre/dav/tests/Sabre/DAV/PartialUpdate/FileMock.php
index e8cdc1666..d6cc406be 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/PartialUpdate/FileMock.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/PartialUpdate/FileMock.php
@@ -3,7 +3,7 @@
namespace Sabre\DAV\PartialUpdate;
use Sabre\DAV;
-class FileMock implements IFile {
+class FileMock implements IPatchSupport {
protected $data = '';
@@ -16,14 +16,56 @@ class FileMock implements IFile {
}
- function putRange($str,$start) {
-
- if (is_resource($str)) {
- $str = stream_get_contents($str);
+ /**
+ * Updates the file based on a range specification.
+ *
+ * The first argument is the data, which is either a readable stream
+ * resource or a string.
+ *
+ * The second argument is the type of update we're doing.
+ * This is either:
+ * * 1. append
+ * * 2. update based on a start byte
+ * * 3. update based on an end byte
+ *;
+ * The third argument is the start or end byte.
+ *
+ * After a successful put operation, you may choose to return an ETag. The
+ * etag must always be surrounded by double-quotes. These quotes must
+ * appear in the actual string you're returning.
+ *
+ * Clients may use the ETag from a PUT request to later on make sure that
+ * when they update the file, the contents haven't changed in the mean
+ * time.
+ *
+ * @param resource|string $data
+ * @param int $rangeType
+ * @param int $offset
+ * @return string|null
+ */
+ function patch($data, $rangeType, $offset = null) {
+
+ if (is_resource($data)) {
+ $data = stream_get_contents($data);
}
- $this->data = substr($this->data, 0, $start) . $str . substr($this->data, $start + strlen($str));
+ switch($rangeType) {
+
+ case 1 :
+ $this->data.=$data;
+ break;
+ case 3 :
+ // Turn the offset into an offset-offset.
+ $offset = strlen($this->data) - $offset;
+ // No break is intentional
+ case 2 :
+ $this->data =
+ substr($this->data, 0, $offset) .
+ $data .
+ substr($this->data, $offset + strlen($data));
+ break;
+ }
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/PartialUpdate/PluginTest.php b/vendor/sabre/dav/tests/Sabre/DAV/PartialUpdate/PluginTest.php
index 32f7e4e2c..5bd696416 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/PartialUpdate/PluginTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/PartialUpdate/PluginTest.php
@@ -12,7 +12,7 @@ class PluginTest extends \Sabre\DAVServerTest {
protected $node;
protected $plugin;
- public function setUp() {
+ function setUp() {
$this->node = new FileMock();
$this->tree[] = $this->node;
@@ -26,124 +26,109 @@ class PluginTest extends \Sabre\DAVServerTest {
}
- public function testInit() {
+ function testInit() {
$this->assertEquals('partialupdate', $this->plugin->getPluginName());
- $this->assertEquals(array('sabredav-partialupdate'), $this->plugin->getFeatures());
- $this->assertEquals(array(
+ $this->assertEquals(['sabredav-partialupdate'], $this->plugin->getFeatures());
+ $this->assertEquals([
'PATCH'
- ), $this->plugin->getHTTPMethods('partial'));
- $this->assertEquals(array(
- ), $this->plugin->getHTTPMethods(''));
-
- $this->assertNull($this->plugin->unknownMethod('FOO','partial'));
+ ], $this->plugin->getHTTPMethods('partial'));
+ $this->assertEquals([
+ ], $this->plugin->getHTTPMethods(''));
}
- public function testPatchNoRange() {
+ function testPatchNoRange() {
- $this->node->put('00000000');
- $request = new HTTP\Request(array(
+ $this->node->put('aaaaaaaa');
+ $request = HTTP\Sapi::createFromServerArray([
'REQUEST_METHOD' => 'PATCH',
'REQUEST_URI' => '/partial',
- ));
+ ]);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 400 Bad request', $response->status, 'Full response body:' . $response->body);
+ $this->assertEquals(400, $response->status, 'Full response body:' . $response->body);
}
- public function testPatchNotSupported() {
-
- $this->node->put('00000000');
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'PATCH',
- 'REQUEST_URI' => '/',
- 'X_UPDATE_RANGE' => '3-4',
+ function testPatchNotSupported() {
- ));
+ $this->node->put('aaaaaaaa');
+ $request = new HTTP\Request('PATCH', '/', ['X-Update-Range' => '3-4']);
$request->setBody(
- '111'
+ 'bbb'
);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 405 Method Not Allowed', $response->status, 'Full response body:' . $response->body);
+ $this->assertEquals(405, $response->status, 'Full response body:' . $response->body);
}
- public function testPatchNoContentType() {
+ function testPatchNoContentType() {
- $this->node->put('00000000');
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'PATCH',
- 'REQUEST_URI' => '/partial',
- 'HTTP_X_UPDATE_RANGE' => 'bytes=3-4',
+ $this->node->put('aaaaaaaa');
+ $request = new HTTP\Request('PATCH', '/partial', ['X-Update-Range' => 'bytes=3-4']);
+ $request->setBody(
+ 'bbb'
+ );
+ $response = $this->request($request);
- ));
+ $this->assertEquals(415, $response->status, 'Full response body:' . $response->body);
+
+ }
+
+ function testPatchBadRange() {
+
+ $this->node->put('aaaaaaaa');
+ $request = new HTTP\Request('PATCH', '/partial', ['X-Update-Range' => 'bytes=3-4', 'Content-Type' => 'application/x-sabredav-partialupdate', 'Content-Length' => '3']);
$request->setBody(
- '111'
+ 'bbb'
);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 415 Unsupported Media Type', $response->status, 'Full response body:' . $response->body);
+ $this->assertEquals(416, $response->status, 'Full response body:' . $response->body);
}
- public function testPatchBadRange() {
+ function testPatchNoLength() {
- $this->node->put('00000000');
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'PATCH',
- 'REQUEST_URI' => '/partial',
- 'HTTP_X_UPDATE_RANGE' => 'bytes=3-4',
- 'HTTP_CONTENT_TYPE' => 'application/x-sabredav-partialupdate',
- ));
+ $this->node->put('aaaaaaaa');
+ $request = new HTTP\Request('PATCH', '/partial', ['X-Update-Range' => 'bytes=3-5', 'Content-Type' => 'application/x-sabredav-partialupdate']);
$request->setBody(
- '111'
+ 'bbb'
);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 411 Length Required', $response->status, 'Full response body:' . $response->body);
+ $this->assertEquals(411, $response->status, 'Full response body:' . $response->body);
}
- public function testPatchSuccess() {
+ function testPatchSuccess() {
- $this->node->put('00000000');
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'PATCH',
- 'REQUEST_URI' => '/partial',
- 'HTTP_X_UPDATE_RANGE' => 'bytes=3-5',
- 'HTTP_CONTENT_TYPE' => 'application/x-sabredav-partialupdate',
- 'HTTP_CONTENT_LENGTH' => 3,
- ));
+ $this->node->put('aaaaaaaa');
+ $request = new HTTP\Request('PATCH', '/partial', ['X-Update-Range' => 'bytes=3-5', 'Content-Type' => 'application/x-sabredav-partialupdate', 'Content-Length' => 3]);
$request->setBody(
- '111'
+ 'bbb'
);
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 204 No Content', $response->status, 'Full response body:' . $response->body);
- $this->assertEquals('00011100', $this->node->get());
+ $this->assertEquals(204, $response->status, 'Full response body:' . $response->body);
+ $this->assertEquals('aaabbbaa', $this->node->get());
}
- public function testPatchNoEndRange() {
+ function testPatchNoEndRange() {
- $this->node->put('00000');
- $request = new HTTP\Request(array(
- 'REQUEST_METHOD' => 'PATCH',
- 'REQUEST_URI' => '/partial',
- 'HTTP_X_UPDATE_RANGE' => 'bytes=3-',
- 'HTTP_CONTENT_TYPE' => 'application/x-sabredav-partialupdate',
- 'HTTP_CONTENT_LENGTH' => 3,
- ));
+ $this->node->put('aaaaa');
+ $request = new HTTP\Request('PATCH', '/partial', ['X-Update-Range' => 'bytes=3-', 'Content-Type' => 'application/x-sabredav-partialupdate', 'Content-Length' => '3']);
$request->setBody(
- '111'
+ 'bbb'
);
+
$response = $this->request($request);
- $this->assertEquals('HTTP/1.1 204 No Content', $response->status, 'Full response body:' . $response->body);
- $this->assertEquals('00111', $this->node->get());
+ $this->assertEquals(204, $response->getStatus(), 'Full response body:' . $response->getBodyAsString());
+ $this->assertEquals('aaabbb', $this->node->get());
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/PartialUpdate/SpecificationTest.php b/vendor/sabre/dav/tests/Sabre/DAV/PartialUpdate/SpecificationTest.php
index 7abe69c55..31be2a1b1 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/PartialUpdate/SpecificationTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/PartialUpdate/SpecificationTest.php
@@ -42,21 +42,21 @@ class SpecificationTest extends \PHPUnit_Framework_TestCase {
*/
public function testUpdateRange($headerValue, $httpStatus, $endResult, $contentLength = 4) {
- $vars = array(
- 'REQUEST_METHOD' => 'PATCH',
- 'HTTP_CONTENT_TYPE' => 'application/x-sabredav-partialupdate',
- 'HTTP_X_UPDATE_RANGE' => $headerValue,
- 'REQUEST_URI' => '/foobar.txt',
- );
+ $headers = [
+ 'Content-Type' => 'application/x-sabredav-partialupdate',
+ 'X-Update-Range' => $headerValue,
+ ];
+
if ($contentLength) {
- $vars['HTTP_CONTENT_LENGTH'] = (string)$contentLength;
+ $headers['Content-Length'] = (string)$contentLength;
}
- $request = new HTTP\Request($vars);
+ $request = new HTTP\Request('PATCH', '/foobar.txt', $headers, '----');
$request->setBody('----');
$this->server->httpRequest = $request;
$this->server->httpResponse = new HTTP\ResponseMock();
+ $this->server->sapi = new HTTP\SapiMock();
$this->server->exec();
$this->assertEquals($httpStatus, $this->server->httpResponse->status, 'Incorrect http status received: ' . $this->server->httpResponse->body);
@@ -70,17 +70,17 @@ class SpecificationTest extends \PHPUnit_Framework_TestCase {
return array(
// Problems
- array('foo', 'HTTP/1.1 400 Bad request', null),
- array('bytes=0-3', 'HTTP/1.1 411 Length Required', null, 0),
- array('bytes=4-1', 'HTTP/1.1 416 Requested Range Not Satisfiable', null),
-
- array('bytes=0-3', 'HTTP/1.1 204 No Content', '----567890'),
- array('bytes=1-4', 'HTTP/1.1 204 No Content', '1----67890'),
- array('bytes=0-', 'HTTP/1.1 204 No Content', '----567890'),
- array('bytes=-4', 'HTTP/1.1 204 No Content', '123456----'),
- array('bytes=-2', 'HTTP/1.1 204 No Content', '12345678----'),
- array('bytes=2-', 'HTTP/1.1 204 No Content', '12----7890'),
- array('append', 'HTTP/1.1 204 No Content', '1234567890----'),
+ array('foo', 400, null),
+ array('bytes=0-3', 411, null, 0),
+ array('bytes=4-1', 416, null),
+
+ array('bytes=0-3', 204, '----567890'),
+ array('bytes=1-4', 204, '1----67890'),
+ array('bytes=0-', 204, '----567890'),
+ array('bytes=-4', 204, '123456----'),
+ array('bytes=-2', 204, '12345678----'),
+ array('bytes=2-', 204, '12----7890'),
+ array('append', 204, '1234567890----'),
);
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Property/GetLastModifiedTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Property/GetLastModifiedTest.php
deleted file mode 100644
index de8ca1283..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAV/Property/GetLastModifiedTest.php
+++ /dev/null
@@ -1,75 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-use Sabre\DAV;
-use Sabre\HTTP;
-
-class GetLastModifiedTest extends \PHPUnit_Framework_TestCase {
-
- function testConstructDateTime() {
-
- $dt = new \DateTime('2010-03-14 16:35', new \DateTimeZone('UTC'));
- $lastMod = new GetLastModified($dt);
- $this->assertEquals($dt->format(\DateTime::ATOM), $lastMod->getTime()->format(\DateTime::ATOM));
-
- }
-
- function testConstructString() {
-
- $dt = new \DateTime('2010-03-14 16:35', new \DateTimeZone('UTC'));
- $lastMod = new GetLastModified('2010-03-14 16:35');
- $this->assertEquals($dt->format(\DateTime::ATOM), $lastMod->getTime()->format(\DateTime::ATOM));
-
- }
-
- function testConstructInt() {
-
- $dt = new \DateTime('2010-03-14 16:35', new \DateTimeZone('UTC'));
- $lastMod = new GetLastModified((int)$dt->format('U'));
- $this->assertEquals($dt->format(\DateTime::ATOM), $lastMod->getTime()->format(\DateTime::ATOM));
-
- }
-
- function testSerialize() {
-
- $dt = new \DateTime('2010-03-14 16:35', new \DateTimeZone('UTC'));
- $lastMod = new GetLastModified($dt);
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:getlastmodified');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $server = new DAV\Server();
-
- $lastMod->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- /*
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:getlastmodified xmlns:d="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.rfc1123">' .
-HTTP\Util::toHTTPDate($dt) .
-'</d:getlastmodified>
-', $xml);
- */
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:getlastmodified xmlns:d="DAV:">' .
-HTTP\Util::toHTTPDate($dt) .
-'</d:getlastmodified>
-', $xml);
-
- $ok = false;
- try {
- GetLastModified::unserialize(DAV\XMLUtil::loadDOMDocument($xml)->firstChild);
- } catch (DAV\Exception $e) {
- $ok = true;
- }
- if (!$ok) $this->markTestFailed('Unserialize should not be supported');
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Property/HrefListTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Property/HrefListTest.php
deleted file mode 100644
index fe2bc81f9..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAV/Property/HrefListTest.php
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-use Sabre\DAV;
-
-class HrefListTest extends \PHPUnit_Framework_TestCase {
-
- function testConstruct() {
-
- $href = new HrefList(array('foo','bar'));
- $this->assertEquals(array('foo','bar'),$href->getHrefs());
-
- }
-
- function testSerialize() {
-
- $href = new HrefList(array('foo','bar'));
- $this->assertEquals(array('foo','bar'),$href->getHrefs());
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:anything');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $server = new DAV\Server();
- $server->setBaseUri('/bla/');
-
- $href->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:anything xmlns:d="DAV:"><d:href>/bla/foo</d:href><d:href>/bla/bar</d:href></d:anything>
-', $xml);
-
- }
-
- function testSerializeNoPrefix() {
-
- $href = new HrefList(array('foo','bar'), false);
- $this->assertEquals(array('foo','bar'),$href->getHrefs());
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:anything');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $server = new DAV\Server();
- $server->setBaseUri('/bla/');
-
- $href->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:anything xmlns:d="DAV:"><d:href>foo</d:href><d:href>bar</d:href></d:anything>
-', $xml);
-
- }
-
- function testUnserialize() {
-
- $xml = '<?xml version="1.0"?>
-<d:anything xmlns:d="urn:DAV"><d:href>/bla/foo</d:href><d:href>/bla/bar</d:href></d:anything>
-';
-
- $dom = new \DOMDocument();
- $dom->loadXML($xml);
-
- $href = HrefList::unserialize($dom->firstChild);
- $this->assertEquals(array('/bla/foo','/bla/bar'),$href->getHrefs());
-
- }
-
- function testUnserializeIncompatible() {
-
- $xml = '<?xml version="1.0"?>
-<d:anything xmlns:d="urn:DAV"><d:href2>/bla/foo</d:href2></d:anything>
-';
-
- $dom = new \DOMDocument();
- $dom->loadXML($xml);
-
- $href = HrefList::unserialize($dom->firstChild);
- $this->assertEquals(array(), $href->getHrefs());
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Property/HrefTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Property/HrefTest.php
deleted file mode 100644
index e5607f51b..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAV/Property/HrefTest.php
+++ /dev/null
@@ -1,119 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-use Sabre\DAV;
-
-class HrefTest extends \PHPUnit_Framework_TestCase {
-
- function testConstruct() {
-
- $href = new Href('path');
- $this->assertEquals('path',$href->getHref());
-
- }
-
- function testSerialize() {
-
- $href = new Href('path');
- $this->assertEquals('path',$href->getHref());
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:anything');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $server = new DAV\Server();
- $server->setBaseUri('/bla/');
-
- $href->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:anything xmlns:d="DAV:"><d:href>/bla/path</d:href></d:anything>
-', $xml);
-
- }
-
- function testSerializeNoPrefix() {
-
- $href = new Href('path',false);
- $this->assertEquals('path',$href->getHref());
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:anything');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $server = new DAV\Server();
- $server->setBaseUri('/bla/');
-
- $href->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:anything xmlns:d="DAV:"><d:href>path</d:href></d:anything>
-', $xml);
-
- }
-
- function testUnserialize() {
-
- $xml = '<?xml version="1.0"?>
-<d:anything xmlns:d="urn:DAV"><d:href>/bla/path</d:href></d:anything>
-';
-
- $dom = new \DOMDocument();
- $dom->loadXML($xml);
-
- $href = Href::unserialize($dom->firstChild);
- $this->assertEquals('/bla/path',$href->getHref());
-
- }
-
- function testUnserializeIncompatible() {
-
- $xml = '<?xml version="1.0"?>
-<d:anything xmlns:d="urn:DAV"><d:href2>/bla/path</d:href2></d:anything>
-';
-
- $dom = new \DOMDocument();
- $dom->loadXML($xml);
-
- $href = Href::unserialize($dom->firstChild);
- $this->assertNull($href);
-
- }
-
- /**
- * This method tests if hrefs containing & are correctly encoded.
- */
- function testSerializeEntity() {
-
- $href = new Href('http://example.org/?a&b', false);
- $this->assertEquals('http://example.org/?a&b',$href->getHref());
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:anything');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $server = new DAV\Server();
- $server->setBaseUri('/bla/');
-
- $href->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:anything xmlns:d="DAV:"><d:href>http://example.org/?a&amp;b</d:href></d:anything>
-', $xml);
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Property/ResourceTypeTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Property/ResourceTypeTest.php
deleted file mode 100644
index 8a579baec..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAV/Property/ResourceTypeTest.php
+++ /dev/null
@@ -1,111 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-use Sabre\DAV;
-
-class ResourceTypeTest extends \PHPUnit_Framework_TestCase {
-
- function testConstruct() {
-
- $resourceType = new ResourceType(array('{DAV:}collection'));
- $this->assertEquals(array('{DAV:}collection'),$resourceType->getValue());
-
- $resourceType = new ResourceType(DAV\Server::NODE_FILE);
- $this->assertEquals(array(),$resourceType->getValue());
-
- $resourceType = new ResourceType(DAV\Server::NODE_DIRECTORY);
- $this->assertEquals(array('{DAV:}collection'),$resourceType->getValue());
-
- $resourceType = new ResourceType('{DAV:}principal');
- $this->assertEquals(array('{DAV:}principal'),$resourceType->getValue());
-
- }
-
- /**
- * @depends testConstruct
- */
- function testSerialize() {
-
- $resourceType = new ResourceType(array('{DAV:}collection','{DAV:}principal'));
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:anything');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $server = new DAV\Server();
- $resourceType->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:anything xmlns:d="DAV:"><d:collection/><d:principal/></d:anything>
-', $xml);
-
- }
-
- /**
- * @depends testSerialize
- */
- function testSerializeCustomNS() {
-
- $resourceType = new ResourceType(array('{http://example.org/NS}article'));
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:anything');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $server = new DAV\Server();
- $resourceType->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:anything xmlns:d="DAV:"><custom:article xmlns:custom="http://example.org/NS"/></d:anything>
-', $xml);
-
- }
-
- /**
- * @depends testConstruct
- */
- function testIs() {
-
- $resourceType = new ResourceType(array('{DAV:}collection','{DAV:}principal'));
- $this->assertTrue($resourceType->is('{DAV:}collection'));
- $this->assertFalse($resourceType->is('{DAV:}blabla'));
-
- }
-
- /**
- * @depends testConstruct
- */
- function testAdd() {
-
- $resourceType = new ResourceType(array('{DAV:}collection','{DAV:}principal'));
- $resourceType->add('{DAV:}foo');
- $this->assertEquals(array('{DAV:}collection','{DAV:}principal','{DAV:}foo'), $resourceType->getValue());
-
- }
-
- /**
- * @depends testConstruct
- */
- function testUnserialize() {
-
- $xml ='<?xml version="1.0"?>
-<d:anything xmlns:d="DAV:"><d:collection/><d:principal/></d:anything>
-';
-
- $dom = DAV\XMLUtil::loadDOMDocument($xml);
-
- $resourceType = ResourceType::unserialize($dom->firstChild);
- $this->assertEquals(array('{DAV:}collection','{DAV:}principal'),$resourceType->getValue());
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Property/ResponseListTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Property/ResponseListTest.php
deleted file mode 100644
index d13066b80..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAV/Property/ResponseListTest.php
+++ /dev/null
@@ -1,19 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-class ResponseListTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * This was the only part not yet covered by other tests, so I'm going to
- * be lazy and (for now) only test this case.
- *
- * @expectedException InvalidArgumentException
- */
- public function testInvalidArg() {
-
- $response = new ResponseList(array(1,2));
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Property/ResponseTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Property/ResponseTest.php
deleted file mode 100644
index 073cbb2ce..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAV/Property/ResponseTest.php
+++ /dev/null
@@ -1,230 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-use Sabre\DAV;
-
-class ResponseTest extends \PHPUnit_Framework_TestCase {
-
- function testSimple() {
-
- $innerProps = array(
- 200 => array(
- '{DAV:}displayname' => 'my file',
- ),
- 404 => array(
- '{DAV:}owner' => null,
- )
- );
-
- $property = new Response('uri',$innerProps);
-
- $this->assertEquals('uri',$property->getHref());
- $this->assertEquals($innerProps,$property->getResponseProperties());
-
-
- }
-
- /**
- * @depends testSimple
- */
- function testSerialize() {
-
- $innerProps = array(
- 200 => array(
- '{DAV:}displayname' => 'my file',
- ),
- 404 => array(
- '{DAV:}owner' => null,
- )
- );
-
- $property = new Response('uri',$innerProps);
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:root');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $server = new DAV\Server();
-
- $property->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:root xmlns:d="DAV:">' .
-'<d:response>' .
-'<d:href>/uri</d:href>' .
-'<d:propstat>' .
-'<d:prop>' .
-'<d:displayname>my file</d:displayname>' .
-'</d:prop>' .
-'<d:status>HTTP/1.1 200 OK</d:status>' .
-'</d:propstat>' .
-'<d:propstat>' .
-'<d:prop>' .
-'<d:owner/>' .
-'</d:prop>' .
-'<d:status>HTTP/1.1 404 Not Found</d:status>' .
-'</d:propstat>' .
-'</d:response>' .
-'</d:root>
-', $xml);
-
- }
-
- /**
- * This one is specifically for testing properties with no namespaces, which is legal xml
- *
- * @depends testSerialize
- */
- function testSerializeEmptyNamespace() {
-
- $innerProps = array(
- 200 => array(
- '{}propertyname' => 'value',
- ),
- );
-
- $property = new Response('uri',$innerProps);
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:root');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $server = new DAV\Server();
-
- $property->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:root xmlns:d="DAV:">' .
-'<d:response>' .
-'<d:href>/uri</d:href>' .
-'<d:propstat>' .
-'<d:prop>' .
-'<propertyname xmlns="">value</propertyname>' .
-'</d:prop>' .
-'<d:status>HTTP/1.1 200 OK</d:status>' .
-'</d:propstat>' .
-'</d:response>' .
-'</d:root>
-', $xml);
-
- }
-
- /**
- * This one is specifically for testing properties with no namespaces, which is legal xml
- *
- * @depends testSerialize
- */
- function testSerializeCustomNamespace() {
-
- $innerProps = array(
- 200 => array(
- '{http://sabredav.org/NS/example}propertyname' => 'value',
- ),
- );
-
- $property = new Response('uri',$innerProps);
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:root');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $server = new DAV\Server();
-
- $property->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:root xmlns:d="DAV:">' .
-'<d:response>' .
-'<d:href>/uri</d:href>' .
-'<d:propstat>' .
-'<d:prop>' .
-'<x2:propertyname xmlns:x2="http://sabredav.org/NS/example">value</x2:propertyname>' .
-'</d:prop>' .
-'<d:status>HTTP/1.1 200 OK</d:status>' .
-'</d:propstat>' .
-'</d:response>' .
-'</d:root>
-', $xml);
-
- }
-
- /**
- * @depends testSerialize
- */
- function testSerializeComplexProperty() {
-
- $innerProps = array(
- 200 => array(
- '{DAV:}link' => new Href('http://sabredav.org/', false)
- ),
- );
-
- $property = new Response('uri',$innerProps);
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:root');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $server = new DAV\Server();
-
- $property->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:root xmlns:d="DAV:">' .
-'<d:response>' .
-'<d:href>/uri</d:href>' .
-'<d:propstat>' .
-'<d:prop>' .
-'<d:link><d:href>http://sabredav.org/</d:href></d:link>' .
-'</d:prop>' .
-'<d:status>HTTP/1.1 200 OK</d:status>' .
-'</d:propstat>' .
-'</d:response>' .
-'</d:root>
-', $xml);
-
- }
-
- /**
- * @depends testSerialize
- * @expectedException Sabre\DAV\Exception
- */
- function testSerializeBreak() {
-
- $innerProps = array(
- 200 => array(
- '{DAV:}link' => new \STDClass()
- ),
- );
-
- $property = new Response('uri',$innerProps);
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:root');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $server = new DAV\Server();
-
- $property->serialize($server, $root);
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Property/SupportedReportSetTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Property/SupportedReportSetTest.php
deleted file mode 100644
index 445e22ab3..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAV/Property/SupportedReportSetTest.php
+++ /dev/null
@@ -1,128 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Property;
-
-use Sabre\DAV;
-use Sabre\HTTP;
-
-require_once 'Sabre/HTTP/ResponseMock.php';
-require_once 'Sabre/DAV/AbstractServer.php';
-
-class SupportedReportSetTest extends DAV\AbstractServer {
-
- public function sendPROPFIND($body) {
-
- $serverVars = array(
- 'REQUEST_URI' => '/',
- 'REQUEST_METHOD' => 'PROPFIND',
- 'HTTP_DEPTH' => '0',
- );
-
- $request = new HTTP\Request($serverVars);
- $request->setBody($body);
-
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- }
-
- /**
- * @covers Sabre\DAV\Property\SupportedReportSet
- */
- function testNoReports() {
-
- $xml = '<?xml version="1.0"?>
-<d:propfind xmlns:d="DAV:">
- <d:prop>
- <d:supported-report-set />
- </d:prop>
-</d:propfind>';
-
- $this->sendPROPFIND($xml);
-
- $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'We expected a multi-status response. Full response body: ' . $this->response->body);
-
- $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body);
- $xml = simplexml_load_string($body);
- $xml->registerXPathNamespace('d','urn:DAV');
-
- $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop');
- $this->assertEquals(1,count($data),'We expected 1 \'d:prop\' element');
-
- $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set');
- $this->assertEquals(1,count($data),'We expected 1 \'d:supported-report-set\' element');
-
- $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status');
- $this->assertEquals(1,count($data),'We expected 1 \'d:status\' element');
-
- $this->assertEquals('HTTP/1.1 200 OK',(string)$data[0],'The status for this property should have been 200');
-
- }
-
- /**
- * @covers Sabre\DAV\Property\SupportedReportSet
- * @depends testNoReports
- */
- function testCustomReport() {
-
- // Intercepting the report property
- $this->server->subscribeEvent('afterGetProperties',array($this,'addProp'));
-
- $xml = '<?xml version="1.0"?>
-<d:propfind xmlns:d="DAV:">
- <d:prop>
- <d:supported-report-set />
- </d:prop>
-</d:propfind>';
-
- $this->sendPROPFIND($xml);
-
- $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'We expected a multi-status response. Full response body: ' . $this->response->body);
-
- $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body);
- $xml = simplexml_load_string($body);
- $xml->registerXPathNamespace('d','urn:DAV');
- $xml->registerXPathNamespace('x','http://www.rooftopsolutions.nl/testnamespace');
-
- $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop');
- $this->assertEquals(1,count($data),'We expected 1 \'d:prop\' element');
-
- $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set');
- $this->assertEquals(1,count($data),'We expected 1 \'d:supported-report-set\' element');
-
- $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report');
- $this->assertEquals(2,count($data),'We expected 2 \'d:supported-report\' elements');
-
- $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report/d:report');
- $this->assertEquals(2,count($data),'We expected 2 \'d:report\' elements');
-
- $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report/d:report/x:myreport');
- $this->assertEquals(1,count($data),'We expected 1 \'x:myreport\' element. Full body: ' . $this->response->body);
-
- $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report/d:report/d:anotherreport');
- $this->assertEquals(1,count($data),'We expected 1 \'d:anotherreport\' element. Full body: ' . $this->response->body);
-
- $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status');
- $this->assertEquals(1,count($data),'We expected 1 \'d:status\' element');
-
- $this->assertEquals('HTTP/1.1 200 OK',(string)$data[0],'The status for this property should have been 200');
-
- }
-
- /**
- * This method is used as a callback for afterGetProperties
- */
- function addProp($path, &$properties) {
-
- if (isset($properties[200]['{DAV:}supported-report-set'])) {
- $properties[200]['{DAV:}supported-report-set']->addReport('{http://www.rooftopsolutions.nl/testnamespace}myreport');
- $properties[200]['{DAV:}supported-report-set']->addReport('{DAV:}anotherreport');
- }
-
- }
-
-
-
-}
-
-?>
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/ServerCopyMoveTest.php b/vendor/sabre/dav/tests/Sabre/DAV/ServerCopyMoveTest.php
deleted file mode 100644
index 88e107c19..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAV/ServerCopyMoveTest.php
+++ /dev/null
@@ -1,268 +0,0 @@
-<?php
-
-namespace Sabre\DAV;
-
-use Sabre\HTTP;
-
-require_once 'Sabre/HTTP/ResponseMock.php';
-
-class ServerCopyMoveTest extends \PHPUnit_Framework_TestCase {
-
- private $response;
- /**
- * @var Server
- */
- private $server;
-
- function setUp() {
-
- $this->response = new HTTP\ResponseMock();
- $dir = new FS\Directory(SABRE_TEMPDIR);
- $tree = new ObjectTree($dir);
- $this->server = new Server($tree);
- $this->server->debugExceptions = true;
- $this->server->httpResponse = $this->response;
- file_put_contents(SABRE_TEMPDIR . '/test.txt', 'Test contents');
- file_put_contents(SABRE_TEMPDIR . '/test2.txt', 'Test contents2');
- mkdir(SABRE_TEMPDIR . '/col');
- file_put_contents(SABRE_TEMPDIR . 'col/test.txt', 'Test contents');
-
- }
-
- function tearDown() {
-
- $cleanUp = array('test.txt','testput.txt','testcol','test2.txt','test3.txt','col/test.txt','col','col2/test.txt','col2');
- foreach($cleanUp as $file) {
- $tmpFile = SABRE_TEMPDIR . '/' . $file;
- if (file_exists($tmpFile)) {
-
- if (is_dir($tmpFile)) {
- rmdir($tmpFile);
- } else {
- unlink($tmpFile);
- }
-
- }
- }
-
- }
-
-
- function testCopyOverWrite() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'COPY',
- 'HTTP_DESTINATION' => '/test2.txt',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status,'Received an incorrect HTTP status. Full body inspection: ' . $this->response->body);
- $this->assertEquals(array(
- 'Content-Length' => '0',
- ),
- $this->response->headers
- );
-
- $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR. '/test2.txt'));
-
- }
-
- function testCopyToSelf() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'COPY',
- 'HTTP_DESTINATION' => '/test.txt',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals('HTTP/1.1 403 Forbidden',$this->response->status,'Received an incorrect HTTP status. Full body inspection: ' . $this->response->body);
- $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR. '/test.txt'));
-
- }
-
- function testMoveToSelf() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'MOVE',
- 'HTTP_DESTINATION' => '/test.txt',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals('HTTP/1.1 403 Forbidden',$this->response->status,'Received an incorrect HTTP status. Full body inspection: ' . $this->response->body);
- $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR. '/test.txt'));
-
- }
-
- function testMoveOverWrite() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'MOVE',
- 'HTTP_DESTINATION' => '/test2.txt',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Length' => 0,
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status);
- $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR . '/test2.txt'));
- $this->assertFalse(file_exists(SABRE_TEMPDIR . '/test.txt'),'The sourcefile test.txt should no longer exist at this point');
-
- }
-
- function testBlockedOverWrite() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'COPY',
- 'HTTP_DESTINATION' => '/test2.txt',
- 'HTTP_OVERWRITE' => 'F',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.1 412 Precondition failed',$this->response->status);
- $this->assertEquals('Test contents2',file_get_contents(SABRE_TEMPDIR . '/test2.txt'));
-
-
- }
-
- function testNonExistantParent() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'COPY',
- 'HTTP_DESTINATION' => '/testcol2/test2.txt',
- 'HTTP_OVERWRITE' => 'F',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.1 409 Conflict',$this->response->status);
-
- }
-
- function testRandomOverwriteHeader() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'COPY',
- 'HTTP_DESTINATION' => '/testcol2/test2.txt',
- 'HTTP_OVERWRITE' => 'SURE!',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status);
-
- }
-
- function testCopyDirectory() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/col',
- 'REQUEST_METHOD' => 'COPY',
- 'HTTP_DESTINATION' => '/col2',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Length' => '0',
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
- $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR . '/col2/test.txt'));
-
- }
-
- function testSimpleCopyFile() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'COPY',
- 'HTTP_DESTINATION' => '/test3.txt',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Length' => '0',
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
- $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR . '/test3.txt'));
-
- }
-
- function testSimpleCopyCollection() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/col',
- 'REQUEST_METHOD' => 'COPY',
- 'HTTP_DESTINATION' => '/col2',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status,'Incorrect status received. Full response body: ' . $this->response->body);
-
- $this->assertEquals(array(
- 'Content-Length' => '0',
- ),
- $this->response->headers
- );
-
-
- $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR . '/col2/test.txt'));
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/ServerEventsTest.php b/vendor/sabre/dav/tests/Sabre/DAV/ServerEventsTest.php
index 2c7a074df..6ac20d2da 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/ServerEventsTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/ServerEventsTest.php
@@ -1,6 +1,7 @@
<?php
namespace Sabre\DAV;
+
use Sabre\HTTP;
require_once 'Sabre/DAV/AbstractServer.php';
@@ -13,11 +14,11 @@ class ServerEventsTest extends AbstractServer {
function testAfterBind() {
- $this->server->subscribeEvent('afterBind',array($this,'afterBindHandler'));
+ $this->server->on('afterBind', [$this, 'afterBindHandler']);
$newPath = 'afterBind';
$this->tempPath = '';
- $this->server->createFile($newPath,'body');
+ $this->server->createFile($newPath, 'body');
$this->assertEquals($newPath, $this->tempPath);
}
@@ -28,25 +29,41 @@ class ServerEventsTest extends AbstractServer {
}
+ function testAfterResponse() {
+
+ $mock = $this->getMock('stdClass', ['afterResponseCallback']);
+ $mock->expects($this->once())->method('afterResponseCallback');
+
+ $this->server->on('afterResponse', [$mock, 'afterResponseCallback']);
+
+ $this->server->httpRequest = HTTP\Sapi::createFromServerArray([
+ 'REQUEST_METHOD' => 'GET',
+ 'REQUEST_URI' => '/test.txt',
+ ]);
+
+ $this->server->exec();
+
+ }
+
function testBeforeBindCancel() {
- $this->server->subscribeEvent('beforeBind', array($this,'beforeBindCancelHandler'));
- $this->assertFalse($this->server->createFile('bla','body'));
+ $this->server->on('beforeBind', [$this, 'beforeBindCancelHandler']);
+ $this->assertFalse($this->server->createFile('bla', 'body'));
// Also testing put()
- $req = new HTTP\Request(array(
+ $req = HTTP\Sapi::createFromServerArray([
'REQUEST_METHOD' => 'PUT',
- 'REQUEST_URI' => '/barbar',
- ));
+ 'REQUEST_URI' => '/barbar',
+ ]);
$this->server->httpRequest = $req;
$this->server->exec();
- $this->assertEquals('',$this->server->httpResponse->status);
+ $this->assertEquals(500, $this->server->httpResponse->getStatus());
}
- function beforeBindCancelHandler() {
+ function beforeBindCancelHandler($path) {
return false;
@@ -54,12 +71,12 @@ class ServerEventsTest extends AbstractServer {
function testException() {
- $this->server->subscribeEvent('exception', array($this, 'exceptionHandler'));
+ $this->server->on('exception', [$this, 'exceptionHandler']);
- $req = new HTTP\Request(array(
+ $req = HTTP\Sapi::createFromServerArray([
'REQUEST_METHOD' => 'GET',
- 'REQUEST_URI' => '/not/exisitng',
- ));
+ 'REQUEST_URI' => '/not/exisitng',
+ ]);
$this->server->httpRequest = $req;
$this->server->exec();
@@ -73,4 +90,35 @@ class ServerEventsTest extends AbstractServer {
}
+ function testMethod() {
+
+ $k = 1;
+ $this->server->on('method', function($request, $response) use (&$k) {
+
+ $k += 1;
+
+ return false;
+
+ });
+ $this->server->on('method', function($request, $response) use (&$k) {
+
+ $k += 2;
+
+ return false;
+
+ });
+
+ try {
+ $this->server->invokeMethod(
+ new HTTP\Request('BLABLA', '/'),
+ new HTTP\Response(),
+ false
+ );
+ } catch (Exception $e) {}
+
+ $this->assertEquals(2, $k);
+
+
+ }
+
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/ServerMKCOLTest.php b/vendor/sabre/dav/tests/Sabre/DAV/ServerMKCOLTest.php
index 34b084dcd..e35189ec3 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/ServerMKCOLTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/ServerMKCOLTest.php
@@ -4,10 +4,6 @@ namespace Sabre\DAV;
use Sabre\HTTP;
-require_once 'Sabre/HTTP/ResponseMock.php';
-require_once 'Sabre/DAV/AbstractServer.php';
-require_once 'Sabre/DAV/Exception.php';
-
class ServerMKCOLTest extends AbstractServer {
function testMkcol() {
@@ -17,16 +13,17 @@ class ServerMKCOLTest extends AbstractServer {
'REQUEST_METHOD' => 'MKCOL',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody("");
$this->server->httpRequest = ($request);
$this->server->exec();
$this->assertEquals(array(
- 'Content-Length' => '0',
- ),$this->response->headers);
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Length' => ['0'],
+ ),$this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
+ $this->assertEquals(201, $this->response->status);
$this->assertEquals('', $this->response->body);
$this->assertTrue(is_dir($this->tempDir . '/testcol'));
@@ -42,16 +39,17 @@ class ServerMKCOLTest extends AbstractServer {
'REQUEST_METHOD' => 'MKCOL',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody("Hello");
$this->server->httpRequest = ($request);
$this->server->exec();
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),$this->response->headers);
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ),$this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 415 Unsupported Media Type',$this->response->status);
+ $this->assertEquals(415, $this->response->status);
}
@@ -66,16 +64,17 @@ class ServerMKCOLTest extends AbstractServer {
'HTTP_CONTENT_TYPE' => 'application/xml',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody("Hello");
$this->server->httpRequest = ($request);
$this->server->exec();
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),$this->response->headers);
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ),$this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status);
+ $this->assertEquals(400, $this->response->getStatus(), $this->response->getBodyAsString() );
}
@@ -90,16 +89,17 @@ class ServerMKCOLTest extends AbstractServer {
'HTTP_CONTENT_TYPE' => 'application/xml',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?><html></html>');
$this->server->httpRequest = ($request);
$this->server->exec();
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),$this->response->headers);
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ),$this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 415 Unsupported Media Type',$this->response->status);
+ $this->assertEquals(400, $this->response->getStatus());
}
@@ -114,7 +114,7 @@ class ServerMKCOLTest extends AbstractServer {
'HTTP_CONTENT_TYPE' => 'application/xml',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?>
<mkcol xmlns="DAV:">
<set>
@@ -127,15 +127,16 @@ class ServerMKCOLTest extends AbstractServer {
$this->server->exec();
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),$this->response->headers);
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ),$this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body);
+ $this->assertEquals(400, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body);
}
/**
- * @depends testMKCOLNoResourceType
+ * @depends testMkcol
*/
function testMKCOLIncorrectResourceType() {
@@ -145,38 +146,7 @@ class ServerMKCOLTest extends AbstractServer {
'HTTP_CONTENT_TYPE' => 'application/xml',
);
- $request = new HTTP\Request($serverVars);
- $request->setBody('<?xml version="1.0"?>
-<mkcol xmlns="DAV:">
- <set>
- <prop>
- <resourcetype><blabla /></resourcetype>
- </prop>
- </set>
-</mkcol>');
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),$this->response->headers);
-
- $this->assertEquals('HTTP/1.1 403 Forbidden',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body);
-
- }
-
- /**
- * @depends testMKCOLIncorrectResourceType
- */
- function testMKCOLIncorrectResourceType2() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/testcol',
- 'REQUEST_METHOD' => 'MKCOL',
- 'HTTP_CONTENT_TYPE' => 'application/xml',
- );
-
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?>
<mkcol xmlns="DAV:">
<set>
@@ -189,15 +159,16 @@ class ServerMKCOLTest extends AbstractServer {
$this->server->exec();
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),$this->response->headers);
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ),$this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 403 Forbidden',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body);
+ $this->assertEquals(403, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body);
}
/**
- * @depends testMKCOLIncorrectResourceType2
+ * @depends testMKCOLIncorrectResourceType
*/
function testMKCOLSuccess() {
@@ -207,7 +178,7 @@ class ServerMKCOLTest extends AbstractServer {
'HTTP_CONTENT_TYPE' => 'application/xml',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?>
<mkcol xmlns="DAV:">
<set>
@@ -220,15 +191,16 @@ class ServerMKCOLTest extends AbstractServer {
$this->server->exec();
$this->assertEquals(array(
- 'Content-Length' => '0',
- ),$this->response->headers);
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Length' => ['0'],
+ ),$this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body);
+ $this->assertEquals(201, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body);
}
/**
- * @depends testMKCOLIncorrectResourceType2
+ * @depends testMKCOLIncorrectResourceType
*/
function testMKCOLWhiteSpaceResourceType() {
@@ -238,7 +210,7 @@ class ServerMKCOLTest extends AbstractServer {
'HTTP_CONTENT_TYPE' => 'application/xml',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?>
<mkcol xmlns="DAV:">
<set>
@@ -253,15 +225,16 @@ class ServerMKCOLTest extends AbstractServer {
$this->server->exec();
$this->assertEquals(array(
- 'Content-Length' => '0',
- ),$this->response->headers);
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Length' => ['0'],
+ ),$this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body);
+ $this->assertEquals(201, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body);
}
/**
- * @depends testMKCOLIncorrectResourceType2
+ * @depends testMKCOLIncorrectResourceType
*/
function testMKCOLNoParent() {
@@ -270,22 +243,23 @@ class ServerMKCOLTest extends AbstractServer {
'REQUEST_METHOD' => 'MKCOL',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('');
$this->server->httpRequest = ($request);
$this->server->exec();
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),$this->response->headers);
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ),$this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 409 Conflict',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body);
+ $this->assertEquals(409, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body);
}
/**
- * @depends testMKCOLIncorrectResourceType2
+ * @depends testMKCOLIncorrectResourceType
*/
function testMKCOLParentIsNoCollection() {
@@ -294,22 +268,23 @@ class ServerMKCOLTest extends AbstractServer {
'REQUEST_METHOD' => 'MKCOL',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('');
$this->server->httpRequest = ($request);
$this->server->exec();
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),$this->response->headers);
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ),$this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 409 Conflict',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body);
+ $this->assertEquals(409, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body);
}
/**
- * @depends testMKCOLIncorrectResourceType2
+ * @depends testMKCOLIncorrectResourceType
*/
function testMKCOLAlreadyExists() {
@@ -318,18 +293,19 @@ class ServerMKCOLTest extends AbstractServer {
'REQUEST_METHOD' => 'MKCOL',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('');
$this->server->httpRequest = ($request);
$this->server->exec();
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- 'Allow' => 'OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT',
- ),$this->response->headers);
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ 'Allow' => ['OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT'],
+ ),$this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 405 Method Not Allowed',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body);
+ $this->assertEquals(405, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body);
}
@@ -345,7 +321,7 @@ class ServerMKCOLTest extends AbstractServer {
'HTTP_CONTENT_TYPE' => 'application/xml',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody('<?xml version="1.0"?>
<mkcol xmlns="DAV:">
<set>
@@ -358,13 +334,12 @@ class ServerMKCOLTest extends AbstractServer {
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body);
+ $this->assertEquals(207, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body);
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),$this->response->headers);
-
-
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ),$this->response->getHeaders());
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/ServerPluginTest.php b/vendor/sabre/dav/tests/Sabre/DAV/ServerPluginTest.php
index 8f1451b49..ab0ad295e 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/ServerPluginTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/ServerPluginTest.php
@@ -24,13 +24,19 @@ class ServerPluginTest extends AbstractServer {
}
/**
- * @covers \Sabre\DAV\ServerPlugin
*/
function testBaseClass() {
$p = new ServerPluginMock();
- $this->assertEquals(array(),$p->getFeatures());
- $this->assertEquals(array(),$p->getHTTPMethods(''));
+ $this->assertEquals([],$p->getFeatures());
+ $this->assertEquals([],$p->getHTTPMethods(''));
+ $this->assertEquals(
+ [
+ 'name' => 'Sabre\DAV\ServerPluginMock',
+ 'description' => null,
+ 'link' => null
+ ], $p->getPluginInfo()
+ );
}
@@ -41,20 +47,20 @@ class ServerPluginTest extends AbstractServer {
'REQUEST_METHOD' => 'OPTIONS',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->httpRequest = ($request);
$this->server->exec();
$this->assertEquals(array(
- 'DAV' => '1, 3, extended-mkcol, drinking',
- 'MS-Author-Via' => 'DAV',
- 'Allow' => 'OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT, BEER, WINE',
- 'Accept-Ranges' => 'bytes',
- 'Content-Length' => '0',
- 'X-Sabre-Version' => Version::VERSION,
- ),$this->response->headers);
-
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ 'DAV' => ['1, 3, extended-mkcol, drinking'],
+ 'MS-Author-Via' => ['DAV'],
+ 'Allow' => ['OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT, BEER, WINE'],
+ 'Accept-Ranges' => ['bytes'],
+ 'Content-Length' => ['0'],
+ 'X-Sabre-Version' => [Version::VERSION],
+ ),$this->response->getHeaders());
+
+ $this->assertEquals(200, $this->response->status);
$this->assertEquals('', $this->response->body);
$this->assertEquals('OPTIONS',$this->testPlugin->beforeMethod);
@@ -82,7 +88,10 @@ class ServerPluginTest extends AbstractServer {
function testGetPlugins() {
$this->assertEquals(
- array(get_class($this->testPlugin) => $this->testPlugin),
+ array(
+ get_class($this->testPlugin) => $this->testPlugin,
+ 'core' => $this->server->getPlugin('core'),
+ ),
$this->server->getPlugins()
);
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/ServerPreconditionTest.php b/vendor/sabre/dav/tests/Sabre/DAV/ServerPreconditionTest.php
index ea09852a7..1dc8d8a37 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/ServerPreconditionTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/ServerPreconditionTest.php
@@ -9,362 +9,311 @@ require_once 'Sabre/HTTP/ResponseMock.php';
class ServerPreconditionsTest extends \PHPUnit_Framework_TestCase {
/**
- * @covers Sabre\DAV\Server::checkPreconditions
* @expectedException Sabre\DAV\Exception\PreconditionFailed
*/
function testIfMatchNoNode() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
- 'HTTP_IF_MATCH' => '*',
- 'REQUEST_URI' => '/bar'
- ));
- $server->httpRequest = $httpRequest;
-
- $server->checkPreconditions();
+ $httpRequest = new HTTP\Request('GET', '/bar', ['If-Match' => '*']);
+ $httpResponse = new HTTP\Response();
+ $server->checkPreconditions($httpRequest, $httpResponse);
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
*/
function testIfMatchHasNode() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
- 'HTTP_IF_MATCH' => '*',
- 'REQUEST_URI' => '/foo'
- ));
- $server->httpRequest = $httpRequest;
-
- $this->assertTrue($server->checkPreconditions());
+ $httpRequest = new HTTP\Request('GET', '/foo', ['If-Match' => '*']);
+ $httpResponse = new HTTP\Response();
+ $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse));
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
* @expectedException Sabre\DAV\Exception\PreconditionFailed
*/
function testIfMatchWrongEtag() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
- 'HTTP_IF_MATCH' => '1234',
- 'REQUEST_URI' => '/foo'
- ));
- $server->httpRequest = $httpRequest;
-
- $server->checkPreconditions();
+ $httpRequest = new HTTP\Request('GET', '/foo', ['If-Match' => '1234']);
+ $httpResponse = new HTTP\Response();
+ $server->checkPreconditions($httpRequest, $httpResponse);
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
*/
function testIfMatchCorrectEtag() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
- 'HTTP_IF_MATCH' => '"abc123"',
- 'REQUEST_URI' => '/foo'
- ));
- $server->httpRequest = $httpRequest;
-
- $this->assertTrue($server->checkPreconditions());
+ $httpRequest = new HTTP\Request('GET', '/foo', ['If-Match' => '"abc123"']);
+ $httpResponse = new HTTP\Response();
+ $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse));
}
/**
* Evolution sometimes uses \" instead of " for If-Match headers.
*
- * @covers \Sabre\DAV\Server::checkPreconditions
* @depends testIfMatchCorrectEtag
*/
function testIfMatchEvolutionEtag() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
- 'HTTP_IF_MATCH' => '\\"abc123\\"',
- 'REQUEST_URI' => '/foo'
- ));
- $server->httpRequest = $httpRequest;
-
- $this->assertTrue($server->checkPreconditions());
+ $httpRequest = new HTTP\Request('GET', '/foo', ['If-Match' => '\\"abc123\\"']);
+ $httpResponse = new HTTP\Response();
+ $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse));
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
*/
function testIfMatchMultiple() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
- 'HTTP_IF_MATCH' => '"hellothere", "abc123"',
- 'REQUEST_URI' => '/foo'
- ));
- $server->httpRequest = $httpRequest;
-
- $this->assertTrue($server->checkPreconditions());
+ $httpRequest = new HTTP\Request('GET', '/foo', ['If-Match' => '"hellothere", "abc123"']);
+ $httpResponse = new HTTP\Response();
+ $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse));
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
*/
function testIfNoneMatchNoNode() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
- 'HTTP_IF_NONE_MATCH' => '*',
- 'REQUEST_URI' => '/bar'
- ));
- $server->httpRequest = $httpRequest;
-
- $this->assertTrue($server->checkPreconditions());
+ $httpRequest = new HTTP\Request('GET', '/bar', ['If-None-Match' => '*']);
+ $httpResponse = new HTTP\Response();
+ $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse));
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
* @expectedException Sabre\DAV\Exception\PreconditionFailed
*/
function testIfNoneMatchHasNode() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
- 'HTTP_IF_NONE_MATCH' => '*',
- 'REQUEST_URI' => '/foo'
- ));
- $server->httpRequest = $httpRequest;
-
- $server->checkPreconditions();
+ $httpRequest = new HTTP\Request('POST', '/foo', ['If-None-Match' => '*']);
+ $httpResponse = new HTTP\Response();
+ $server->checkPreconditions($httpRequest, $httpResponse);
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
*/
function testIfNoneMatchWrongEtag() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
- 'HTTP_IF_NONE_MATCH' => '"1234"',
- 'REQUEST_URI' => '/foo'
- ));
- $server->httpRequest = $httpRequest;
-
- $this->assertTrue($server->checkPreconditions());
+ $httpRequest = new HTTP\Request('POST', '/foo', ['If-None-Match' => '"1234"']);
+ $httpResponse = new HTTP\Response();
+ $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse));
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
*/
function testIfNoneMatchWrongEtagMultiple() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
- 'HTTP_IF_NONE_MATCH' => '"1234", "5678"',
- 'REQUEST_URI' => '/foo'
- ));
- $server->httpRequest = $httpRequest;
-
- $this->assertTrue($server->checkPreconditions());
+ $httpRequest = new HTTP\Request('POST', '/foo', ['If-None-Match' => '"1234", "5678"']);
+ $httpResponse = new HTTP\Response();
+ $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse));
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
* @expectedException Sabre\DAV\Exception\PreconditionFailed
*/
public function testIfNoneMatchCorrectEtag() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
- 'HTTP_IF_NONE_MATCH' => '"abc123"',
- 'REQUEST_URI' => '/foo'
- ));
- $server->httpRequest = $httpRequest;
-
- $server->checkPreconditions();
+ $httpRequest = new HTTP\Request('POST', '/foo', ['If-None-Match' => '"abc123"']);
+ $httpResponse = new HTTP\Response();
+ $server->checkPreconditions($httpRequest, $httpResponse);
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
* @expectedException Sabre\DAV\Exception\PreconditionFailed
*/
public function testIfNoneMatchCorrectEtagMultiple() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
- 'HTTP_IF_NONE_MATCH' => '"1234", "abc123"',
- 'REQUEST_URI' => '/foo'
- ));
- $server->httpRequest = $httpRequest;
-
- $server->checkPreconditions();
+ $httpRequest = new HTTP\Request('POST', '/foo', ['If-None-Match' => '"1234, "abc123"']);
+ $httpResponse = new HTTP\Response();
+ $server->checkPreconditions($httpRequest, $httpResponse);
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
*/
public function testIfNoneMatchCorrectEtagAsGet() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
- 'HTTP_IF_NONE_MATCH' => '"abc123"',
- 'REQUEST_URI' => '/foo'
- ));
+ $httpRequest = new HTTP\Request('GET', '/foo', ['If-None-Match' => '"abc123"']);
+ $server->httpResponse = new HTTP\ResponseMock();
+
+ $this->assertFalse($server->checkPreconditions($httpRequest, $server->httpResponse));
+ $this->assertEquals(304, $server->httpResponse->getStatus());
+ $this->assertEquals(['ETag' => ['"abc123"']], $server->httpResponse->getHeaders());
+
+ }
+
+ /**
+ * This was a test written for issue #515.
+ */
+ public function testNoneMatchCorrectEtagEnsureSapiSent() {
+
+ $root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
+ $server = new Server($root);
+ $server->sapi = new HTTP\SapiMock();
+ HTTP\SapiMock::$sent = 0;
+ $httpRequest = new HTTP\Request('GET', '/foo', ['If-None-Match' => '"abc123"']);
$server->httpRequest = $httpRequest;
$server->httpResponse = new HTTP\ResponseMock();
- $this->assertFalse($server->checkPreconditions(true));
- $this->assertEquals('HTTP/1.1 304 Not Modified',$server->httpResponse->status);
+ $server->exec();
+
+ $this->assertFalse($server->checkPreconditions($httpRequest, $server->httpResponse));
+ $this->assertEquals(304, $server->httpResponse->getStatus());
+ $this->assertEquals([
+ 'ETag' => ['"abc123"'],
+ 'X-Sabre-Version' => [Version::VERSION],
+ ], $server->httpResponse->getHeaders());
+ $this->assertEquals(1, HTTP\SapiMock::$sent);
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
*/
public function testIfModifiedSinceUnModified() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
+ $httpRequest = HTTP\Sapi::createFromServerArray(array(
'HTTP_IF_MODIFIED_SINCE' => 'Sun, 06 Nov 1994 08:49:37 GMT',
'REQUEST_URI' => '/foo'
));
- $server->httpRequest = $httpRequest;
$server->httpResponse = new HTTP\ResponseMock();
- $this->assertFalse($server->checkPreconditions());
+ $this->assertFalse($server->checkPreconditions($httpRequest, $server->httpResponse));
- $this->assertEquals('HTTP/1.1 304 Not Modified',$server->httpResponse->status);
+ $this->assertEquals(304, $server->httpResponse->status);
$this->assertEquals(array(
- 'Last-Modified' => 'Sat, 06 Apr 1985 23:30:00 GMT',
- ), $server->httpResponse->headers);
+ 'Last-Modified' => ['Sat, 06 Apr 1985 23:30:00 GMT'],
+ ), $server->httpResponse->getHeaders());
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
*/
public function testIfModifiedSinceModified() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
+ $httpRequest = HTTP\Sapi::createFromServerArray(array(
'HTTP_IF_MODIFIED_SINCE' => 'Tue, 06 Nov 1984 08:49:37 GMT',
'REQUEST_URI' => '/foo'
));
- $server->httpRequest = $httpRequest;
- $server->httpResponse = new HTTP\ResponseMock();
- $this->assertTrue($server->checkPreconditions());
+
+ $httpResponse = new HTTP\ResponseMock();
+ $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse));
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
*/
public function testIfModifiedSinceInvalidDate() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
+ $httpRequest = HTTP\Sapi::createFromServerArray(array(
'HTTP_IF_MODIFIED_SINCE' => 'Your mother',
'REQUEST_URI' => '/foo'
));
- $server->httpRequest = $httpRequest;
- $server->httpResponse = new HTTP\ResponseMock();
+ $httpResponse = new HTTP\ResponseMock();
// Invalid dates must be ignored, so this should return true
- $this->assertTrue($server->checkPreconditions());
+ $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse));
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
*/
public function testIfModifiedSinceInvalidDate2() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
+ $httpRequest = HTTP\Sapi::createFromServerArray(array(
'HTTP_IF_MODIFIED_SINCE' => 'Sun, 06 Nov 1994 08:49:37 EST',
'REQUEST_URI' => '/foo'
));
- $server->httpRequest = $httpRequest;
- $server->httpResponse = new HTTP\ResponseMock();
- $this->assertTrue($server->checkPreconditions());
+ $httpResponse = new HTTP\ResponseMock();
+ $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse));
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
*/
public function testIfUnmodifiedSinceUnModified() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
+ $httpRequest = HTTP\Sapi::createFromServerArray(array(
'HTTP_IF_UNMODIFIED_SINCE' => 'Sun, 06 Nov 1994 08:49:37 GMT',
'REQUEST_URI' => '/foo'
));
- $server->httpRequest = $httpRequest;
- $this->assertTrue($server->checkPreconditions());
+ $httpResponse = new HTTP\Response();
+ $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse));
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
* @expectedException Sabre\DAV\Exception\PreconditionFailed
*/
public function testIfUnmodifiedSinceModified() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
+ $httpRequest = HTTP\Sapi::createFromServerArray(array(
'HTTP_IF_UNMODIFIED_SINCE' => 'Tue, 06 Nov 1984 08:49:37 GMT',
'REQUEST_URI' => '/foo'
));
- $server->httpRequest = $httpRequest;
- $server->httpResponse = new HTTP\ResponseMock();
- $server->checkPreconditions();
+ $httpResponse = new HTTP\ResponseMock();
+ $server->checkPreconditions($httpRequest, $httpResponse);
}
/**
- * @covers \Sabre\DAV\Server::checkPreconditions
*/
public function testIfUnmodifiedSinceInvalidDate() {
$root = new SimpleCollection('root',array(new ServerPreconditionsNode()));
$server = new Server($root);
- $httpRequest = new HTTP\Request(array(
+ $httpRequest = HTTP\Sapi::createFromServerArray(array(
'HTTP_IF_UNMODIFIED_SINCE' => 'Sun, 06 Nov 1984 08:49:37 CET',
'REQUEST_URI' => '/foo'
));
- $server->httpRequest = $httpRequest;
- $server->httpResponse = new HTTP\ResponseMock();
- $this->assertTrue($server->checkPreconditions());
+ $httpResponse = new HTTP\ResponseMock();
+ $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse));
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/ServerPropsTest.php b/vendor/sabre/dav/tests/Sabre/DAV/ServerPropsTest.php
index 859a91070..253200be7 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/ServerPropsTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/ServerPropsTest.php
@@ -1,6 +1,7 @@
<?php
namespace Sabre\DAV;
+
use Sabre\HTTP;
require_once 'Sabre/HTTP/ResponseMock.php';
@@ -16,7 +17,7 @@ class ServerPropsTest extends AbstractServer {
function setUp() {
- if (file_exists(SABRE_TEMPDIR.'../.sabredav')) unlink(SABRE_TEMPDIR.'../.sabredav');
+ if (file_exists(SABRE_TEMPDIR . '../.sabredav')) unlink(SABRE_TEMPDIR . '../.sabredav');
parent::setUp();
file_put_contents(SABRE_TEMPDIR . '/test2.txt', 'Test contents2');
mkdir(SABRE_TEMPDIR . '/col');
@@ -28,64 +29,68 @@ class ServerPropsTest extends AbstractServer {
function tearDown() {
parent::tearDown();
- if (file_exists(SABRE_TEMPDIR.'../.locksdb')) unlink(SABRE_TEMPDIR.'../.locksdb');
+ if (file_exists(SABRE_TEMPDIR . '../.locksdb')) unlink(SABRE_TEMPDIR . '../.locksdb');
}
- private function sendRequest($body) {
-
- $serverVars = array(
- 'REQUEST_URI' => '/',
- 'REQUEST_METHOD' => 'PROPFIND',
- 'HTTP_DEPTH' => '0',
- );
+ private function sendRequest($body, $path = '/', $headers = ['Depth' => '0']) {
- $request = new HTTP\Request($serverVars);
- $request->setBody($body);
+ $request = new HTTP\Request('PROPFIND', $path, $headers, $body);
- $this->server->httpRequest = ($request);
+ $this->server->httpRequest = $request;
$this->server->exec();
}
- public function testPropFindEmptyBody() {
+ function testPropFindEmptyBody() {
- $hasFired = false;
+ $this->sendRequest("");
+ $this->assertEquals(207, $this->response->status);
+
+ $this->assertEquals([
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ 'DAV' => ['1, 3, extended-mkcol, 2'],
+ 'Vary' => ['Brief,Prefer'],
+ ],
+ $this->response->getHeaders()
+ );
- $self = $this;
- // Also testing the beforeGetPropertiesForPath event.
- $this->server->subscribeEvent('beforeGetPropertiesForPath', function($path, $properties, $depth) use ($self, &$hasFired) {
+ $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body);
+ $xml = simplexml_load_string($body);
+ $xml->registerXPathNamespace('d', 'urn:DAV');
- $hasFired = true;
- $self->assertEquals('', $path);
- $self->assertEquals(array(), $properties);
- $self->assertEquals(0, $depth);
+ list($data) = $xml->xpath('/d:multistatus/d:response/d:href');
+ $this->assertEquals('/', (string)$data, 'href element should have been /');
- });
+ $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:resourcetype');
+ $this->assertEquals(1, count($data));
- $this->sendRequest("");
+ }
- $this->assertTrue($hasFired);
+ function testPropFindEmptyBodyFile() {
- $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status);
+ $this->sendRequest("", '/test2.txt', []);
+ $this->assertEquals(207, $this->response->status);
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- 'DAV' => '1, 3, extended-mkcol, 2',
- 'Vary' => 'Brief,Prefer',
- ),
- $this->response->headers
+ $this->assertEquals([
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ 'DAV' => ['1, 3, extended-mkcol, 2'],
+ 'Vary' => ['Brief,Prefer'],
+ ],
+ $this->response->getHeaders()
);
- $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body);
+ $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body);
$xml = simplexml_load_string($body);
- $xml->registerXPathNamespace('d','urn:DAV');
+ $xml->registerXPathNamespace('d', 'urn:DAV');
list($data) = $xml->xpath('/d:multistatus/d:response/d:href');
- $this->assertEquals('/',(string)$data,'href element should have been /');
+ $this->assertEquals('/test2.txt', (string)$data, 'href element should have been /test2.txt');
- $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:resourcetype');
- $this->assertEquals(1,count($data));
+ $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:getcontentlength');
+ $this->assertEquals(1, count($data));
}
@@ -100,27 +105,27 @@ class ServerPropsTest extends AbstractServer {
$this->sendRequest($xml);
- $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body);
+ $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body);
$xml = simplexml_load_string($body);
- $xml->registerXPathNamespace('d','urn:DAV');
+ $xml->registerXPathNamespace('d', 'urn:DAV');
$data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry');
- $this->assertEquals(2,count($data),'We expected two \'d:lockentry\' tags');
+ $this->assertEquals(2, count($data), 'We expected two \'d:lockentry\' tags');
$data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope');
- $this->assertEquals(2,count($data),'We expected two \'d:lockscope\' tags');
+ $this->assertEquals(2, count($data), 'We expected two \'d:lockscope\' tags');
$data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:locktype');
- $this->assertEquals(2,count($data),'We expected two \'d:locktype\' tags');
+ $this->assertEquals(2, count($data), 'We expected two \'d:locktype\' tags');
$data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope/d:shared');
- $this->assertEquals(1,count($data),'We expected a \'d:shared\' tag');
+ $this->assertEquals(1, count($data), 'We expected a \'d:shared\' tag');
$data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope/d:exclusive');
- $this->assertEquals(1,count($data),'We expected a \'d:exclusive\' tag');
+ $this->assertEquals(1, count($data), 'We expected a \'d:exclusive\' tag');
$data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:locktype/d:write');
- $this->assertEquals(2,count($data),'We expected two \'d:write\' tags');
+ $this->assertEquals(2, count($data), 'We expected two \'d:write\' tags');
}
function testLockDiscovery() {
@@ -134,12 +139,12 @@ class ServerPropsTest extends AbstractServer {
$this->sendRequest($xml);
- $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body);
+ $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body);
$xml = simplexml_load_string($body);
- $xml->registerXPathNamespace('d','urn:DAV');
+ $xml->registerXPathNamespace('d', 'urn:DAV');
$data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:lockdiscovery');
- $this->assertEquals(1,count($data),'We expected a \'d:lockdiscovery\' tag');
+ $this->assertEquals(1, count($data), 'We expected a \'d:lockdiscovery\' tag');
}
@@ -153,31 +158,28 @@ class ServerPropsTest extends AbstractServer {
</d:propfind>';
$this->sendRequest($xml);
- $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body);
+ $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body);
$xml = simplexml_load_string($body);
- $xml->registerXPathNamespace('d','urn:DAV');
- $pathTests = array(
+ $xml->registerXPathNamespace('d', 'urn:DAV');
+ $pathTests = [
'/d:multistatus',
'/d:multistatus/d:response',
'/d:multistatus/d:response/d:propstat',
'/d:multistatus/d:response/d:propstat/d:status',
'/d:multistatus/d:response/d:propstat/d:prop',
'/d:multistatus/d:response/d:propstat/d:prop/d:macaroni',
- );
- foreach($pathTests as $test) {
- $this->assertTrue(count($xml->xpath($test))==true,'We expected the ' . $test . ' element to appear in the response, we got: ' . $body);
+ ];
+ foreach ($pathTests as $test) {
+ $this->assertTrue(count($xml->xpath($test)) == true, 'We expected the ' . $test . ' element to appear in the response, we got: ' . $body);
}
$val = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status');
- $this->assertEquals(1,count($val),$body);
- $this->assertEquals('HTTP/1.1 404 Not Found',(string)$val[0]);
+ $this->assertEquals(1, count($val), $body);
+ $this->assertEquals('HTTP/1.1 404 Not Found', (string)$val[0]);
}
- /**
- * @covers Sabre\DAV\Server::parsePropPatchRequest
- */
- public function testParsePropPatchRequest() {
+ function testParsePropPatchRequest() {
$body = '<?xml version="1.0"?>
<d:propertyupdate xmlns:d="DAV:" xmlns:s="http://sabredav.org/NS/test">
@@ -187,226 +189,12 @@ class ServerPropsTest extends AbstractServer {
<d:remove><d:prop><s:someprop3 /></d:prop></d:remove>
</d:propertyupdate>';
- $result = $this->server->parsePropPatchRequest($body);
- $this->assertEquals(array(
- '{http://sabredav.org/NS/test}someprop' => 'somevalue',
+ $result = $this->server->xml->parse($body);
+ $this->assertEquals([
+ '{http://sabredav.org/NS/test}someprop' => 'somevalue',
'{http://sabredav.org/NS/test}someprop2' => null,
'{http://sabredav.org/NS/test}someprop3' => null,
- ), $result);
-
- }
-
- /**
- * @covers Sabre\DAV\Server::updateProperties
- */
- public function testUpdateProperties() {
-
- $props = array(
- '{http://sabredav.org/NS/test}someprop' => 'somevalue',
- );
-
- $result = $this->server->updateProperties('/test2.txt',$props);
-
- $this->assertEquals(array(
- '200' => array('{http://sabredav.org/NS/test}someprop' => null),
- 'href' => '/test2.txt',
- ), $result);
-
- }
-
- /**
- * @covers Sabre\DAV\Server::updateProperties
- * @depends testUpdateProperties
- */
- public function testUpdatePropertiesProtected() {
-
- $props = array(
- '{http://sabredav.org/NS/test}someprop' => 'somevalue',
- '{DAV:}getcontentlength' => 50,
- );
-
- $result = $this->server->updateProperties('/test2.txt',$props);
-
- $this->assertEquals(array(
- '424' => array('{http://sabredav.org/NS/test}someprop' => null),
- '403' => array('{DAV:}getcontentlength' => null),
- 'href' => '/test2.txt',
- ), $result);
-
- }
-
- /**
- * @covers Sabre\DAV\Server::updateProperties
- * @depends testUpdateProperties
- */
- public function testUpdatePropertiesFail1() {
-
- $dir = new PropTestDirMock('updatepropsfalse');
- $objectTree = new ObjectTree($dir);
- $this->server->tree = $objectTree;
-
- $props = array(
- '{http://sabredav.org/NS/test}someprop' => 'somevalue',
- );
-
- $result = $this->server->updateProperties('/',$props);
-
- $this->assertEquals(array(
- '403' => array('{http://sabredav.org/NS/test}someprop' => null),
- 'href' => '/',
- ), $result);
-
- }
-
- /**
- * @covers Sabre\DAV\Server::updateProperties
- * @depends testUpdateProperties
- */
- public function testUpdatePropertiesFail2() {
-
- $dir = new PropTestDirMock('updatepropsarray');
- $objectTree = new ObjectTree($dir);
- $this->server->tree = $objectTree;
-
- $props = array(
- '{http://sabredav.org/NS/test}someprop' => 'somevalue',
- );
-
- $result = $this->server->updateProperties('/',$props);
-
- $this->assertEquals(array(
- '402' => array('{http://sabredav.org/NS/test}someprop' => null),
- 'href' => '/',
- ), $result);
-
- }
-
- /**
- * @covers Sabre\DAV\Server::updateProperties
- * @depends testUpdateProperties
- * @expectedException Sabre\DAV\Exception
- */
- public function testUpdatePropertiesFail3() {
-
- $dir = new PropTestDirMock('updatepropsobj');
- $objectTree = new ObjectTree($dir);
- $this->server->tree = $objectTree;
-
- $props = array(
- '{http://sabredav.org/NS/test}someprop' => 'somevalue',
- );
-
- $result = $this->server->updateProperties('/',$props);
-
- }
-
- /**
- * @depends testParsePropPatchRequest
- * @depends testUpdateProperties
- * @covers Sabre\DAV\Server::httpPropPatch
- */
- public function testPropPatch() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/',
- 'REQUEST_METHOD' => 'PROPPATCH',
- );
-
- $body = '<?xml version="1.0"?>
-<d:propertyupdate xmlns:d="DAV:" xmlns:s="http://www.rooftopsolutions.nl/testnamespace">
- <d:set><d:prop><s:someprop>somevalue</s:someprop></d:prop></d:set>
-</d:propertyupdate>';
-
- $request = new HTTP\Request($serverVars);
- $request->setBody($body);
-
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- 'Vary' => 'Brief,Prefer',
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'We got the wrong status. Full XML response: ' . $this->response->body);
-
- $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body);
- $xml = simplexml_load_string($body);
- $xml->registerXPathNamespace('d','urn:DAV');
- $xml->registerXPathNamespace('bla','http://www.rooftopsolutions.nl/testnamespace');
-
- $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop');
- $this->assertEquals(1,count($data),'We expected one \'d:prop\' element. Response body: ' . $body);
-
- $data = $xml->xpath('//bla:someprop');
- $this->assertEquals(1,count($data),'We expected one \'s:someprop\' element. Response body: ' . $body);
-
- $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status');
- $this->assertEquals(1,count($data),'We expected one \'s:status\' element. Response body: ' . $body);
-
- $this->assertEquals('HTTP/1.1 200 OK',(string)$data[0]);
-
- }
-
- /**
- * @depends testPropPatch
- */
- public function testPropPatchAndFetch() {
-
- $this->testPropPatch();
- $xml = '<?xml version="1.0"?>
-<d:propfind xmlns:d="DAV:" xmlns:s="http://www.rooftopsolutions.nl/testnamespace">
- <d:prop>
- <s:someprop />
- </d:prop>
-</d:propfind>';
-
- $this->sendRequest($xml);
-
- $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body);
- $xml = simplexml_load_string($body);
- $xml->registerXPathNamespace('d','urn:DAV');
- $xml->registerXPathNamespace('bla','http://www.rooftopsolutions.nl/testnamespace');
-
- $xpath='//bla:someprop';
- $result = $xml->xpath($xpath);
- $this->assertEquals(1,count($result),'We couldn\'t find our new property in the response. Full response body:' . "\n" . $body);
- $this->assertEquals('somevalue',(string)$result[0],'We couldn\'t find our new property in the response. Full response body:' . "\n" . $body);
-
- }
-
-}
-
-class PropTestDirMock extends SimpleCollection implements IProperties {
-
- public $type;
-
- function __construct($type) {
-
- $this->type =$type;
- parent::__construct('root');
-
- }
-
- function updateProperties($updateProperties) {
-
- switch($this->type) {
- case 'updatepropsfalse' : return false;
- case 'updatepropsarray' :
- $r = array(402 => array());
- foreach($updateProperties as $k=>$v) $r[402][$k] = null;
- return $r;
- case 'updatepropsobj' :
- return new \STDClass();
- }
-
- }
-
- function getProperties($requestedPropeties) {
-
- return array();
+ ], $result->properties);
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/ServerRangeTest.php b/vendor/sabre/dav/tests/Sabre/DAV/ServerRangeTest.php
index a06fcb0be..bafbef6e4 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/ServerRangeTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/ServerRangeTest.php
@@ -1,42 +1,62 @@
<?php
namespace Sabre\DAV;
-use Sabre\HTTP;
-require_once 'Sabre/DAV/AbstractServer.php';
+use DateTime;
+use Sabre\HTTP;
-class ServerRangeTest extends AbstractServer{
+/**
+ * This file tests HTTP requests that use the Range: header.
+ *
+ * @copyright Copyright (C) fruux GmbH. (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ServerRangeTest extends \Sabre\DAVServerTest {
- protected function getRootNode() {
+ protected $setupFiles = true;
- return new FSExt\Directory(SABRE_TEMPDIR);
+ /**
+ * We need this string a lot
+ */
+ protected $lastModified;
- }
+ function setUp() {
- function testRange() {
+ parent::setUp();
+ $this->server->createFile('files/test.txt', 'Test contents');
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'GET',
- 'HTTP_RANGE' => 'bytes=2-5',
+ $this->lastModified = HTTP\Util::toHTTPDate(
+ new DateTime('@' . $this->server->tree->getNodeForPath('files/test.txt')->getLastModified())
);
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
+ $stream = popen('echo "Test contents"', 'r');
+ $streamingFile = new Mock\StreamingFile(
+ 'no-seeking.txt',
+ $stream
+ );
+ $streamingFile->setSize(12);
+ $this->server->tree->getNodeForPath('files')->addNode($streamingFile);
- $this->assertEquals(array(
- 'Content-Type' => 'application/octet-stream',
- 'Content-Length' => 4,
- 'Content-Range' => 'bytes 2-5/13',
- 'Last-Modified' => HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt'))),
- 'ETag' => '"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')). '"',
- ),
- $this->response->headers
- );
+ }
- $this->assertEquals('HTTP/1.1 206 Partial Content',$this->response->status);
- $this->assertEquals('st c', stream_get_contents($this->response->body));
+ function testRange() {
+
+ $request = new HTTP\Request('GET', '/files/test.txt', ['Range' => 'bytes=2-5']);
+ $response = $this->request($request);
+
+ $this->assertEquals([
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/octet-stream'],
+ 'Content-Length' => [4],
+ 'Content-Range' => ['bytes 2-5/13'],
+ 'ETag' => ['"' . md5('Test contents') . '"'],
+ 'Last-Modified' => [$this->lastModified],
+ ],
+ $response->getHeaders()
+ );
+ $this->assertEquals(206, $response->getStatus());
+ $this->assertEquals('st c', $response->getBodyAsString());
}
@@ -45,28 +65,22 @@ class ServerRangeTest extends AbstractServer{
*/
function testStartRange() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'GET',
- 'HTTP_RANGE' => 'bytes=2-',
+ $request = new HTTP\Request('GET', '/files/test.txt', ['Range' => 'bytes=2-']);
+ $response = $this->request($request);
+
+ $this->assertEquals([
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/octet-stream'],
+ 'Content-Length' => [11],
+ 'Content-Range' => ['bytes 2-12/13'],
+ 'ETag' => ['"' . md5('Test contents') . '"'],
+ 'Last-Modified' => [$this->lastModified],
+ ],
+ $response->getHeaders()
);
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Type' => 'application/octet-stream',
- 'Content-Length' => 11,
- 'Content-Range' => 'bytes 2-12/13',
- 'Last-Modified' => HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt'))),
- 'ETag' => '"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')) . '"',
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.1 206 Partial Content',$this->response->status);
- $this->assertEquals('st contents', stream_get_contents($this->response->body));
+ $this->assertEquals(206, $response->getStatus());
+ $this->assertEquals('st contents', $response->getBodyAsString());
}
@@ -75,28 +89,22 @@ class ServerRangeTest extends AbstractServer{
*/
function testEndRange() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'GET',
- 'HTTP_RANGE' => 'bytes=-8',
+ $request = new HTTP\Request('GET', '/files/test.txt', ['Range' => 'bytes=-8']);
+ $response = $this->request($request);
+
+ $this->assertEquals([
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/octet-stream'],
+ 'Content-Length' => [8],
+ 'Content-Range' => ['bytes 5-12/13'],
+ 'ETag' => ['"' . md5('Test contents') . '"'],
+ 'Last-Modified' => [$this->lastModified],
+ ],
+ $response->getHeaders()
);
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Type' => 'application/octet-stream',
- 'Content-Length' => 8,
- 'Content-Range' => 'bytes 5-12/13',
- 'Last-Modified' => HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt'))),
- 'ETag' => '"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')). '"',
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.1 206 Partial Content',$this->response->status);
- $this->assertEquals('contents', stream_get_contents($this->response->body));
+ $this->assertEquals(206, $response->getStatus());
+ $this->assertEquals('contents', $response->getBodyAsString());
}
@@ -105,17 +113,10 @@ class ServerRangeTest extends AbstractServer{
*/
function testTooHighRange() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'GET',
- 'HTTP_RANGE' => 'bytes=100-200',
- );
+ $request = new HTTP\Request('GET', '/files/test.txt', ['Range' => 'bytes=100-200']);
+ $response = $this->request($request);
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals('HTTP/1.1 416 Requested Range Not Satisfiable',$this->response->status);
+ $this->assertEquals(416, $response->getStatus());
}
@@ -124,151 +125,138 @@ class ServerRangeTest extends AbstractServer{
*/
function testCrazyRange() {
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'GET',
- 'HTTP_RANGE' => 'bytes=8-4',
- );
+ $request = new HTTP\Request('GET', '/files/test.txt', ['Range' => 'bytes=8-4']);
+ $response = $this->request($request);
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
+ $this->assertEquals(416, $response->getStatus());
- $this->assertEquals('HTTP/1.1 416 Requested Range Not Satisfiable',$this->response->status);
+ }
+
+ function testNonSeekableStream() {
+
+ $request = new HTTP\Request('GET', '/files/no-seeking.txt', ['Range' => 'bytes=2-5']);
+ $response = $this->request($request);
+
+ $this->assertEquals(206, $response->getStatus(), $response);
+ $this->assertEquals([
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/octet-stream'],
+ 'Content-Length' => [4],
+ 'Content-Range' => ['bytes 2-5/12'],
+ // 'ETag' => ['"' . md5('Test contents') . '"'],
+ 'Last-Modified' => [$this->lastModified],
+ ],
+ $response->getHeaders()
+ );
+
+ $this->assertEquals('st c', $response->getBodyAsString());
}
/**
* @depends testRange
- * @covers \Sabre\DAV\Server::httpGet
*/
function testIfRangeEtag() {
- $node = $this->server->tree->getNodeForPath('test.txt');
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'GET',
- 'HTTP_RANGE' => 'bytes=2-5',
- 'HTTP_IF_RANGE' => $node->getETag(),
+ $request = new HTTP\Request('GET', '/files/test.txt', [
+ 'Range' => 'bytes=2-5',
+ 'If-Range' => '"' . md5('Test contents') . '"',
+ ]);
+ $response = $this->request($request);
+
+ $this->assertEquals([
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/octet-stream'],
+ 'Content-Length' => [4],
+ 'Content-Range' => ['bytes 2-5/13'],
+ 'ETag' => ['"' . md5('Test contents') . '"'],
+ 'Last-Modified' => [$this->lastModified],
+ ],
+ $response->getHeaders()
);
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Type' => 'application/octet-stream',
- 'Content-Length' => 4,
- 'Content-Range' => 'bytes 2-5/13',
- 'Last-Modified' => HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt'))),
- 'ETag' => '"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')) . '"',
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.1 206 Partial Content',$this->response->status);
- $this->assertEquals('st c', stream_get_contents($this->response->body));
+ $this->assertEquals(206, $response->getStatus());
+ $this->assertEquals('st c', $response->getBodyAsString());
}
/**
- * @depends testRange
- * @covers \Sabre\DAV\Server::httpGet
+ * @depends testIfRangeEtag
*/
function testIfRangeEtagIncorrect() {
- $node = $this->server->tree->getNodeForPath('test.txt');
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'GET',
- 'HTTP_RANGE' => 'bytes=2-5',
- 'HTTP_IF_RANGE' => $node->getETag() . 'blabla',
+ $request = new HTTP\Request('GET', '/files/test.txt', [
+ 'Range' => 'bytes=2-5',
+ 'If-Range' => '"foobar"',
+ ]);
+ $response = $this->request($request);
+
+ $this->assertEquals([
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/octet-stream'],
+ 'Content-Length' => [13],
+ 'ETag' => ['"' . md5('Test contents') . '"'],
+ 'Last-Modified' => [$this->lastModified],
+ ],
+ $response->getHeaders()
);
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Type' => 'application/octet-stream',
- 'Content-Length' => 13,
- 'Last-Modified' => HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt'))),
- 'ETag' => '"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')) . '"',
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
- $this->assertEquals('Test contents', stream_get_contents($this->response->body));
+ $this->assertEquals(200, $response->getStatus());
+ $this->assertEquals('Test contents', $response->getBodyAsString());
}
/**
- * @depends testRange
- * @covers \Sabre\DAV\Server::httpGet
+ * @depends testIfRangeEtag
*/
function testIfRangeModificationDate() {
- $node = $this->server->tree->getNodeForPath('test.txt');
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'GET',
- 'HTTP_RANGE' => 'bytes=2-5',
- 'HTTP_IF_RANGE' => 'tomorrow',
+ $request = new HTTP\Request('GET', '/files/test.txt', [
+ 'Range' => 'bytes=2-5',
+ 'If-Range' => 'tomorrow',
+ ]);
+ $response = $this->request($request);
+
+ $this->assertEquals([
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/octet-stream'],
+ 'Content-Length' => [4],
+ 'Content-Range' => ['bytes 2-5/13'],
+ 'ETag' => ['"' . md5('Test contents') . '"'],
+ 'Last-Modified' => [$this->lastModified],
+ ],
+ $response->getHeaders()
);
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Type' => 'application/octet-stream',
- 'Content-Length' => 4,
- 'Content-Range' => 'bytes 2-5/13',
- 'Last-Modified' => HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt'))),
- 'ETag' => '"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')) . '"',
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.1 206 Partial Content',$this->response->status);
- $this->assertEquals('st c', stream_get_contents($this->response->body));
+ $this->assertEquals(206, $response->getStatus());
+ $this->assertEquals('st c', $response->getBodyAsString());
}
/**
- * @depends testRange
- * @covers \Sabre\DAV\Server::httpGet
+ * @depends testIfRangeModificationDate
*/
function testIfRangeModificationDateModified() {
- $node = $this->server->tree->getNodeForPath('test.txt');
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'GET',
- 'HTTP_RANGE' => 'bytes=2-5',
- 'HTTP_IF_RANGE' => '-2 years',
+ $request = new HTTP\Request('GET', '/files/test.txt', [
+ 'Range' => 'bytes=2-5',
+ 'If-Range' => '-2 years',
+ ]);
+ $response = $this->request($request);
+
+ $this->assertEquals([
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/octet-stream'],
+ 'Content-Length' => [13],
+ 'ETag' => ['"' . md5('Test contents') . '"'],
+ 'Last-Modified' => [$this->lastModified],
+ ],
+ $response->getHeaders()
);
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Type' => 'application/octet-stream',
- 'Content-Length' => 13,
- 'Last-Modified' => HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt'))),
- 'ETag' => '"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')) . '"',
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
- $this->assertEquals('Test contents', stream_get_contents($this->response->body));
+ $this->assertEquals(200, $response->getStatus());
+ $this->assertEquals('Test contents', $response->getBodyAsString());
}
+
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/ServerSimpleTest.php b/vendor/sabre/dav/tests/Sabre/DAV/ServerSimpleTest.php
index 21e0ab2ea..66dde9db8 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/ServerSimpleTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/ServerSimpleTest.php
@@ -4,17 +4,13 @@ namespace Sabre\DAV;
use Sabre\HTTP;
-require_once 'Sabre/HTTP/ResponseMock.php';
-require_once 'Sabre/DAV/AbstractServer.php';
-require_once 'Sabre/DAV/Exception.php';
-
class ServerSimpleTest extends AbstractServer{
function testConstructArray() {
- $nodes = array(
+ $nodes = [
new SimpleCollection('hello')
- );
+ ];
$server = new Server($nodes);
$this->assertEquals($nodes[0], $server->tree->getNodeForPath('hello'));
@@ -26,10 +22,10 @@ class ServerSimpleTest extends AbstractServer{
*/
function testConstructIncorrectObj() {
- $nodes = array(
+ $nodes = [
new SimpleCollection('hello'),
new \STDClass(),
- );
+ ];
$server = new Server($nodes);
@@ -44,253 +40,108 @@ class ServerSimpleTest extends AbstractServer{
}
- function testGet() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'GET',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Type' => 'application/octet-stream',
- 'Content-Length' => 13,
- 'Last-Modified' => HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt'))),
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
- $this->assertEquals('Test contents', stream_get_contents($this->response->body));
-
- }
- function testGetHttp10() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'GET',
- 'SERVER_PROTOCOL' => 'HTTP/1.0',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Type' => 'application/octet-stream',
- 'Content-Length' => 13,
- 'Last-Modified' => HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt'))),
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.0 200 OK',$this->response->status);
- $this->assertEquals('Test contents', stream_get_contents($this->response->body));
-
- }
-
- function testGetDoesntExist() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt_randomblbla',
- 'REQUEST_METHOD' => 'GET',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
- $this->assertEquals('HTTP/1.1 404 Not Found',$this->response->status);
-
- }
-
- function testGetDoesntExist2() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt/randomblbla',
- 'REQUEST_METHOD' => 'GET',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
- $this->assertEquals('HTTP/1.1 404 Not Found',$this->response->status);
-
- }
-
- /**
- * This test should have the exact same result as testGet.
- *
- * The idea is that double slashes // are converted to single ones /
- *
- */
- function testGetDoubleSlash() {
-
- $serverVars = array(
- 'REQUEST_URI' => '//test.txt',
- 'REQUEST_METHOD' => 'GET',
- );
+ function testOptions() {
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
+ $request = new HTTP\Request('OPTIONS', '/');
+ $this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals(array(
- 'Content-Type' => 'application/octet-stream',
- 'Content-Length' => 13,
- 'Last-Modified' => HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt'))),
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
- $this->assertEquals('Test contents', stream_get_contents($this->response->body));
-
- }
-
-
- function testHEAD() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/test.txt',
- 'REQUEST_METHOD' => 'HEAD',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
+ $this->assertEquals([
+ 'DAV' => ['1, 3, extended-mkcol'],
+ 'MS-Author-Via' => ['DAV'],
+ 'Allow' => ['OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT'],
+ 'Accept-Ranges' => ['bytes'],
+ 'Content-Length' => ['0'],
+ 'X-Sabre-Version' => [Version::VERSION],
+ ], $this->response->getHeaders());
- $this->assertEquals(array(
- 'Content-Type' => 'application/octet-stream',
- 'Content-Length' => 13,
- 'Last-Modified' => HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt'))),
- ),
- $this->response->headers
- );
-
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200, $this->response->status);
$this->assertEquals('', $this->response->body);
}
- function testOptions() {
+ function testOptionsUnmapped() {
- $serverVars = array(
- 'REQUEST_URI' => '/',
- 'REQUEST_METHOD' => 'OPTIONS',
- );
+ $request = new HTTP\Request('OPTIONS', '/unmapped');
+ $this->server->httpRequest = $request;
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals(array(
- 'DAV' => '1, 3, extended-mkcol',
- 'MS-Author-Via' => 'DAV',
- 'Allow' => 'OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT',
- 'Accept-Ranges' => 'bytes',
- 'Content-Length' => '0',
- 'X-Sabre-Version' => Version::VERSION,
- ),$this->response->headers);
+ $this->assertEquals([
+ 'DAV' => ['1, 3, extended-mkcol'],
+ 'MS-Author-Via' => ['DAV'],
+ 'Allow' => ['OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT, MKCOL'],
+ 'Accept-Ranges' => ['bytes'],
+ 'Content-Length' => ['0'],
+ 'X-Sabre-Version' => [Version::VERSION],
+ ], $this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200, $this->response->status);
$this->assertEquals('', $this->response->body);
-
}
+
function testNonExistantMethod() {
- $serverVars = array(
+ $serverVars = [
'REQUEST_URI' => '/',
'REQUEST_METHOD' => 'BLABLA',
- );
+ ];
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),$this->response->headers);
+ $this->assertEquals([
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ], $this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 501 Not Implemented',$this->response->status);
+ $this->assertEquals(501, $this->response->status);
}
- function testGETOnCollection() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/',
- 'REQUEST_METHOD' => 'GET',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),$this->response->headers);
-
- $this->assertEquals('HTTP/1.1 501 Not Implemented',$this->response->status);
-
- }
-
- function testHEADOnCollection() {
-
- $serverVars = array(
- 'REQUEST_URI' => '/',
- 'REQUEST_METHOD' => 'HEAD',
- );
-
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
- $this->server->exec();
-
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
-
- }
-
function testBaseUri() {
- $serverVars = array(
+ $serverVars = [
'REQUEST_URI' => '/blabla/test.txt',
'REQUEST_METHOD' => 'GET',
- );
+ ];
+ $filename = $this->tempDir . '/test.txt';
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->setBaseUri('/blabla/');
- $this->assertEquals('/blabla/',$this->server->getBaseUri());
+ $this->assertEquals('/blabla/', $this->server->getBaseUri());
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals(array(
- 'Content-Type' => 'application/octet-stream',
- 'Content-Length' => 13,
- 'Last-Modified' => HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt'))),
- ),
- $this->response->headers
+ $this->assertEquals([
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/octet-stream'],
+ 'Content-Length' => [13],
+ 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($filename)))],
+ 'ETag' => ['"' . sha1(fileinode($filename) . filesize($filename) . filemtime($filename)) . '"'],
+ ],
+ $this->response->getHeaders()
);
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200, $this->response->status);
$this->assertEquals('Test contents', stream_get_contents($this->response->body));
}
function testBaseUriAddSlash() {
- $tests = array(
+ $tests = [
'/' => '/',
'/foo' => '/foo/',
'/foo/' => '/foo/',
'/foo/bar' => '/foo/bar/',
'/foo/bar/' => '/foo/bar/',
- );
+ ];
- foreach($tests as $test=>$result) {
+ foreach ($tests as $test => $result) {
$this->server->setBaseUri($test);
$this->assertEquals($result, $this->server->getBaseUri());
@@ -301,25 +152,25 @@ class ServerSimpleTest extends AbstractServer{
function testCalculateUri() {
- $uris = array(
+ $uris = [
'http://www.example.org/root/somepath',
'/root/somepath',
'/root/somepath/',
- );
+ ];
$this->server->setBaseUri('/root/');
- foreach($uris as $uri) {
+ foreach ($uris as $uri) {
- $this->assertEquals('somepath',$this->server->calculateUri($uri));
+ $this->assertEquals('somepath', $this->server->calculateUri($uri));
}
$this->server->setBaseUri('/root');
- foreach($uris as $uri) {
+ foreach ($uris as $uri) {
- $this->assertEquals('somepath',$this->server->calculateUri($uri));
+ $this->assertEquals('somepath', $this->server->calculateUri($uri));
}
@@ -329,72 +180,60 @@ class ServerSimpleTest extends AbstractServer{
function testCalculateUriSpecialChars() {
- $uris = array(
+ $uris = [
'http://www.example.org/root/%C3%A0fo%C3%B3',
'/root/%C3%A0fo%C3%B3',
'/root/%C3%A0fo%C3%B3/'
- );
+ ];
$this->server->setBaseUri('/root/');
- foreach($uris as $uri) {
+ foreach ($uris as $uri) {
- $this->assertEquals("\xc3\xa0fo\xc3\xb3",$this->server->calculateUri($uri));
+ $this->assertEquals("\xc3\xa0fo\xc3\xb3", $this->server->calculateUri($uri));
}
$this->server->setBaseUri('/root');
- foreach($uris as $uri) {
+ foreach ($uris as $uri) {
- $this->assertEquals("\xc3\xa0fo\xc3\xb3",$this->server->calculateUri($uri));
+ $this->assertEquals("\xc3\xa0fo\xc3\xb3", $this->server->calculateUri($uri));
}
$this->server->setBaseUri('/');
- foreach($uris as $uri) {
+ foreach ($uris as $uri) {
- $this->assertEquals("root/\xc3\xa0fo\xc3\xb3",$this->server->calculateUri($uri));
+ $this->assertEquals("root/\xc3\xa0fo\xc3\xb3", $this->server->calculateUri($uri));
}
}
- function testBaseUriCheck() {
-
- $uris = array(
- 'http://www.example.org/root/somepath',
- '/root/somepath',
- '/root/somepath/'
- );
-
- try {
-
- $this->server->setBaseUri('root/');
- $this->server->calculateUri('/root/testuri');
-
- $this->fail('Expected an exception');
-
- } catch (Exception\Forbidden $e) {
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ function testCalculateUriBreakout() {
- // This was expected
+ $uri = '/path1/';
- }
+ $this->server->setBaseUri('/path2/');
+ $this->server->calculateUri($uri);
}
/**
- * @covers \Sabre\DAV\Server::guessBaseUri
*/
function testGuessBaseUri() {
- $serverVars = array(
+ $serverVars = [
'REQUEST_URI' => '/index.php/root',
'PATH_INFO' => '/root',
- );
+ ];
- $httpRequest = new HTTP\Request($serverVars);
+ $httpRequest = HTTP\Sapi::createFromServerArray($serverVars);
$server = new Server();
$server->httpRequest = $httpRequest;
@@ -404,16 +243,15 @@ class ServerSimpleTest extends AbstractServer{
/**
* @depends testGuessBaseUri
- * @covers Sabre\DAV\Server::guessBaseUri
*/
function testGuessBaseUriPercentEncoding() {
- $serverVars = array(
+ $serverVars = [
'REQUEST_URI' => '/index.php/dir/path2/path%20with%20spaces',
'PATH_INFO' => '/dir/path2/path with spaces',
- );
+ ];
- $httpRequest = new HTTP\Request($serverVars);
+ $httpRequest = HTTP\Sapi::createFromServerArray($serverVars);
$server = new Server();
$server->httpRequest = $httpRequest;
@@ -423,18 +261,17 @@ class ServerSimpleTest extends AbstractServer{
/**
* @depends testGuessBaseUri
- * @covers \Sabre\DAV\Server::guessBaseUri
*/
/*
function testGuessBaseUriPercentEncoding2() {
$this->markTestIncomplete('This behaviour is not yet implemented');
- $serverVars = array(
+ $serverVars = [
'REQUEST_URI' => '/some%20directory+mixed/index.php/dir/path2/path%20with%20spaces',
'PATH_INFO' => '/dir/path2/path with spaces',
- );
+ ];
- $httpRequest = new HTTP\Request($serverVars);
+ $httpRequest = HTTP\Sapi::createFromServerArray($serverVars);
$server = new Server();
$server->httpRequest = $httpRequest;
@@ -444,12 +281,12 @@ class ServerSimpleTest extends AbstractServer{
function testGuessBaseUri2() {
- $serverVars = array(
+ $serverVars = [
'REQUEST_URI' => '/index.php/root/',
'PATH_INFO' => '/root/',
- );
+ ];
- $httpRequest = new HTTP\Request($serverVars);
+ $httpRequest = HTTP\Sapi::createFromServerArray($serverVars);
$server = new Server();
$server->httpRequest = $httpRequest;
@@ -459,11 +296,11 @@ class ServerSimpleTest extends AbstractServer{
function testGuessBaseUriNoPathInfo() {
- $serverVars = array(
+ $serverVars = [
'REQUEST_URI' => '/index.php/root',
- );
+ ];
- $httpRequest = new HTTP\Request($serverVars);
+ $httpRequest = HTTP\Sapi::createFromServerArray($serverVars);
$server = new Server();
$server->httpRequest = $httpRequest;
@@ -473,11 +310,11 @@ class ServerSimpleTest extends AbstractServer{
function testGuessBaseUriNoPathInfo2() {
- $serverVars = array(
+ $serverVars = [
'REQUEST_URI' => '/a/b/c/test.php',
- );
+ ];
- $httpRequest = new HTTP\Request($serverVars);
+ $httpRequest = HTTP\Sapi::createFromServerArray($serverVars);
$server = new Server();
$server->httpRequest = $httpRequest;
@@ -487,17 +324,16 @@ class ServerSimpleTest extends AbstractServer{
/**
- * @covers \Sabre\DAV\Server::guessBaseUri
* @depends testGuessBaseUri
*/
function testGuessBaseUriQueryString() {
- $serverVars = array(
+ $serverVars = [
'REQUEST_URI' => '/index.php/root?query_string=blabla',
'PATH_INFO' => '/root',
- );
+ ];
- $httpRequest = new HTTP\Request($serverVars);
+ $httpRequest = HTTP\Sapi::createFromServerArray($serverVars);
$server = new Server();
$server->httpRequest = $httpRequest;
@@ -506,18 +342,17 @@ class ServerSimpleTest extends AbstractServer{
}
/**
- * @covers \Sabre\DAV\Server::guessBaseUri
* @depends testGuessBaseUri
* @expectedException \Sabre\DAV\Exception
*/
function testGuessBaseUriBadConfig() {
- $serverVars = array(
+ $serverVars = [
'REQUEST_URI' => '/index.php/root/heyyy',
'PATH_INFO' => '/root',
- );
+ ];
- $httpRequest = new HTTP\Request($serverVars);
+ $httpRequest = HTTP\Sapi::createFromServerArray($serverVars);
$server = new Server();
$server->httpRequest = $httpRequest;
@@ -527,25 +362,25 @@ class ServerSimpleTest extends AbstractServer{
function testTriggerException() {
- $serverVars = array(
- 'REQUEST_URI' => '/',
+ $serverVars = [
+ 'REQUEST_URI' => '/',
'REQUEST_METHOD' => 'FOO',
- );
+ ];
- $httpRequest = new HTTP\Request($serverVars);
+ $httpRequest = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->httpRequest = $httpRequest;
- $this->server->subscribeEvent('beforeMethod',array($this,'exceptionTrigger'));
+ $this->server->on('beforeMethod', [$this, 'exceptionTrigger']);
$this->server->exec();
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),$this->response->headers);
+ $this->assertEquals([
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ], $this->response->getHeaders());
- $this->assertEquals('HTTP/1.1 500 Internal Server Error',$this->response->status);
+ $this->assertEquals(500, $this->response->status);
}
- function exceptionTrigger() {
+ function exceptionTrigger($request, $response) {
throw new Exception('Hola');
@@ -553,54 +388,56 @@ class ServerSimpleTest extends AbstractServer{
function testReportNotFound() {
- $serverVars = array(
+ $serverVars = [
'REQUEST_URI' => '/',
'REQUEST_METHOD' => 'REPORT',
- );
+ ];
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->httpRequest = ($request);
$this->server->httpRequest->setBody('<?xml version="1.0"?><bla:myreport xmlns:bla="http://www.rooftopsolutions.nl/NS"></bla:myreport>');
$this->server->exec();
- $this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),
- $this->response->headers
+ $this->assertEquals([
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ],
+ $this->response->getHeaders()
);
- $this->assertEquals('HTTP/1.1 403 Forbidden',$this->response->status,'We got an incorrect status back. Full response body follows: ' . $this->response->body);
+ $this->assertEquals(415, $this->response->status, 'We got an incorrect status back. Full response body follows: ' . $this->response->body);
}
function testReportIntercepted() {
- $serverVars = array(
+ $serverVars = [
'REQUEST_URI' => '/',
'REQUEST_METHOD' => 'REPORT',
- );
+ ];
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$this->server->httpRequest = ($request);
$this->server->httpRequest->setBody('<?xml version="1.0"?><bla:myreport xmlns:bla="http://www.rooftopsolutions.nl/NS"></bla:myreport>');
- $this->server->subscribeEvent('report',array($this,'reportHandler'));
+ $this->server->on('report', [$this, 'reportHandler']);
$this->server->exec();
- $this->assertEquals(array(
- 'testheader' => 'testvalue',
- ),
- $this->response->headers
+ $this->assertEquals([
+ 'X-Sabre-Version' => [Version::VERSION],
+ 'testheader' => ['testvalue'],
+ ],
+ $this->response->getHeaders()
);
- $this->assertEquals('HTTP/1.1 418 I\'m a teapot',$this->response->status,'We got an incorrect status back. Full response body follows: ' . $this->response->body);
+ $this->assertEquals(418, $this->response->status, 'We got an incorrect status back. Full response body follows: ' . $this->response->body);
}
- function reportHandler($reportName) {
+ function reportHandler($reportName, $result, $path) {
- if ($reportName=='{http://www.rooftopsolutions.nl/NS}myreport') {
- $this->server->httpResponse->sendStatus(418);
- $this->server->httpResponse->setHeader('testheader','testvalue');
+ if ($reportName == '{http://www.rooftopsolutions.nl/NS}myreport') {
+ $this->server->httpResponse->setStatus(418);
+ $this->server->httpResponse->setHeader('testheader', 'testvalue');
return false;
}
else return;
@@ -609,16 +446,29 @@ class ServerSimpleTest extends AbstractServer{
function testGetPropertiesForChildren() {
- $result = $this->server->getPropertiesForChildren('',array(
+ $result = $this->server->getPropertiesForChildren('', [
'{DAV:}getcontentlength',
- ));
+ ]);
- $expected = array(
- 'test.txt' => array('{DAV:}getcontentlength' => 13),
- 'dir/' => array(),
- );
+ $expected = [
+ 'test.txt' => ['{DAV:}getcontentlength' => 13],
+ 'dir/' => [],
+ ];
+
+ $this->assertEquals($expected, $result);
+
+ }
- $this->assertEquals($expected,$result);
+ /**
+ * There are certain cases where no HTTP status may be set. We need to
+ * intercept these and set it to a default error message.
+ */
+ function testNoHTTPSTatusSet() {
+
+ $this->server->on('method:GET', function() { return false; }, 1);
+ $this->server->httpRequest = new HTTP\Request('GET', '/');
+ $this->server->exec();
+ $this->assertEquals(500, $this->response->getStatus());
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/ServerUpdatePropertiesTest.php b/vendor/sabre/dav/tests/Sabre/DAV/ServerUpdatePropertiesTest.php
index a73e8d13f..7fde11b22 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/ServerUpdatePropertiesTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/ServerUpdatePropertiesTest.php
@@ -17,10 +17,7 @@ class ServerUpdatePropertiesTest extends \PHPUnit_Framework_TestCase {
));
$expected = array(
- 'href' => 'foo',
- '403' => array(
- '{DAV:}foo' => null,
- ),
+ '{DAV:}foo' => 403,
);
$this->assertEquals($expected, $result);
@@ -33,19 +30,17 @@ class ServerUpdatePropertiesTest extends \PHPUnit_Framework_TestCase {
);
$server = new Server($tree);
+ $server->on('propPatch', function($path, PropPatch $propPatch) {
+ $propPatch->handleRemaining(function() { return true; });
+ });
$result = $server->updateProperties('foo', array(
'{DAV:}getetag' => 'bla',
'{DAV:}foo' => 'bar'
));
$expected = array(
- 'href' => 'foo',
- '403' => array(
- '{DAV:}getetag' => null,
- ),
- '424' => array(
- '{DAV:}foo' => null,
- ),
+ '{DAV:}getetag' => 403,
+ '{DAV:}foo' => 424,
);
$this->assertEquals($expected, $result);
@@ -57,7 +52,10 @@ class ServerUpdatePropertiesTest extends \PHPUnit_Framework_TestCase {
new SimpleCollection('foo'),
);
$server = new Server($tree);
- $server->subscribeEvent('updateProperties', array($this,'updatepropfail'));
+ $server->on('propPatch', function($path, PropPatch $propPatch) {
+ $propPatch->setResultCode('{DAV:}foo', 404);
+ $propPatch->handleRemaining(function() { return true; });
+ });
$result = $server->updateProperties('foo', array(
'{DAV:}foo' => 'bar',
@@ -65,36 +63,29 @@ class ServerUpdatePropertiesTest extends \PHPUnit_Framework_TestCase {
));
$expected = array(
- 'href' => 'foo',
- '404' => array(
- '{DAV:}foo' => null,
- ),
- '424' => array(
- '{DAV:}foo2' => null,
- ),
+ '{DAV:}foo' => 404,
+ '{DAV:}foo2' => 424,
);
$this->assertEquals($expected, $result);
}
- function updatePropFail(&$propertyDelta, &$result, $node) {
-
- $result[404] = array(
- '{DAV:}foo' => null,
- );
- unset($propertyDelta['{DAV:}foo']);
- return false;
-
- }
-
-
function testUpdatePropertiesEventSuccess() {
$tree = array(
new SimpleCollection('foo'),
);
$server = new Server($tree);
- $server->subscribeEvent('updateProperties', array($this,'updatepropsuccess'));
+ $server->on('propPatch', function($path, PropPatch $propPatch) {
+
+ $propPatch->handle(['{DAV:}foo', '{DAV:}foo2'], function() {
+ return [
+ '{DAV:}foo' => 200,
+ '{DAV:}foo2' => 201,
+ ];
+ });
+
+ });
$result = $server->updateProperties('foo', array(
'{DAV:}foo' => 'bar',
@@ -102,29 +93,11 @@ class ServerUpdatePropertiesTest extends \PHPUnit_Framework_TestCase {
));
$expected = array(
- 'href' => 'foo',
- '200' => array(
- '{DAV:}foo' => null,
- ),
- '201' => array(
- '{DAV:}foo2' => null,
- ),
+ '{DAV:}foo' => 200,
+ '{DAV:}foo2' => 201,
);
$this->assertEquals($expected, $result);
}
- function updatePropSuccess(&$propertyDelta, &$result, $node) {
-
- $result[200] = array(
- '{DAV:}foo' => null,
- );
- $result[201] = array(
- '{DAV:}foo2' => null,
- );
- unset($propertyDelta['{DAV:}foo']);
- unset($propertyDelta['{DAV:}foo2']);
- return;
-
- }
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/SimpleFileTest.php b/vendor/sabre/dav/tests/Sabre/DAV/SimpleFileTest.php
index de8b05734..9b083b998 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/SimpleFileTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/SimpleFileTest.php
@@ -10,8 +10,8 @@ class SimpleFileTest extends \PHPUnit_Framework_TestCase {
$this->assertEquals('filename.txt', $file->getName());
$this->assertEquals('contents', $file->get());
- $this->assertEquals('8', $file->getSize());
- $this->assertEquals('"' . md5('contents') . '"', $file->getETag());
+ $this->assertEquals(8, $file->getSize());
+ $this->assertEquals('"' . sha1('contents') . '"', $file->getETag());
$this->assertEquals('text/plain', $file->getContentType());
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/TemporaryFileFilterTest.php b/vendor/sabre/dav/tests/Sabre/DAV/TemporaryFileFilterTest.php
index d136eeb17..7122f4a01 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/TemporaryFileFilterTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/TemporaryFileFilterTest.php
@@ -16,19 +16,14 @@ class TemporaryFileFilterTest extends AbstractServer {
function testPutNormal() {
- $serverVars = array(
- 'REQUEST_URI' => '/testput.txt',
- 'REQUEST_METHOD' => 'PUT',
- );
+ $request = new HTTP\Request('PUT', '/testput.txt', [], 'Testing new file');
- $request = new HTTP\Request($serverVars);
- $request->setBody('Testing new file');
$this->server->httpRequest = ($request);
$this->server->exec();
$this->assertEquals('', $this->response->body);
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
- $this->assertEquals('0', $this->response->headers['Content-Length']);
+ $this->assertEquals(201, $this->response->status);
+ $this->assertEquals('0', $this->response->getHeader('Content-Length'));
$this->assertEquals('Testing new file',file_get_contents(SABRE_TEMPDIR . '/testput.txt'));
@@ -37,21 +32,16 @@ class TemporaryFileFilterTest extends AbstractServer {
function testPutTemp() {
// mimicking an OS/X resource fork
- $serverVars = array(
- 'REQUEST_URI' => '/._testput.txt',
- 'REQUEST_METHOD' => 'PUT',
- );
+ $request = new HTTP\Request('PUT', '/._testput.txt', [], 'Testing new file');
- $request = new HTTP\Request($serverVars);
- $request->setBody('Testing new file');
$this->server->httpRequest = ($request);
$this->server->exec();
$this->assertEquals('', $this->response->body);
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
+ $this->assertEquals(201, $this->response->status);
$this->assertEquals(array(
- 'X-Sabre-Temp' => 'true',
- ),$this->response->headers);
+ 'X-Sabre-Temp' => ['true'],
+ ),$this->response->getHeaders());
$this->assertFalse(file_exists(SABRE_TEMPDIR . '/._testput.txt'),'._testput.txt should not exist in the regular file structure.');
@@ -60,70 +50,54 @@ class TemporaryFileFilterTest extends AbstractServer {
function testPutTempIfNoneMatch() {
// mimicking an OS/X resource fork
- $serverVars = array(
- 'REQUEST_URI' => '/._testput.txt',
- 'REQUEST_METHOD' => 'PUT',
- 'HTTP_IF_NONE_MATCH' => '*',
- );
-
- $request = new HTTP\Request($serverVars);
- $request->setBody('Testing new file');
+ $request = new HTTP\Request('PUT', '/._testput.txt', ['If-None-Match' => '*'], 'Testing new file');
+
$this->server->httpRequest = ($request);
$this->server->exec();
$this->assertEquals('', $this->response->body);
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
+ $this->assertEquals(201, $this->response->status);
$this->assertEquals(array(
- 'X-Sabre-Temp' => 'true',
- ),$this->response->headers);
+ 'X-Sabre-Temp' => ['true'],
+ ),$this->response->getHeaders());
$this->assertFalse(file_exists(SABRE_TEMPDIR . '/._testput.txt'),'._testput.txt should not exist in the regular file structure.');
$this->server->exec();
- $this->assertEquals('HTTP/1.1 412 Precondition failed',$this->response->status);
+ $this->assertEquals(412, $this->response->status);
$this->assertEquals(array(
- 'X-Sabre-Temp' => 'true',
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),$this->response->headers);
+ 'X-Sabre-Temp' => ['true'],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ),$this->response->getHeaders());
}
function testPutGet() {
// mimicking an OS/X resource fork
- $serverVars = array(
- 'REQUEST_URI' => '/._testput.txt',
- 'REQUEST_METHOD' => 'PUT',
- );
-
- $request = new HTTP\Request($serverVars);
- $request->setBody('Testing new file');
+ $request = new HTTP\Request('PUT', '/._testput.txt', [], 'Testing new file');
$this->server->httpRequest = ($request);
$this->server->exec();
$this->assertEquals('', $this->response->body);
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
+ $this->assertEquals(201, $this->response->status);
$this->assertEquals(array(
- 'X-Sabre-Temp' => 'true',
- ),$this->response->headers);
+ 'X-Sabre-Temp' => ['true'],
+ ),$this->response->getHeaders());
- $serverVars = array(
- 'REQUEST_URI' => '/._testput.txt',
- 'REQUEST_METHOD' => 'GET',
- );
+ $request = new HTTP\Request('GET', '/._testput.txt');
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
+ $this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 200 OK',$this->response->status);
+ $this->assertEquals(200, $this->response->status);
$this->assertEquals(array(
- 'X-Sabre-Temp' => 'true',
- 'Content-Length' => 16,
- 'Content-Type' => 'application/octet-stream',
- ),$this->response->headers);
+ 'X-Sabre-Temp' => ['true'],
+ 'Content-Length' => [16],
+ 'Content-Type' => ['application/octet-stream'],
+ ),$this->response->getHeaders());
$this->assertEquals('Testing new file',stream_get_contents($this->response->body));
@@ -132,18 +106,12 @@ class TemporaryFileFilterTest extends AbstractServer {
function testLockNonExistant() {
mkdir(SABRE_TEMPDIR . '/locksdir');
- $locksBackend = new Locks\Backend\FS(SABRE_TEMPDIR . '/locksdir');
+ $locksBackend = new Locks\Backend\File(SABRE_TEMPDIR . '/locks');
$locksPlugin = new Locks\Plugin($locksBackend);
$this->server->addPlugin($locksPlugin);
// mimicking an OS/X resource fork
- $serverVars = array(
- 'REQUEST_URI' => '/._testlock.txt',
- 'REQUEST_METHOD' => 'LOCK',
- );
-
- $request = new HTTP\Request($serverVars);
-
+ $request = new HTTP\Request('LOCK', '/._testput.txt');
$request->setBody('<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
<D:lockscope><D:exclusive/></D:lockscope>
@@ -156,10 +124,10 @@ class TemporaryFileFilterTest extends AbstractServer {
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
- $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']);
- $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')');
- $this->assertEquals('true',$this->response->headers['X-Sabre-Temp']);
+ $this->assertEquals(201, $this->response->status);
+ $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type'));
+ $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
+ $this->assertEquals('true',$this->response->getHeader('X-Sabre-Temp'));
$this->assertFalse(file_exists(SABRE_TEMPDIR . '/._testlock.txt'),'._testlock.txt should not exist in the regular file structure.');
@@ -168,35 +136,25 @@ class TemporaryFileFilterTest extends AbstractServer {
function testPutDelete() {
// mimicking an OS/X resource fork
- $serverVars = array(
- 'REQUEST_URI' => '/._testput.txt',
- 'REQUEST_METHOD' => 'PUT',
- );
+ $request = new HTTP\Request('PUT', '/._testput.txt', [], 'Testing new file');
- $request = new HTTP\Request($serverVars);
- $request->setBody('Testing new file');
- $this->server->httpRequest = ($request);
+ $this->server->httpRequest = $request;
$this->server->exec();
$this->assertEquals('', $this->response->body);
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
+ $this->assertEquals(201, $this->response->status);
$this->assertEquals(array(
- 'X-Sabre-Temp' => 'true',
- ),$this->response->headers);
-
- $serverVars = array(
- 'REQUEST_URI' => '/._testput.txt',
- 'REQUEST_METHOD' => 'DELETE',
- );
+ 'X-Sabre-Temp' => ['true'],
+ ),$this->response->getHeaders());
- $request = new HTTP\Request($serverVars);
- $this->server->httpRequest = ($request);
+ $request = new HTTP\Request('DELETE', '/._testput.txt');
+ $this->server->httpRequest = $request;
$this->server->exec();
- $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status, "Incorrect status code received. Full body:\n". $this->response->body);
+ $this->assertEquals(204, $this->response->status, "Incorrect status code received. Full body:\n". $this->response->body);
$this->assertEquals(array(
- 'X-Sabre-Temp' => 'true',
- ),$this->response->headers);
+ 'X-Sabre-Temp' => ['true'],
+ ),$this->response->getHeaders());
$this->assertEquals('',$this->response->body);
@@ -205,37 +163,26 @@ class TemporaryFileFilterTest extends AbstractServer {
function testPutPropfind() {
// mimicking an OS/X resource fork
- $serverVars = array(
- 'REQUEST_URI' => '/._testput.txt',
- 'REQUEST_METHOD' => 'PUT',
- );
-
- $request = new HTTP\Request($serverVars);
- $request->setBody('Testing new file');
- $this->server->httpRequest = ($request);
+ $request = new HTTP\Request('PUT', '/._testput.txt', [], 'Testing new file');
+ $this->server->httpRequest = $request;
$this->server->exec();
$this->assertEquals('', $this->response->body);
- $this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
+ $this->assertEquals(201, $this->response->status);
$this->assertEquals(array(
- 'X-Sabre-Temp' => 'true',
- ),$this->response->headers);
+ 'X-Sabre-Temp' => ['true'],
+ ),$this->response->getHeaders());
- $serverVars = array(
- 'REQUEST_URI' => '/._testput.txt',
- 'REQUEST_METHOD' => 'PROPFIND',
- );
+ $request = new HTTP\Request('PROPFIND', '/._testput.txt');
- $request = new HTTP\Request($serverVars);
- $request->setBody('');
$this->server->httpRequest = ($request);
$this->server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Incorrect status code returned. Body: ' . $this->response->body);
+ $this->assertEquals(207, $this->response->status,'Incorrect status code returned. Body: ' . $this->response->body);
$this->assertEquals(array(
- 'X-Sabre-Temp' => 'true',
- 'Content-Type' => 'application/xml; charset=utf-8',
- ),$this->response->headers);
+ 'X-Sabre-Temp' => ['true'],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ),$this->response->getHeaders());
$body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body);
$xml = simplexml_load_string($body);
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/TestPlugin.php b/vendor/sabre/dav/tests/Sabre/DAV/TestPlugin.php
index 9cf5edbb0..bb5ea6acc 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/TestPlugin.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/TestPlugin.php
@@ -2,31 +2,35 @@
namespace Sabre\DAV;
+use
+ Sabre\HTTP\RequestInterface,
+ Sabre\HTTP\ResponseInterface;
+
class TestPlugin extends ServerPlugin {
public $beforeMethod;
function getFeatures() {
- return array('drinking');
+ return ['drinking'];
}
function getHTTPMethods($uri) {
- return array('BEER','WINE');
+ return ['BEER','WINE'];
}
function initialize(Server $server) {
- $server->subscribeEvent('beforeMethod',array($this,'beforeMethod'));
+ $server->on('beforeMethod', [$this,'beforeMethod']);
}
- function beforeMethod($method) {
+ function beforeMethod(RequestInterface $request, ResponseInterface $response) {
- $this->beforeMethod = $method;
+ $this->beforeMethod = $request->getMethod();
return true;
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/Tree/FilesystemTest.php b/vendor/sabre/dav/tests/Sabre/DAV/Tree/FilesystemTest.php
deleted file mode 100644
index 19b08460f..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAV/Tree/FilesystemTest.php
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-
-namespace Sabre\DAV\Tree;
-
-use Sabre\DAV;
-
-/**
- * @covers Sabre\DAV\Tree
- * @covers Sabre\DAV\Tree\Filesystem
- * @covers Sabre\DAV\FS\Node
- * @covers Sabre\DAV\FS\File
- * @covers Sabre\DAV\FS\Directory
- */
-class FilesystemTest extends \PHPUnit_Framework_TestCase {
-
- function setUp() {
-
- \Sabre\TestUtil::clearTempDir();
- file_put_contents(SABRE_TEMPDIR. '/file.txt','Body');
- mkdir(SABRE_TEMPDIR.'/dir');
- file_put_contents(SABRE_TEMPDIR.'/dir/subfile.txt','Body');
-
- }
-
- function tearDown() {
-
- \Sabre\TestUtil::clearTempDir();
-
- }
-
- function testGetNodeForPath_File() {
-
- $fs = new Filesystem(SABRE_TEMPDIR);
- $node = $fs->getNodeForPath('file.txt');
- $this->assertTrue($node instanceof DAV\FS\File);
-
- }
-
- /**
- * @expectedException \Sabre\DAV\Exception\NotFound
- */
- function testGetNodeForPath_DoesntExist() {
-
- $fs = new Filesystem(SABRE_TEMPDIR);
- $node = $fs->getNodeForPath('whoop/file.txt');
-
- }
-
- function testGetNodeForPath_Directory() {
-
- $fs = new Filesystem(SABRE_TEMPDIR);
- $node = $fs->getNodeForPath('dir');
- $this->assertTrue($node instanceof DAV\FS\Directory);
- $this->assertEquals('dir', $node->getName());
- $this->assertInternalType('array', $node->getChildren());
-
- }
-
- function testCopy() {
-
- $fs = new Filesystem(SABRE_TEMPDIR);
- $fs->copy('file.txt','file2.txt');
- $this->assertTrue(file_exists(SABRE_TEMPDIR . '/file2.txt'));
- $this->assertEquals('Body',file_get_contents(SABRE_TEMPDIR . '/file2.txt'));
-
- }
-
- function testCopyDir() {
-
- $fs = new Filesystem(SABRE_TEMPDIR);
- $fs->copy('dir','dir2');
- $this->assertTrue(file_exists(SABRE_TEMPDIR . '/dir2'));
- $this->assertEquals('Body',file_get_contents(SABRE_TEMPDIR . '/dir2/subfile.txt'));
-
- }
-
- function testMove() {
-
- $fs = new Filesystem(SABRE_TEMPDIR);
- $fs->move('file.txt','file2.txt');
- $this->assertTrue(file_exists(SABRE_TEMPDIR . '/file2.txt'));
- $this->assertTrue(!file_exists(SABRE_TEMPDIR . '/file.txt'));
- $this->assertEquals('Body',file_get_contents(SABRE_TEMPDIR . '/file2.txt'));
-
- }
-
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/TreeTest.php b/vendor/sabre/dav/tests/Sabre/DAV/TreeTest.php
index 90df6427e..9516c2390 100644
--- a/vendor/sabre/dav/tests/Sabre/DAV/TreeTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAV/TreeTest.php
@@ -2,9 +2,6 @@
namespace Sabre\DAV;
-/**
- * @covers \Sabre\DAV\Tree
- */
class TreeTest extends \PHPUnit_Framework_TestCase {
function testNodeExists() {
@@ -59,11 +56,31 @@ class TreeTest extends \PHPUnit_Framework_TestCase {
$tree = new TreeMock();
$children = $tree->getChildren('');
- $this->assertEquals(1,count($children));
+ $this->assertEquals(2,count($children));
$this->assertEquals('hi', $children[0]->getName());
}
+ function testGetMultipleNodes() {
+
+ $tree = new TreeMock();
+ $result = $tree->getMultipleNodes(['hi/sub', 'hi/file']);
+ $this->assertArrayHasKey('hi/sub', $result);
+ $this->assertArrayHasKey('hi/file', $result);
+
+ $this->assertEquals('sub', $result['hi/sub']->getName());
+ $this->assertEquals('file', $result['hi/file']->getName());
+
+ }
+ function testGetMultipleNodes2() {
+
+ $tree = new TreeMock();
+ $result = $tree->getMultipleNodes(['multi/1', 'multi/2']);
+ $this->assertArrayHasKey('multi/1', $result);
+ $this->assertArrayHasKey('multi/2', $result);
+
+ }
+
}
class TreeMock extends Tree {
@@ -72,19 +89,23 @@ class TreeMock extends Tree {
function __construct() {
- $this->nodes['hi/sub'] = new TreeDirectoryTester('sub');
- $this->nodes['hi/file'] = new TreeFileTester('file');
- $this->nodes['hi/file']->properties = array('test1' => 'value');
- $this->nodes['hi/file']->data = 'foobar';
- $this->nodes['hi'] = new TreeDirectoryTester('hi',array($this->nodes['hi/sub'], $this->nodes['hi/file']));
- $this->nodes[''] = new TreeDirectoryTester('hi', array($this->nodes['hi']));
-
- }
-
- function getNodeForPath($path) {
+ $file = new TreeFileTester('file');
+ $file->properties = ['test1'=>'value'];
+ $file->data = 'foobar';
- if (isset($this->nodes[$path])) return $this->nodes[$path];
- throw new Exception\NotFound('item not found');
+ parent::__construct(
+ new TreeDirectoryTester('root', [
+ new TreeDirectoryTester('hi', [
+ new TreeDirectoryTester('sub'),
+ $file,
+ ]),
+ new TreeMultiGetTester('multi', [
+ new TreeFileTester('1'),
+ new TreeFileTester('2'),
+ new TreeFileTester('3'),
+ ])
+ ])
+ );
}
@@ -117,6 +138,12 @@ class TreeDirectoryTester extends SimpleCollection {
}
+ function childExists($name) {
+
+ return !!$this->getChild($name);
+
+ }
+
function delete() {
$this->isDeleted = true;
@@ -164,12 +191,51 @@ class TreeFileTester extends File implements IProperties {
}
- function updateProperties($properties) {
+ /**
+ * Updates properties on this node.
+ *
+ * This method received a PropPatch object, which contains all the
+ * information about the update.
+ *
+ * To update specific properties, call the 'handle' method on this object.
+ * Read the PropPatch documentation for more information.
+ *
+ * @param array $mutations
+ * @return bool|array
+ */
+ function propPatch(PropPatch $propPatch) {
- $this->properties = $properties;
- return true;
+ $this->properties = $propPatch->getMutations();
+ $propPatch->setRemainingResultCode(200);
}
}
+class TreeMultiGetTester extends TreeDirectoryTester implements IMultiGet {
+
+ /**
+ * This method receives a list of paths in it's first argument.
+ * It must return an array with Node objects.
+ *
+ * If any children are not found, you do not have to return them.
+ *
+ * @return array
+ */
+ function getMultipleChildren(array $paths) {
+
+ $result = [];
+ foreach($paths as $path) {
+ try {
+ $child = $this->getChild($path);
+ $result[] = $child;
+ } catch (Exception\NotFound $e) {
+ // Do nothing
+ }
+ }
+
+ return $result;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/URLUtilTest.php b/vendor/sabre/dav/tests/Sabre/DAV/URLUtilTest.php
deleted file mode 100644
index 5d1380865..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAV/URLUtilTest.php
+++ /dev/null
@@ -1,131 +0,0 @@
-<?php
-
-namespace Sabre\DAV;
-
-class URLUtilTest extends \PHPUnit_Framework_TestCase{
-
- function testEncodePath() {
-
- $str = '';
- for($i=0;$i<128;$i++) $str.=chr($i);
-
- $newStr = URLUtil::encodePath($str);
-
- $this->assertEquals(
- '%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f'.
- '%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f'.
- '%20%21%22%23%24%25%26%27()%2a%2b%2c-./'.
- '0123456789:%3b%3c%3d%3e%3f'.
- '%40ABCDEFGHIJKLMNO' .
- 'PQRSTUVWXYZ%5b%5c%5d%5e_' .
- '%60abcdefghijklmno' .
- 'pqrstuvwxyz%7b%7c%7d~%7f',
- $newStr);
-
- $this->assertEquals($str,URLUtil::decodePath($newStr));
-
- }
-
- function testEncodePathSegment() {
-
- $str = '';
- for($i=0;$i<128;$i++) $str.=chr($i);
-
- $newStr = URLUtil::encodePathSegment($str);
-
- // Note: almost exactly the same as the last test, with the
- // exception of the encoding of / (ascii code 2f)
- $this->assertEquals(
- '%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f'.
- '%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f'.
- '%20%21%22%23%24%25%26%27()%2a%2b%2c-.%2f'.
- '0123456789:%3b%3c%3d%3e%3f'.
- '%40ABCDEFGHIJKLMNO' .
- 'PQRSTUVWXYZ%5b%5c%5d%5e_' .
- '%60abcdefghijklmno' .
- 'pqrstuvwxyz%7b%7c%7d~%7f',
- $newStr);
-
- $this->assertEquals($str,URLUtil::decodePathSegment($newStr));
-
- }
-
- function testDecode() {
-
- $str = 'Hello%20Test+Test2.txt';
- $newStr = URLUtil::decodePath($str);
- $this->assertEquals('Hello Test+Test2.txt',$newStr);
-
- }
-
- /**
- * @depends testDecode
- */
- function testDecodeUmlaut() {
-
- $str = 'Hello%C3%BC.txt';
- $newStr = URLUtil::decodePath($str);
- $this->assertEquals("Hello\xC3\xBC.txt",$newStr);
-
- }
-
- /**
- * @depends testDecodeUmlaut
- */
- function testDecodeUmlautLatin1() {
-
- $str = 'Hello%FC.txt';
- $newStr = URLUtil::decodePath($str);
- $this->assertEquals("Hello\xC3\xBC.txt",$newStr);
-
- }
-
- /**
- * This testcase was sent by a bug reporter
- *
- * @depends testDecode
- */
- function testDecodeAccentsWindows7() {
-
- $str = '/webdav/%C3%A0fo%C3%B3';
- $newStr = URLUtil::decodePath($str);
- $this->assertEquals(strtolower($str),URLUtil::encodePath($newStr));
-
- }
-
- function testSplitPath() {
-
- $strings = array(
-
- // input // expected result
- '/foo/bar' => array('/foo','bar'),
- '/foo/bar/' => array('/foo','bar'),
- 'foo/bar/' => array('foo','bar'),
- 'foo/bar' => array('foo','bar'),
- 'foo/bar/baz' => array('foo/bar','baz'),
- 'foo/bar/baz/' => array('foo/bar','baz'),
- 'foo' => array('','foo'),
- 'foo/' => array('','foo'),
- '/foo/' => array('','foo'),
- '/foo' => array('','foo'),
- '' => array(null,null),
-
- // UTF-8
- "/\xC3\xA0fo\xC3\xB3/bar" => array("/\xC3\xA0fo\xC3\xB3",'bar'),
- "/\xC3\xA0foo/b\xC3\xBCr/" => array("/\xC3\xA0foo","b\xC3\xBCr"),
- "foo/\xC3\xA0\xC3\xBCr" => array("foo","\xC3\xA0\xC3\xBCr"),
-
- );
-
- foreach($strings as $input => $expected) {
-
- $output = URLUtil::splitPath($input);
- $this->assertEquals($expected, $output, 'The expected output for \'' . $input . '\' was incorrect');
-
-
- }
-
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAV/XMLUtilTest.php b/vendor/sabre/dav/tests/Sabre/DAV/XMLUtilTest.php
deleted file mode 100644
index 1d2bfd133..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAV/XMLUtilTest.php
+++ /dev/null
@@ -1,284 +0,0 @@
-<?php
-
-namespace Sabre\DAV;
-
-class XMLUtilTest extends \PHPUnit_Framework_TestCase {
-
- function testToClarkNotation() {
-
- $dom = new \DOMDocument();
- $dom->loadXML('<?xml version="1.0"?><test1 xmlns="http://www.example.org/">Testdoc</test1>');
-
- $this->assertEquals(
- '{http://www.example.org/}test1',
- XMLUtil::toClarkNotation($dom->firstChild)
- );
-
- }
-
- function testToClarkNotation2() {
-
- $dom = new \DOMDocument();
- $dom->loadXML('<?xml version="1.0"?><s:test1 xmlns:s="http://www.example.org/">Testdoc</s:test1>');
-
- $this->assertEquals(
- '{http://www.example.org/}test1',
- XMLUtil::toClarkNotation($dom->firstChild)
- );
-
- }
-
- function testToClarkNotationDAVNamespace() {
-
- $dom = new \DOMDocument();
- $dom->loadXML('<?xml version="1.0"?><s:test1 xmlns:s="urn:DAV">Testdoc</s:test1>');
-
- $this->assertEquals(
- '{DAV:}test1',
- XMLUtil::toClarkNotation($dom->firstChild)
- );
-
- }
-
- function testToClarkNotationNoElem() {
-
- $dom = new \DOMDocument();
- $dom->loadXML('<?xml version="1.0"?><s:test1 xmlns:s="urn:DAV">Testdoc</s:test1>');
-
- $this->assertNull(
- XMLUtil::toClarkNotation($dom->firstChild->firstChild)
- );
-
- }
-
- function testConvertDAVNamespace() {
-
- $xml='<?xml version="1.0"?><document xmlns="DAV:">blablabla</document>';
- $this->assertEquals(
- '<?xml version="1.0"?><document xmlns="urn:DAV">blablabla</document>',
- XMLUtil::convertDAVNamespace($xml)
- );
-
- }
-
- function testConvertDAVNamespace2() {
-
- $xml='<?xml version="1.0"?><s:document xmlns:s="DAV:">blablabla</s:document>';
- $this->assertEquals(
- '<?xml version="1.0"?><s:document xmlns:s="urn:DAV">blablabla</s:document>',
- XMLUtil::convertDAVNamespace($xml)
- );
-
- }
-
- function testConvertDAVNamespace3() {
-
- $xml='<?xml version="1.0"?><s:document xmlns="http://bla" xmlns:s="DAV:" xmlns:z="http://othernamespace">blablabla</s:document>';
- $this->assertEquals(
- '<?xml version="1.0"?><s:document xmlns="http://bla" xmlns:s="urn:DAV" xmlns:z="http://othernamespace">blablabla</s:document>',
- XMLUtil::convertDAVNamespace($xml)
- );
-
- }
-
- function testConvertDAVNamespace4() {
-
- $xml='<?xml version="1.0"?><document xmlns=\'DAV:\'>blablabla</document>';
- $this->assertEquals(
- '<?xml version="1.0"?><document xmlns=\'urn:DAV\'>blablabla</document>',
- XMLUtil::convertDAVNamespace($xml)
- );
-
- }
-
- function testConvertDAVNamespaceMixedQuotes() {
-
- $xml='<?xml version="1.0"?><document xmlns=\'DAV:" xmlns="Another attribute\'>blablabla</document>';
- $this->assertEquals(
- $xml,
- XMLUtil::convertDAVNamespace($xml)
- );
-
- }
-
- /**
- * @depends testConvertDAVNamespace
- */
- function testLoadDOMDocument() {
-
- $xml='<?xml version="1.0"?><document></document>';
- $dom = XMLUtil::loadDOMDocument($xml);
- $this->assertTrue($dom instanceof \DOMDocument);
-
- }
-
- /**
- * @depends testLoadDOMDocument
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testLoadDOMDocumentEmpty() {
-
- XMLUtil::loadDOMDocument('');
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- * @depends testConvertDAVNamespace
- */
- function testLoadDOMDocumentInvalid() {
-
- $xml='<?xml version="1.0"?><document></docu';
- $dom = XMLUtil::loadDOMDocument($xml);
-
- }
-
- /**
- * @depends testLoadDOMDocument
- */
- function testLoadDOMDocumentUTF16() {
-
- $xml='<?xml version="1.0" encoding="UTF-16"?><root xmlns="DAV:">blabla</root>';
- $xml = iconv('UTF-8','UTF-16LE',$xml);
- $dom = XMLUtil::loadDOMDocument($xml);
- $this->assertEquals('blabla',$dom->firstChild->nodeValue);
-
- }
-
-
- function testParseProperties() {
-
- $xml='<?xml version="1.0"?>
-<root xmlns="DAV:">
- <prop>
- <displayname>Calendars</displayname>
- </prop>
-</root>';
-
- $dom = XMLUtil::loadDOMDocument($xml);
- $properties = XMLUtil::parseProperties($dom->firstChild);
-
- $this->assertEquals(array(
- '{DAV:}displayname' => 'Calendars',
- ), $properties);
-
-
-
- }
-
- /**
- * @depends testParseProperties
- */
- function testParsePropertiesEmpty() {
-
- $xml='<?xml version="1.0"?>
-<root xmlns="DAV:" xmlns:s="http://www.rooftopsolutions.nl/example">
- <prop>
- <displayname>Calendars</displayname>
- </prop>
- <prop>
- <s:example />
- </prop>
-</root>';
-
- $dom = XMLUtil::loadDOMDocument($xml);
- $properties = XMLUtil::parseProperties($dom->firstChild);
-
- $this->assertEquals(array(
- '{DAV:}displayname' => 'Calendars',
- '{http://www.rooftopsolutions.nl/example}example' => null
- ), $properties);
-
- }
-
-
- /**
- * @depends testParseProperties
- */
- function testParsePropertiesComplex() {
-
- $xml='<?xml version="1.0"?>
-<root xmlns="DAV:">
- <prop>
- <displayname>Calendars</displayname>
- </prop>
- <prop>
- <someprop>Complex value <b>right here</b></someprop>
- </prop>
-</root>';
-
- $dom = XMLUtil::loadDOMDocument($xml);
- $properties = XMLUtil::parseProperties($dom->firstChild);
-
- $this->assertEquals(array(
- '{DAV:}displayname' => 'Calendars',
- '{DAV:}someprop' => 'Complex value right here',
- ), $properties);
-
- }
-
-
- /**
- * @depends testParseProperties
- */
- function testParsePropertiesNoProperties() {
-
- $xml='<?xml version="1.0"?>
-<root xmlns="DAV:">
- <prop>
- </prop>
-</root>';
-
- $dom = XMLUtil::loadDOMDocument($xml);
- $properties = XMLUtil::parseProperties($dom->firstChild);
-
- $this->assertEquals(array(), $properties);
-
- }
-
- function testParsePropertiesMapHref() {
-
- $xml='<?xml version="1.0"?>
-<root xmlns="DAV:">
- <prop>
- <displayname>Calendars</displayname>
- </prop>
- <prop>
- <someprop><href>http://sabredav.org/</href></someprop>
- </prop>
-</root>';
-
- $dom = XMLUtil::loadDOMDocument($xml);
- $properties = XMLUtil::parseProperties($dom->firstChild,array('{DAV:}someprop'=>'Sabre\\DAV\\Property\\Href'));
-
- $this->assertEquals(array(
- '{DAV:}displayname' => 'Calendars',
- '{DAV:}someprop' => new Property\Href('http://sabredav.org/',false),
- ), $properties);
-
- }
-
- function testParseClarkNotation() {
-
- $this->assertEquals(array(
- 'DAV:',
- 'foo',
- ), XMLUtil::parseClarkNotation('{DAV:}foo'));
-
- $this->assertEquals(array(
- 'http://example.org/ns/bla',
- 'bar-soap',
- ), XMLUtil::parseClarkNotation('{http://example.org/ns/bla}bar-soap'));
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testParseClarkNotationFail() {
-
- XMLUtil::parseClarkNotation('}foo');
-
- }
-
-}
-
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/ACLMethodTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/ACLMethodTest.php
index 9960180a3..4ecd42717 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/ACLMethodTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/ACLMethodTest.php
@@ -16,17 +16,7 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
$server = new DAV\Server();
$server->addPlugin($acl);
- $acl->unknownMethod('ACL','test');
-
- }
-
- function testCallbackPassthru() {
-
- $acl = new Plugin();
- $server = new DAV\Server();
- $server->addPlugin($acl);
-
- $this->assertNull($acl->unknownMethod('FOO','test'));
+ $acl->httpAcl($server->httpRequest, $server->httpResponse);
}
@@ -49,7 +39,7 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
$server->httpRequest->setBody($body);
$server->addPlugin($acl);
- $acl->httpACL('test');
+ $acl->httpACL($server->httpRequest, $server->httpResponse);
}
@@ -61,13 +51,15 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
$acl = new Plugin();
$server = new DAV\Server($tree);
$server->httpRequest = new HTTP\Request();
+ $server->httpRequest->setUrl('/test');
+
$body = '<?xml version="1.0"?>
<d:acl xmlns:d="DAV:">
</d:acl>';
$server->httpRequest->setBody($body);
$server->addPlugin($acl);
- $this->assertNull($acl->httpACL('test'));
+ $this->assertFalse($acl->httpACL($server->httpRequest, $server->httpResponse));
}
@@ -81,7 +73,7 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
);
$acl = new Plugin();
$server = new DAV\Server($tree);
- $server->httpRequest = new HTTP\Request();
+ $server->httpRequest = new HTTP\Request('ACL','/test');
$body = '<?xml version="1.0"?>
<d:acl xmlns:d="DAV:">
<d:ace>
@@ -92,7 +84,7 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
$server->httpRequest->setBody($body);
$server->addPlugin($acl);
- $acl->httpACL('test');
+ $acl->httpACL($server->httpRequest, $server->httpResponse);
}
@@ -109,7 +101,7 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
);
$acl = new Plugin();
$server = new DAV\Server($tree);
- $server->httpRequest = new HTTP\Request();
+ $server->httpRequest = new HTTP\Request('ACL','/test');
$body = '<?xml version="1.0"?>
<d:acl xmlns:d="DAV:">
<d:ace>
@@ -120,7 +112,7 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
$server->httpRequest->setBody($body);
$server->addPlugin($acl);
- $acl->httpACL('test');
+ $acl->httpACL($server->httpRequest, $server->httpResponse);
}
@@ -134,7 +126,7 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
);
$acl = new Plugin();
$server = new DAV\Server($tree);
- $server->httpRequest = new HTTP\Request();
+ $server->httpRequest = new HTTP\Request('ACL','/test');
$body = '<?xml version="1.0"?>
<d:acl xmlns:d="DAV:">
<d:ace>
@@ -145,7 +137,7 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
$server->httpRequest->setBody($body);
$server->addPlugin($acl);
- $acl->httpACL('test');
+ $acl->httpACL($server->httpRequest, $server->httpResponse);
}
@@ -159,18 +151,18 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
);
$acl = new Plugin();
$server = new DAV\Server($tree);
- $server->httpRequest = new HTTP\Request();
+ $server->httpRequest = new HTTP\Request('ACL','/test');
$body = '<?xml version="1.0"?>
<d:acl xmlns:d="DAV:">
<d:ace>
- <d:grant><d:privilege><d:read-acl /></d:privilege></d:grant>
+ <d:grant><d:privilege><d:all /></d:privilege></d:grant>
<d:principal><d:href>/principals/notfound</d:href></d:principal>
</d:ace>
</d:acl>';
$server->httpRequest->setBody($body);
$server->addPlugin($acl);
- $acl->httpACL('test');
+ $acl->httpACL($server->httpRequest, $server->httpResponse);
}
@@ -192,7 +184,7 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
);
$acl = new Plugin();
$server = new DAV\Server($tree);
- $server->httpRequest = new HTTP\Request();
+ $server->httpRequest = new HTTP\Request('ACL','/test');
$body = '<?xml version="1.0"?>
<d:acl xmlns:d="DAV:">
<d:ace>
@@ -203,7 +195,7 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
$server->httpRequest->setBody($body);
$server->addPlugin($acl);
- $acl->httpACL('test');
+ $acl->httpACL($server->httpRequest, $server->httpResponse);
}
@@ -225,7 +217,7 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
);
$acl = new Plugin();
$server = new DAV\Server($tree);
- $server->httpRequest = new HTTP\Request();
+ $server->httpRequest = new HTTP\Request('ACL','/test');
$body = '<?xml version="1.0"?>
<d:acl xmlns:d="DAV:">
<d:ace>
@@ -236,7 +228,7 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
$server->httpRequest->setBody($body);
$server->addPlugin($acl);
- $acl->httpACL('test');
+ $acl->httpACL($server->httpRequest, $server->httpResponse);
}
@@ -258,7 +250,7 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
);
$acl = new Plugin();
$server = new DAV\Server($tree);
- $server->httpRequest = new HTTP\Request();
+ $server->httpRequest = new HTTP\Request('ACL','/test');
$body = '<?xml version="1.0"?>
<d:acl xmlns:d="DAV:">
<d:ace>
@@ -269,11 +261,11 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
$server->httpRequest->setBody($body);
$server->addPlugin($acl);
- $acl->httpACL('test');
+ $acl->httpACL($server->httpRequest, $server->httpResponse);
}
- function testSuccessComplex () {
+ function testSuccessComplex() {
$oldACL = array(
array(
@@ -296,7 +288,7 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
);
$acl = new Plugin();
$server = new DAV\Server($tree);
- $server->httpRequest = new HTTP\Request();
+ $server->httpRequest = new HTTP\Request('ACL','/test');
$body = '<?xml version="1.0"?>
<d:acl xmlns:d="DAV:">
<d:ace>
@@ -312,7 +304,8 @@ class ACLMethodTest extends \PHPUnit_Framework_TestCase {
$server->httpRequest->setBody($body);
$server->addPlugin($acl);
- $this->assertFalse($acl->unknownMethod('ACL','test'));
+
+ $this->assertFalse($acl->httpAcl($server->httpRequest, $server->httpResponse));
$this->assertEquals(array(
array(
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/AllowAccessTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/AllowAccessTest.php
index 3a9b35b45..14a80003a 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/AllowAccessTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/AllowAccessTest.php
@@ -27,112 +27,104 @@ class AllowAccessTest extends \PHPUnit_Framework_TestCase {
function testGet() {
- $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('GET','testdir')));
+ $this->server->httpRequest->setMethod('GET');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]));
}
function testGetDoesntExist() {
- $r = $this->server->broadcastEvent('beforeMethod',array('GET','foo'));
- $this->assertTrue($r);
+ $this->server->httpRequest->setMethod('GET');
+ $this->server->httpRequest->setUrl('/foo');
+
+ $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]));
}
function testHEAD() {
- $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('HEAD','testdir')));
+ $this->server->httpRequest->setMethod('HEAD');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]));
}
function testOPTIONS() {
- $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('OPTIONS','testdir')));
+ $this->server->httpRequest->setMethod('OPTIONS');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]));
}
function testPUT() {
- $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('PUT','testdir')));
+ $this->server->httpRequest->setMethod('PUT');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]));
}
function testACL() {
- $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('ACL','testdir')));
+ $this->server->httpRequest->setMethod('ACL');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]));
}
function testPROPPATCH() {
- $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('PROPPATCH','testdir')));
+ $this->server->httpRequest->setMethod('PROPPATCH');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]));
}
function testCOPY() {
- $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('COPY','testdir')));
+ $this->server->httpRequest->setMethod('COPY');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]));
}
function testMOVE() {
- $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('MOVE','testdir')));
+ $this->server->httpRequest->setMethod('MOVE');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]));
}
function testLOCK() {
- $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('LOCK','testdir')));
+ $this->server->httpRequest->setMethod('LOCK');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]));
}
function testBeforeBind() {
- $this->assertTrue($this->server->broadcastEvent('beforeBind',array('testdir/file')));
+ $this->assertTrue($this->server->emit('beforeBind', ['testdir/file']));
}
function testBeforeUnbind() {
- $this->assertTrue($this->server->broadcastEvent('beforeUnbind',array('testdir')));
-
- }
-
- function testAfterGetProperties() {
-
- $properties = array(
- 'href' => 'foo',
- '200' => array(
- '{DAV:}displayname' => 'foo',
- '{DAV:}getcontentlength' => 500,
- ),
- '404' => array(
- '{DAV:}bar' => null,
- ),
- '403' => array(
- '{DAV:}owner' => null,
- ),
- );
-
- $expected = array(
- 'href' => 'foo',
- '200' => array(
- '{DAV:}displayname' => 'foo',
- '{DAV:}getcontentlength' => 500,
- ),
- '404' => array(
- '{DAV:}bar' => null,
- ),
- '403' => array(
- '{DAV:}owner' => null,
- ),
- );
-
- $r = $this->server->broadcastEvent('afterGetProperties',array('testdir',&$properties));
- $this->assertTrue($r);
-
- $this->assertEquals($expected, $properties);
+ $this->assertTrue($this->server->emit('beforeUnbind', ['testdir']));
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/BlockAccessTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/BlockAccessTest.php
index 345d2cc5d..be3e9dae9 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/BlockAccessTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/BlockAccessTest.php
@@ -15,9 +15,9 @@ class BlockAccessTest extends \PHPUnit_Framework_TestCase {
function setUp() {
- $nodes = array(
+ $nodes = [
new DAV\SimpleCollection('testdir'),
- );
+ ];
$this->server = new DAV\Server($nodes);
$this->plugin = new Plugin();
@@ -31,13 +31,19 @@ class BlockAccessTest extends \PHPUnit_Framework_TestCase {
*/
function testGet() {
- $this->server->broadcastEvent('beforeMethod',array('GET','testdir'));
+ $this->server->httpRequest->setMethod('GET');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]);
}
function testGetDoesntExist() {
- $r = $this->server->broadcastEvent('beforeMethod',array('GET','foo'));
+ $this->server->httpRequest->setMethod('GET');
+ $this->server->httpRequest->setUrl('/foo');
+
+ $r = $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]);
$this->assertTrue($r);
}
@@ -47,7 +53,10 @@ class BlockAccessTest extends \PHPUnit_Framework_TestCase {
*/
function testHEAD() {
- $this->server->broadcastEvent('beforeMethod',array('HEAD','testdir'));
+ $this->server->httpRequest->setMethod('HEAD');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]);
}
@@ -56,7 +65,10 @@ class BlockAccessTest extends \PHPUnit_Framework_TestCase {
*/
function testOPTIONS() {
- $this->server->broadcastEvent('beforeMethod',array('OPTIONS','testdir'));
+ $this->server->httpRequest->setMethod('OPTIONS');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]);
}
@@ -65,7 +77,10 @@ class BlockAccessTest extends \PHPUnit_Framework_TestCase {
*/
function testPUT() {
- $this->server->broadcastEvent('beforeMethod',array('PUT','testdir'));
+ $this->server->httpRequest->setMethod('PUT');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]);
}
@@ -74,7 +89,10 @@ class BlockAccessTest extends \PHPUnit_Framework_TestCase {
*/
function testPROPPATCH() {
- $this->server->broadcastEvent('beforeMethod',array('PROPPATCH','testdir'));
+ $this->server->httpRequest->setMethod('PROPPATCH');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]);
}
@@ -83,7 +101,10 @@ class BlockAccessTest extends \PHPUnit_Framework_TestCase {
*/
function testCOPY() {
- $this->server->broadcastEvent('beforeMethod',array('COPY','testdir'));
+ $this->server->httpRequest->setMethod('COPY');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]);
}
@@ -92,7 +113,10 @@ class BlockAccessTest extends \PHPUnit_Framework_TestCase {
*/
function testMOVE() {
- $this->server->broadcastEvent('beforeMethod',array('MOVE','testdir'));
+ $this->server->httpRequest->setMethod('MOVE');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]);
}
@@ -101,7 +125,10 @@ class BlockAccessTest extends \PHPUnit_Framework_TestCase {
*/
function testACL() {
- $this->server->broadcastEvent('beforeMethod',array('ACL','testdir'));
+ $this->server->httpRequest->setMethod('ACL');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]);
}
@@ -110,7 +137,10 @@ class BlockAccessTest extends \PHPUnit_Framework_TestCase {
*/
function testLOCK() {
- $this->server->broadcastEvent('beforeMethod',array('LOCK','testdir'));
+ $this->server->httpRequest->setMethod('LOCK');
+ $this->server->httpRequest->setUrl('/testdir');
+
+ $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]);
}
@@ -119,7 +149,7 @@ class BlockAccessTest extends \PHPUnit_Framework_TestCase {
*/
function testBeforeBind() {
- $this->server->broadcastEvent('beforeBind',array('testdir/file'));
+ $this->server->emit('beforeBind', ['testdir/file']);
}
@@ -128,62 +158,48 @@ class BlockAccessTest extends \PHPUnit_Framework_TestCase {
*/
function testBeforeUnbind() {
- $this->server->broadcastEvent('beforeUnbind',array('testdir'));
+ $this->server->emit('beforeUnbind', ['testdir']);
}
- function testBeforeGetProperties() {
+ function testPropFind() {
- $requestedProperties = array(
+ $propFind = new DAV\PropFind('testdir', [
'{DAV:}displayname',
'{DAV:}getcontentlength',
'{DAV:}bar',
'{DAV:}owner',
- );
- $returnedProperties = array();
+ ]);
- $arguments = array(
- 'testdir',
- new DAV\SimpleCollection('testdir'),
- &$requestedProperties,
- &$returnedProperties
- );
- $r = $this->server->broadcastEvent('beforeGetProperties',$arguments);
+ $r = $this->server->emit('propFind', [$propFind, new DAV\SimpleCollection('testdir')]);
$this->assertTrue($r);
- $expected = array(
- '403' => array(
+ $expected = [
+ 200 => [],
+ 404 => [],
+ 403 => [
'{DAV:}displayname' => null,
'{DAV:}getcontentlength' => null,
'{DAV:}bar' => null,
'{DAV:}owner' => null,
- ),
- );
+ ],
+ ];
- $this->assertEquals($expected, $returnedProperties);
- $this->assertEquals(array(), $requestedProperties);
+ $this->assertEquals($expected, $propFind->getResultForMultiStatus());
}
function testBeforeGetPropertiesNoListing() {
$this->plugin->hideNodesFromListings = true;
-
- $requestedProperties = array(
+ $propFind = new DAV\PropFind('testdir', [
'{DAV:}displayname',
'{DAV:}getcontentlength',
'{DAV:}bar',
'{DAV:}owner',
- );
- $returnedProperties = array();
+ ]);
- $arguments = array(
- 'testdir',
- new DAV\SimpleCollection('testdir'),
- &$requestedProperties,
- &$returnedProperties
- );
- $r = $this->server->broadcastEvent('beforeGetProperties',$arguments);
+ $r = $this->server->emit('propFind', [$propFind, new DAV\SimpleCollection('testdir')]);
$this->assertFalse($r);
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/ExpandPropertiesTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/ExpandPropertiesTest.php
index 324788d4a..5e99f2e73 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/ExpandPropertiesTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/ExpandPropertiesTest.php
@@ -12,23 +12,24 @@ class ExpandPropertiesTest extends \PHPUnit_Framework_TestCase {
function getServer() {
$tree = array(
- new MockPropertyNode('node1', array(
+ new DAV\Mock\PropertiesCollection('node1', [], array(
'{http://sabredav.org/ns}simple' => 'foo',
- '{http://sabredav.org/ns}href' => new DAV\Property\Href('node2'),
+ '{http://sabredav.org/ns}href' => new DAV\Xml\Property\Href('node2'),
'{DAV:}displayname' => 'Node 1',
)),
- new MockPropertyNode('node2', array(
+ new DAV\Mock\PropertiesCollection('node2', [], array(
'{http://sabredav.org/ns}simple' => 'simple',
- '{http://sabredav.org/ns}hreflist' => new DAV\Property\HrefList(array('node1','node3')),
+ '{http://sabredav.org/ns}hreflist' => new DAV\Xml\Property\Href(['node1','node3']),
'{DAV:}displayname' => 'Node 2',
)),
- new MockPropertyNode('node3', array(
+ new DAV\Mock\PropertiesCollection('node3', [], array(
'{http://sabredav.org/ns}simple' => 'simple',
'{DAV:}displayname' => 'Node 3',
)),
);
$fakeServer = new DAV\Server($tree);
+ $fakeServer->sapi = new HTTP\SapiMock();
$fakeServer->debugExceptions = true;
$fakeServer->httpResponse = new HTTP\ResponseMock();
$plugin = new Plugin();
@@ -58,7 +59,7 @@ class ExpandPropertiesTest extends \PHPUnit_Framework_TestCase {
'REQUEST_URI' => '/node1',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody($xml);
$server = $this->getServer();
@@ -66,10 +67,11 @@ class ExpandPropertiesTest extends \PHPUnit_Framework_TestCase {
$server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status', $server->httpResponse->status,'Incorrect status code received. Full body: ' . $server->httpResponse->body);
+ $this->assertEquals(207, $server->httpResponse->status,'Incorrect status code received. Full body: ' . $server->httpResponse->body);
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ), $server->httpResponse->headers);
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ), $server->httpResponse->getHeaders());
$check = array(
@@ -120,7 +122,7 @@ class ExpandPropertiesTest extends \PHPUnit_Framework_TestCase {
'REQUEST_URI' => '/node1',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody($xml);
$server = $this->getServer();
@@ -128,10 +130,11 @@ class ExpandPropertiesTest extends \PHPUnit_Framework_TestCase {
$server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status', $server->httpResponse->status, 'Incorrect response status received. Full response body: ' . $server->httpResponse->body);
+ $this->assertEquals(207, $server->httpResponse->status, 'Incorrect response status received. Full response body: ' . $server->httpResponse->body);
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ), $server->httpResponse->headers);
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ), $server->httpResponse->getHeaders());
$check = array(
@@ -160,7 +163,7 @@ class ExpandPropertiesTest extends \PHPUnit_Framework_TestCase {
$count = 1;
if (!is_int($v1)) $count = $v2;
- $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result));
+ $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . ' Full response body: ' . $server->httpResponse->getBodyAsString());
}
@@ -184,7 +187,7 @@ class ExpandPropertiesTest extends \PHPUnit_Framework_TestCase {
'REQUEST_URI' => '/node2',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody($xml);
$server = $this->getServer();
@@ -192,10 +195,11 @@ class ExpandPropertiesTest extends \PHPUnit_Framework_TestCase {
$server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status', $server->httpResponse->status);
+ $this->assertEquals(207, $server->httpResponse->status);
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ), $server->httpResponse->headers);
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ), $server->httpResponse->getHeaders());
$check = array(
@@ -251,7 +255,7 @@ class ExpandPropertiesTest extends \PHPUnit_Framework_TestCase {
'REQUEST_URI' => '/node2',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody($xml);
$server = $this->getServer();
@@ -259,10 +263,11 @@ class ExpandPropertiesTest extends \PHPUnit_Framework_TestCase {
$server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status', $server->httpResponse->status);
+ $this->assertEquals(207, $server->httpResponse->status);
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ), $server->httpResponse->headers);
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ), $server->httpResponse->getHeaders());
$check = array(
@@ -303,56 +308,3 @@ class ExpandPropertiesTest extends \PHPUnit_Framework_TestCase {
}
}
-class MockPropertyNode implements DAV\INode, DAV\IProperties {
-
- function __construct($name, array $properties) {
-
- $this->name = $name;
- $this->properties = $properties;
-
- }
-
- function getName() {
-
- return $this->name;
-
- }
-
- function getProperties($requestedProperties) {
-
- $returnedProperties = array();
- foreach($requestedProperties as $requestedProperty) {
- if (isset($this->properties[$requestedProperty])) {
- $returnedProperties[$requestedProperty] =
- $this->properties[$requestedProperty];
- }
- }
- return $returnedProperties;
-
- }
-
- function delete() {
-
- throw new DAV\Exception('Not implemented');
-
- }
-
- function setName($name) {
-
- throw new DAV\Exception('Not implemented');
-
- }
-
- function getLastModified() {
-
- return null;
-
- }
-
- function updateProperties($properties) {
-
- throw new DAV\Exception('Not implemented');
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/PluginAdminTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/PluginAdminTest.php
index 23c4b6e85..fb7516a78 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/PluginAdminTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/PluginAdminTest.php
@@ -11,7 +11,9 @@ require_once 'Sabre/HTTP/ResponseMock.php';
class PluginAdminTest extends \PHPUnit_Framework_TestCase {
- function testNoAdminAccess() {
+ public $server;
+
+ function setUp() {
$principalBackend = new PrincipalBackend\Mock();
@@ -20,13 +22,18 @@ class PluginAdminTest extends \PHPUnit_Framework_TestCase {
new PrincipalCollection($principalBackend),
);
- $fakeServer = new DAV\Server($tree);
- $plugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'realm');
- $fakeServer->addPlugin($plugin);
+ $this->server = new DAV\Server($tree);
+ $this->server->sapi = new HTTP\SapiMock();
+ $plugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock());
+ $this->server->addPlugin($plugin);
+ }
+
+ function testNoAdminAccess() {
+
$plugin = new Plugin();
- $fakeServer->addPlugin($plugin);
+ $this->server->addPlugin($plugin);
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'OPTIONS',
'HTTP_DEPTH' => 1,
'REQUEST_URI' => '/adminonly',
@@ -34,12 +41,12 @@ class PluginAdminTest extends \PHPUnit_Framework_TestCase {
$response = new HTTP\ResponseMock();
- $fakeServer->httpRequest = $request;
- $fakeServer->httpResponse = $response;
+ $this->server->httpRequest = $request;
+ $this->server->httpResponse = $response;
- $fakeServer->exec();
+ $this->server->exec();
- $this->assertEquals('HTTP/1.1 403 Forbidden', $response->status);
+ $this->assertEquals(403, $response->status);
}
@@ -48,23 +55,13 @@ class PluginAdminTest extends \PHPUnit_Framework_TestCase {
*/
function testAdminAccess() {
- $principalBackend = new PrincipalBackend\Mock();
-
- $tree = array(
- new MockACLNode('adminonly', array()),
- new PrincipalCollection($principalBackend),
- );
-
- $fakeServer = new DAV\Server($tree);
- $plugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'realm');
- $fakeServer->addPlugin($plugin);
$plugin = new Plugin();
$plugin->adminPrincipals = array(
'principals/admin',
);
- $fakeServer->addPlugin($plugin);
+ $this->server->addPlugin($plugin);
- $request = new HTTP\Request(array(
+ $request = HTTP\Sapi::createFromServerArray(array(
'REQUEST_METHOD' => 'OPTIONS',
'HTTP_DEPTH' => 1,
'REQUEST_URI' => '/adminonly',
@@ -72,12 +69,12 @@ class PluginAdminTest extends \PHPUnit_Framework_TestCase {
$response = new HTTP\ResponseMock();
- $fakeServer->httpRequest = $request;
- $fakeServer->httpResponse = $response;
+ $this->server->httpRequest = $request;
+ $this->server->httpResponse = $response;
- $fakeServer->exec();
+ $this->server->exec();
- $this->assertEquals('HTTP/1.1 200 OK', $response->status);
+ $this->assertEquals(200, $response->status);
}
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/PluginPropertiesTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/PluginPropertiesTest.php
index 8c0626e50..e5b7e1a3f 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/PluginPropertiesTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/PluginPropertiesTest.php
@@ -5,42 +5,37 @@ namespace Sabre\DAVACL;
use Sabre\DAV;
use Sabre\HTTP;
-
class PluginPropertiesTest extends \PHPUnit_Framework_TestCase {
function testPrincipalCollectionSet() {
$plugin = new Plugin();
- $plugin->principalCollectionSet = array(
+ $plugin->principalCollectionSet = [
'principals1',
'principals2',
- );
+ ];
- $requestedProperties = array(
+ $requestedProperties = [
'{DAV:}principal-collection-set',
- );
-
- $returnedProperties = array(
- 200 => array(),
- 404 => array(),
- );
+ ];
- $server = new DAV\Server();
+ $server = new DAV\Server(new DAV\SimpleCollection('root'));
$server->addPlugin($plugin);
- $this->assertNull($plugin->beforeGetProperties('', new DAV\SimpleCollection('root'), $requestedProperties, $returnedProperties));
+ $result = $server->getPropertiesForPath('', $requestedProperties);
+ $result = $result[0];
- $this->assertEquals(1,count($returnedProperties[200]));
- $this->assertArrayHasKey('{DAV:}principal-collection-set',$returnedProperties[200]);
- $this->assertInstanceOf('Sabre\\DAV\\Property\\HrefList', $returnedProperties[200]['{DAV:}principal-collection-set']);
+ $this->assertEquals(1,count($result[200]));
+ $this->assertArrayHasKey('{DAV:}principal-collection-set',$result[200]);
+ $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $result[200]['{DAV:}principal-collection-set']);
- $expected = array(
+ $expected = [
'principals1/',
'principals2/',
- );
+ ];
- $this->assertEquals($expected, $returnedProperties[200]['{DAV:}principal-collection-set']->getHrefs());
+ $this->assertEquals($expected, $result[200]['{DAV:}principal-collection-set']->getHrefs());
}
@@ -48,50 +43,35 @@ class PluginPropertiesTest extends \PHPUnit_Framework_TestCase {
function testCurrentUserPrincipal() {
$fakeServer = new DAV\Server();
- $plugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'realm');
+ $plugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock());
$fakeServer->addPlugin($plugin);
$plugin = new Plugin();
$fakeServer->addPlugin($plugin);
- $requestedProperties = array(
+ $requestedProperties = [
'{DAV:}current-user-principal',
- );
-
- $returnedProperties = array(
- 200 => array(),
- 404 => array(),
- );
+ ];
- $this->assertNull($plugin->beforeGetProperties('', new DAV\SimpleCollection('root'), $requestedProperties, $returnedProperties));
+ $result = $fakeServer->getPropertiesForPath('', $requestedProperties);
+ $result = $result[0];
- $this->assertEquals(1,count($returnedProperties[200]));
- $this->assertArrayHasKey('{DAV:}current-user-principal',$returnedProperties[200]);
- $this->assertInstanceOf('Sabre\DAVACL\Property\Principal', $returnedProperties[200]['{DAV:}current-user-principal']);
- $this->assertEquals(Property\Principal::UNAUTHENTICATED, $returnedProperties[200]['{DAV:}current-user-principal']->getType());
+ $this->assertEquals(1,count($result[200]));
+ $this->assertArrayHasKey('{DAV:}current-user-principal',$result[200]);
+ $this->assertInstanceOf('Sabre\DAVACL\Xml\Property\Principal', $result[200]['{DAV:}current-user-principal']);
+ $this->assertEquals(Xml\Property\Principal::UNAUTHENTICATED, $result[200]['{DAV:}current-user-principal']->getType());
// This will force the login
- $fakeServer->broadCastEvent('beforeMethod',array('GET',''));
-
-
- $requestedProperties = array(
- '{DAV:}current-user-principal',
- );
+ $fakeServer->emit('beforeMethod', [$fakeServer->httpRequest, $fakeServer->httpResponse]);
- $returnedProperties = array(
- 200 => array(),
- 404 => array(),
- );
+ $result = $fakeServer->getPropertiesForPath('', $requestedProperties);
+ $result = $result[0];
-
- $this->assertNull($plugin->beforeGetProperties('', new DAV\SimpleCollection('root'), $requestedProperties, $returnedProperties));
-
-
- $this->assertEquals(1,count($returnedProperties[200]));
- $this->assertArrayHasKey('{DAV:}current-user-principal',$returnedProperties[200]);
- $this->assertInstanceOf('Sabre\DAVACL\Property\Principal', $returnedProperties[200]['{DAV:}current-user-principal']);
- $this->assertEquals(Property\Principal::HREF, $returnedProperties[200]['{DAV:}current-user-principal']->getType());
- $this->assertEquals('principals/admin/', $returnedProperties[200]['{DAV:}current-user-principal']->getHref());
+ $this->assertEquals(1,count($result[200]));
+ $this->assertArrayHasKey('{DAV:}current-user-principal',$result[200]);
+ $this->assertInstanceOf('Sabre\DAVACL\Xml\Property\Principal', $result[200]['{DAV:}current-user-principal']);
+ $this->assertEquals(Xml\Property\Principal::HREF, $result[200]['{DAV:}current-user-principal']->getType());
+ $this->assertEquals('principals/admin/', $result[200]['{DAV:}current-user-principal']->getHref());
}
@@ -101,33 +81,23 @@ class PluginPropertiesTest extends \PHPUnit_Framework_TestCase {
$server = new DAV\Server();
$server->addPlugin($plugin);
- $requestedProperties = array(
+ $requestedProperties = [
'{DAV:}supported-privilege-set',
- );
+ ];
- $returnedProperties = array(
- 200 => array(),
- 404 => array(),
- );
+ $result = $server->getPropertiesForPath('', $requestedProperties);
+ $result = $result[0];
-
- $this->assertNull($plugin->beforeGetProperties('', new DAV\SimpleCollection('root'), $requestedProperties, $returnedProperties));
-
- $this->assertEquals(1,count($returnedProperties[200]));
- $this->assertArrayHasKey('{DAV:}supported-privilege-set',$returnedProperties[200]);
- $this->assertInstanceOf('Sabre\\DAVACL\\Property\\SupportedPrivilegeSet', $returnedProperties[200]['{DAV:}supported-privilege-set']);
+ $this->assertEquals(1,count($result[200]));
+ $this->assertArrayHasKey('{DAV:}supported-privilege-set',$result[200]);
+ $this->assertInstanceOf('Sabre\\DAVACL\\Xml\\Property\\SupportedPrivilegeSet', $result[200]['{DAV:}supported-privilege-set']);
$server = new DAV\Server();
- $prop = $returnedProperties[200]['{DAV:}supported-privilege-set'];
-
- $dom = new \DOMDocument('1.0', 'utf-8');
- $root = $dom->createElement('d:root');
- $root->setAttribute('xmlns:d','DAV:');
- $dom->appendChild($root);
- $prop->serialize($server, $root);
+ $prop = $result[200]['{DAV:}supported-privilege-set'];
+ $result = $server->xml->write('{DAV:}root', $prop);
- $xpaths = array(
+ $xpaths = [
'/d:root' => 1,
'/d:root/d:supported-privilege' => 1,
'/d:root/d:supported-privilege/d:privilege' => 1,
@@ -147,19 +117,19 @@ class PluginPropertiesTest extends \PHPUnit_Framework_TestCase {
'/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:bind' => 1,
'/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:unbind' => 1,
'/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:unlock' => 1,
- '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:abstract' => 8,
- );
+ '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:abstract' => 0,
+ ];
// reloading because php dom sucks
$dom2 = new \DOMDocument('1.0', 'utf-8');
- $dom2->loadXML($dom->saveXML());
+ $dom2->loadXML($result);
$dxpath = new \DOMXPath($dom2);
$dxpath->registerNamespace('d','DAV:');
foreach($xpaths as $xpath=>$count) {
- $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count);
+ $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count. ' Full XML: ' . $result);
}
@@ -169,42 +139,37 @@ class PluginPropertiesTest extends \PHPUnit_Framework_TestCase {
$plugin = new Plugin();
- $nodes = array(
- new MockACLNode('foo', array(
- array(
+ $nodes = [
+ new MockACLNode('foo', [
+ [
'principal' => 'principals/admin',
'privilege' => '{DAV:}read',
- )
- )),
- new DAV\SimpleCollection('principals', array(
+ ]
+ ]),
+ new DAV\SimpleCollection('principals', [
$principal = new MockPrincipal('admin','principals/admin'),
- )),
+ ]),
- );
+ ];
$server = new DAV\Server($nodes);
$server->addPlugin($plugin);
- $authPlugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'realm');
+ $authPlugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock());
$server->addPlugin($authPlugin);
// Force login
- $authPlugin->beforeMethod('BLA','foo');
+ $authPlugin->beforeMethod(new HTTP\Request(), new HTTP\Response());
- $requestedProperties = array(
+ $requestedProperties = [
'{DAV:}acl',
- );
+ ];
- $returnedProperties = array(
- 200 => array(),
- 404 => array(),
- );
+ $result = $server->getPropertiesForPath('foo', $requestedProperties);
+ $result = $result[0];
-
- $this->assertNull($plugin->beforeGetProperties('foo', $nodes[0], $requestedProperties, $returnedProperties));
-
- $this->assertEquals(1,count($returnedProperties[200]),'The {DAV:}acl property did not return from the list. Full list: ' . print_r($returnedProperties,true));
- $this->assertArrayHasKey('{DAV:}acl',$returnedProperties[200]);
- $this->assertInstanceOf('Sabre\\DAVACL\\Property\\ACL', $returnedProperties[200]['{DAV:}acl']);
+ $this->assertEquals(1,count($result[200]),'The {DAV:}acl property did not return from the list. Full list: ' . print_r($result, true));
+ $this->assertArrayHasKey('{DAV:}acl',$result[200]);
+ $this->assertInstanceOf('Sabre\\DAVACL\\Xml\Property\\Acl', $result[200]['{DAV:}acl']);
}
@@ -212,196 +177,176 @@ class PluginPropertiesTest extends \PHPUnit_Framework_TestCase {
$plugin = new Plugin();
- $nodes = array(
- new MockACLNode('foo', array(
- array(
+ $nodes = [
+ new MockACLNode('foo', [
+ [
'principal' => 'principals/admin',
'privilege' => '{DAV:}read',
- )
- )),
- new DAV\SimpleCollection('principals', array(
+ ]
+ ]),
+ new DAV\SimpleCollection('principals', [
$principal = new MockPrincipal('admin','principals/admin'),
- )),
+ ]),
- );
+ ];
$server = new DAV\Server($nodes);
$server->addPlugin($plugin);
- $authPlugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'realm');
+ $authPlugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock());
$server->addPlugin($authPlugin);
// Force login
- $authPlugin->beforeMethod('BLA','foo');
+ $authPlugin->beforeMethod(new HTTP\Request(), new HTTP\Response());
- $requestedProperties = array(
+ $requestedProperties = [
'{DAV:}acl-restrictions',
- );
-
- $returnedProperties = array(
- 200 => array(),
- 404 => array(),
- );
+ ];
+ $result = $server->getPropertiesForPath('foo', $requestedProperties);
+ $result = $result[0];
- $this->assertNull($plugin->beforeGetProperties('foo', $nodes[0], $requestedProperties, $returnedProperties));
-
- $this->assertEquals(1,count($returnedProperties[200]),'The {DAV:}acl-restrictions property did not return from the list. Full list: ' . print_r($returnedProperties,true));
- $this->assertArrayHasKey('{DAV:}acl-restrictions',$returnedProperties[200]);
- $this->assertInstanceOf('Sabre\\DAVACL\\Property\\ACLRestrictions', $returnedProperties[200]['{DAV:}acl-restrictions']);
+ $this->assertEquals(1,count($result[200]),'The {DAV:}acl-restrictions property did not return from the list. Full list: ' . print_r($result, true));
+ $this->assertArrayHasKey('{DAV:}acl-restrictions',$result[200]);
+ $this->assertInstanceOf('Sabre\\DAVACL\\Xml\\Property\\AclRestrictions', $result[200]['{DAV:}acl-restrictions']);
}
function testAlternateUriSet() {
- $tree = array(
- new DAV\SimpleCollection('principals', array(
+ $tree = [
+ new DAV\SimpleCollection('principals', [
$principal = new MockPrincipal('user','principals/user'),
- )),
- );
+ ])
+ ];
$fakeServer = new DAV\Server($tree);
- //$plugin = new DAV\Auth\Plugin(new DAV\Auth\MockBackend(),'realm');
+ //$plugin = new DAV\Auth\Plugin(new DAV\Auth\MockBackend())
//$fakeServer->addPlugin($plugin);
$plugin = new Plugin();
$fakeServer->addPlugin($plugin);
- $requestedProperties = array(
+ $requestedProperties = [
'{DAV:}alternate-URI-set',
- );
- $returnedProperties = array();
-
- $result = $plugin->beforeGetProperties('principals/user',$principal,$requestedProperties,$returnedProperties);
-
- $this->assertNull($result);
+ ];
+ $result = $fakeServer->getPropertiesForPath('principals/user', $requestedProperties);
+ $result = $result[0];
- $this->assertTrue(isset($returnedProperties[200]));
- $this->assertTrue(isset($returnedProperties[200]['{DAV:}alternate-URI-set']));
- $this->assertInstanceOf('Sabre\\DAV\\Property\\HrefList', $returnedProperties[200]['{DAV:}alternate-URI-set']);
+ $this->assertTrue(isset($result[200]));
+ $this->assertTrue(isset($result[200]['{DAV:}alternate-URI-set']));
+ $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $result[200]['{DAV:}alternate-URI-set']);
- $this->assertEquals(array(), $returnedProperties[200]['{DAV:}alternate-URI-set']->getHrefs());
+ $this->assertEquals([], $result[200]['{DAV:}alternate-URI-set']->getHrefs());
}
function testPrincipalURL() {
- $tree = array(
- new DAV\SimpleCollection('principals', array(
+ $tree = [
+ new DAV\SimpleCollection('principals', [
$principal = new MockPrincipal('user','principals/user'),
- )),
- );
+ ]),
+ ];
$fakeServer = new DAV\Server($tree);
- //$plugin = new DAV\Auth\Plugin(new DAV\Auth\MockBackend(),'realm');
+ //$plugin = new DAV\Auth\Plugin(new DAV\Auth\MockBackend());
//$fakeServer->addPlugin($plugin);
$plugin = new Plugin();
$fakeServer->addPlugin($plugin);
- $requestedProperties = array(
+ $requestedProperties = [
'{DAV:}principal-URL',
- );
- $returnedProperties = array();
+ ];
- $result = $plugin->beforeGetProperties('principals/user',$principal,$requestedProperties,$returnedProperties);
+ $result = $fakeServer->getPropertiesForPath('principals/user', $requestedProperties);
+ $result = $result[0];
- $this->assertNull($result);
+ $this->assertTrue(isset($result[200]));
+ $this->assertTrue(isset($result[200]['{DAV:}principal-URL']));
+ $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $result[200]['{DAV:}principal-URL']);
- $this->assertTrue(isset($returnedProperties[200]));
- $this->assertTrue(isset($returnedProperties[200]['{DAV:}principal-URL']));
- $this->assertInstanceOf('Sabre\\DAV\\Property\\Href', $returnedProperties[200]['{DAV:}principal-URL']);
-
- $this->assertEquals('principals/user/', $returnedProperties[200]['{DAV:}principal-URL']->getHref());
+ $this->assertEquals('principals/user/', $result[200]['{DAV:}principal-URL']->getHref());
}
function testGroupMemberSet() {
- $tree = array(
- new DAV\SimpleCollection('principals', array(
+ $tree = [
+ new DAV\SimpleCollection('principals', [
$principal = new MockPrincipal('user','principals/user'),
- )),
- );
+ ]),
+ ];
$fakeServer = new DAV\Server($tree);
- //$plugin = new DAV\Auth\Plugin(new DAV\Auth\MockBackend(),'realm');
+ //$plugin = new DAV\Auth\Plugin(new DAV\Auth\MockBackend());
//$fakeServer->addPlugin($plugin);
$plugin = new Plugin();
$fakeServer->addPlugin($plugin);
- $requestedProperties = array(
+ $requestedProperties = [
'{DAV:}group-member-set',
- );
- $returnedProperties = array();
-
- $result = $plugin->beforeGetProperties('principals/user',$principal,$requestedProperties,$returnedProperties);
+ ];
- $this->assertNull($result);
+ $result = $fakeServer->getPropertiesForPath('principals/user', $requestedProperties);
+ $result = $result[0];
- $this->assertTrue(isset($returnedProperties[200]));
- $this->assertTrue(isset($returnedProperties[200]['{DAV:}group-member-set']));
- $this->assertInstanceOf('Sabre\\DAV\\Property\\HrefList', $returnedProperties[200]['{DAV:}group-member-set']);
+ $this->assertTrue(isset($result[200]));
+ $this->assertTrue(isset($result[200]['{DAV:}group-member-set']));
+ $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $result[200]['{DAV:}group-member-set']);
- $this->assertEquals(array(), $returnedProperties[200]['{DAV:}group-member-set']->getHrefs());
+ $this->assertEquals([], $result[200]['{DAV:}group-member-set']->getHrefs());
}
function testGroupMemberShip() {
- $tree = array(
- new DAV\SimpleCollection('principals', array(
+ $tree = [
+ new DAV\SimpleCollection('principals', [
$principal = new MockPrincipal('user','principals/user'),
- )),
- );
+ ]),
+ ];
$fakeServer = new DAV\Server($tree);
- //$plugin = new DAV\Auth\Plugin(new DAV\Auth\MockBackend(),'realm');
- //$fakeServer->addPlugin($plugin);
$plugin = new Plugin();
$fakeServer->addPlugin($plugin);
- $requestedProperties = array(
+ $requestedProperties = [
'{DAV:}group-membership',
- );
- $returnedProperties = array();
+ ];
- $result = $plugin->beforeGetProperties('principals/user',$principal,$requestedProperties,$returnedProperties);
+ $result = $fakeServer->getPropertiesForPath('principals/user', $requestedProperties);
+ $result = $result[0];
- $this->assertNull($result);
+ $this->assertTrue(isset($result[200]));
+ $this->assertTrue(isset($result[200]['{DAV:}group-membership']));
+ $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $result[200]['{DAV:}group-membership']);
- $this->assertTrue(isset($returnedProperties[200]));
- $this->assertTrue(isset($returnedProperties[200]['{DAV:}group-membership']));
- $this->assertInstanceOf('Sabre\\DAV\\Property\\HrefList', $returnedProperties[200]['{DAV:}group-membership']);
-
- $this->assertEquals(array(), $returnedProperties[200]['{DAV:}group-membership']->getHrefs());
+ $this->assertEquals([], $result[200]['{DAV:}group-membership']->getHrefs());
}
function testGetDisplayName() {
- $tree = array(
- new DAV\SimpleCollection('principals', array(
+ $tree = [
+ new DAV\SimpleCollection('principals', [
$principal = new MockPrincipal('user','principals/user'),
- )),
- );
+ ]),
+ ];
$fakeServer = new DAV\Server($tree);
- //$plugin = new DAV\Auth\Plugin(new DAV\Auth\MockBackend(),'realm');
- //$fakeServer->addPlugin($plugin);
$plugin = new Plugin();
$fakeServer->addPlugin($plugin);
- $requestedProperties = array(
+ $requestedProperties = [
'{DAV:}displayname',
- );
- $returnedProperties = array();
-
- $result = $plugin->beforeGetProperties('principals/user',$principal,$requestedProperties,$returnedProperties);
+ ];
- $this->assertNull($result);
+ $result = $fakeServer->getPropertiesForPath('principals/user', $requestedProperties);
+ $result = $result[0];
- $this->assertTrue(isset($returnedProperties[200]));
- $this->assertTrue(isset($returnedProperties[200]['{DAV:}displayname']));
+ $this->assertTrue(isset($result[200]));
+ $this->assertTrue(isset($result[200]['{DAV:}displayname']));
- $this->assertEquals('user', $returnedProperties[200]['{DAV:}displayname']);
+ $this->assertEquals('user', $result[200]['{DAV:}displayname']);
}
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/PluginUpdatePropertiesTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/PluginUpdatePropertiesTest.php
index 53568654f..64cedd142 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/PluginUpdatePropertiesTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/PluginUpdatePropertiesTest.php
@@ -10,7 +10,7 @@ require_once 'Sabre/DAVACL/MockPrincipal.php';
class PluginUpdatePropertiesTest extends \PHPUnit_Framework_TestCase {
- public function testUpdatePropertiesPassthrough() {
+ function testUpdatePropertiesPassthrough() {
$tree = array(
new DAV\SimpleCollection('foo'),
@@ -23,17 +23,14 @@ class PluginUpdatePropertiesTest extends \PHPUnit_Framework_TestCase {
));
$expected = array(
- 'href' => 'foo',
- '403' => array(
- '{DAV:}foo' => null,
- ),
+ '{DAV:}foo' => 403,
);
$this->assertEquals($expected, $result);
}
- public function testRemoveGroupMembers() {
+ function testRemoveGroupMembers() {
$tree = array(
new MockPrincipal('foo','foo'),
@@ -46,10 +43,7 @@ class PluginUpdatePropertiesTest extends \PHPUnit_Framework_TestCase {
));
$expected = array(
- 'href' => 'foo',
- '200' => array(
- '{DAV:}group-member-set' => null,
- ),
+ '{DAV:}group-member-set' => 204
);
$this->assertEquals($expected, $result);
@@ -57,34 +51,31 @@ class PluginUpdatePropertiesTest extends \PHPUnit_Framework_TestCase {
}
- public function testSetGroupMembers() {
+ function testSetGroupMembers() {
- $tree = array(
+ $tree = [
new MockPrincipal('foo','foo'),
- );
+ ];
$server = new DAV\Server($tree);
$server->addPlugin(new Plugin());
- $result = $server->updateProperties('foo', array(
- '{DAV:}group-member-set' => new DAV\Property\HrefList(array('/bar','/baz'), true),
- ));
+ $result = $server->updateProperties('foo', [
+ '{DAV:}group-member-set' => new DAV\Xml\Property\Href(['/bar','/baz'], true),
+ ]);
- $expected = array(
- 'href' => 'foo',
- '200' => array(
- '{DAV:}group-member-set' => null,
- ),
- );
+ $expected = [
+ '{DAV:}group-member-set' => 200
+ ];
$this->assertEquals($expected, $result);
- $this->assertEquals(array('bar','baz'),$tree[0]->getGroupMemberSet());
+ $this->assertEquals(['bar', 'baz'],$tree[0]->getGroupMemberSet());
}
/**
* @expectedException Sabre\DAV\Exception
*/
- public function testSetBadValue() {
+ function testSetBadValue() {
$tree = array(
new MockPrincipal('foo','foo'),
@@ -98,28 +89,21 @@ class PluginUpdatePropertiesTest extends \PHPUnit_Framework_TestCase {
}
- public function testSetBadNode() {
+ function testSetBadNode() {
- $tree = array(
+ $tree = [
new DAV\SimpleCollection('foo'),
- );
+ ];
$server = new DAV\Server($tree);
$server->addPlugin(new Plugin());
- $result = $server->updateProperties('foo', array(
- '{DAV:}group-member-set' => new DAV\Property\HrefList(array('/bar','/baz'),false),
- '{DAV:}bar' => 'baz',
- ));
+ $result = $server->updateProperties('foo', [
+ '{DAV:}group-member-set' => new DAV\Xml\Property\Href(['/bar','/baz'],false),
+ ]);
- $expected = array(
- 'href' => 'foo',
- '403' => array(
- '{DAV:}group-member-set' => null,
- ),
- '424' => array(
- '{DAV:}bar' => null,
- ),
- );
+ $expected = [
+ '{DAV:}group-member-set' => 403,
+ ];
$this->assertEquals($expected, $result);
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/AbstractPDOTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/AbstractPDOTest.php
index 3fe75ca0e..3814ebc0d 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/AbstractPDOTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/AbstractPDOTest.php
@@ -128,10 +128,12 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
$pdo = $this->getPDO();
$backend = new PDO($pdo);
- $result = $backend->updatePrincipal('principals/user', array(
+ $propPatch = new DAV\PropPatch([
'{DAV:}displayname' => 'pietje',
- '{http://sabredav.org/ns}vcard-url' => 'blabla',
- ));
+ ]);
+
+ $backend->updatePrincipal('principals/user', $propPatch);
+ $result = $propPatch->commit();
$this->assertTrue($result);
@@ -139,7 +141,6 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
'id' => 1,
'uri' => 'principals/user',
'{DAV:}displayname' => 'pietje',
- '{http://sabredav.org/ns}vcard-url' => 'blabla',
'{http://sabredav.org/ns}email-address' => 'user@example.org',
), $backend->getPrincipalByPath('principals/user'));
@@ -150,21 +151,20 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
$pdo = $this->getPDO();
$backend = new PDO($pdo);
- $result = $backend->updatePrincipal('principals/user', array(
+ $propPatch = new DAV\PropPatch([
'{DAV:}displayname' => 'pietje',
- '{http://sabredav.org/ns}vcard-url' => 'blabla',
'{DAV:}unknown' => 'foo',
- ));
+ ]);
+
+ $backend->updatePrincipal('principals/user', $propPatch);
+ $result = $propPatch->commit();
+
+ $this->assertFalse($result);
$this->assertEquals(array(
- 424 => array(
- '{DAV:}displayname' => null,
- '{http://sabredav.org/ns}vcard-url' => null,
- ),
- 403 => array(
- '{DAV:}unknown' => null,
- ),
- ), $result);
+ '{DAV:}displayname' => 424,
+ '{DAV:}unknown' => 403
+ ), $propPatch->getResult());
$this->assertEquals(array(
'id' => '1',
@@ -175,4 +175,24 @@ abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase {
}
+ function testFindByUriUnknownScheme() {
+
+ $pdo = $this->getPDO();
+ $backend = new PDO($pdo);
+ $this->assertNull($backend->findByUri('http://foo', 'principals'));
+
+ }
+
+
+ function testFindByUri() {
+
+ $pdo = $this->getPDO();
+ $backend = new PDO($pdo);
+ $this->assertEquals(
+ 'principals/user',
+ $backend->findByUri('mailto:user@example.org', 'principals')
+ );
+
+ }
+
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/Mock.php b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/Mock.php
index 354446e34..afb094a39 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/Mock.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/Mock.php
@@ -4,40 +4,46 @@ namespace Sabre\DAVACL\PrincipalBackend;
class Mock extends AbstractBackend {
- public $groupMembers = array();
+ public $groupMembers = [];
public $principals;
- function __construct() {
+ function __construct(array $principals = null) {
- $this->principals = array(
- array(
- 'uri' => 'principals/user1',
- '{DAV:}displayname' => 'User 1',
+ $this->principals = $principals;
+
+ if (is_null($principals)) {
+
+ $this->principals = [
+ [
+ 'uri' => 'principals/user1',
+ '{DAV:}displayname' => 'User 1',
'{http://sabredav.org/ns}email-address' => 'user1.sabredav@sabredav.org',
- '{http://sabredav.org/ns}vcard-url' => 'addressbooks/user1/book1/vcard1.vcf',
- ),
- array(
- 'uri' => 'principals/admin',
+ '{http://sabredav.org/ns}vcard-url' => 'addressbooks/user1/book1/vcard1.vcf',
+ ],
+ [
+ 'uri' => 'principals/admin',
'{DAV:}displayname' => 'Admin',
- ),
- array(
- 'uri' => 'principals/user2',
- '{DAV:}displayname' => 'User 2',
+ ],
+ [
+ 'uri' => 'principals/user2',
+ '{DAV:}displayname' => 'User 2',
'{http://sabredav.org/ns}email-address' => 'user2.sabredav@sabredav.org',
- ),
- );
+ ],
+ ];
+ }
}
function getPrincipalsByPrefix($prefix) {
- $prefix = trim($prefix,'/') . '/';
- $return = array();
+ $prefix = trim($prefix, '/');
+ if ($prefix) $prefix .= '/';
+ $return = [];
- foreach($this->principals as $principal) {
+ foreach ($this->principals as $principal) {
- if (strpos($principal['uri'], $prefix)!==0) continue;
+ if ($prefix && strpos($principal['uri'], $prefix) !== 0) continue;
$return[] = $principal;
@@ -55,26 +61,33 @@ class Mock extends AbstractBackend {
function getPrincipalByPath($path) {
- foreach($this->getPrincipalsByPrefix('principals') as $principal) {
+ foreach ($this->getPrincipalsByPrefix('principals') as $principal) {
if ($principal['uri'] === $path) return $principal;
}
}
- function searchPrincipals($prefixPath, array $searchProperties) {
+ function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
- $matches = array();
- foreach($this->getPrincipalsByPrefix($prefixPath) as $principal) {
+ $matches = [];
+ foreach ($this->getPrincipalsByPrefix($prefixPath) as $principal) {
- foreach($searchProperties as $key=>$value) {
+ foreach ($searchProperties as $key => $value) {
if (!isset($principal[$key])) {
continue 2;
}
- if (mb_stripos($principal[$key],$value, 0, 'UTF-8')===false) {
+ if (mb_stripos($principal[$key], $value, 0, 'UTF-8') === false) {
continue 2;
}
+ // We have a match for this searchProperty!
+ if ($test === 'allof') {
+ continue;
+ } else {
+ break;
+ }
+
}
$matches[] = $principal['uri'];
@@ -85,14 +98,14 @@ class Mock extends AbstractBackend {
function getGroupMemberSet($path) {
- return isset($this->groupMembers[$path]) ? $this->groupMembers[$path] : array();
+ return isset($this->groupMembers[$path]) ? $this->groupMembers[$path] : [];
}
function getGroupMembership($path) {
- $membership = array();
- foreach($this->groupMembers as $group=>$members) {
+ $membership = [];
+ foreach ($this->groupMembers as $group => $members) {
if (in_array($path, $members)) $membership[] = $group;
}
return $membership;
@@ -108,75 +121,46 @@ class Mock extends AbstractBackend {
/**
* Updates one ore more webdav properties on a principal.
*
- * The list of mutations is supplied as an array. Each key in the array is
- * a propertyname, such as {DAV:}displayname.
- *
- * Each value is the actual value to be updated. If a value is null, it
- * must be deleted.
- *
- * This method should be atomic. It must either completely succeed, or
- * completely fail. Success and failure can simply be returned as 'true' or
- * 'false'.
- *
- * It is also possible to return detailed failure information. In that case
- * an array such as this should be returned:
+ * The list of mutations is stored in a Sabre\DAV\PropPatch object.
+ * To do the actual updates, you must tell this object which properties
+ * you're going to process with the handle() method.
*
- * array(
- * 200 => array(
- * '{DAV:}prop1' => null,
- * ),
- * 201 => array(
- * '{DAV:}prop2' => null,
- * ),
- * 403 => array(
- * '{DAV:}prop3' => null,
- * ),
- * 424 => array(
- * '{DAV:}prop4' => null,
- * ),
- * );
+ * Calling the handle method is like telling the PropPatch object "I
+ * promise I can handle updating this property".
*
- * In this previous example prop1 was successfully updated or deleted, and
- * prop2 was succesfully created.
- *
- * prop3 failed to update due to '403 Forbidden' and because of this prop4
- * also could not be updated with '424 Failed dependency'.
- *
- * This last example was actually incorrect. While 200 and 201 could appear
- * in 1 response, if there's any error (403) the other properties should
- * always fail with 423 (failed dependency).
- *
- * But anyway, if you don't want to scratch your head over this, just
- * return true or false.
+ * Read the PropPatch documenation for more info and examples.
*
* @param string $path
- * @param array $mutations
- * @return array|bool
+ * @param \Sabre\DAV\PropPatch $propPatch
*/
- public function updatePrincipal($path, $mutations) {
+ function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) {
$value = null;
- foreach($this->principals as $principalIndex=>$value) {
+ foreach ($this->principals as $principalIndex => $value) {
if ($value['uri'] === $path) {
$principal = $value;
break;
}
}
- if (!$principal) return false;
+ if (!$principal) return;
+
+ $propPatch->handleRemaining(function($mutations) use ($principal, $principalIndex) {
- foreach($mutations as $prop=>$value) {
+ foreach ($mutations as $prop => $value) {
+
+ if (is_null($value) && isset($principal[$prop])) {
+ unset($principal[$prop]);
+ } else {
+ $principal[$prop] = $value;
+ }
- if (is_null($value) && isset($principal[$prop])) {
- unset($principal[$prop]);
- } else {
- $principal[$prop] = $value;
}
- }
+ $this->principals[$principalIndex] = $principal;
- $this->principals[$principalIndex] = $principal;
+ return true;
- return true;
+ });
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOMySQLTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOMySQLTest.php
index 84ba062ca..83353c86c 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOMySQLTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOMySQLTest.php
@@ -16,25 +16,30 @@ class PDOMySQLTest extends AbstractPDOTest {
$pdo = \Sabre\TestUtil::getMySQLDB();
if (!$pdo) $this->markTestSkipped('Could not connect to MySQL database');
$pdo->query("DROP TABLE IF EXISTS principals");
- $pdo->query("
+ $pdo->query(<<<SQL
create table principals (
- id integer unsigned not null primary key auto_increment,
- uri varchar(50),
- email varchar(80),
- displayname VARCHAR(80),
- vcardurl VARCHAR(80),
- unique(uri)
-);");
+ id integer unsigned not null primary key auto_increment,
+ uri varchar(50),
+ email varchar(80),
+ displayname VARCHAR(80),
+ vcardurl VARCHAR(80),
+ unique(uri)
+)
+SQL
+ );
$pdo->query("INSERT INTO principals (uri,email,displayname) VALUES ('principals/user','user@example.org','User')");
$pdo->query("INSERT INTO principals (uri,email,displayname) VALUES ('principals/group','group@example.org','Group')");
$pdo->query("DROP TABLE IF EXISTS groupmembers");
- $pdo->query("CREATE TABLE groupmembers (
- id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
- principal_id INTEGER UNSIGNED NOT NULL,
- member_id INTEGER UNSIGNED NOT NULL,
- UNIQUE(principal_id, member_id)
- );");
+ $pdo->query(<<<SQL
+CREATE TABLE groupmembers (
+ id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ principal_id INTEGER UNSIGNED NOT NULL,
+ member_id INTEGER UNSIGNED NOT NULL,
+ UNIQUE(principal_id, member_id)
+)
+SQL
+ );
$pdo->query("INSERT INTO groupmembers (principal_id,member_id) VALUES (2,1)");
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOSqliteTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOSqliteTest.php
index 192e188f9..f335ed51f 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOSqliteTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOSqliteTest.php
@@ -22,16 +22,19 @@ class PDOSQLiteTest extends AbstractPDOTest {
if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available');
$pdo = new \PDO('sqlite:'.SABRE_TEMPDIR.'/pdobackend');
$pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION);
- $pdo->query('CREATE TABLE principals (id INTEGER PRIMARY KEY ASC, uri TEXT, email VARCHAR(80), displayname VARCHAR(80), vcardurl VARCHAR(80))');
- $pdo->query('INSERT INTO principals VALUES (1, "principals/user","user@example.org","User",null)');
- $pdo->query('INSERT INTO principals VALUES (2, "principals/group","group@example.org","Group",null)');
-
- $pdo->query("CREATE TABLE groupmembers (
- id INTEGER PRIMARY KEY ASC,
- principal_id INT,
- member_id INT,
- UNIQUE(principal_id, member_id)
- );");
+ $pdo->query('CREATE TABLE principals (id INTEGER PRIMARY KEY ASC, uri TEXT, email VARCHAR(80), displayname VARCHAR(80))');
+ $pdo->query('INSERT INTO principals VALUES (1, "principals/user","user@example.org","User")');
+ $pdo->query('INSERT INTO principals VALUES (2, "principals/group","group@example.org","Group")');
+
+ $pdo->query(<<<SQL
+CREATE TABLE groupmembers (
+ id INTEGER PRIMARY KEY ASC,
+ principal_id INT,
+ member_id INT,
+ UNIQUE(principal_id, member_id)
+)
+SQL
+ );
$pdo->query("INSERT INTO groupmembers (principal_id,member_id) VALUES (2,1)");
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalCollectionTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalCollectionTest.php
index 10b0c04da..f51d2dcce 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalCollectionTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalCollectionTest.php
@@ -49,4 +49,13 @@ class PrincipalCollectionTest extends \PHPUnit_Framework_TestCase {
}
+ public function testFindByUri() {
+
+ $backend = new PrincipalBackend\Mock();
+ $pc = new PrincipalCollection($backend);
+ $this->assertEquals('principals/user1', $pc->findByUri('mailto:user1.sabredav@sabredav.org'));
+ $this->assertNull($pc->findByUri('mailto:fake.user.sabredav@sabredav.org'));
+ $this->assertNull($pc->findByUri(''));
+ }
+
}
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalPropertySearchTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalPropertySearchTest.php
index 9c3be4f9a..8e4c86782 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalPropertySearchTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalPropertySearchTest.php
@@ -17,10 +17,11 @@ class PrincipalPropertySearchTest extends \PHPUnit_Framework_TestCase {
$principals = new PrincipalCollection($backend);
$dir->addChild($principals);
- $fakeServer = new DAV\Server(new DAV\ObjectTree($dir));
+ $fakeServer = new DAV\Server($dir);
+ $fakeServer->sapi = new HTTP\SapiMock();
$fakeServer->httpResponse = new HTTP\ResponseMock();
$fakeServer->debugExceptions = true;
- $plugin = new MockPlugin($backend,'realm');
+ $plugin = new MockPlugin();
$plugin->allowAccessToNodesWithoutACL = true;
$this->assertTrue($plugin instanceof Plugin);
@@ -53,7 +54,7 @@ class PrincipalPropertySearchTest extends \PHPUnit_Framework_TestCase {
'REQUEST_URI' => '/principals',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody($xml);
$server = $this->getServer();
@@ -61,10 +62,11 @@ class PrincipalPropertySearchTest extends \PHPUnit_Framework_TestCase {
$server->exec();
- $this->assertEquals('HTTP/1.1 400 Bad request', $server->httpResponse->status);
+ $this->assertEquals(400, $server->httpResponse->getStatus(), $server->httpResponse->getBodyAsString());
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ), $server->httpResponse->headers);
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ), $server->httpResponse->getHeaders());
}
@@ -91,7 +93,7 @@ class PrincipalPropertySearchTest extends \PHPUnit_Framework_TestCase {
'REQUEST_URI' => '/principals',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody($xml);
$server = $this->getServer();
@@ -99,11 +101,12 @@ class PrincipalPropertySearchTest extends \PHPUnit_Framework_TestCase {
$server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status', $server->httpResponse->status);
+ $this->assertEquals(207, $server->httpResponse->getStatus(), "Full body: " . $server->httpResponse->getBodyAsString());
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- 'Vary' => 'Brief,Prefer',
- ), $server->httpResponse->headers);
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ 'Vary' => ['Brief,Prefer'],
+ ), $server->httpResponse->getHeaders());
}
@@ -130,7 +133,7 @@ class PrincipalPropertySearchTest extends \PHPUnit_Framework_TestCase {
'REQUEST_URI' => '/',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody($xml);
$server = $this->getServer();
@@ -138,11 +141,157 @@ class PrincipalPropertySearchTest extends \PHPUnit_Framework_TestCase {
$server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status', $server->httpResponse->status, $server->httpResponse->body);
+ $this->assertEquals(207, $server->httpResponse->status, $server->httpResponse->body);
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- 'Vary' => 'Brief,Prefer',
- ), $server->httpResponse->headers);
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ 'Vary' => ['Brief,Prefer'],
+ ), $server->httpResponse->getHeaders());
+
+
+ $check = array(
+ '/d:multistatus',
+ '/d:multistatus/d:response' => 2,
+ '/d:multistatus/d:response/d:href' => 2,
+ '/d:multistatus/d:response/d:propstat' => 4,
+ '/d:multistatus/d:response/d:propstat/d:prop' => 4,
+ '/d:multistatus/d:response/d:propstat/d:prop/d:displayname' => 2,
+ '/d:multistatus/d:response/d:propstat/d:prop/d:getcontentlength' => 2,
+ '/d:multistatus/d:response/d:propstat/d:status' => 4,
+ );
+
+ $xml = simplexml_load_string($server->httpResponse->body);
+ $xml->registerXPathNamespace('d','DAV:');
+ foreach($check as $v1=>$v2) {
+
+ $xpath = is_int($v1)?$v2:$v1;
+
+ $result = $xml->xpath($xpath);
+
+ $count = 1;
+ if (!is_int($v1)) $count = $v2;
+
+ $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response body: ' . $server->httpResponse->body);
+
+ }
+
+ }
+
+ function testAND() {
+
+ $xml = '<?xml version="1.0"?>
+<d:principal-property-search xmlns:d="DAV:">
+ <d:apply-to-principal-collection-set />
+ <d:property-search>
+ <d:prop>
+ <d:displayname />
+ </d:prop>
+ <d:match>user</d:match>
+ </d:property-search>
+ <d:property-search>
+ <d:prop>
+ <d:foo />
+ </d:prop>
+ <d:match>bar</d:match>
+ </d:property-search>
+ <d:prop>
+ <d:displayname />
+ <d:getcontentlength />
+ </d:prop>
+</d:principal-property-search>';
+
+ $serverVars = array(
+ 'REQUEST_METHOD' => 'REPORT',
+ 'HTTP_DEPTH' => '0',
+ 'REQUEST_URI' => '/',
+ );
+
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
+ $request->setBody($xml);
+
+ $server = $this->getServer();
+ $server->httpRequest = $request;
+
+ $server->exec();
+
+ $this->assertEquals(207, $server->httpResponse->status, $server->httpResponse->body);
+ $this->assertEquals(array(
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ 'Vary' => ['Brief,Prefer'],
+ ), $server->httpResponse->getHeaders());
+
+
+ $check = array(
+ '/d:multistatus',
+ '/d:multistatus/d:response' => 0,
+ '/d:multistatus/d:response/d:href' => 0,
+ '/d:multistatus/d:response/d:propstat' => 0,
+ '/d:multistatus/d:response/d:propstat/d:prop' => 0,
+ '/d:multistatus/d:response/d:propstat/d:prop/d:displayname' => 0,
+ '/d:multistatus/d:response/d:propstat/d:prop/d:getcontentlength' => 0,
+ '/d:multistatus/d:response/d:propstat/d:status' => 0,
+ );
+
+ $xml = simplexml_load_string($server->httpResponse->body);
+ $xml->registerXPathNamespace('d','DAV:');
+ foreach($check as $v1=>$v2) {
+
+ $xpath = is_int($v1)?$v2:$v1;
+
+ $result = $xml->xpath($xpath);
+
+ $count = 1;
+ if (!is_int($v1)) $count = $v2;
+
+ $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response body: ' . $server->httpResponse->body);
+
+ }
+
+ }
+ function testOR() {
+
+ $xml = '<?xml version="1.0"?>
+<d:principal-property-search xmlns:d="DAV:" test="anyof">
+ <d:apply-to-principal-collection-set />
+ <d:property-search>
+ <d:prop>
+ <d:displayname />
+ </d:prop>
+ <d:match>user</d:match>
+ </d:property-search>
+ <d:property-search>
+ <d:prop>
+ <d:foo />
+ </d:prop>
+ <d:match>bar</d:match>
+ </d:property-search>
+ <d:prop>
+ <d:displayname />
+ <d:getcontentlength />
+ </d:prop>
+</d:principal-property-search>';
+
+ $serverVars = array(
+ 'REQUEST_METHOD' => 'REPORT',
+ 'HTTP_DEPTH' => '0',
+ 'REQUEST_URI' => '/',
+ );
+
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
+ $request->setBody($xml);
+
+ $server = $this->getServer();
+ $server->httpRequest = $request;
+
+ $server->exec();
+
+ $this->assertEquals(207, $server->httpResponse->status, $server->httpResponse->body);
+ $this->assertEquals(array(
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ 'Vary' => ['Brief,Prefer'],
+ ), $server->httpResponse->getHeaders());
$check = array(
@@ -194,7 +343,7 @@ class PrincipalPropertySearchTest extends \PHPUnit_Framework_TestCase {
'REQUEST_URI' => '/',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody($xml);
$server = $this->getServer();
@@ -202,11 +351,12 @@ class PrincipalPropertySearchTest extends \PHPUnit_Framework_TestCase {
$server->exec();
- $this->assertEquals('HTTP/1.1 207 Multi-Status', $server->httpResponse->status, $server->httpResponse->body);
+ $this->assertEquals(207, $server->httpResponse->status, $server->httpResponse->body);
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- 'Vary' => 'Brief,Prefer',
- ), $server->httpResponse->headers);
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ 'Vary' => ['Brief,Prefer'],
+ ), $server->httpResponse->getHeaders());
$check = array(
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalSearchPropertySetTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalSearchPropertySetTest.php
index 412389e8b..952dc174a 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalSearchPropertySetTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalSearchPropertySetTest.php
@@ -17,9 +17,10 @@ class PrincipalSearchPropertySetTest extends \PHPUnit_Framework_TestCase {
$principals = new PrincipalCollection($backend);
$dir->addChild($principals);
- $fakeServer = new DAV\Server(new DAV\ObjectTree($dir));
+ $fakeServer = new DAV\Server($dir);
+ $fakeServer->sapi = new HTTP\SapiMock();
$fakeServer->httpResponse = new HTTP\ResponseMock();
- $plugin = new Plugin($backend,'realm');
+ $plugin = new Plugin();
$this->assertTrue($plugin instanceof Plugin);
$fakeServer->addPlugin($plugin);
$this->assertEquals($plugin, $fakeServer->getPlugin('acl'));
@@ -39,7 +40,7 @@ class PrincipalSearchPropertySetTest extends \PHPUnit_Framework_TestCase {
'REQUEST_URI' => '/principals',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody($xml);
$server = $this->getServer();
@@ -47,10 +48,11 @@ class PrincipalSearchPropertySetTest extends \PHPUnit_Framework_TestCase {
$server->exec();
- $this->assertEquals('HTTP/1.1 400 Bad request', $server->httpResponse->status);
+ $this->assertEquals(400, $server->httpResponse->status);
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ), $server->httpResponse->headers);
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ), $server->httpResponse->getHeaders());
}
@@ -65,7 +67,7 @@ class PrincipalSearchPropertySetTest extends \PHPUnit_Framework_TestCase {
'REQUEST_URI' => '/principals',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody($xml);
$server = $this->getServer();
@@ -73,10 +75,11 @@ class PrincipalSearchPropertySetTest extends \PHPUnit_Framework_TestCase {
$server->exec();
- $this->assertEquals('HTTP/1.1 400 Bad request', $server->httpResponse->status, $server->httpResponse->body);
+ $this->assertEquals(400, $server->httpResponse->status, $server->httpResponse->body);
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ), $server->httpResponse->headers);
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ), $server->httpResponse->getHeaders());
}
@@ -91,7 +94,7 @@ class PrincipalSearchPropertySetTest extends \PHPUnit_Framework_TestCase {
'REQUEST_URI' => '/principals',
);
- $request = new HTTP\Request($serverVars);
+ $request = HTTP\Sapi::createFromServerArray($serverVars);
$request->setBody($xml);
$server = $this->getServer();
@@ -99,10 +102,11 @@ class PrincipalSearchPropertySetTest extends \PHPUnit_Framework_TestCase {
$server->exec();
- $this->assertEquals('HTTP/1.1 200 OK', $server->httpResponse->status, $server->httpResponse->body);
+ $this->assertEquals(200, $server->httpResponse->status, $server->httpResponse->body);
$this->assertEquals(array(
- 'Content-Type' => 'application/xml; charset=utf-8',
- ), $server->httpResponse->headers);
+ 'X-Sabre-Version' => [DAV\Version::VERSION],
+ 'Content-Type' => ['application/xml; charset=utf-8'],
+ ), $server->httpResponse->getHeaders());
$check = array(
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalTest.php
index 2d4371138..03fd9d64d 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/PrincipalTest.php
@@ -75,8 +75,12 @@ class PrincipalTest extends \PHPUnit_Framework_TestCase {
$principalBackend = new PrincipalBackend\Mock();
$principal = new Principal($principalBackend, array('uri' => 'principals/admin'));
- $result = $principal->updateProperties(array('{DAV:}yourmom'=>'test'));
- $this->assertEquals(true,$result);
+
+ $propPatch = new DAV\PropPatch(array('{DAV:}yourmom' => 'test'));
+
+ $result = $principal->propPatch($propPatch);
+ $result = $propPatch->commit();
+ $this->assertTrue($result);
}
@@ -175,7 +179,7 @@ class PrincipalTest extends \PHPUnit_Framework_TestCase {
$this->assertEquals(array(
array(
'privilege' => '{DAV:}read',
- 'principal' => 'principals/admin',
+ 'principal' => '{DAV:}authenticated',
'protected' => true,
)
),$principal->getACL());
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/Property/ACLRestrictionsTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/Property/ACLRestrictionsTest.php
deleted file mode 100644
index 72a2f36a4..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/Property/ACLRestrictionsTest.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-namespace Sabre\DAVACL\Property;
-
-use Sabre\DAV;
-use Sabre\HTTP;
-
-class ACLRestrictionsTest extends \PHPUnit_Framework_TestCase {
-
- function testConstruct() {
-
- $prop = new AclRestrictions();
-
- }
-
- function testSerializeEmpty() {
-
- $dom = new \DOMDocument('1.0');
- $root = $dom->createElementNS('DAV:','d:root');
-
- $dom->appendChild($root);
-
- $acl = new AclRestrictions();
- $acl->serialize(new DAV\Server(), $root);
-
- $xml = $dom->saveXML();
- $expected = '<?xml version="1.0"?>
-<d:root xmlns:d="DAV:"><d:grant-only/><d:no-invert/></d:root>
-';
- $this->assertEquals($expected, $xml);
-
- }
-
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/Property/ACLTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/Property/ACLTest.php
deleted file mode 100644
index 7f2014df3..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/Property/ACLTest.php
+++ /dev/null
@@ -1,335 +0,0 @@
-<?php
-
-namespace Sabre\DAVACL\Property;
-
-use Sabre\DAV;
-use Sabre\HTTP;
-
-
-class ACLTest extends \PHPUnit_Framework_TestCase {
-
- function testConstruct() {
-
- $acl = new Acl(array());
-
- }
-
- function testSerializeEmpty() {
-
- $dom = new \DOMDocument('1.0');
- $root = $dom->createElementNS('DAV:','d:root');
-
- $dom->appendChild($root);
-
- $acl = new Acl(array());
- $acl->serialize(new DAV\Server(), $root);
-
- $xml = $dom->saveXML();
- $expected = '<?xml version="1.0"?>
-<d:root xmlns:d="DAV:"/>
-';
- $this->assertEquals($expected, $xml);
-
- }
-
- function testSerialize() {
-
- $dom = new \DOMDocument('1.0');
- $root = $dom->createElementNS('DAV:','d:root');
-
- $dom->appendChild($root);
-
- $privileges = array(
- array(
- 'principal' => 'principals/evert',
- 'privilege' => '{DAV:}write',
- 'uri' => 'articles',
- ),
- array(
- 'principal' => 'principals/foo',
- 'privilege' => '{DAV:}read',
- 'uri' => 'articles',
- 'protected' => true,
- ),
- );
-
- $acl = new Acl($privileges);
- $acl->serialize(new DAV\Server(), $root);
-
- $dom->formatOutput = true;
-
- $xml = $dom->saveXML();
- $expected = '<?xml version="1.0"?>
-<d:root xmlns:d="DAV:">
- <d:ace>
- <d:principal>
- <d:href>/principals/evert/</d:href>
- </d:principal>
- <d:grant>
- <d:privilege>
- <d:write/>
- </d:privilege>
- </d:grant>
- </d:ace>
- <d:ace>
- <d:principal>
- <d:href>/principals/foo/</d:href>
- </d:principal>
- <d:grant>
- <d:privilege>
- <d:read/>
- </d:privilege>
- </d:grant>
- <d:protected/>
- </d:ace>
-</d:root>
-';
- $this->assertEquals($expected, $xml);
-
- }
-
- function testSerializeSpecialPrincipals() {
-
- $dom = new \DOMDocument('1.0');
- $root = $dom->createElementNS('DAV:','d:root');
-
- $dom->appendChild($root);
-
- $privileges = array(
- array(
- 'principal' => '{DAV:}authenticated',
- 'privilege' => '{DAV:}write',
- 'uri' => 'articles',
- ),
- array(
- 'principal' => '{DAV:}unauthenticated',
- 'privilege' => '{DAV:}write',
- 'uri' => 'articles',
- ),
- array(
- 'principal' => '{DAV:}all',
- 'privilege' => '{DAV:}write',
- 'uri' => 'articles',
- ),
-
- );
-
- $acl = new Acl($privileges);
- $acl->serialize(new DAV\Server(), $root);
-
- $dom->formatOutput = true;
-
- $xml = $dom->saveXML();
- $expected = '<?xml version="1.0"?>
-<d:root xmlns:d="DAV:">
- <d:ace>
- <d:principal>
- <d:authenticated/>
- </d:principal>
- <d:grant>
- <d:privilege>
- <d:write/>
- </d:privilege>
- </d:grant>
- </d:ace>
- <d:ace>
- <d:principal>
- <d:unauthenticated/>
- </d:principal>
- <d:grant>
- <d:privilege>
- <d:write/>
- </d:privilege>
- </d:grant>
- </d:ace>
- <d:ace>
- <d:principal>
- <d:all/>
- </d:principal>
- <d:grant>
- <d:privilege>
- <d:write/>
- </d:privilege>
- </d:grant>
- </d:ace>
-</d:root>
-';
- $this->assertEquals($expected, $xml);
-
- }
-
- function testUnserialize() {
-
- $source = '<?xml version="1.0"?>
-<d:root xmlns:d="DAV:">
- <d:ace>
- <d:principal>
- <d:href>/principals/evert/</d:href>
- </d:principal>
- <d:grant>
- <d:privilege>
- <d:write/>
- </d:privilege>
- </d:grant>
- </d:ace>
- <d:ace>
- <d:principal>
- <d:href>/principals/foo/</d:href>
- </d:principal>
- <d:grant>
- <d:privilege>
- <d:read/>
- </d:privilege>
- </d:grant>
- <d:protected/>
- </d:ace>
-</d:root>
-';
-
- $dom = DAV\XMLUtil::loadDOMDocument($source);
- $result = Acl::unserialize($dom->firstChild);
-
- $this->assertInstanceOf('Sabre\\DAVACL\\Property\\ACL', $result);
-
- $expected = array(
- array(
- 'principal' => '/principals/evert/',
- 'protected' => false,
- 'privilege' => '{DAV:}write',
- ),
- array(
- 'principal' => '/principals/foo/',
- 'protected' => true,
- 'privilege' => '{DAV:}read',
- ),
- );
-
- $this->assertEquals($expected, $result->getPrivileges());
-
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testUnserializeNoPrincipal() {
-
- $source = '<?xml version="1.0"?>
-<d:root xmlns:d="DAV:">
- <d:ace>
- <d:grant>
- <d:privilege>
- <d:write/>
- </d:privilege>
- </d:grant>
- </d:ace>
-</d:root>
-';
-
- $dom = DAV\XMLUtil::loadDOMDocument($source);
- Acl::unserialize($dom->firstChild);
-
- }
-
- function testUnserializeOtherPrincipal() {
-
- $source = '<?xml version="1.0"?>
-<d:root xmlns:d="DAV:">
- <d:ace>
- <d:grant>
- <d:privilege>
- <d:write/>
- </d:privilege>
- </d:grant>
- <d:principal><d:authenticated /></d:principal>
- </d:ace>
- <d:ace>
- <d:grant>
- <d:privilege>
- <d:write/>
- </d:privilege>
- </d:grant>
- <d:principal><d:unauthenticated /></d:principal>
- </d:ace>
- <d:ace>
- <d:grant>
- <d:privilege>
- <d:write/>
- </d:privilege>
- </d:grant>
- <d:principal><d:all /></d:principal>
- </d:ace>
-</d:root>
-';
-
- $dom = DAV\XMLUtil::loadDOMDocument($source);
- $result = Acl::unserialize($dom->firstChild);
-
- $this->assertInstanceOf('Sabre\\DAVACL\\Property\\Acl', $result);
-
- $expected = array(
- array(
- 'principal' => '{DAV:}authenticated',
- 'protected' => false,
- 'privilege' => '{DAV:}write',
- ),
- array(
- 'principal' => '{DAV:}unauthenticated',
- 'protected' => false,
- 'privilege' => '{DAV:}write',
- ),
- array(
- 'principal' => '{DAV:}all',
- 'protected' => false,
- 'privilege' => '{DAV:}write',
- ),
- );
-
- $this->assertEquals($expected, $result->getPrivileges());
-
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\NotImplemented
- */
- function testUnserializeDeny() {
-
- $source = '<?xml version="1.0"?>
-<d:root xmlns:d="DAV:">
- <d:ace>
- <d:deny>
- <d:privilege>
- <d:write/>
- </d:privilege>
- </d:deny>
- <d:principal><d:href>/principals/evert</d:href></d:principal>
- </d:ace>
-</d:root>
-';
-
- $dom = DAV\XMLUtil::loadDOMDocument($source);
- Acl::unserialize($dom->firstChild);
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testUnserializeMissingPriv() {
-
- $source = '<?xml version="1.0"?>
-<d:root xmlns:d="DAV:">
- <d:ace>
- <d:grant>
- <d:privilege />
- </d:grant>
- <d:principal><d:href>/principals/evert</d:href></d:principal>
- </d:ace>
-</d:root>
-';
-
- $dom = DAV\XMLUtil::loadDOMDocument($source);
- Acl::unserialize($dom->firstChild);
-
- }
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/Property/CurrentUserPrivilegeSetTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/Property/CurrentUserPrivilegeSetTest.php
deleted file mode 100644
index e71addb65..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/Property/CurrentUserPrivilegeSetTest.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-namespace Sabre\DAVACL\Property;
-
-use Sabre\DAV;
-use Sabre\HTTP;
-
-
-class CurrentUserPrivilegeSetTest extends \PHPUnit_Framework_TestCase {
-
- function testSerialize() {
-
- $privileges = array(
- '{DAV:}read',
- '{DAV:}write',
- );
- $prop = new CurrentUserPrivilegeSet($privileges);
-
- $server = new DAV\Server();
- $dom = new \DOMDocument('1.0','utf-8');
- $root = $dom->createElementNS('DAV:','d:root');
- $dom->appendChild($root);
-
- $prop->serialize($server, $root);
-
- $xpaths = array(
- '/d:root' => 1,
- '/d:root/d:privilege' => 2,
- '/d:root/d:privilege/d:read' => 1,
- '/d:root/d:privilege/d:write' => 1,
- );
-
- // Reloading because PHP DOM sucks
- $dom2 = new \DOMDocument('1.0', 'utf-8');
- $dom2->loadXML($dom->saveXML());
-
- $dxpath = new \DOMXPath($dom2);
- $dxpath->registerNamespace('d','DAV:');
- foreach($xpaths as $xpath=>$count) {
-
- $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count);
-
- }
-
- }
-
- function testUnserialize() {
-
- $source = '<?xml version="1.0"?>
-<d:root xmlns:d="DAV:">
- <d:privilege>
- <d:write-properties />
- </d:privilege>
- <d:privilege>
- <d:read />
- </d:privilege>
-</d:root>
-';
-
- $dom = DAV\XMLUtil::loadDOMDocument($source);
- $result = CurrentUserPrivilegeSet::unserialize($dom->firstChild, array());
- $this->assertTrue($result->has('{DAV:}read'));
- $this->assertTrue($result->has('{DAV:}write-properties'));
- $this->assertFalse($result->has('{DAV:}bind'));
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/Property/PrincipalTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/Property/PrincipalTest.php
deleted file mode 100644
index be12c79ee..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/Property/PrincipalTest.php
+++ /dev/null
@@ -1,181 +0,0 @@
-<?php
-
-namespace Sabre\DAVACL\Property;
-
-use Sabre\DAV;
-use Sabre\HTTP;
-
-class PrincipalTest extends \PHPUnit_Framework_TestCase {
-
- function testSimple() {
-
- $principal = new Principal(Principal::UNAUTHENTICATED);
- $this->assertEquals(Principal::UNAUTHENTICATED, $principal->getType());
- $this->assertNull($principal->getHref());
-
- $principal = new Principal(Principal::AUTHENTICATED);
- $this->assertEquals(Principal::AUTHENTICATED, $principal->getType());
- $this->assertNull($principal->getHref());
-
- $principal = new Principal(Principal::HREF,'admin');
- $this->assertEquals(Principal::HREF, $principal->getType());
- $this->assertEquals('admin',$principal->getHref());
-
- }
-
- /**
- * @depends testSimple
- * @expectedException Sabre\DAV\Exception
- */
- function testNoHref() {
-
- $principal = new Principal(Principal::HREF);
-
- }
-
- /**
- * @depends testSimple
- */
- function testSerializeUnAuthenticated() {
-
- $prin = new Principal(Principal::UNAUTHENTICATED);
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:principal');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $objectTree = new DAV\ObjectTree(new DAV\SimpleCollection('rootdir'));
- $server = new DAV\Server($objectTree);
-
- $prin->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:principal xmlns:d="DAV:">' .
-'<d:unauthenticated/>' .
-'</d:principal>
-', $xml);
-
- }
-
-
- /**
- * @depends testSerializeUnAuthenticated
- */
- function testSerializeAuthenticated() {
-
- $prin = new Principal(Principal::AUTHENTICATED);
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:principal');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $objectTree = new DAV\ObjectTree(new DAV\SimpleCollection('rootdir'));
- $server = new DAV\Server($objectTree);
-
- $prin->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:principal xmlns:d="DAV:">' .
-'<d:authenticated/>' .
-'</d:principal>
-', $xml);
-
- }
-
-
- /**
- * @depends testSerializeUnAuthenticated
- */
- function testSerializeHref() {
-
- $prin = new Principal(Principal::HREF,'principals/admin');
-
- $doc = new \DOMDocument();
- $root = $doc->createElement('d:principal');
- $root->setAttribute('xmlns:d','DAV:');
-
- $doc->appendChild($root);
- $objectTree = new DAV\ObjectTree(new DAV\SimpleCollection('rootdir'));
- $server = new DAV\Server($objectTree);
-
- $prin->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:principal xmlns:d="DAV:">' .
-'<d:href>/principals/admin</d:href>' .
-'</d:principal>
-', $xml);
-
- }
-
- function testUnserializeHref() {
-
- $xml = '<?xml version="1.0"?>
-<d:principal xmlns:d="DAV:">' .
-'<d:href>/principals/admin</d:href>' .
-'</d:principal>';
-
- $dom = DAV\XMLUtil::loadDOMDocument($xml);
-
- $principal = Principal::unserialize($dom->firstChild);
- $this->assertEquals(Principal::HREF, $principal->getType());
- $this->assertEquals('/principals/admin', $principal->getHref());
-
- }
-
- function testUnserializeAuthenticated() {
-
- $xml = '<?xml version="1.0"?>
-<d:principal xmlns:d="DAV:">' .
-' <d:authenticated />' .
-'</d:principal>';
-
- $dom = DAV\XMLUtil::loadDOMDocument($xml);
-
- $principal = Principal::unserialize($dom->firstChild);
- $this->assertEquals(Principal::AUTHENTICATED, $principal->getType());
-
- }
-
- function testUnserializeUnauthenticated() {
-
- $xml = '<?xml version="1.0"?>
-<d:principal xmlns:d="DAV:">' .
-' <d:unauthenticated />' .
-'</d:principal>';
-
- $dom = DAV\XMLUtil::loadDOMDocument($xml);
-
- $principal = Principal::unserialize($dom->firstChild);
- $this->assertEquals(Principal::UNAUTHENTICATED, $principal->getType());
-
- }
-
- /**
- * @expectedException Sabre\DAV\Exception\BadRequest
- */
- function testUnserializeUnknown() {
-
- $xml = '<?xml version="1.0"?>
-<d:principal xmlns:d="DAV:">' .
-' <d:foo />' .
-'</d:principal>';
-
- $dom = DAV\XMLUtil::loadDOMDocument($xml);
-
- Principal::unserialize($dom->firstChild);
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/Property/SupportedPrivilegeSetTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/Property/SupportedPrivilegeSetTest.php
deleted file mode 100644
index 943316331..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/Property/SupportedPrivilegeSetTest.php
+++ /dev/null
@@ -1,106 +0,0 @@
-<?php
-
-namespace Sabre\DAVACL\Property;
-
-use Sabre\DAV;
-use Sabre\HTTP;
-
-
-
-class SupportedPrivilegeSetTest extends \PHPUnit_Framework_TestCase {
-
- function testSimple() {
-
- $prop = new SupportedPrivilegeSet(array(
- 'privilege' => '{DAV:}all',
- ));
-
- }
-
-
- /**
- * @depends testSimple
- */
- function testSerializeSimple() {
-
- $prop = new SupportedPrivilegeSet(array(
- 'privilege' => '{DAV:}all',
- ));
-
- $doc = new \DOMDocument();
- $root = $doc->createElementNS('DAV:', 'd:supported-privilege-set');
-
- $doc->appendChild($root);
-
- $server = new DAV\Server();
- $prop->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:supported-privilege-set xmlns:d="DAV:">' .
-'<d:supported-privilege>' .
-'<d:privilege>' .
-'<d:all/>' .
-'</d:privilege>' .
-'</d:supported-privilege>' .
-'</d:supported-privilege-set>
-', $xml);
-
- }
-
- /**
- * @depends testSimple
- */
- function testSerializeAggregate() {
-
- $prop = new SupportedPrivilegeSet(array(
- 'privilege' => '{DAV:}all',
- 'abstract' => true,
- 'aggregates' => array(
- array(
- 'privilege' => '{DAV:}read',
- ),
- array(
- 'privilege' => '{DAV:}write',
- 'description' => 'booh',
- ),
- ),
- ));
-
- $doc = new \DOMDocument();
- $root = $doc->createElementNS('DAV:', 'd:supported-privilege-set');
-
- $doc->appendChild($root);
-
- $server = new DAV\Server();
- $prop->serialize($server, $root);
-
- $xml = $doc->saveXML();
-
- $this->assertEquals(
-'<?xml version="1.0"?>
-<d:supported-privilege-set xmlns:d="DAV:">' .
-'<d:supported-privilege>' .
-'<d:privilege>' .
-'<d:all/>' .
-'</d:privilege>' .
-'<d:abstract/>' .
-'<d:supported-privilege>' .
-'<d:privilege>' .
-'<d:read/>' .
-'</d:privilege>' .
-'</d:supported-privilege>' .
-'<d:supported-privilege>' .
-'<d:privilege>' .
-'<d:write/>' .
-'</d:privilege>' .
-'<d:description>booh</d:description>' .
-'</d:supported-privilege>' .
-'</d:supported-privilege>' .
-'</d:supported-privilege-set>
-', $xml);
-
- }
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/SimplePluginTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/SimplePluginTest.php
index 04ed5c330..fb73cc16a 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/SimplePluginTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVACL/SimplePluginTest.php
@@ -30,6 +30,11 @@ class SimplePluginTest extends \PHPUnit_Framework_TestCase {
$this->assertEquals(array('ACL'), $aclPlugin->getMethods(''));
+
+ $this->assertEquals(
+ 'acl',
+ $aclPlugin->getPluginInfo()['name']
+ );
}
function testGetFlatPrivilegeSet() {
@@ -55,15 +60,15 @@ class SimplePluginTest extends \PHPUnit_Framework_TestCase {
),
'{DAV:}read-acl' => array(
'privilege' => '{DAV:}read-acl',
- 'abstract' => true,
+ 'abstract' => false,
'aggregates' => array(),
- 'concrete' => '{DAV:}read',
+ 'concrete' => '{DAV:}read-acl',
),
'{DAV:}read-current-user-privilege-set' => array(
'privilege' => '{DAV:}read-current-user-privilege-set',
- 'abstract' => true,
+ 'abstract' => false,
'aggregates' => array(),
- 'concrete' => '{DAV:}read',
+ 'concrete' => '{DAV:}read-current-user-privilege-set',
),
'{DAV:}write' => array(
'privilege' => '{DAV:}write',
@@ -80,39 +85,39 @@ class SimplePluginTest extends \PHPUnit_Framework_TestCase {
),
'{DAV:}write-acl' => array(
'privilege' => '{DAV:}write-acl',
- 'abstract' => true,
+ 'abstract' => false,
'aggregates' => array(),
- 'concrete' => '{DAV:}write',
+ 'concrete' => '{DAV:}write-acl',
),
'{DAV:}write-properties' => array(
'privilege' => '{DAV:}write-properties',
- 'abstract' => true,
+ 'abstract' => false,
'aggregates' => array(),
- 'concrete' => '{DAV:}write',
+ 'concrete' => '{DAV:}write-properties',
),
'{DAV:}write-content' => array(
'privilege' => '{DAV:}write-content',
- 'abstract' => true,
+ 'abstract' => false,
'aggregates' => array(),
- 'concrete' => '{DAV:}write',
+ 'concrete' => '{DAV:}write-content',
),
'{DAV:}unlock' => array(
'privilege' => '{DAV:}unlock',
- 'abstract' => true,
+ 'abstract' => false,
'aggregates' => array(),
- 'concrete' => '{DAV:}write',
+ 'concrete' => '{DAV:}unlock',
),
'{DAV:}bind' => array(
'privilege' => '{DAV:}bind',
- 'abstract' => true,
+ 'abstract' => false,
'aggregates' => array(),
- 'concrete' => '{DAV:}write',
+ 'concrete' => '{DAV:}bind',
),
'{DAV:}unbind' => array(
'privilege' => '{DAV:}unbind',
- 'abstract' => true,
+ 'abstract' => false,
'aggregates' => array(),
- 'concrete' => '{DAV:}write',
+ 'concrete' => '{DAV:}unbind',
),
);
@@ -148,11 +153,11 @@ class SimplePluginTest extends \PHPUnit_Framework_TestCase {
$server = new DAV\Server($tree);
$server->addPlugin($acl);
- $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'SabreDAV');
+ $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock());
$server->addPlugin($auth);
//forcing login
- $auth->beforeMethod('GET','/');
+ $auth->beforeMethod(new HTTP\Request(), new HTTP\Response());
$this->assertEquals(array('principals/admin'),$acl->getCurrentUserPrincipals());
@@ -175,11 +180,11 @@ class SimplePluginTest extends \PHPUnit_Framework_TestCase {
$server = new DAV\Server($tree);
$server->addPlugin($acl);
- $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'SabreDAV');
+ $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock());
$server->addPlugin($auth);
//forcing login
- $auth->beforeMethod('GET','/');
+ $auth->beforeMethod(new HTTP\Request(), new HTTP\Response());
$expected = array(
'principals/admin',
@@ -252,11 +257,11 @@ class SimplePluginTest extends \PHPUnit_Framework_TestCase {
$aclPlugin = new Plugin();
$server->addPlugin($aclPlugin);
- $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'SabreDAV');
+ $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock());
$server->addPlugin($auth);
//forcing login
- $auth->beforeMethod('GET','/');
+ $auth->beforeMethod(new HTTP\Request(), new HTTP\Response());
$expected = array(
'{DAV:}write',
@@ -306,7 +311,7 @@ class SimplePluginTest extends \PHPUnit_Framework_TestCase {
$aclPlugin = new Plugin();
$server->addPlugin($aclPlugin);
- $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'SabreDAV');
+ $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock());
$server->addPlugin($auth);
//forcing login
diff --git a/vendor/sabre/dav/tests/Sabre/DAVACL/VersionTest.php b/vendor/sabre/dav/tests/Sabre/DAVACL/VersionTest.php
deleted file mode 100644
index c432527dc..000000000
--- a/vendor/sabre/dav/tests/Sabre/DAVACL/VersionTest.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-namespace Sabre\DAVACL;
-
-class VersionTest extends \PHPUnit_Framework_TestCase {
-
- function testString() {
-
- $v = Version::VERSION;
- $this->assertEquals(-1, version_compare('1.0.0',$v));
-
- $s = Version::STABILITY;
- $this->assertTrue($s == 'alpha' || $s == 'beta' || $s =='stable');
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/DAVServerTest.php b/vendor/sabre/dav/tests/Sabre/DAVServerTest.php
index 207687d90..d329b5b05 100644
--- a/vendor/sabre/dav/tests/Sabre/DAVServerTest.php
+++ b/vendor/sabre/dav/tests/Sabre/DAVServerTest.php
@@ -2,17 +2,9 @@
namespace Sabre;
-require_once 'Sabre/HTTP/ResponseMock.php';
-
-require_once 'Sabre/DAV/Auth/Backend/Mock.php';
-require_once 'Sabre/DAV/Mock/File.php';
-require_once 'Sabre/DAV/Mock/Collection.php';
-
-require_once 'Sabre/DAVACL/PrincipalBackend/Mock.php';
-
-require_once 'Sabre/CalDAV/Backend/Mock.php';
-
-require_once 'Sabre/CardDAV/Backend/Mock.php';
+use Sabre\HTTP\Request;
+use Sabre\HTTP\Response;
+use Sabre\HTTP\Sapi;
/**
* This class may be used as a basis for other webdav-related unittests.
@@ -20,7 +12,7 @@ require_once 'Sabre/CardDAV/Backend/Mock.php';
* This class is supposed to provide a reasonably big framework to quickly get
* a testing environment running.
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
@@ -30,22 +22,35 @@ abstract class DAVServerTest extends \PHPUnit_Framework_TestCase {
protected $setupCardDAV = false;
protected $setupACL = false;
protected $setupCalDAVSharing = false;
+ protected $setupCalDAVScheduling = false;
+ protected $setupCalDAVSubscriptions = false;
+ protected $setupCalDAVICSExport = false;
+ protected $setupLocks = false;
+ protected $setupFiles = false;
+ protected $setupPropertyStorage = false;
- protected $caldavCalendars = array();
- protected $caldavCalendarObjects = array();
+ /**
+ * An array with calendars. Every calendar should have
+ * - principaluri
+ * - uri
+ */
+ protected $caldavCalendars = [];
+ protected $caldavCalendarObjects = [];
- protected $carddavAddressBooks = array();
- protected $carddavCards = array();
+ protected $carddavAddressBooks = [];
+ protected $carddavCards = [];
/**
* @var Sabre\DAV\Server
*/
protected $server;
- protected $tree = array();
+ protected $tree = [];
protected $caldavBackend;
protected $carddavBackend;
protected $principalBackend;
+ protected $locksBackend;
+ protected $propertyStorageBackend;
/**
* @var Sabre\CalDAV\Plugin
@@ -68,11 +73,28 @@ abstract class DAVServerTest extends \PHPUnit_Framework_TestCase {
protected $caldavSharingPlugin;
/**
+ * CalDAV scheduling plugin
+ *
+ * @var CalDAV\Schedule\Plugin
+ */
+ protected $caldavSchedulePlugin;
+
+ /**
* @var Sabre\DAV\Auth\Plugin
*/
protected $authPlugin;
/**
+ * @var Sabre\DAV\Locks\Plugin
+ */
+ protected $locksPlugin;
+
+ /**
+ * @var Sabre\DAV\PropertyStorage\Plugin
+ */
+ protected $propertyStoragePlugin;
+
+ /**
* If this string is set, we will automatically log in the user with this
* name.
*/
@@ -84,6 +106,7 @@ abstract class DAVServerTest extends \PHPUnit_Framework_TestCase {
$this->setUpTree();
$this->server = new DAV\Server($this->tree);
+ $this->server->sapi = new HTTP\SapiMock();
$this->server->debugExceptions = true;
if ($this->setupCalDAV) {
@@ -94,6 +117,17 @@ abstract class DAVServerTest extends \PHPUnit_Framework_TestCase {
$this->caldavSharingPlugin = new CalDAV\SharingPlugin();
$this->server->addPlugin($this->caldavSharingPlugin);
}
+ if ($this->setupCalDAVScheduling) {
+ $this->caldavSchedulePlugin = new CalDAV\Schedule\Plugin();
+ $this->server->addPlugin($this->caldavSchedulePlugin);
+ }
+ if ($this->setupCalDAVSubscriptions) {
+ $this->server->addPlugin(new CalDAV\Subscriptions\Plugin());
+ }
+ if ($this->setupCalDAVICSExport) {
+ $this->caldavICSExportPlugin = new CalDAV\ICSExportPlugin();
+ $this->server->addPlugin($this->caldavICSExportPlugin);
+ }
if ($this->setupCardDAV) {
$this->carddavPlugin = new CardDAV\Plugin();
$this->server->addPlugin($this->carddavPlugin);
@@ -102,14 +136,26 @@ abstract class DAVServerTest extends \PHPUnit_Framework_TestCase {
$this->aclPlugin = new DAVACL\Plugin();
$this->server->addPlugin($this->aclPlugin);
}
+ if ($this->setupLocks) {
+ $this->locksPlugin = new DAV\Locks\Plugin(
+ $this->locksBackend
+ );
+ $this->server->addPlugin($this->locksPlugin);
+ }
+ if ($this->setupPropertyStorage) {
+ $this->propertyStoragePlugin = new DAV\PropertyStorage\Plugin(
+ $this->propertyStorageBackend
+ );
+ $this->server->addPlugin($this->propertyStoragePlugin);
+ }
if ($this->autoLogin) {
$authBackend = new DAV\Auth\Backend\Mock();
- $authBackend->defaultUser = $this->autoLogin;
- $this->authPlugin = new DAV\Auth\Plugin($authBackend, 'SabreDAV');
+ $authBackend->setPrincipal('principals/' . $this->autoLogin);
+ $this->authPlugin = new DAV\Auth\Plugin($authBackend);
$this->server->addPlugin($this->authPlugin);
// This will trigger the actual login procedure
- $this->authPlugin->beforeMethod('OPTIONS','/');
+ $this->authPlugin->beforeMethod(new Request(), new Response());
}
}
@@ -126,7 +172,7 @@ abstract class DAVServerTest extends \PHPUnit_Framework_TestCase {
function request($request) {
if (is_array($request)) {
- $request = new HTTP\Request($request);
+ $request = HTTP\Request::createFromServerArray($request);
}
$this->server->httpRequest = $request;
$this->server->httpResponse = new HTTP\ResponseMock();
@@ -136,10 +182,13 @@ abstract class DAVServerTest extends \PHPUnit_Framework_TestCase {
}
+ /**
+ * Override this to provide your own Tree for your test-case.
+ */
function setUpTree() {
if ($this->setupCalDAV) {
- $this->tree[] = new CalDAV\CalendarRootNode(
+ $this->tree[] = new CalDAV\CalendarRoot(
$this->principalBackend,
$this->caldavBackend
);
@@ -152,17 +201,32 @@ abstract class DAVServerTest extends \PHPUnit_Framework_TestCase {
}
if ($this->setupCardDAV || $this->setupCalDAV) {
- $this->tree[] = new DAVACL\PrincipalCollection(
+ $this->tree[] = new CalDAV\Principal\Collection(
$this->principalBackend
);
}
+ if ($this->setupFiles) {
+
+ $this->tree[] = new DAV\Mock\Collection('files');
+
+ }
}
function setUpBackends() {
+ if ($this->setupCalDAVSharing && is_null($this->caldavBackend)) {
+ $this->caldavBackend = new CalDAV\Backend\MockSharing($this->caldavCalendars, $this->caldavCalendarObjects);
+ }
+ if ($this->setupCalDAVSubscriptions && is_null($this->caldavBackend)) {
+ $this->caldavBackend = new CalDAV\Backend\MockSubscriptionSupport($this->caldavCalendars, $this->caldavCalendarObjects);
+ }
if ($this->setupCalDAV && is_null($this->caldavBackend)) {
- $this->caldavBackend = new CalDAV\Backend\Mock($this->caldavCalendars, $this->caldavCalendarObjects);
+ if ($this->setupCalDAVScheduling) {
+ $this->caldavBackend = new CalDAV\Backend\MockScheduling($this->caldavCalendars, $this->caldavCalendarObjects);
+ } else {
+ $this->caldavBackend = new CalDAV\Backend\Mock($this->caldavCalendars, $this->caldavCalendarObjects);
+ }
}
if ($this->setupCardDAV && is_null($this->carddavBackend)) {
$this->carddavBackend = new CardDAV\Backend\Mock($this->carddavAddressBooks, $this->carddavCards);
@@ -170,6 +234,12 @@ abstract class DAVServerTest extends \PHPUnit_Framework_TestCase {
if ($this->setupCardDAV || $this->setupCalDAV) {
$this->principalBackend = new DAVACL\PrincipalBackend\Mock();
}
+ if ($this->setupLocks) {
+ $this->locksBackend = new DAV\Locks\Backend\Mock();
+ }
+ if ($this->setupPropertyStorage) {
+ $this->propertyStorageBackend = new DAV\PropertyStorage\Backend\Mock();
+ }
}
@@ -177,7 +247,7 @@ abstract class DAVServerTest extends \PHPUnit_Framework_TestCase {
function assertHTTPStatus($expectedStatus, HTTP\Request $req) {
$resp = $this->request($req);
- $this->assertEquals($resp->getStatusMessage($expectedStatus), $resp->status,'Incorrect HTTP status received: ' . $resp->body);
+ $this->assertEquals((int)$expectedStatus, (int)$resp->status, 'Incorrect HTTP status received: ' . $resp->body);
}
diff --git a/vendor/sabre/dav/tests/Sabre/HTTP/AWSAuthTest.php b/vendor/sabre/dav/tests/Sabre/HTTP/AWSAuthTest.php
deleted file mode 100644
index 569ec2e7d..000000000
--- a/vendor/sabre/dav/tests/Sabre/HTTP/AWSAuthTest.php
+++ /dev/null
@@ -1,242 +0,0 @@
-<?php
-
-namespace Sabre\HTTP;
-
-require_once 'Sabre/HTTP/ResponseMock.php';
-
-class AWSAuthTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @var Sabre\HTTP\ResponseMock
- */
- private $response;
- /**
- * @var Sabre\HTTP\AWSAuth
- */
- private $auth;
-
- const REALM = 'SabreDAV unittest';
-
- public function setUp() {
-
- $this->response = new ResponseMock();
- $this->auth = new AWSAuth();
- $this->auth->setRealm(self::REALM);
- $this->auth->setHTTPResponse($this->response);
-
- }
-
- public function testNoHeader() {
-
- $request = new Request(array(
- 'REQUEST_METHOD' => 'GET',
- ));
-
- $this->auth->setHTTPRequest($request);
-
- $result = $this->auth->init();
-
- $this->assertFalse($result,'No AWS Authorization header was supplied, so we should have gotten false');
- $this->assertEquals(AWSAuth::ERR_NOAWSHEADER,$this->auth->errorCode);
-
- }
-
- public function testIncorrectContentMD5() {
-
- $accessKey = 'accessKey';
- $secretKey = 'secretKey';
-
- $request = new Request(array(
- 'REQUEST_METHOD' => 'GET',
- 'HTTP_AUTHORIZATION' => "AWS $accessKey:sig",
- 'HTTP_CONTENT_MD5' => 'garbage',
- 'REQUEST_URI' => '/',
- ));
-
- $this->auth->setHTTPRequest($request);
- $this->auth->init();
- $result = $this->auth->validate($secretKey);
-
- $this->assertFalse($result);
- $this->assertEquals(AWSAuth::ERR_MD5CHECKSUMWRONG,$this->auth->errorCode);
-
- }
-
- public function testNoDate() {
-
- $accessKey = 'accessKey';
- $secretKey = 'secretKey';
- $content = 'thisisthebody';
- $contentMD5 = base64_encode(md5($content,true));
-
-
- $request = new Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'HTTP_AUTHORIZATION' => "AWS $accessKey:sig",
- 'HTTP_CONTENT_MD5' => $contentMD5,
- ));
-
- $request->setBody($content);
-
- $this->auth->setHTTPRequest($request);
- $this->auth->init();
- $result = $this->auth->validate($secretKey);
-
- $this->assertFalse($result);
- $this->assertEquals(AWSAuth::ERR_INVALIDDATEFORMAT,$this->auth->errorCode);
-
- }
-
- public function testFutureDate() {
-
- $accessKey = 'accessKey';
- $secretKey = 'secretKey';
- $content = 'thisisthebody';
- $contentMD5 = base64_encode(md5($content,true));
-
- $date = new \DateTime('@' . (time() + (60*20)));
- $date->setTimeZone(new \DateTimeZone('GMT'));
- $date = $date->format('D, d M Y H:i:s \\G\\M\\T');
-
- $request = new Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'HTTP_AUTHORIZATION' => "AWS $accessKey:sig",
- 'HTTP_CONTENT_MD5' => $contentMD5,
- 'HTTP_DATE' => $date,
- ));
-
- $request->setBody($content);
-
- $this->auth->setHTTPRequest($request);
- $this->auth->init();
- $result = $this->auth->validate($secretKey);
-
- $this->assertFalse($result);
- $this->assertEquals(AWSAuth::ERR_REQUESTTIMESKEWED,$this->auth->errorCode);
-
- }
-
- public function testPastDate() {
-
- $accessKey = 'accessKey';
- $secretKey = 'secretKey';
- $content = 'thisisthebody';
- $contentMD5 = base64_encode(md5($content,true));
-
- $date = new \DateTime('@' . (time() - (60*20)));
- $date->setTimeZone(new \DateTimeZone('GMT'));
- $date = $date->format('D, d M Y H:i:s \\G\\M\\T');
-
- $request = new Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'HTTP_AUTHORIZATION' => "AWS $accessKey:sig",
- 'HTTP_CONTENT_MD5' => $contentMD5,
- 'HTTP_X_AMZ_DATE' => $date,
- ));
-
- $request->setBody($content);
-
- $this->auth->setHTTPRequest($request);
- $this->auth->init();
- $result = $this->auth->validate($secretKey);
-
- $this->assertFalse($result);
- $this->assertEquals(AWSAuth::ERR_REQUESTTIMESKEWED,$this->auth->errorCode);
-
- }
-
- public function testIncorrectSignature() {
-
- $accessKey = 'accessKey';
- $secretKey = 'secretKey';
- $content = 'thisisthebody';
-
- $contentMD5 = base64_encode(md5($content,true));
-
- $date = new \DateTime('now');
- $date->setTimeZone(new \DateTimeZone('GMT'));
- $date = $date->format('D, d M Y H:i:s \\G\\M\\T');
-
- $request = new Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'HTTP_AUTHORIZATION' => "AWS $accessKey:sig",
- 'HTTP_CONTENT_MD5' => $contentMD5,
- 'HTTP_X_AMZ_DATE' => $date,
- 'REQUEST_URI' => '/',
- ));
-
- $request->setBody($content);
-
- $this->auth->setHTTPRequest($request);
- $this->auth->init();
- $result = $this->auth->validate($secretKey);
-
- $this->assertFalse($result);
- $this->assertEquals(AWSAuth::ERR_INVALIDSIGNATURE,$this->auth->errorCode);
-
- }
-
- public function testValidRequest() {
-
- $accessKey = 'accessKey';
- $secretKey = 'secretKey';
- $content = 'thisisthebody';
- $contentMD5 = base64_encode(md5($content,true));
-
- $date = new \DateTime('now');
- $date->setTimeZone(new \DateTimeZone('GMT'));
- $date = $date->format('D, d M Y H:i:s \\G\\M\\T');
-
-
- $sig = base64_encode($this->hmacsha1($secretKey,
- "POST\n$contentMD5\n\n$date\nx-amz-date:$date\n/evert"
- ));
-
- $request = new Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'HTTP_AUTHORIZATION' => "AWS $accessKey:$sig",
- 'HTTP_CONTENT_MD5' => $contentMD5,
- 'HTTP_X_AMZ_DATE' => $date,
- 'REQUEST_URI' => '/evert',
- ));
-
- $request->setBody($content);
-
- $this->auth->setHTTPRequest($request);
- $this->auth->init();
- $result = $this->auth->validate($secretKey);
-
- $this->assertTrue($result,'Signature did not validate, got errorcode ' . $this->auth->errorCode);
- $this->assertEquals($accessKey,$this->auth->getAccessKey());
-
- }
-
- public function test401() {
-
- $this->auth->requireLogin();
- $test = preg_match('/^AWS$/',$this->response->headers['WWW-Authenticate'],$matches);
- $this->assertTrue($test==true,'The WWW-Authenticate response didn\'t match our pattern');
-
- }
-
- /**
- * Generates an HMAC-SHA1 signature
- *
- * @param string $key
- * @param string $message
- * @return string
- */
- private function hmacsha1($key, $message) {
-
- $blocksize=64;
- if (strlen($key)>$blocksize)
- $key=pack('H*', sha1($key));
- $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))));
- return $hmac;
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/HTTP/BasicAuthTest.php b/vendor/sabre/dav/tests/Sabre/HTTP/BasicAuthTest.php
deleted file mode 100644
index 77c5c7179..000000000
--- a/vendor/sabre/dav/tests/Sabre/HTTP/BasicAuthTest.php
+++ /dev/null
@@ -1,132 +0,0 @@
-<?php
-
-namespace Sabre\HTTP;
-
-require_once 'Sabre/HTTP/ResponseMock.php';
-
-class BasicAuthTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @var Sabre\HTTP\ResponseMock
- */
- private $response;
- /**
- * @var Sabre\HTTP\BasicAuth
- */
- private $basicAuth;
-
- function setUp() {
-
- $this->response = new ResponseMock();
- $this->basicAuth = new BasicAuth();
- $this->basicAuth->setHTTPResponse($this->response);
-
- }
-
- function testGetUserPassApache() {
-
- $server = array(
- 'PHP_AUTH_USER' => 'admin',
- 'PHP_AUTH_PW' => '1234',
- );
-
- $request = new Request($server);
- $this->basicAuth->setHTTPRequest($request);
-
- $userPass = $this->basicAuth->getUserPass();
-
- $this->assertEquals(
- array('admin','1234'),
- $userPass,
- 'We did not get the username and password we expected'
- );
-
- }
-
- function testGetUserPassIIS() {
-
- $server = array(
- 'HTTP_AUTHORIZATION' => 'Basic ' . base64_encode('admin:1234'),
- );
-
- $request = new Request($server);
- $this->basicAuth->setHTTPRequest($request);
-
- $userPass = $this->basicAuth->getUserPass();
-
- $this->assertEquals(
- array('admin','1234'),
- $userPass,
- 'We did not get the username and password we expected'
- );
-
- }
-
- function testGetUserPassWithColon() {
-
- $server = array(
- 'HTTP_AUTHORIZATION' => 'Basic ' . base64_encode('admin:1234:5678'),
- );
-
- $request = new Request($server);
- $this->basicAuth->setHTTPRequest($request);
-
- $userPass = $this->basicAuth->getUserPass();
-
- $this->assertEquals(
- array('admin','1234:5678'),
- $userPass,
- 'We did not get the username and password we expected'
- );
-
- }
-
- function testGetUserPassApacheEdgeCase() {
-
- $server = array(
- 'REDIRECT_HTTP_AUTHORIZATION' => 'Basic ' . base64_encode('admin:1234'),
- );
-
- $request = new Request($server);
- $this->basicAuth->setHTTPRequest($request);
-
- $userPass = $this->basicAuth->getUserPass();
-
- $this->assertEquals(
- array('admin','1234'),
- $userPass,
- 'We did not get the username and password we expected'
- );
-
- }
-
- function testGetUserPassNothing() {
-
- $this->assertEquals(
- false,
- $this->basicAuth->getUserPass()
- );
-
- }
-
- function testRequireLogin() {
-
- $this->basicAuth->requireLogin();
- $this->assertEquals('SabreDAV',$this->basicAuth->getRealm());
- $this->assertEquals(
- 'HTTP/1.1 401 Unauthorized',
- $this->response->status,
- 'We expected a 401 status to be set'
- );
-
- $this->assertEquals(
- 'Basic realm="SabreDAV"',
- $this->response->headers['WWW-Authenticate'],
- 'The WWW-Autenticate header was not set!'
- );
-
-
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/HTTP/DigestAuthTest.php b/vendor/sabre/dav/tests/Sabre/HTTP/DigestAuthTest.php
deleted file mode 100644
index 576a00d4a..000000000
--- a/vendor/sabre/dav/tests/Sabre/HTTP/DigestAuthTest.php
+++ /dev/null
@@ -1,228 +0,0 @@
-<?php
-
-namespace Sabre\HTTP;
-
-require_once 'Sabre/HTTP/ResponseMock.php';
-
-class DigestAuthTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @var Sabre\HTTP\ResponseMock
- */
- private $response;
- /**
- * @var Sabre\HTTP\DigestAuth
- */
- private $auth;
-
- const REALM = 'SabreDAV unittest';
-
- public function setUp() {
-
- $this->response = new ResponseMock();
- $this->auth = new DigestAuth();
- $this->auth->setRealm(self::REALM);
- $this->auth->setHTTPResponse($this->response);
-
- }
-
- public function testDigest() {
-
- list($nonce,$opaque) = $this->getServerTokens();
-
- $username = 'admin';
- $password = 12345;
- $nc = '00002';
- $cnonce = uniqid();
-
- $digestHash = md5(
- md5($username . ':' . self::REALM . ':' . $password) . ':' .
- $nonce . ':' .
- $nc . ':' .
- $cnonce . ':' .
- 'auth:' .
- md5('GET' . ':' . '/')
- );
-
- $request = new Request(array(
- 'REQUEST_METHOD' => 'GET',
- 'PHP_AUTH_DIGEST' => 'username="'.$username.'", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth,nc='.$nc.',cnonce="' . $cnonce . '"',
- ));
-
- $this->auth->setHTTPRequest($request);
- $this->auth->init();
-
- $this->assertEquals($username,$this->auth->getUserName());
- $this->assertEquals(self::REALM,$this->auth->getRealm());
- $this->assertTrue($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . $password)),'Authentication is deemed invalid through validateA1');
- $this->assertTrue($this->auth->validatePassword($password),'Authentication is deemed invalid through validatePassword');
-
- }
-
- public function testDigestCGIFormat() {
-
- list($nonce,$opaque) = $this->getServerTokens();
-
- $username = 'admin';
- $password = 12345;
- $nc = '00002';
- $cnonce = uniqid();
-
- $digestHash = md5(
- md5($username . ':' . self::REALM . ':' . $password) . ':' .
- $nonce . ':' .
- $nc . ':' .
- $cnonce . ':' .
- 'auth:' .
- md5('GET' . ':' . '/')
- );
-
- $request = new Request(array(
- 'REQUEST_METHOD' => 'GET',
- 'HTTP_AUTHORIZATION' => 'Digest username="'.$username.'", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth,nc='.$nc.',cnonce="' . $cnonce . '"',
- ));
-
- $this->auth->setHTTPRequest($request);
- $this->auth->init();
-
- $this->assertTrue($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . $password)),'Authentication is deemed invalid through validateA1');
- $this->assertTrue($this->auth->validatePassword($password),'Authentication is deemed invalid through validatePassword');
-
- }
-
- public function testDigestApacheEdgeCase() {
-
- list($nonce,$opaque) = $this->getServerTokens();
-
- $username = 'admin';
- $password = 12345;
- $nc = '00002';
- $cnonce = uniqid();
-
- $digestHash = md5(
- md5($username . ':' . self::REALM . ':' . $password) . ':' .
- $nonce . ':' .
- $nc . ':' .
- $cnonce . ':' .
- 'auth:' .
- md5('GET' . ':' . '/')
- );
-
- $request = new Request(array(
- 'REQUEST_METHOD' => 'GET',
- 'REDIRECT_HTTP_AUTHORIZATION' => 'Digest username="'.$username.'", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth,nc='.$nc.',cnonce="' . $cnonce . '"',
- ));
-
- $this->auth->setHTTPRequest($request);
- $this->auth->init();
-
- $this->assertTrue($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . $password)),'Authentication is deemed invalid through validateA1');
- $this->assertTrue($this->auth->validatePassword($password),'Authentication is deemed invalid through validatePassword');
-
- }
-
- public function testInvalidDigest() {
-
- list($nonce,$opaque) = $this->getServerTokens();
-
- $username = 'admin';
- $password = 12345;
- $nc = '00002';
- $cnonce = uniqid();
-
- $digestHash = md5(
- md5($username . ':' . self::REALM . ':' . $password) . ':' .
- $nonce . ':' .
- $nc . ':' .
- $cnonce . ':' .
- 'auth:' .
- md5('GET' . ':' . '/')
- );
-
- $request = new Request(array(
- 'REQUEST_METHOD' => 'GET',
- 'PHP_AUTH_DIGEST' => 'username="'.$username.'", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth,nc='.$nc.',cnonce="' . $cnonce . '"',
- ));
-
- $this->auth->setHTTPRequest($request);
- $this->auth->init();
-
- $this->assertFalse($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . ($password . 'randomness'))),'Authentication is deemed invalid through validateA1');
-
- }
-
- public function testInvalidDigest2() {
-
- $request = new Request(array(
- 'REQUEST_METHOD' => 'GET',
- 'HTTP_AUTHORIZATION' => 'basic blablabla',
- ));
-
- $this->auth->setHTTPRequest($request);
- $this->auth->init();
-
- $this->assertFalse($this->auth->validateA1(md5('user:realm:password')));
-
- }
-
-
- public function testDigestAuthInt() {
-
- $this->auth->setQOP(DigestAuth::QOP_AUTHINT | DigestAuth::QOP_AUTH);
- list($nonce,$opaque) = $this->getServerTokens(DigestAuth::QOP_AUTHINT| DigestAuth::QOP_AUTH);
-
- $username = 'admin';
- $password = 12345;
- $nc = '00003';
- $cnonce = uniqid();
-
- $digestHash = md5(
- md5($username . ':' . self::REALM . ':' . $password) . ':' .
- $nonce . ':' .
- $nc . ':' .
- $cnonce . ':' .
- 'auth-int:' .
- md5('POST' . ':' . '/' . ':' . md5('body'))
- );
-
- $request = new Request(array(
- 'REQUEST_METHOD' => 'POST',
- 'PHP_AUTH_DIGEST' => 'username="'.$username.'", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth-int,nc='.$nc.',cnonce="' . $cnonce . '"',
- ));
- $request->setBody('body');
-
- $this->auth->setHTTPRequest($request);
-
- $this->auth->init();
-
- $this->assertTrue($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . $password)),'Authentication is deemed invalid through validateA1');
-
- }
-
- private function getServerTokens($qop = DigestAuth::QOP_AUTH) {
-
- $this->auth->requireLogin();
-
- switch($qop) {
- case DigestAuth::QOP_AUTH : $qopstr='auth'; break;
- case DigestAuth::QOP_AUTHINT : $qopstr='auth-int'; break;
- default : $qopstr='auth,auth-int'; break;
- }
-
- $test = preg_match('/Digest realm="'.self::REALM.'",qop="'.$qopstr.'",nonce="([0-9a-f]*)",opaque="([0-9a-f]*)"/',
- $this->response->headers['WWW-Authenticate'],$matches);
-
- $this->assertTrue($test==true,'The WWW-Authenticate response didn\'t match our pattern. We received: ' . $this->response->headers['WWW-Authenticate']);
-
- $nonce = $matches[1];
- $opaque = $matches[2];
-
- // Reset our environment
- $this->setUp();
- $this->auth->setQOP($qop);
-
- return array($nonce,$opaque);
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/HTTP/RequestTest.php b/vendor/sabre/dav/tests/Sabre/HTTP/RequestTest.php
deleted file mode 100644
index c52ce351d..000000000
--- a/vendor/sabre/dav/tests/Sabre/HTTP/RequestTest.php
+++ /dev/null
@@ -1,150 +0,0 @@
-<?php
-
-namespace Sabre\HTTP;
-
-/**
- * @covers Sabre\HTTP\Request
- */
-class RequestTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @var Sabre\HTTP\Request
- */
- private $request;
-
- function setUp() {
-
- $server = array(
- 'HTTP_HOST' => 'www.example.org',
- 'REQUEST_METHOD' => 'PUT',
- 'REQUEST_URI' => '/testuri/',
- 'CONTENT_TYPE' => 'text/xml',
- );
-
- $this->request = new Request($server);
-
- }
-
- function testGetHeader() {
-
- $this->assertEquals('www.example.org', $this->request->getHeader('Host'));
- $this->assertEquals('text/xml', $this->request->getHeader('Content-Type'));
-
- }
-
- function testGetNonExistantHeader() {
-
- $this->assertNull($this->request->getHeader('doesntexist'));
- $this->assertNull($this->request->getHeader('Content-Length'));
-
- }
-
- function testGetHeaders() {
-
- $expected = array(
- 'host' => 'www.example.org',
- 'content-type' => 'text/xml',
- );
-
- $this->assertEquals($expected, $this->request->getHeaders());
-
- }
-
- function testGetMethod() {
-
- $this->assertEquals('PUT', $this->request->getMethod(), 'It seems as if we didn\'t get a valid HTTP Request method back');
-
- }
-
- function testGetUri() {
-
- $this->assertEquals('/testuri/', $this->request->getUri(), 'We got an invalid uri back');
-
- }
-
- function testSetGetBody() {
-
- $h = fopen('php://memory','r+');
- fwrite($h,'testing');
- rewind($h);
- $this->request->setBody($h);
- $this->assertEquals('testing',$this->request->getBody(true),'We didn\'t get our testbody back');
-
- }
-
- function testSetGetBodyStream() {
-
- $h = fopen('php://memory','r+');
- fwrite($h,'testing');
- rewind($h);
- $this->request->setBody($h);
- $this->assertEquals('testing',stream_get_contents($this->request->getBody()),'We didn\'t get our testbody back');
-
- }
-
-
- function testDefaultInputStream() {
-
- $h = fopen('php://memory','r+');
- fwrite($h,'testing');
- rewind($h);
-
- $previousValue = Request::$defaultInputStream;
- Request::$defaultInputStream = $h;
-
- $this->assertEquals('testing',$this->request->getBody(true),'We didn\'t get our testbody back');
- Request::$defaultInputStream = $previousValue;
-
- }
-
- function testGetAbsoluteUri() {
-
- $s = array(
- 'HTTP_HOST' => 'sabredav.org',
- 'REQUEST_URI' => '/foo'
- );
-
- $r = new Request($s);
-
- $this->assertEquals('http://sabredav.org/foo', $r->getAbsoluteUri());
-
- $s = array(
- 'HTTP_HOST' => 'sabredav.org',
- 'REQUEST_URI' => '/foo',
- 'HTTPS' => 'on',
- );
-
- $r = new Request($s);
-
- $this->assertEquals('https://sabredav.org/foo', $r->getAbsoluteUri());
-
- }
-
- function testGetQueryString() {
-
- $s = array(
- 'QUERY_STRING' => 'bla',
- );
-
- $r = new Request($s);
- $this->assertEquals('bla', $r->getQueryString());
-
- $s = array();
-
- $r = new Request($s);
- $this->assertEquals('', $r->getQueryString());
-
- }
-
- function testGetPostVars() {
-
- $post = array(
- 'bla' => 'foo',
- );
- $r = new Request(array(),$post);
- $this->assertEquals($post, $r->getPostVars('bla'));
-
- }
-
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/HTTP/ResponseMock.php b/vendor/sabre/dav/tests/Sabre/HTTP/ResponseMock.php
index 16c034099..eb486bf5b 100644
--- a/vendor/sabre/dav/tests/Sabre/HTTP/ResponseMock.php
+++ b/vendor/sabre/dav/tests/Sabre/HTTP/ResponseMock.php
@@ -2,28 +2,21 @@
namespace Sabre\HTTP;
+/**
+ * HTTP Response Mock object
+ *
+ * This class exists to make the transition to sabre/http easier.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
class ResponseMock extends Response {
- public $headers = array();
- public $status = '';
- public $body = '';
-
- function setHeader($name,$value,$overwrite = true) {
-
- $this->headers[$name] = $value;
-
- }
-
- function sendStatus($code) {
-
- $this->status = $this->getStatusMessage($code, $this->defaultHttpVersion);
-
- }
-
- function sendBody($body) {
-
- $this->body = $body;
-
- }
+ /**
+ * Making these public.
+ */
+ public $body;
+ public $status;
}
diff --git a/vendor/sabre/dav/tests/Sabre/HTTP/ResponseTest.php b/vendor/sabre/dav/tests/Sabre/HTTP/ResponseTest.php
deleted file mode 100644
index f5302c993..000000000
--- a/vendor/sabre/dav/tests/Sabre/HTTP/ResponseTest.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-namespace Sabre\HTTP;
-
-require_once 'Sabre/HTTP/ResponseMock.php';
-
-class ResponseTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @var Sabre\HTTP\ResponseMock
- */
- private $response;
-
- function setUp() {
-
- $this->response = new ResponseMock();
-
- }
-
- function testGetStatusMessage() {
-
- $msg = $this->response->getStatusMessage(200);
- $this->assertEquals('HTTP/1.1 200 OK',$msg);
-
- }
-
- function testSetHeader() {
-
- $this->response->setHeader('Content-Type','text/html');
- $this->assertEquals('text/html', $this->response->headers['Content-Type']);
-
-
- }
- function testSetHeaders() {
-
- $this->response->setHeaders(array('Content-Type'=>'text/html'));
- $this->assertEquals('text/html', $this->response->headers['Content-Type']);
-
-
- }
-
- function testSendStatus() {
-
- $this->response->sendStatus(404);
- $this->assertEquals('HTTP/1.1 404 Not Found', $this->response->status);
-
- }
-
- function testSendBody() {
-
- ob_start();
- $response = new Response();
- $response->sendBody('hello');
- $this->assertEquals('hello',ob_get_clean());
-
- }
-
- function testSendBodyStream() {
-
- ob_start();
- $stream = fopen('php://memory','r+');
- fwrite($stream,'hello');
- rewind($stream);
- $response = new Response();
- $response->sendBody($stream);
- $this->assertEquals('hello',ob_get_clean());
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/HTTP/UtilTest.php b/vendor/sabre/dav/tests/Sabre/HTTP/UtilTest.php
deleted file mode 100644
index 47a7b98bd..000000000
--- a/vendor/sabre/dav/tests/Sabre/HTTP/UtilTest.php
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-
-namespace Sabre\HTTP;
-
-class UtilTest extends \PHPUnit_Framework_TestCase {
-
- function testParseHTTPDate() {
-
- $times = array(
- 'Wed, 13 Oct 2010 10:26:00 GMT',
- 'Wednesday, 13-Oct-10 10:26:00 GMT',
- 'Wed Oct 13 10:26:00 2010',
- );
-
- $expected = 1286965560;
-
- foreach($times as $time) {
- $result = Util::parseHTTPDate($time);
- $this->assertEquals($expected, $result->format('U'));
- }
-
- $result = Util::parseHTTPDate('Wed Oct 6 10:26:00 2010');
- $this->assertEquals(1286360760, $result->format('U'));
-
- }
-
- function testParseHTTPDateFail() {
-
- $times = array(
- //random string
- 'NOW',
- // not-GMT timezone
- 'Wednesday, 13-Oct-10 10:26:00 UTC',
- // No space before the 6
- 'Wed Oct 6 10:26:00 2010',
- );
-
- foreach($times as $time) {
- $this->assertFalse(Util::parseHTTPDate($time), 'We used the string: ' . $time);
- }
-
- }
-
- function testTimezones() {
-
- $default = date_default_timezone_get();
- date_default_timezone_set('Europe/Amsterdam');
-
- $this->testParseHTTPDate();
-
- date_default_timezone_set($default);
-
- }
-
- function testToHTTPDate() {
-
- $dt = new \DateTime('2011-12-10 12:00:00 +0200');
-
- $this->assertEquals(
- 'Sat, 10 Dec 2011 10:00:00 GMT',
- Util::toHTTPDate($dt)
- );
-
- }
-
- function testStrtotimeFail() {
-
- // Strtotime may return -1 when the date cannot be parsed.
- // We are simulating this situation by testing a date that actually
- // results in -1. (because I have found no other way to break this
- // code)
-
- $time = 'Wed, 13 Oct 1960 10:26:00 GMT';
-
- $this->assertNull(Util::parseHTTPDate($time));
-
- }
-}
diff --git a/vendor/sabre/dav/tests/Sabre/HTTP/VersionTest.php b/vendor/sabre/dav/tests/Sabre/HTTP/VersionTest.php
deleted file mode 100644
index c7094b3bc..000000000
--- a/vendor/sabre/dav/tests/Sabre/HTTP/VersionTest.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-namespace Sabre\HTTP;
-
-class VersionTest extends \PHPUnit_Framework_TestCase {
-
- function testString() {
-
- $v = Version::VERSION;
- $this->assertEquals(-1, version_compare('1.0.0',$v));
-
- $s = Version::STABILITY;
- $this->assertTrue($s == 'alpha' || $s == 'beta' || $s =='stable');
-
- }
-
-}
diff --git a/vendor/sabre/dav/tests/Sabre/TestUtil.php b/vendor/sabre/dav/tests/Sabre/TestUtil.php
index 5a9062498..20bce1ea0 100644
--- a/vendor/sabre/dav/tests/Sabre/TestUtil.php
+++ b/vendor/sabre/dav/tests/Sabre/TestUtil.php
@@ -47,5 +47,12 @@ class TestUtil {
}
+ static function getSQLiteDB() {
+
+ $pdo = new \PDO('sqlite:'.SABRE_TEMPDIR.'/pdobackend');
+ $pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION);
+ return $pdo;
+
+ }
}
diff --git a/vendor/sabre/dav/tests/bootstrap.php b/vendor/sabre/dav/tests/bootstrap.php
index a6493ce6b..325ccd3c7 100644
--- a/vendor/sabre/dav/tests/bootstrap.php
+++ b/vendor/sabre/dav/tests/bootstrap.php
@@ -1,23 +1,36 @@
<?php
-define('SABRE_MYSQLDSN','mysql:host=127.0.0.1;dbname=sabredav');
-define('SABRE_MYSQLUSER','root');
-define('SABRE_MYSQLPASS','');
-
set_include_path(__DIR__ . '/../lib/' . PATH_SEPARATOR . __DIR__ . PATH_SEPARATOR . get_include_path());
-include __DIR__ . '/../vendor/autoload.php';
-include 'Sabre/TestUtil.php';
-include 'Sabre/DAVServerTest.php';
+$autoLoader = include __DIR__ . '/../vendor/autoload.php';
+
+// SabreDAV tests auto loading
+$autoLoader->add('Sabre\\', __DIR__);
+// VObject tests auto loading
+$autoLoader->addPsr4('Sabre\\VObject\\',__DIR__ . '/../vendor/sabre/vobject/tests/VObject');
+$autoLoader->addPsr4('Sabre\\Xml\\',__DIR__ . '/../vendor/sabre/xml/tests/Sabre/Xml');
date_default_timezone_set('UTC');
-define("SABRE_TEMPDIR",dirname(__FILE__) . '/temp/');
+$config = [
+ 'SABRE_TEMPDIR' => dirname(__FILE__) . '/temp/',
+ 'SABRE_HASSQLITE' => in_array('sqlite',PDO::getAvailableDrivers()),
+ 'SABRE_HASMYSQL' => in_array('mysql',PDO::getAvailableDrivers()),
+ 'SABRE_MYSQLDSN' => 'mysql:host=127.0.0.1;dbname=sabredav',
+ 'SABRE_MYSQLUSER' => 'root',
+ 'SABRE_MYSQLPASS' => '',
+];
+
+if (file_exists(__DIR__ . '/config.user.php')) {
+ include __DIR__ . '/config.user.php';
+ foreach($userConfig as $key=>$value) {
+ $config[$key] = $value;
+ }
+}
-// If sqlite is not available, this constant is used to skip the relevant
-// tests
-define('SABRE_HASSQLITE',in_array('sqlite',PDO::getAvailableDrivers()));
-define('SABRE_HASMYSQL', in_array('mysql',PDO::getAvailableDrivers()) && defined('SABRE_MYSQLDSN') && defined('SABRE_MYSQLUSER') && defined('SABRE_MYSQLPASS'));
+foreach($config as $key=>$value) {
+ if (!defined($key)) define($key, $value);
+}
if (!file_exists(SABRE_TEMPDIR)) mkdir(SABRE_TEMPDIR);
if (file_exists('.sabredav')) unlink('.sabredav');
diff --git a/vendor/sabre/dav/tests/phpunit.xml b/vendor/sabre/dav/tests/phpunit.xml
index e93933049..db475f12b 100644
--- a/vendor/sabre/dav/tests/phpunit.xml
+++ b/vendor/sabre/dav/tests/phpunit.xml
@@ -4,12 +4,35 @@
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
+ strict="true"
>
- <testsuite name="sabre-dav">
- <directory>Sabre/</directory>
+ <testsuite name="sabre-event">
+ <directory>../vendor/sabre/event/tests/</directory>
+ </testsuite>
+ <testsuite name="sabre-uri">
+ <directory>../vendor/sabre/uri/tests/</directory>
+ </testsuite>
+ <testsuite name="sabre-xml">
+ <directory>../vendor/sabre/xml/tests/Sabre/Xml/</directory>
+ </testsuite>
+ <testsuite name="sabre-http">
+ <directory>../vendor/sabre/http/tests/HTTP</directory>
</testsuite>
<testsuite name="sabre-vobject">
- <directory>../vendor/sabre/vobject/tests/Sabre/VObject</directory>
+ <directory>../vendor/sabre/vobject/tests/VObject</directory>
+ </testsuite>
+
+ <testsuite name="sabre-dav">
+ <directory>Sabre/DAV</directory>
+ </testsuite>
+ <testsuite name="sabre-davacl">
+ <directory>Sabre/DAVACL</directory>
+ </testsuite>
+ <testsuite name="sabre-caldav">
+ <directory>Sabre/CalDAV</directory>
+ </testsuite>
+ <testsuite name="sabre-carddav">
+ <directory>Sabre/CardDAV</directory>
</testsuite>
<filter>
@@ -17,11 +40,6 @@
<directory suffix=".php">../lib/</directory>
<exclude>
<file>../lib/Sabre/autoload.php</file>
- <file>../lib/Sabre/CalDAV/includes.php</file>
- <file>../lib/Sabre/CardDAV/includes.php</file>
- <file>../lib/Sabre/DAVACL/includes.php</file>
- <file>../lib/Sabre/HTTP/includes.php</file>
- <file>../lib/Sabre/DAV/includes.php</file>
<file>../lib/Sabre/VObject/includes.php</file>
</exclude>
</whitelist>
diff --git a/vendor/sabre/event/.gitignore b/vendor/sabre/event/.gitignore
new file mode 100644
index 000000000..d06a78164
--- /dev/null
+++ b/vendor/sabre/event/.gitignore
@@ -0,0 +1,14 @@
+#composer
+vendor
+composer.lock
+
+#binaries
+bin/sabre-cs-fixer
+bin/php-cs-fixer
+bin/phpunit
+
+#vim lock files
+.*.swp
+
+#development stuff
+tests/cov
diff --git a/vendor/sabre/event/.travis.yml b/vendor/sabre/event/.travis.yml
new file mode 100644
index 000000000..b6719f591
--- /dev/null
+++ b/vendor/sabre/event/.travis.yml
@@ -0,0 +1,26 @@
+language: php
+php:
+ - 5.5
+ - 5.6
+ - 7
+ - hhvm
+
+matrix:
+ allow_failures:
+ - php: hhvm
+
+env:
+ matrix:
+ - LOWEST_DEPS=""
+ - LOWEST_DEPS="--prefer-lowest"
+
+before_script:
+ - composer update --prefer-source $LOWEST_DEPS
+
+script:
+ - ./bin/phpunit
+ - ./bin/sabre-cs-fixer fix . --dry-run --diff
+
+sudo: false
+
+cache: vendor
diff --git a/vendor/sabre/event/CHANGELOG.md b/vendor/sabre/event/CHANGELOG.md
new file mode 100644
index 000000000..9d6d7cfaa
--- /dev/null
+++ b/vendor/sabre/event/CHANGELOG.md
@@ -0,0 +1,78 @@
+ChangeLog
+=========
+
+3.0.0 (2015-11-05)
+------------------
+
+* Now requires PHP 5.5!
+* `Promise::all()` is moved to `Promise\all()`.
+* Aside from the `Promise\all()` function, there's now also `Promise\race()`.
+* `Promise\reject()` and `Promise\resolve()` have also been added.
+* Now 100% compatible with the Ecmascript 6 Promise.
+
+
+3.0.0-alpha1 (2015-10-23)
+-------------------------
+
+* This package now requires PHP 5.5.
+* #26: Added an event loop implementation. Also knows as the Reactor Pattern.
+* Renamed `Promise::error` to `Promise::otherwise` to be consistent with
+ ReactPHP and Guzzle. The `error` method is kept for BC but will be removed
+ in a future version.
+* #27: Support for Promise-based coroutines via the `Sabre\Event\coroutine`
+ function.
+* BC Break: Promises now use the EventLoop to run "then"-events in a separate
+ execution context. In practise that means you need to run the event loop to
+ wait for any `then`/`otherwise` callbacks to trigger.
+* Promises now have a `wait()` method. Allowing you to make a promise
+ synchronous and simply wait for a result (or exception) to happen.
+
+
+2.0.2 (2015-05-19)
+------------------
+
+* This release has no functional changes. It's just been brought up to date
+ with the latest coding standards.
+
+
+2.0.1 (2014-10-06)
+------------------
+
+* Fixed: `$priority` was ignored in `EventEmitter::once` method.
+* Fixed: Breaking the event chain was not possible in `EventEmitter::once`.
+
+
+2.0.0 (2014-06-21)
+------------------
+
+* Added: When calling emit, it's now possible to specify a callback that will be
+ triggered after each method handled. This is dubbed the 'continueCallback' and
+ can be used to implement strategy patterns.
+* Added: Promise object!
+* Changed: EventEmitter::listeners now returns just the callbacks for an event,
+ and no longer returns the list by reference. The list is now automatically
+ sorted by priority.
+* Update: Speed improvements.
+* Updated: It's now possible to remove all listeners for every event.
+* Changed: Now uses psr-4 autoloading.
+
+
+1.0.1 (2014-06-12)
+------------------
+
+* hhvm compatible!
+* Fixed: Issue #4. Compatiblitiy for PHP < 5.4.14.
+
+
+1.0.0 (2013-07-19)
+------------------
+
+* Added: removeListener, removeAllListeners
+* Added: once, to only listen to an event emitting once.
+* Added README.md.
+
+
+0.0.1-alpha (2013-06-29)
+------------------------
+
+* First version!
diff --git a/vendor/sabre/event/LICENSE b/vendor/sabre/event/LICENSE
new file mode 100644
index 000000000..9a495cef0
--- /dev/null
+++ b/vendor/sabre/event/LICENSE
@@ -0,0 +1,27 @@
+Copyright (C) 2013-2015 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 Sabre 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/vendor/sabre/event/README.md b/vendor/sabre/event/README.md
new file mode 100644
index 000000000..364906fd4
--- /dev/null
+++ b/vendor/sabre/event/README.md
@@ -0,0 +1,50 @@
+sabre/event
+===========
+
+A lightweight library for event-based development in PHP.
+
+This library provides the following event-based concepts:
+
+1. EventEmitter.
+2. Promises.
+3. An event loop.
+4. Co-routines.
+
+Full documentation can be found on [the website][1].
+
+Installation
+------------
+
+Make sure you have [composer][3] installed, and then run:
+
+ composer require sabre/event "~3.0.0"
+
+This package requires PHP 5.5. The 2.0 branch is still maintained as well, and
+supports PHP 5.4.
+
+Build status
+------------
+
+| branch | status |
+| ------ | ------ |
+| master | [![Build Status](https://travis-ci.org/fruux/sabre-event.svg?branch=master)](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) |
+
+
+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/event/
+[3]: http://getcomposer.org/
+[4]: http://groups.google.com/group/sabredav-discuss
+[5]: https://github.com/fruux/sabre-event/issues/
diff --git a/vendor/sabre/event/bin/.empty b/vendor/sabre/event/bin/.empty
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/vendor/sabre/event/bin/.empty
diff --git a/vendor/sabre/event/lib/EventEmitter.php b/vendor/sabre/event/lib/EventEmitter.php
new file mode 100644
index 000000000..1bb1c3cf9
--- /dev/null
+++ b/vendor/sabre/event/lib/EventEmitter.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Sabre\Event;
+
+/**
+ * EventEmitter object.
+ *
+ * Instantiate this class, or subclass it for easily creating event emitters.
+ *
+ * @copyright Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class EventEmitter implements EventEmitterInterface {
+
+ use EventEmitterTrait;
+
+}
diff --git a/vendor/sabre/event/lib/EventEmitterInterface.php b/vendor/sabre/event/lib/EventEmitterInterface.php
new file mode 100644
index 000000000..0e2be2cef
--- /dev/null
+++ b/vendor/sabre/event/lib/EventEmitterInterface.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace Sabre\Event;
+
+/**
+ * Event Emitter Interface
+ *
+ * Anything that accepts listeners and emits events should implement this
+ * interface.
+ *
+ * @copyright Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface EventEmitterInterface {
+
+ /**
+ * Subscribe to an event.
+ *
+ * @param string $eventName
+ * @param callable $callBack
+ * @param int $priority
+ * @return void
+ */
+ function on($eventName, callable $callBack, $priority = 100);
+
+ /**
+ * Subscribe to an event exactly once.
+ *
+ * @param string $eventName
+ * @param callable $callBack
+ * @param int $priority
+ * @return void
+ */
+ function once($eventName, callable $callBack, $priority = 100);
+
+ /**
+ * 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.
+ *
+ * @param string $eventName
+ * @param array $arguments
+ * @param callback $continueCallBack
+ * @return bool
+ */
+ function emit($eventName, array $arguments = [], callable $continueCallBack = null);
+
+ /**
+ * 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.
+ *
+ * @param string $eventName
+ * @return callable[]
+ */
+ function listeners($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.
+ *
+ * @param string $eventName
+ * @param callable $listener
+ * @return bool
+ */
+ function removeListener($eventName, callable $listener);
+
+ /**
+ * 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.
+ *
+ * @param string $eventName
+ * @return void
+ */
+ function removeAllListeners($eventName = null);
+
+}
diff --git a/vendor/sabre/event/lib/EventEmitterTrait.php b/vendor/sabre/event/lib/EventEmitterTrait.php
new file mode 100644
index 000000000..257629fae
--- /dev/null
+++ b/vendor/sabre/event/lib/EventEmitterTrait.php
@@ -0,0 +1,211 @@
+<?php
+
+namespace Sabre\Event;
+
+/**
+ * Event Emitter Trait
+ *
+ * This trait contains all the basic functions to implement an
+ * EventEmitterInterface.
+ *
+ * Using the trait + interface allows you to add EventEmitter capabilities
+ * without having to change your base-class.
+ *
+ * @copyright Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+trait EventEmitterTrait {
+
+ /**
+ * The list of listeners
+ *
+ * @var array
+ */
+ protected $listeners = [];
+
+ /**
+ * Subscribe to an event.
+ *
+ * @param string $eventName
+ * @param callable $callBack
+ * @param int $priority
+ * @return void
+ */
+ function on($eventName, callable $callBack, $priority = 100) {
+
+ if (!isset($this->listeners[$eventName])) {
+ $this->listeners[$eventName] = [
+ true, // If there's only one item, it's sorted
+ [$priority],
+ [$callBack]
+ ];
+ } else {
+ $this->listeners[$eventName][0] = false; // marked as unsorted
+ $this->listeners[$eventName][1][] = $priority;
+ $this->listeners[$eventName][2][] = $callBack;
+ }
+
+ }
+
+ /**
+ * Subscribe to an event exactly once.
+ *
+ * @param string $eventName
+ * @param callable $callBack
+ * @param int $priority
+ * @return void
+ */
+ function once($eventName, callable $callBack, $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.
+ *
+ * @param string $eventName
+ * @param array $arguments
+ * @param callback $continueCallBack
+ * @return bool
+ */
+ function emit($eventName, array $arguments = [], callable $continueCallBack = null) {
+
+ 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.
+ *
+ * @param string $eventName
+ * @return callable[]
+ */
+ function listeners($eventName) {
+
+ if (!isset($this->listeners[$eventName])) {
+ return [];
+ }
+
+ // The list is not sorted
+ if (!$this->listeners[$eventName][0]) {
+
+ // Sorting
+ array_multisort($this->listeners[$eventName][1], SORT_NUMERIC, $this->listeners[$eventName][2]);
+
+ // Marking the listeners as sorted
+ $this->listeners[$eventName][0] = true;
+ }
+
+ return $this->listeners[$eventName][2];
+
+ }
+
+ /**
+ * 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.
+ *
+ * @param string $eventName
+ * @param callable $listener
+ * @return bool
+ */
+ function removeListener($eventName, callable $listener) {
+
+ if (!isset($this->listeners[$eventName])) {
+ return false;
+ }
+ foreach ($this->listeners[$eventName][2] as $index => $check) {
+ if ($check === $listener) {
+ unset($this->listeners[$eventName][1][$index]);
+ unset($this->listeners[$eventName][2][$index]);
+ 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.
+ *
+ * @param string $eventName
+ * @return void
+ */
+ function removeAllListeners($eventName = null) {
+
+ if (!is_null($eventName)) {
+ unset($this->listeners[$eventName]);
+ } else {
+ $this->listeners = [];
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/event/lib/Loop/Loop.php b/vendor/sabre/event/lib/Loop/Loop.php
new file mode 100644
index 000000000..86ee7c8b0
--- /dev/null
+++ b/vendor/sabre/event/lib/Loop/Loop.php
@@ -0,0 +1,386 @@
+<?php
+
+namespace Sabre\Event\Loop;
+
+/**
+ * A simple eventloop implementation.
+ *
+ * This eventloop supports:
+ * * nextTick
+ * * setTimeout for delayed functions
+ * * setInterval for repeating functions
+ * * stream events using stream_select
+ *
+ * @copyright Copyright (C) 2007-2015 fruux GmbH. (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Loop {
+
+ /**
+ * Executes a function after x seconds.
+ *
+ * @param callable $cb
+ * @param float $timeout timeout in seconds
+ * @return void
+ */
+ function setTimeout(callable $cb, $timeout) {
+
+ $triggerTime = microtime(true) + ($timeout);
+
+ if (!$this->timers) {
+ // Special case when the timers array was empty.
+ $this->timers[] = [$triggerTime, $cb];
+ return;
+ }
+
+ // We need to insert these values in the timers array, but the timers
+ // array must be in reverse-order of trigger times.
+ //
+ // So here we search the array for the insertion point.
+ $index = count($this->timers) - 1;
+ while (true) {
+ if ($triggerTime < $this->timers[$index][0]) {
+ array_splice(
+ $this->timers,
+ $index + 1,
+ 0,
+ [[$triggerTime, $cb]]
+ );
+ break;
+ } elseif ($index === 0) {
+ array_unshift($this->timers, [$triggerTime, $cb]);
+ break;
+ }
+ $index--;
+
+ }
+
+ }
+
+ /**
+ * Executes a function every x seconds.
+ *
+ * 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) {
+
+ $keepGoing = true;
+ $f = null;
+
+ $f = function() use ($cb, &$f, $timeout, &$keepGoing) {
+ if ($keepGoing) {
+ $cb();
+ $this->setTimeout($f, $timeout);
+ }
+ };
+ $this->setTimeout($f, $timeout);
+
+ // Really the only thing that matters is returning the $keepGoing
+ // boolean value.
+ //
+ // We need to pack it in an array to allow returning by reference.
+ // Because I'm worried people will be confused by using a boolean as a
+ // sort of identifier, I added an extra string.
+ return ['I\'m an implementation detail', &$keepGoing];
+
+ }
+
+ /**
+ * Stops a running internval.
+ *
+ * @param array $intervalId
+ * @return void
+ */
+ function clearInterval($intervalId) {
+
+ $intervalId[1] = false;
+
+ }
+
+ /**
+ * Runs a function immediately at the next iteration of the loop.
+ *
+ * @param callable $cb
+ * @return void
+ */
+ function nextTick(callable $cb) {
+
+ $this->nextTick[] = $cb;
+
+ }
+
+
+ /**
+ * Adds a read stream.
+ *
+ * The callback will be called as soon as there is something to read from
+ * the stream.
+ *
+ * You MUST call removeReadStream after you are done with the stream, to
+ * prevent the eventloop from never stopping.
+ *
+ * @param resource $stream
+ * @param callable $cb
+ * @return void
+ */
+ function addReadStream($stream, callable $cb) {
+
+ $this->readStreams[(int)$stream] = $stream;
+ $this->readCallbacks[(int)$stream] = $cb;
+
+ }
+
+ /**
+ * Adds a write stream.
+ *
+ * The callback will be called as soon as the system reports it's ready to
+ * receive writes on the stream.
+ *
+ * You MUST call removeWriteStream after you are done with the stream, to
+ * prevent the eventloop from never stopping.
+ *
+ * @param resource $stream
+ * @param callable $cb
+ * @return void
+ */
+ function addWriteStream($stream, callable $cb) {
+
+ $this->writeStreams[(int)$stream] = $stream;
+ $this->writeCallbacks[(int)$stream] = $cb;
+
+ }
+
+ /**
+ * Stop watching a stream for reads.
+ *
+ * @param resource $stream
+ * @return void
+ */
+ function removeReadStream($stream) {
+
+ unset(
+ $this->readStreams[(int)$stream],
+ $this->readCallbacks[(int)$stream]
+ );
+
+ }
+
+ /**
+ * Stop watching a stream for writes.
+ *
+ * @param resource $stream
+ * @return void
+ */
+ function removeWriteStream($stream) {
+
+ unset(
+ $this->writeStreams[(int)$stream],
+ $this->writeCallbacks[(int)$stream]
+ );
+
+ }
+
+
+ /**
+ * Runs the loop.
+ *
+ * This function will run continiously, until there's no more events to
+ * handle.
+ *
+ * @return void
+ */
+ function run() {
+
+ $this->running = true;
+
+ do {
+
+ $hasEvents = $this->tick(true);
+
+ } while ($this->running && $hasEvents);
+ $this->running = false;
+
+ }
+
+ /**
+ * Executes all pending events.
+ *
+ * If $block is turned true, this function will block until any event is
+ * triggered.
+ *
+ * If there are now timeouts, nextTick callbacks or events in the loop at
+ * all, this function will exit immediately.
+ *
+ * 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) {
+
+ $this->runNextTicks();
+ $nextTimeout = $this->runTimers();
+
+ // Calculating how long runStreams should at most wait.
+ if (!$block) {
+ // Don't wait
+ $streamWait = 0;
+ } elseif ($this->nextTick) {
+ // There's a pending 'nextTick'. Don't wait.
+ $streamWait = 0;
+ } elseif (is_numeric($nextTimeout)) {
+ // Wait until the next Timeout should trigger.
+ $streamWait = $nextTimeout;
+ } else {
+ // Wait indefinitely
+ $streamWait = null;
+ }
+
+ $this->runStreams($streamWait);
+
+ return ($this->readStreams || $this->writeStreams || $this->nextTick || $this->timers);
+
+ }
+
+ /**
+ * Stops a running eventloop
+ *
+ * @return void
+ */
+ function stop() {
+
+ $this->running = false;
+
+ }
+
+ /**
+ * Executes all 'nextTick' callbacks.
+ *
+ * return void
+ */
+ protected function runNextTicks() {
+
+ $nextTick = $this->nextTick;
+ $this->nextTick = [];
+
+ foreach ($nextTick as $cb) {
+ $cb();
+ }
+
+ }
+
+ /**
+ * Runs all pending timers.
+ *
+ * After running the timer callbacks, this function returns the number of
+ * seconds until the next timer should be executed.
+ *
+ * If there's no more pending timers, this function returns null.
+ *
+ * @return float
+ */
+ protected function runTimers() {
+
+ $now = microtime(true);
+ while (($timer = array_pop($this->timers)) && $timer[0] < $now) {
+ $timer[1]();
+ }
+ // Add the last timer back to the array.
+ if ($timer) {
+ $this->timers[] = $timer;
+ return $timer[0] - microtime(true);
+ }
+
+ }
+
+ /**
+ * Runs all pending stream events.
+ *
+ * @param float $timeout
+ */
+ protected function runStreams($timeout) {
+
+ if ($this->readStreams || $this->writeStreams) {
+
+ $read = $this->readStreams;
+ $write = $this->writeStreams;
+ $except = null;
+ if (stream_select($read, $write, $except, null, $timeout)) {
+
+ // See PHP Bug https://bugs.php.net/bug.php?id=62452
+ // Fixed in PHP7
+ foreach ($read as $readStream) {
+ $readCb = $this->readCallbacks[(int)$readStream];
+ $readCb();
+ }
+ foreach ($write as $writeStream) {
+ $writeCb = $this->writeCallbacks[(int)$writeStream];
+ $writeCb();
+ }
+
+ }
+
+ } elseif ($this->running && ($this->nextTick || $this->timers)) {
+ usleep($timeout !== null ? $timeout * 1000000 : 200000);
+ }
+
+ }
+
+ /**
+ * Is the main loop active
+ *
+ * @var bool
+ */
+ protected $running = false;
+
+ /**
+ * A list of timers, added by setTimeout.
+ *
+ * @var array
+ */
+ protected $timers = [];
+
+ /**
+ * A list of 'nextTick' callbacks.
+ *
+ * @var callable[]
+ */
+ protected $nextTick = [];
+
+ /**
+ * List of readable streams for stream_select, indexed by stream id.
+ *
+ * @var resource[]
+ */
+ protected $readStreams = [];
+
+ /**
+ * List of writable streams for stream_select, indexed by stream id.
+ *
+ * @var resource[]
+ */
+ protected $writeStreams = [];
+
+ /**
+ * List of read callbacks, indexed by stream id.
+ *
+ * @var callback[]
+ */
+ protected $readCallbacks = [];
+
+ /**
+ * List of write callbacks, indexed by stream id.
+ *
+ * @var callback[]
+ */
+ protected $writeCallbacks = [];
+
+
+}
diff --git a/vendor/sabre/event/lib/Loop/functions.php b/vendor/sabre/event/lib/Loop/functions.php
new file mode 100644
index 000000000..56c5bc8c7
--- /dev/null
+++ b/vendor/sabre/event/lib/Loop/functions.php
@@ -0,0 +1,183 @@
+<?php
+
+namespace Sabre\Event\Loop;
+
+/**
+ * Executes a function after x seconds.
+ *
+ * @param callable $cb
+ * @param float $timeout timeout in seconds
+ * @return void
+ */
+function setTimeout(callable $cb, $timeout) {
+
+ instance()->setTimeout($cb, $timeout);
+
+}
+
+/**
+ * Executes a function every x seconds.
+ *
+ * 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) {
+
+ return instance()->setInterval($cb, $timeout);
+
+}
+
+/**
+ * Stops a running internval.
+ *
+ * @param array $intervalId
+ * @return void
+ */
+function clearInterval($intervalId) {
+
+ instance()->clearInterval($intervalId);
+
+}
+
+/**
+ * Runs a function immediately at the next iteration of the loop.
+ *
+ * @param callable $cb
+ * @return void
+ */
+function nextTick(callable $cb) {
+
+ instance()->nextTick($cb);
+
+}
+
+
+/**
+ * Adds a read stream.
+ *
+ * The callback will be called as soon as there is something to read from
+ * the stream.
+ *
+ * You MUST call removeReadStream after you are done with the stream, to
+ * prevent the eventloop from never stopping.
+ *
+ * @param resource $stream
+ * @param callable $cb
+ * @return void
+ */
+function addReadStream($stream, callable $cb) {
+
+ instance()->addReadStream($stream, $cb);
+
+}
+
+/**
+ * Adds a write stream.
+ *
+ * The callback will be called as soon as the system reports it's ready to
+ * receive writes on the stream.
+ *
+ * You MUST call removeWriteStream after you are done with the stream, to
+ * prevent the eventloop from never stopping.
+ *
+ * @param resource $stream
+ * @param callable $cb
+ * @return void
+ */
+function addWriteStream($stream, callable $cb) {
+
+ instance()->addWriteStream($stream, $cb);
+
+}
+
+/**
+ * Stop watching a stream for reads.
+ *
+ * @param resource $stream
+ * @return void
+ */
+function removeReadStream($stream) {
+
+ instance()->removeReadStream($stream);
+
+}
+
+/**
+ * Stop watching a stream for writes.
+ *
+ * @param resource $stream
+ * @return void
+ */
+function removeWriteStream($stream) {
+
+ instance()->removeWriteStream($stream);
+
+}
+
+
+/**
+ * Runs the loop.
+ *
+ * This function will run continiously, until there's no more events to
+ * handle.
+ *
+ * @return void
+ */
+function run() {
+
+ instance()->run();
+
+}
+
+/**
+ * Executes all pending events.
+ *
+ * If $block is turned true, this function will block until any event is
+ * triggered.
+ *
+ * If there are now timeouts, nextTick callbacks or events in the loop at
+ * all, this function will exit immediately.
+ *
+ * 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) {
+
+ return instance()->tick($block);
+
+}
+
+/**
+ * Stops a running eventloop
+ *
+ * @return void
+ */
+function stop() {
+
+ instance()->stop();
+
+}
+
+/**
+ * Retrieves or sets the global Loop object.
+ *
+ * @param Loop $newLoop
+ */
+function instance(Loop $newLoop = null) {
+
+ static $loop;
+ if ($newLoop) {
+ $loop = $newLoop;
+ } elseif (!$loop) {
+ $loop = new Loop();
+ }
+ return $loop;
+
+}
diff --git a/vendor/sabre/event/lib/Promise.php b/vendor/sabre/event/lib/Promise.php
new file mode 100644
index 000000000..1c874c1bd
--- /dev/null
+++ b/vendor/sabre/event/lib/Promise.php
@@ -0,0 +1,320 @@
+<?php
+
+namespace Sabre\Event;
+
+use Exception;
+
+/**
+ * An implementation of the Promise pattern.
+ *
+ * A promise represents the result of an asynchronous operation.
+ * At any given point a promise can be in one of three states:
+ *
+ * 1. Pending (the promise does not have a result yet).
+ * 2. Fulfilled (the asynchronous operation has completed with a result).
+ * 3. Rejected (the asynchronous operation has completed with an error).
+ *
+ * To get a callback when the operation has finished, use the `then` method.
+ *
+ * @copyright Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Promise {
+
+ /**
+ * The asynchronous operation is pending.
+ */
+ const PENDING = 0;
+
+ /**
+ * The asynchronous operation has completed, and has a result.
+ */
+ const FULFILLED = 1;
+
+ /**
+ * The asynchronous operation has completed with an error.
+ */
+ const REJECTED = 2;
+
+ /**
+ * The current state of this promise.
+ *
+ * @var int
+ */
+ public $state = self::PENDING;
+
+ /**
+ * Creates the promise.
+ *
+ * The passed argument is the executor. The executor is automatically
+ * called with two arguments.
+ *
+ * Each are callbacks that map to $this->fulfill and $this->reject.
+ * Using the executor is optional.
+ *
+ * @param callable $executor
+ */
+ function __construct(callable $executor = null) {
+
+ if ($executor) {
+ $executor(
+ [$this, 'fulfill'],
+ [$this, 'reject']
+ );
+ }
+
+ }
+
+ /**
+ * This method allows you to specify the callback that will be called after
+ * the promise has been fulfilled or rejected.
+ *
+ * Both arguments are optional.
+ *
+ * This method returns a new promise, which can be used for chaining.
+ * If either the onFulfilled or onRejected callback is called, you may
+ * return a result from this callback.
+ *
+ * If the result of this callback is yet another promise, the result of
+ * _that_ promise will be used to set the result of the returned promise.
+ *
+ * If either of the callbacks return any other value, the returned promise
+ * is automatically fulfilled with that value.
+ *
+ * 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) {
+
+ // This new subPromise will be returned from this function, and will
+ // be fulfilled with the result of the onFulfilled or onRejected event
+ // handlers.
+ $subPromise = new self();
+
+ switch ($this->state) {
+ case self::PENDING :
+ // The operation is pending, so we keep a reference to the
+ // event handlers so we can call them later.
+ $this->subscribers[] = [$subPromise, $onFulfilled, $onRejected];
+ break;
+ case self::FULFILLED :
+ // The async operation is already fulfilled, so we trigger the
+ // onFulfilled callback asap.
+ $this->invokeCallback($subPromise, $onFulfilled);
+ break;
+ case self::REJECTED :
+ // The async operation failed, so we call teh onRejected
+ // callback asap.
+ $this->invokeCallback($subPromise, $onRejected);
+ break;
+ }
+ return $subPromise;
+
+ }
+
+ /**
+ * Add a callback for when this promise is rejected.
+ *
+ * Its usage is identical to then(). However, the otherwise() function is
+ * preferred.
+ *
+ * @param callable $onRejected
+ * @return Promise
+ */
+ function otherwise(callable $onRejected) {
+
+ return $this->then(null, $onRejected);
+
+ }
+
+ /**
+ * Marks this promise as fulfilled and sets its return value.
+ *
+ * @param mixed $value
+ * @return void
+ */
+ function fulfill($value = null) {
+ 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');
+ }
+ $this->state = self::FULFILLED;
+ $this->value = $value;
+ foreach ($this->subscribers as $subscriber) {
+ $this->invokeCallback($subscriber[0], $subscriber[1]);
+ }
+ }
+
+ /**
+ * 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) {
+ 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');
+ }
+ $this->state = self::REJECTED;
+ $this->value = $reason;
+ foreach ($this->subscribers as $subscriber) {
+ $this->invokeCallback($subscriber[0], $subscriber[2]);
+ }
+
+ }
+
+ /**
+ * Stops execution until this promise is resolved.
+ *
+ * This method stops exection completely. If the promise is successful with
+ * a value, this method will return this value. If the promise was
+ * rejected, this method will throw an exception.
+ *
+ * This effectively turns the asynchronous operation into a synchronous
+ * one. In PHP it might be useful to call this on the last promise in a
+ * chain.
+ *
+ * @throws Exception
+ * @return mixed
+ */
+ function wait() {
+
+ $hasEvents = true;
+ while ($this->state === self::PENDING) {
+
+ if (!$hasEvents) {
+ throw new \LogicException('There were no more events in the loop. This promise will never be fulfilled.');
+ }
+
+ // As long as the promise is not fulfilled, we tell the event loop
+ // to handle events, and to block.
+ $hasEvents = Loop\tick(true);
+
+ }
+
+ if ($this->state === self::FULFILLED) {
+ // If the state of this promise is fulfilled, we can return the value.
+ return $this->value;
+ } 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);
+ }
+ }
+
+
+ }
+
+
+ /**
+ * A list of subscribers. Subscribers are the callbacks that want us to let
+ * them know if the callback was fulfilled or rejected.
+ *
+ * @var array
+ */
+ protected $subscribers = [];
+
+ /**
+ * The result of the promise.
+ *
+ * If the promise was fulfilled, this will be the result value. If the
+ * promise was rejected, this property hold the rejection reason.
+ *
+ * @var mixed
+ */
+ protected $value = null;
+
+ /**
+ * This method is used to call either an onFulfilled or onRejected callback.
+ *
+ * This method makes sure that the result of these callbacks are handled
+ * correctly, and any chained promises are also correctly fulfilled or
+ * rejected.
+ *
+ * @param Promise $subPromise
+ * @param callable $callBack
+ * @return void
+ */
+ private function invokeCallback(Promise $subPromise, callable $callBack = null) {
+
+ // We use 'nextTick' to ensure that the event handlers are always
+ // triggered outside of the calling stack in which they were originally
+ // passed to 'then'.
+ //
+ // This makes the order of execution more predictable.
+ Loop\nextTick(function() use ($callBack, $subPromise) {
+ if (is_callable($callBack)) {
+ try {
+
+ $result = $callBack($this->value);
+ if ($result instanceof self) {
+ // If the callback (onRejected or onFulfilled)
+ // returned a promise, we only fulfill or reject the
+ // chained promise once that promise has also been
+ // resolved.
+ $result->then([$subPromise, 'fulfill'], [$subPromise, 'reject']);
+ } else {
+ // If the callback returned any other value, we
+ // immediately fulfill the chained promise.
+ $subPromise->fulfill($result);
+ }
+ } catch (Exception $e) {
+ // If the event handler threw an exception, we need to make sure that
+ // the chained promise is rejected as well.
+ $subPromise->reject($e);
+ }
+ } else {
+ if ($this->state === self::FULFILLED) {
+ $subPromise->fulfill($this->value);
+ } else {
+ $subPromise->reject($this->value);
+ }
+ }
+ });
+ }
+
+ /**
+ * 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/vendor/sabre/event/lib/Promise/functions.php b/vendor/sabre/event/lib/Promise/functions.php
new file mode 100644
index 000000000..3604b8aaa
--- /dev/null
+++ b/vendor/sabre/event/lib/Promise/functions.php
@@ -0,0 +1,135 @@
+<?php
+
+namespace Sabre\Event\Promise;
+
+use Sabre\Event\Promise;
+
+/**
+ * This file contains a set of functions that are useful for dealing with the
+ * Promise object.
+ *
+ * @copyright Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+
+
+/**
+ * This function takes an array of Promises, and returns a Promise that
+ * resolves when all of the given arguments have resolved.
+ *
+ * The returned Promise will resolve with a value that's an array of all the
+ * values the given promises have been resolved with.
+ *
+ * This array will be in the exact same order as the array of input promises.
+ *
+ * If any of the given Promises fails, the returned promise will immidiately
+ * fail with the first Promise that fails, and its reason.
+ *
+ * @param Promise[] $promises
+ * @return Promise
+ */
+function all(array $promises) {
+
+ return new Promise(function($success, $fail) use ($promises) {
+
+ $successCount = 0;
+ $completeResult = [];
+
+ foreach ($promises as $promiseIndex => $subPromise) {
+
+ $subPromise->then(
+ function($result) use ($promiseIndex, &$completeResult, &$successCount, $success, $promises) {
+ $completeResult[$promiseIndex] = $result;
+ $successCount++;
+ if ($successCount === count($promises)) {
+ $success($completeResult);
+ }
+ return $result;
+ }
+ )->error(
+ function($reason) use ($fail) {
+ $fail($reason);
+ }
+ );
+
+ }
+ });
+
+}
+
+/**
+ * The race function returns a promise that resolves or rejects as soon as
+ * one of the promises in the argument resolves or rejects.
+ *
+ * The returned promise will resolve or reject with the value or reason of
+ * that first promise.
+ *
+ * @param Promise[] $promises
+ * @return Promise
+ */
+function race(array $promises) {
+
+ return new Promise(function($success, $fail) use ($promises) {
+
+ $alreadyDone = false;
+ foreach ($promises as $promise) {
+
+ $promise->then(
+ function($result) use ($success, &$alreadyDone) {
+ if ($alreadyDone) {
+ return;
+ }
+ $alreadyDone = true;
+ $success($result);
+ },
+ function($reason) use ($fail, &$alreadyDone) {
+ if ($alreadyDone) {
+ return;
+ }
+ $alreadyDone = true;
+ $fail($reason);
+ }
+ );
+
+ }
+
+ });
+
+}
+
+
+/**
+ * Returns a Promise that resolves with the given value.
+ *
+ * If the value is a promise, the returned promise will attach itself to that
+ * promise and eventually get the same state as the followed promise.
+ *
+ * @param mixed $value
+ * @return Promise
+ */
+function resolve($value) {
+
+ if ($value instanceof Promise) {
+ return $value->then();
+ } else {
+ $promise = new Promise();
+ $promise->fulfill($value);
+ return $promise;
+ }
+
+}
+
+/**
+ * Returns a Promise that will reject with the given reason.
+ *
+ * @param mixed $reason
+ * @return Promise
+ */
+function reject($reason) {
+
+ $promise = new Promise();
+ $promise->reject($reason);
+ return $promise;
+
+}
diff --git a/vendor/sabre/event/lib/PromiseAlreadyResolvedException.php b/vendor/sabre/event/lib/PromiseAlreadyResolvedException.php
new file mode 100644
index 000000000..86a6c5b3f
--- /dev/null
+++ b/vendor/sabre/event/lib/PromiseAlreadyResolvedException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Sabre\Event;
+
+/**
+ * This exception is thrown when the user tried to reject or fulfill a promise,
+ * after either of these actions were already performed.
+ *
+ * @copyright Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PromiseAlreadyResolvedException extends \LogicException {
+
+}
diff --git a/vendor/sabre/event/lib/Version.php b/vendor/sabre/event/lib/Version.php
new file mode 100644
index 000000000..5de22193f
--- /dev/null
+++ b/vendor/sabre/event/lib/Version.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Sabre\Event;
+
+/**
+ * This class contains the version number for this package.
+ *
+ * @copyright Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Version {
+
+ /**
+ * Full version number
+ */
+ const VERSION = '3.0.0';
+
+}
diff --git a/vendor/sabre/event/lib/coroutine.php b/vendor/sabre/event/lib/coroutine.php
new file mode 100644
index 000000000..19c0ba8a7
--- /dev/null
+++ b/vendor/sabre/event/lib/coroutine.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace Sabre\Event;
+
+use Generator;
+use Exception;
+
+/**
+ * Turn asynchronous promise-based code into something that looks synchronous
+ * again, through the use of generators.
+ *
+ * Example without coroutines:
+ *
+ * $promise = $httpClient->request('GET', '/foo');
+ * $promise->then(function($value) {
+ *
+ * return $httpClient->request('DELETE','/foo');
+ *
+ * })->then(function($value) {
+ *
+ * return $httpClient->request('PUT', '/foo');
+ *
+ * })->error(function($reason) {
+ *
+ * echo "Failed because: $reason\n";
+ *
+ * });
+ *
+ * Example with coroutines:
+ *
+ * coroutine(function() {
+ *
+ * try {
+ * yield $httpClient->request('GET', '/foo');
+ * yield $httpClient->request('DELETE', /foo');
+ * yield $httpClient->request('PUT', '/foo');
+ * } catch(\Exception $reason) {
+ * echo "Failed because: $reason\n";
+ * }
+ *
+ * });
+ *
+ * @copyright Copyright (C) 2013-2015 fruux GmbH. All rights reserved.
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+function coroutine(callable $gen) {
+
+ $generator = $gen();
+ if (!$generator instanceof Generator) {
+ throw new \InvalidArgumentException('You must pass a generator function');
+ }
+
+ // 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.
+ */
+ $advanceGenerator = function() use (&$advanceGenerator, $generator, $promise, &$lastYieldResult) {
+
+ while ($generator->valid()) {
+
+ $yieldedValue = $generator->current();
+ 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));
+ }
+ $advanceGenerator();
+ }
+ )->error(function($reason) use ($promise) {
+ // This error handler would be called, if something in the
+ // generator throws an exception, and it's not caught
+ // locally.
+ $promise->reject($reason);
+ });
+ // We need to break out of the loop, because $advanceGenerator
+ // will be called asynchronously when the promise has a result.
+ 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);
+ }
+
+ };
+
+ try {
+ $advanceGenerator();
+ } catch (Exception $e) {
+ $promise->reject($e);
+ }
+
+ return $promise;
+
+}
diff --git a/vendor/sabre/event/phpunit.xml.dist b/vendor/sabre/event/phpunit.xml.dist
new file mode 100644
index 000000000..ccd59be9c
--- /dev/null
+++ b/vendor/sabre/event/phpunit.xml.dist
@@ -0,0 +1,18 @@
+<phpunit
+ colors="true"
+ bootstrap="vendor/autoload.php"
+ convertErrorsToExceptions="true"
+ convertNoticesToExceptions="true"
+ convertWarningsToExceptions="true"
+ strict="true"
+ >
+ <testsuite name="sabre-event">
+ <directory>tests/</directory>
+ </testsuite>
+
+ <filter>
+ <whitelist addUncoveredFilesFromWhitelist="true">
+ <directory suffix=".php">./lib/</directory>
+ </whitelist>
+ </filter>
+</phpunit>
diff --git a/vendor/sabre/http/.gitignore b/vendor/sabre/http/.gitignore
new file mode 100644
index 000000000..8c97686fb
--- /dev/null
+++ b/vendor/sabre/http/.gitignore
@@ -0,0 +1,15 @@
+# Composer
+vendor/
+composer.lock
+
+# Tests
+tests/cov/
+
+# Composer binaries
+bin/phpunit
+bin/phpcs
+bin/php-cs-fixer
+bin/sabre-cs-fixer
+
+# Vim
+.*.swp
diff --git a/vendor/sabre/http/.travis.yml b/vendor/sabre/http/.travis.yml
new file mode 100644
index 000000000..490e42e1e
--- /dev/null
+++ b/vendor/sabre/http/.travis.yml
@@ -0,0 +1,24 @@
+language: php
+php:
+ - 5.4
+ - 5.5
+ - 5.6
+ - 7
+ - hhvm
+
+matrix:
+ fast_finish: true
+
+env:
+ matrix:
+ - PREFER_LOWEST=""
+ - PREFER_LOWEST="--prefer-lowest"
+
+
+before_script:
+ - composer self-update
+ - composer update --prefer-source $PREFER_LOWEST
+
+script:
+ - ./bin/phpunit --configuration tests/phpunit.xml
+ - ./bin/sabre-cs-fixer fix . --dry-run --diff
diff --git a/vendor/sabre/http/CHANGELOG.md b/vendor/sabre/http/CHANGELOG.md
new file mode 100644
index 000000000..9a751d8fb
--- /dev/null
+++ b/vendor/sabre/http/CHANGELOG.md
@@ -0,0 +1,250 @@
+ChangeLog
+=========
+
+4.2.1 (2016-01-06)
+------------------
+
+* #56: `getBodyAsString` now returns at most as many bytes as the contents of
+ the `Content-Length` header. This allows users to pass much larger strings
+ without having to copy and truncate them.
+
+
+4.2.0 (2016-01-04)
+------------------
+
+* This package now supports sabre/event 3.0.
+* The client now sets a default `User-Agent` header identifying this library.
+
+
+4.1.0 (2015-09-04)
+------------------
+
+* The async client wouldn't `wait()` for new http requests being started
+ after the (previous) last request in the queue was resolved.
+* Added `Sabre\HTTP\Auth\Bearer`, to easily extract a OAuth2 bearer token.
+
+
+4.0.0 (2015-05-20)
+------------------
+
+* Deprecated: All static functions from `Sabre\HTTP\URLUtil` and
+ `Sabre\HTTP\Util` moved to a separate `functions.php`, which is also
+ autoloaded. The old functions are still there, but will be removed in a
+ future version. (#49)
+
+
+4.0.0-alpha3 (2015-05-19)
+-------------------------
+
+* Added a parser for the HTTP `Prefer` header, as defined in [RFC7240][rfc7240].
+* Deprecated `Sabre\HTTP\Util::parseHTTPDate`, use `Sabre\HTTP\parseDate()`.
+* Deprecated `Sabre\HTTP\Util::toHTTPDate` use `Sabre\HTTP\toDate()`.
+
+
+4.0.0-alpha2 (2015-05-18)
+-------------------------
+
+* #45: Don't send more data than what is promised in the HTTP content-length.
+ (@dratini0).
+* #43: `getCredentials` returns null if incomplete. (@Hywan)
+* #48: Now using php-cs-fixer to make our CS consistent (yay!)
+* This includes fixes released in version 3.0.5.
+
+
+4.0.0-alpha1 (2015-02-25)
+-------------------------
+
+* #41: Fixing bugs related to comparing URLs in `Request::getPath()`.
+* #41: This library now uses the `sabre/uri` package for uri handling.
+* Added `421 Misdirected Request` from the HTTP/2.0 spec.
+
+
+3.0.5 (2015-05-11)
+------------------
+
+* #47 #35: When re-using the client and doing any request after a `HEAD`
+ request, the client discards the body.
+
+
+3.0.4 (2014-12-10)
+------------------
+
+* #38: The Authentication helpers no longer overwrite any existing
+ `WWW-Authenticate` headers, but instead append new headers. This ensures
+ that multiple authentication systems can exist in the same environment.
+
+
+3.0.3 (2014-12-03)
+------------------
+
+* Hiding `Authorization` header value from `Request::__toString`.
+
+
+3.0.2 (2014-10-09)
+------------------
+
+* When parsing `Accept:` headers, we're ignoring invalid parts. Before we
+ would throw a PHP E_NOTICE.
+
+
+3.0.1 (2014-09-29)
+------------------
+
+* Minor change in unittests.
+
+
+3.0.0 (2014-09-23)
+------------------
+
+* `getHeaders()` now returns header values as an array, just like psr/http.
+* Added `hasHeader()`.
+
+
+2.1.0-alpha1 (2014-09-15)
+-------------------------
+
+* Changed: Copied most of the header-semantics for the PSR draft for
+ representing HTTP messages. [Reference here][psr-http].
+* This means that `setHeaders()` does not wipe out every existing header
+ anymore.
+* We also support multiple headers with the same name.
+* Use `Request::getHeaderAsArray()` and `Response::getHeaderAsArray()` to
+ get a hold off multiple headers with the same name.
+* If you use `getHeader()`, and there's more than 1 header with that name, we
+ concatenate all these with a comma.
+* `addHeader()` will now preserve an existing header with that name, and add a
+ second header with the same name.
+* The message class should be a lot faster now for looking up headers. No more
+ array traversal, because we maintain a tiny index.
+* Added: `URLUtil::resolve()` to make resolving relative urls super easy.
+* Switched to PSR-4.
+* #12: Circumventing CURL's FOLLOW_LOCATION and doing it in PHP instead. This
+ fixes compatibility issues with people that have open_basedir turned on.
+* Added: Content negotiation now correctly support mime-type parameters such as
+ charset.
+* Changed: `Util::negotiate()` is now deprecated. Use
+ `Util::negotiateContentType()` instead.
+* #14: The client now only follows http and https urls.
+
+
+2.0.4 (2014-07-14)
+------------------
+
+* Changed: No longer escaping @ in urls when it's not needed.
+* Fixed: #7: Client now correctly deals with responses without a body.
+
+
+2.0.3 (2014-04-17)
+------------------
+
+* Now works on hhvm!
+* Fixed: Now throwing an error when a Request object is being created with
+ arguments that were valid for sabre/http 1.0. Hopefully this will aid with
+ debugging for upgraders.
+
+
+2.0.2 (2014-02-09)
+------------------
+
+* Fixed: Potential security problem in the client.
+
+
+2.0.1 (2014-01-09)
+------------------
+
+* Fixed: getBodyAsString on an empty body now works.
+* Fixed: Version string
+
+
+2.0.0 (2014-01-08)
+------------------
+
+* Removed: Request::createFromPHPRequest. This is now handled by
+ Sapi::getRequest.
+
+
+2.0.0alpha6 (2014-01-03)
+------------------------
+
+* Added: Asynchronous HTTP client. See examples/asyncclient.php.
+* Fixed: Issue #4: Don't escape colon (:) when it's not needed.
+* Fixed: Fixed a bug in the content negotation script.
+* Fixed: Fallback for when CURLOPT_POSTREDIR is not defined (mainly for hhvm).
+* Added: The Request and Response object now have a `__toString()` method that
+ serializes the objects into a standard HTTP message. This is mainly for
+ debugging purposes.
+* Changed: Added Response::getStatusText(). This method returns the
+ human-readable HTTP status message. This part has been removed from
+ Response::getStatus(), which now always returns just the status code as an
+ int.
+* Changed: Response::send() is now Sapi::sendResponse($response).
+* Changed: Request::createFromPHPRequest is now Sapi::getRequest().
+* Changed: Message::getBodyAsStream and Message::getBodyAsString were added. The
+ existing Message::getBody changed it's behavior, so be careful.
+
+
+2.0.0alpha5 (2013-11-07)
+------------------------
+
+* Added: HTTP Status 451 Unavailable For Legal Reasons. Fight government
+ censorship!
+* Added: Ability to catch and retry http requests in the client when a curl
+ error occurs.
+* Changed: Request::getPath does not return the query part of the url, so
+ everything after the ? is stripped.
+* Added: a reverse proxy example.
+
+
+2.0.0alpha4 (2013-08-07)
+------------------------
+
+* Fixed: Doing a GET request with the client uses the last used HTTP method
+ instead.
+* Added: HttpException
+* Added: The Client class can now automatically emit exceptions when HTTP errors
+ occurred.
+
+
+2.0.0alpha3 (2013-07-24)
+------------------------
+
+* Changed: Now depends on sabre/event package.
+* Changed: setHeaders() now overwrites any existing http headers.
+* Added: getQueryParameters to RequestInterface.
+* Added: Util::negotiate.
+* Added: RequestDecorator, ResponseDecorator.
+* Added: A very simple HTTP client.
+* Added: addHeaders() to append a list of new headers.
+* Fixed: Not erroring on unknown HTTP status codes.
+* Fixed: Throwing exceptions on invalid HTTP status codes (not 3 digits).
+* Fixed: Much better README.md
+* Changed: getBody() now uses a bitfield to specify what type to return.
+
+
+2.0.0alpha2 (2013-07-02)
+------------------------
+
+* Added: Digest & AWS Authentication.
+* Added: Message::getHttpVersion and Message::setHttpVersion.
+* Added: Request::setRawServerArray, getRawServerValue.
+* Added: Request::createFromPHPRequest
+* Added: Response::send
+* Added: Request::getQueryParameters
+* Added: Utility for dealing with HTTP dates.
+* Added: Request::setPostData and Request::getPostData.
+* Added: Request::setAbsoluteUrl and Request::getAbsoluteUrl.
+* Added: URLUtil, methods for calculation relative and base urls.
+* Removed: Response::sendBody
+
+
+2.0.0alpha1 (2012-10-07)
+------------------------
+
+* Fixed: Lots of small naming improvements
+* Added: Introduction of Message, MessageInterface, Response, ResponseInterface.
+
+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
diff --git a/vendor/sabre/http/LICENSE b/vendor/sabre/http/LICENSE
new file mode 100644
index 000000000..19812ad7a
--- /dev/null
+++ b/vendor/sabre/http/LICENSE
@@ -0,0 +1,27 @@
+Copyright (C) 2009-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 Sabre 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/vendor/sabre/http/README.md b/vendor/sabre/http/README.md
new file mode 100644
index 000000000..ae03a796e
--- /dev/null
+++ b/vendor/sabre/http/README.md
@@ -0,0 +1,746 @@
+sabre/http
+==========
+
+This library provides a toolkit to make working with the HTTP protocol easier.
+
+Most PHP scripts run within a HTTP request but accessing information about the
+HTTP request is cumbersome at least.
+
+There's bad practices, inconsistencies and confusion. This library is
+effectively a wrapper around the following PHP constructs:
+
+For Input:
+
+* `$_GET`,
+* `$_POST`,
+* `$_SERVER`,
+* `php://input` or `$HTTP_RAW_POST_DATA`.
+
+For output:
+
+* `php://output` or `echo`,
+* `header()`.
+
+What this library provides, is a `Request` object, and a `Response` object.
+
+The objects are extendable and easily mockable.
+
+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) |
+
+Installation
+------------
+
+Make sure you have [composer][1] installed. In your project directory, create,
+or edit a `composer.json` file, and make sure it contains something like this:
+
+```json
+{
+ "require" : {
+ "sabre/http" : "~3.0.0"
+ }
+}
+```
+
+After that, just hit `composer install` and you should be rolling.
+
+Quick history
+-------------
+
+This library came to existence in 2009, as a part of the [`sabre/dav`][2]
+project, which uses it heavily.
+
+It got split off into a separate library to make it easier to manage
+releases and hopefully giving it use outside of the scope of just `sabre/dav`.
+
+Although completely independently developed, this library has a LOT of
+overlap with [Symfony's `HttpFoundation`][3].
+
+Said library does a lot more stuff and is significantly more popular,
+so if you are looking for something to fulfill this particular requirement,
+I'd recommend also considering [`HttpFoundation`][3].
+
+
+Getting started
+---------------
+
+First and foremost, this library wraps the superglobals. The easiest way to
+instantiate a request object is as follows:
+
+```php
+use Sabre\HTTP;
+
+include 'vendor/autoload.php';
+
+$request = HTTP\Sapi::getRequest();
+```
+
+This line should only happen once in your entire application. Everywhere else
+you should pass this request object around using dependency injection.
+
+You should always typehint on it's interface:
+
+```php
+function handleRequest(HTTP\RequestInterface $request) {
+
+ // Do something with this request :)
+
+}
+```
+
+A response object you can just create as such:
+
+```php
+use Sabre\HTTP;
+
+include 'vendor/autoload.php';
+
+$response = new HTTP\Response();
+$response->setStatus(201); // created !
+$response->setHeader('X-Foo', 'bar');
+$response->setBody(
+ 'success!'
+);
+
+```
+
+After you fully constructed your response, you must call:
+
+```php
+HTTP\Sapi::sendResponse($response);
+```
+
+This line should generally also appear once in your application (at the very
+end).
+
+Decorators
+----------
+
+It may be useful to extend the `Request` and `Response` objects in your
+application, if you for example would like them to carry a bit more
+information about the current request.
+
+For instance, you may want to add an `isLoggedIn` method to the Request
+object.
+
+Simply extending Request and Response may pose some problems:
+
+1. You may want to extend the objects with new behaviors differently, in
+ different subsystems of your application,
+2. The `Sapi::getRequest` factory always returns a instance of
+ `Request` so you would have to override the factory method as well,
+3. By controlling the instantation and depend on specific `Request` and
+ `Response` instances in your library or application, you make it harder to
+ work with other applications which also use `sabre/http`.
+
+In short: it would be bad design. Instead, it's recommended to use the
+[decorator pattern][6] to add new behavior where you need it. `sabre/http`
+provides helper classes to quickly do this.
+
+Example:
+
+```php
+use Sabre\HTTP;
+
+class MyRequest extends HTTP\RequestDecorator {
+
+ function isLoggedIn() {
+
+ return true;
+
+ }
+
+}
+```
+
+Our application assumes that the true `Request` object was instantiated
+somewhere else, by some other subsystem. This could simply be a call like
+`$request = Sapi::getRequest()` at the top of your application,
+but could also be somewhere in a unittest.
+
+All we know in the current subsystem, is that we received a `$request` and
+that it implements `Sabre\HTTP\RequestInterface`. To decorate this object,
+all we need to do is:
+
+```php
+$request = new MyRequest($request);
+```
+
+And that's it, we now have an `isLoggedIn` method, without having to mess
+with the core instances.
+
+
+Client
+------
+
+This package also contains a simple wrapper around [cURL][4], which will allow
+you to write simple clients, using the `Request` and `Response` objects you're
+already familiar with.
+
+It's by no means a replacement for something like [Guzzle][7], but it provides
+a simple and lightweight API for making the occasional API call.
+
+### Usage
+
+```php
+use Sabre\HTTP;
+
+$request = new HTTP\Request('GET', 'http://example.org/');
+$request->setHeader('X-Foo', 'Bar');
+
+$client = new HTTP\Client();
+$response = $client->send($request);
+
+echo $response->getBodyAsString();
+```
+
+The client emits 3 event using [`sabre/event`][5]. `beforeRequest`,
+`afterRequest` and `error`.
+
+```php
+$client = new HTTP\Client();
+$client->on('beforeRequest', function($request) {
+
+ // You could use beforeRequest to for example inject a few extra headers.
+ // into the Request object.
+
+});
+
+$client->on('afterRequest', function($request, $response) {
+
+ // The afterRequest event could be a good time to do some logging, or
+ // do some rewriting in the response.
+
+});
+
+$client->on('error', function($request, $response, &$retry, $retryCount) {
+
+ // The error event is triggered for every response with a HTTP code higher
+ // than 399.
+
+});
+
+$client->on('error:401', function($request, $response, &$retry, $retryCount) {
+
+ // You can also listen for specific error codes. This example shows how
+ // to inject HTTP authentication headers if a 401 was returned.
+
+ if ($retryCount > 1) {
+ // We're only going to retry exactly once.
+ }
+
+ $request->setHeader('Authorization', 'Basic xxxxxxxxxx');
+ $retry = true;
+
+});
+```
+
+### Asynchronous requests
+
+The `Client` also supports doing asynchronous requests. This is especially handy
+if you need to perform a number of requests, that are allowed to be executed
+in parallel.
+
+The underlying system for this is simply [cURL's multi request handler][8],
+but this provides a much nicer API to handle this.
+
+Sample usage:
+
+```php
+
+use Sabre\HTTP;
+
+$request = new Request('GET', 'http://localhost/');
+$client = new Client();
+
+// Executing 1000 requests
+for ($i = 0; $i < 1000; $i++) {
+ $client->sendAsync(
+ $request,
+ function(ResponseInterface $response) {
+ // Success handler
+ },
+ function($error) {
+ // Error handler
+ }
+ );
+}
+
+// Wait for all requests to get a result.
+$client->wait();
+
+```
+
+Check out `examples/asyncclient.php` for more information.
+
+Writing a reverse proxy
+-----------------------
+
+With all these tools combined, it becomes very easy to write a simple reverse
+http proxy.
+
+```php
+use
+ Sabre\HTTP\Sapi,
+ Sabre\HTTP\Client;
+
+// The url we're proxying to.
+$remoteUrl = 'http://example.org/';
+
+// The url we're proxying from. Please note that this must be a relative url,
+// and basically acts as the base url.
+//
+// If youre $remoteUrl doesn't end with a slash, this one probably shouldn't
+// either.
+$myBaseUrl = '/reverseproxy.php';
+// $myBaseUrl = '/~evert/sabre/http/examples/reverseproxy.php/';
+
+$request = Sapi::getRequest();
+$request->setBaseUrl($myBaseUrl);
+
+$subRequest = clone $request;
+
+// Removing the Host header.
+$subRequest->removeHeader('Host');
+
+// Rewriting the url.
+$subRequest->setUrl($remoteUrl . $request->getPath());
+
+$client = new Client();
+
+// Sends the HTTP request to the server
+$response = $client->send($subRequest);
+
+// Sends the response back to the client that connected to the proxy.
+Sapi::sendResponse($response);
+```
+
+The Request and Response API's
+------------------------------
+
+### Request
+
+```php
+
+/**
+ * Creates the request object
+ *
+ * @param string $method
+ * @param string $url
+ * @param array $headers
+ * @param resource $body
+ */
+public function __construct($method = null, $url = null, array $headers = null, $body = null);
+
+/**
+ * Returns the current HTTP method
+ *
+ * @return string
+ */
+function getMethod();
+
+/**
+ * Sets the HTTP method
+ *
+ * @param string $method
+ * @return void
+ */
+function setMethod($method);
+
+/**
+ * Returns the request url.
+ *
+ * @return string
+ */
+function getUrl();
+
+/**
+ * Sets the request url.
+ *
+ * @param string $url
+ * @return void
+ */
+function setUrl($url);
+
+/**
+ * Returns the absolute url.
+ *
+ * @return string
+ */
+function getAbsoluteUrl();
+
+/**
+ * Sets the absolute url.
+ *
+ * @param string $url
+ * @return void
+ */
+function setAbsoluteUrl($url);
+
+/**
+ * Returns the current base url.
+ *
+ * @return string
+ */
+function getBaseUrl();
+
+/**
+ * Sets a base url.
+ *
+ * This url is used for relative path calculations.
+ *
+ * The base url should default to /
+ *
+ * @param string $url
+ * @return void
+ */
+function setBaseUrl($url);
+
+/**
+ * Returns the relative path.
+ *
+ * This is being calculated using the base url. This path will not start
+ * with a slash, so it will always return something like
+ * 'example/path.html'.
+ *
+ * If the full path is equal to the base url, this method will return an
+ * empty string.
+ *
+ * This method will also urldecode the path, and if the url was incoded as
+ * 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();
+
+/**
+ * Returns the list of query parameters.
+ *
+ * This is equivalent to PHP's $_GET superglobal.
+ *
+ * @return array
+ */
+function getQueryParameters();
+
+/**
+ * Returns the POST data.
+ *
+ * This is equivalent to PHP's $_POST superglobal.
+ *
+ * @return array
+ */
+function getPostData();
+
+/**
+ * Sets the post data.
+ *
+ * This is equivalent to PHP's $_POST superglobal.
+ *
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * Sets the _SERVER array.
+ *
+ * @param array $data
+ * @return void
+ */
+function setRawServerData(array $data);
+
+/**
+ * Returns the body as a readable stream resource.
+ *
+ * Note that the stream may not be rewindable, and therefore may only be
+ * read once.
+ *
+ * @return resource
+ */
+function getBodyAsStream();
+
+/**
+ * Returns the body as a string.
+ *
+ * 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();
+
+/**
+ * Returns the message body, as it's internal representation.
+ *
+ * This could be either a string or a stream.
+ *
+ * @return resource|string
+ */
+function getBody();
+
+/**
+ * Updates the body resource with a new stream.
+ *
+ * @param resource $body
+ * @return void
+ */
+function setBody($body);
+
+/**
+ * Returns all the HTTP headers as an array.
+ *
+ * @return array
+ */
+function getHeaders();
+
+/**
+ * Returns a specific HTTP header, based on it's name.
+ *
+ * The name must be treated as case-insensitive.
+ *
+ * If the header does not exist, this method must return null.
+ *
+ * @param string $name
+ * @return string|null
+ */
+function getHeader($name);
+
+/**
+ * Updates a HTTP header.
+ *
+ * The case-sensitity of the name value must be retained as-is.
+ *
+ * @param string $name
+ * @param string $value
+ * @return void
+ */
+function setHeader($name, $value);
+
+/**
+ * Resets HTTP headers
+ *
+ * This method overwrites all existing HTTP headers
+ *
+ * @param array $headers
+ * @return void
+ */
+function setHeaders(array $headers);
+
+/**
+ * Adds a new set of HTTP headers.
+ *
+ * Any header specified in the array that already exists will be
+ * overwritten, but any other existing headers will be retained.
+ *
+ * @param array $headers
+ * @return void
+ */
+function addHeaders(array $headers);
+
+/**
+ * Removes a HTTP header.
+ *
+ * The specified header name must be treated as case-insenstive.
+ * This method should return true if the header was successfully deleted,
+ * and false if the header did not exist.
+ *
+ * @return bool
+ */
+function removeHeader($name);
+
+/**
+ * Sets the HTTP version.
+ *
+ * Should be 1.0 or 1.1.
+ *
+ * @param string $version
+ * @return void
+ */
+function setHttpVersion($version);
+
+/**
+ * Returns the HTTP version.
+ *
+ * @return string
+ */
+function getHttpVersion();
+```
+
+### Response
+
+```php
+/**
+ * Returns the current HTTP status.
+ *
+ * This is the status-code as well as the human readable string.
+ *
+ * @return string
+ */
+function getStatus();
+
+/**
+ * Sets the HTTP status code.
+ *
+ * This can be either the full HTTP status code with human readable string,
+ * for example: "403 I can't let you do that, Dave".
+ *
+ * Or just the code, in which case the appropriate default message will be
+ * added.
+ *
+ * @param string|int $status
+ * @throws \InvalidArgumentExeption
+ * @return void
+ */
+function setStatus($status);
+
+/**
+ * Returns the body as a readable stream resource.
+ *
+ * Note that the stream may not be rewindable, and therefore may only be
+ * read once.
+ *
+ * @return resource
+ */
+function getBodyAsStream();
+
+/**
+ * Returns the body as a string.
+ *
+ * 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();
+
+/**
+ * Returns the message body, as it's internal representation.
+ *
+ * This could be either a string or a stream.
+ *
+ * @return resource|string
+ */
+function getBody();
+
+
+/**
+ * Updates the body resource with a new stream.
+ *
+ * @param resource $body
+ * @return void
+ */
+function setBody($body);
+
+/**
+ * Returns all the HTTP headers as an array.
+ *
+ * @return array
+ */
+function getHeaders();
+
+/**
+ * Returns a specific HTTP header, based on it's name.
+ *
+ * The name must be treated as case-insensitive.
+ *
+ * If the header does not exist, this method must return null.
+ *
+ * @param string $name
+ * @return string|null
+ */
+function getHeader($name);
+
+/**
+ * Updates a HTTP header.
+ *
+ * The case-sensitity of the name value must be retained as-is.
+ *
+ * @param string $name
+ * @param string $value
+ * @return void
+ */
+function setHeader($name, $value);
+
+/**
+ * Resets HTTP headers
+ *
+ * This method overwrites all existing HTTP headers
+ *
+ * @param array $headers
+ * @return void
+ */
+function setHeaders(array $headers);
+
+/**
+ * Adds a new set of HTTP headers.
+ *
+ * Any header specified in the array that already exists will be
+ * overwritten, but any other existing headers will be retained.
+ *
+ * @param array $headers
+ * @return void
+ */
+function addHeaders(array $headers);
+
+/**
+ * Removes a HTTP header.
+ *
+ * The specified header name must be treated as case-insenstive.
+ * This method should return true if the header was successfully deleted,
+ * and false if the header did not exist.
+ *
+ * @return bool
+ */
+function removeHeader($name);
+
+/**
+ * Sets the HTTP version.
+ *
+ * Should be 1.0 or 1.1.
+ *
+ * @param string $version
+ * @return void
+ */
+function setHttpVersion($version);
+
+/**
+ * Returns the HTTP version.
+ *
+ * @return string
+ */
+function getHttpVersion();
+```
+
+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://getcomposer.org/
+[2]: http://sabre.io/
+[3]: https://github.com/symfony/HttpFoundation
+[4]: http://php.net/curl
+[5]: https://github.com/fruux/sabre-event
+[6]: http://en.wikipedia.org/wiki/Decorator_pattern
+[7]: http://guzzlephp.org/
+[8]: http://php.net/curl_multi_init
diff --git a/vendor/sabre/http/bin/.empty b/vendor/sabre/http/bin/.empty
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/vendor/sabre/http/bin/.empty
diff --git a/vendor/sabre/dav/lib/Sabre/HTTP/AWSAuth.php b/vendor/sabre/http/lib/Auth/AWS.php
index 603470fb4..5e176646a 100644
--- a/vendor/sabre/dav/lib/Sabre/HTTP/AWSAuth.php
+++ b/vendor/sabre/http/lib/Auth/AWS.php
@@ -1,17 +1,19 @@
<?php
-namespace Sabre\HTTP;
+namespace Sabre\HTTP\Auth;
+
+use Sabre\HTTP\Util;
/**
* HTTP AWS Authentication handler
*
* Use this class to leverage amazon's AWS authentication header
*
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class AWSAuth extends AbstractAuth {
+class AWS extends AbstractAuth {
/**
* The signature supplied by the HTTP client
@@ -49,17 +51,17 @@ class AWSAuth extends AbstractAuth {
*
* @return bool
*/
- public function init() {
+ function init() {
- $authHeader = $this->httpRequest->getHeader('Authorization');
- $authHeader = explode(' ',$authHeader);
+ $authHeader = $this->request->getHeader('Authorization');
+ $authHeader = explode(' ', $authHeader);
- if ($authHeader[0]!='AWS' || !isset($authHeader[1])) {
+ if ($authHeader[0] != 'AWS' || !isset($authHeader[1])) {
$this->errorCode = self::ERR_NOAWSHEADER;
return false;
}
- list($this->accessKey,$this->signature) = explode(':',$authHeader[1]);
+ list($this->accessKey, $this->signature) = explode(':', $authHeader[1]);
return true;
@@ -70,7 +72,7 @@ class AWSAuth extends AbstractAuth {
*
* @return string
*/
- public function getAccessKey() {
+ function getAccessKey() {
return $this->accessKey;
@@ -82,16 +84,16 @@ class AWSAuth extends AbstractAuth {
* @param string $secretKey
* @return bool
*/
- public function validate($secretKey) {
+ function validate($secretKey) {
- $contentMD5 = $this->httpRequest->getHeader('Content-MD5');
+ $contentMD5 = $this->request->getHeader('Content-MD5');
if ($contentMD5) {
// We need to validate the integrity of the request
- $body = $this->httpRequest->getBody(true);
- $this->httpRequest->setBody($body,true);
+ $body = $this->request->getBody();
+ $this->request->setBody($body);
- if ($contentMD5!=base64_encode(md5($body,true))) {
+ if ($contentMD5 != base64_encode(md5($body, true))) {
// content-md5 header did not match md5 signature of body
$this->errorCode = self::ERR_MD5CHECKSUMWRONG;
return false;
@@ -99,8 +101,8 @@ class AWSAuth extends AbstractAuth {
}
- if (!$requestDate = $this->httpRequest->getHeader('x-amz-date'))
- $requestDate = $this->httpRequest->getHeader('Date');
+ if (!$requestDate = $this->request->getHeader('x-amz-date'))
+ $requestDate = $this->request->getHeader('Date');
if (!$this->validateRFC2616Date($requestDate))
return false;
@@ -109,12 +111,12 @@ class AWSAuth extends AbstractAuth {
$signature = base64_encode(
$this->hmacsha1($secretKey,
- $this->httpRequest->getMethod() . "\n" .
+ $this->request->getMethod() . "\n" .
$contentMD5 . "\n" .
- $this->httpRequest->getHeader('Content-type') . "\n" .
+ $this->request->getHeader('Content-type') . "\n" .
$requestDate . "\n" .
$amzHeaders .
- $this->httpRequest->getURI()
+ $this->request->getUrl()
)
);
@@ -137,10 +139,10 @@ class AWSAuth extends AbstractAuth {
*
* @return void
*/
- public function requireLogin() {
+ function requireLogin() {
- $this->httpResponse->setHeader('WWW-Authenticate','AWS');
- $this->httpResponse->sendStatus(401);
+ $this->response->addHeader('WWW-Authenticate', 'AWS');
+ $this->response->setStatus(401);
}
@@ -186,18 +188,18 @@ class AWSAuth extends AbstractAuth {
*/
protected function getAmzHeaders() {
- $amzHeaders = array();
- $headers = $this->httpRequest->getHeaders();
- foreach($headers as $headerName => $headerValue) {
- if (strpos(strtolower($headerName),'x-amz-')===0) {
- $amzHeaders[strtolower($headerName)] = str_replace(array("\r\n"),array(' '),$headerValue) . "\n";
+ $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";
}
}
ksort($amzHeaders);
$headerStr = '';
- foreach($amzHeaders as $h=>$v) {
- $headerStr.=$h.':'.$v;
+ foreach ($amzHeaders as $h => $v) {
+ $headerStr .= $h . ':' . $v;
}
return $headerStr;
@@ -213,13 +215,18 @@ class AWSAuth extends AbstractAuth {
*/
private function hmacsha1($key, $message) {
- $blocksize=64;
- if (strlen($key)>$blocksize)
- $key=pack('H*', sha1($key));
- $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))));
+ if (function_exists('hash_hmac')) {
+ return hash_hmac('sha1', $message, $key, true);
+ }
+
+ $blocksize = 64;
+ if (strlen($key) > $blocksize) {
+ $key = pack('H*', sha1($key));
+ }
+ $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))));
return $hmac;
}
diff --git a/vendor/sabre/http/lib/Auth/AbstractAuth.php b/vendor/sabre/http/lib/Auth/AbstractAuth.php
new file mode 100644
index 000000000..ae45b3ee2
--- /dev/null
+++ b/vendor/sabre/http/lib/Auth/AbstractAuth.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Sabre\HTTP\Auth;
+
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * HTTP Authentication base class.
+ *
+ * This class provides some common functionality for the various base classes.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class AbstractAuth {
+
+ /**
+ * Authentication realm
+ *
+ * @var string
+ */
+ protected $realm;
+
+ /**
+ * Request object
+ *
+ * @var RequestInterface
+ */
+ protected $request;
+
+ /**
+ * Response object
+ *
+ * @var ResponseInterface
+ */
+ protected $response;
+
+ /**
+ * Creates the object
+ *
+ * @param string $realm
+ * @return void
+ */
+ function __construct($realm = 'SabreTooth', RequestInterface $request, ResponseInterface $response) {
+
+ $this->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();
+
+ /**
+ * Returns the HTTP realm
+ *
+ * @return string
+ */
+ function getRealm() {
+
+ return $this->realm;
+
+ }
+
+}
diff --git a/vendor/sabre/http/lib/Auth/Basic.php b/vendor/sabre/http/lib/Auth/Basic.php
new file mode 100644
index 000000000..60633b957
--- /dev/null
+++ b/vendor/sabre/http/lib/Auth/Basic.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace Sabre\HTTP\Auth;
+
+/**
+ * HTTP Basic authentication utility.
+ *
+ * This class helps you setup basic auth. The process is fairly simple:
+ *
+ * 1. Instantiate the class.
+ * 2. Call getCredentials (this will return null or a user/pass pair)
+ * 3. If you didn't get valid credentials, call 'requireLogin'
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Basic extends AbstractAuth {
+
+ /**
+ * This method returns a numeric array with a username and password as the
+ * only elements.
+ *
+ * If no credentials were found, this method returns null.
+ *
+ * @return null|array
+ */
+ function getCredentials() {
+
+ $auth = $this->request->getHeader('Authorization');
+
+ if (!$auth) {
+ return null;
+ }
+
+ if (strtolower(substr($auth, 0, 6)) !== 'basic ') {
+ return null;
+ }
+
+ $credentials = explode(':', base64_decode(substr($auth, 6)), 2);
+
+ if (2 !== count($credentials)) {
+ return null;
+ }
+
+ 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 . '"');
+ $this->response->setStatus(401);
+
+ }
+
+}
diff --git a/vendor/sabre/http/lib/Auth/Bearer.php b/vendor/sabre/http/lib/Auth/Bearer.php
new file mode 100644
index 000000000..eefdf11ee
--- /dev/null
+++ b/vendor/sabre/http/lib/Auth/Bearer.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Sabre\HTTP\Auth;
+
+/**
+ * HTTP Bearer authentication utility.
+ *
+ * This class helps you setup bearer auth. The process is fairly simple:
+ *
+ * 1. Instantiate the class.
+ * 2. Call getToken (this will return null or a token as string)
+ * 3. If you didn't get a valid token, call 'requireLogin'
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author François Kooman (fkooman@tuxed.net)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Bearer extends AbstractAuth {
+
+ /**
+ * This method returns a string with an access token.
+ *
+ * If no token was found, this method returns null.
+ *
+ * @return null|string
+ */
+ function getToken() {
+
+ $auth = $this->request->getHeader('Authorization');
+
+ if (!$auth) {
+ return null;
+ }
+
+ if (strtolower(substr($auth, 0, 7)) !== 'bearer ') {
+ 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 . '"');
+ $this->response->setStatus(401);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/Sabre/HTTP/DigestAuth.php b/vendor/sabre/http/lib/Auth/Digest.php
index aae6d84d6..4b3f0746f 100644
--- a/vendor/sabre/dav/lib/Sabre/HTTP/DigestAuth.php
+++ b/vendor/sabre/http/lib/Auth/Digest.php
@@ -1,6 +1,9 @@
<?php
-namespace Sabre\HTTP;
+namespace Sabre\HTTP\Auth;
+
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
/**
* HTTP Digest Authentication handler
@@ -11,7 +14,7 @@ namespace Sabre\HTTP;
* 1. Create the object
* 2. Call the setRealm() method with the realm you plan to use
* 3. Call the init method function.
- * 4. Call the getUserName() function. This function may return false if no
+ * 4. Call the getUserName() function. This function may return null if no
* authentication information was supplied. Based on the username you
* should check your internal database for either the associated password,
* or the so-called A1 hash of the digest.
@@ -20,12 +23,11 @@ namespace Sabre\HTTP;
* 6. To make sure an authentication prompt is displayed, call the
* requireLogin() method.
*
- *
- * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
-class DigestAuth extends AbstractAuth {
+class Digest extends AbstractAuth {
/**
* These constants are used in setQOP();
@@ -42,11 +44,11 @@ class DigestAuth extends AbstractAuth {
/**
* Initializes the object
*/
- public function __construct() {
+ function __construct($realm = 'SabreTooth', RequestInterface $request, ResponseInterface $response) {
$this->nonce = uniqid();
- $this->opaque = md5($this->realm);
- parent::__construct();
+ $this->opaque = md5($realm);
+ parent::__construct($realm, $request, $response);
}
@@ -57,7 +59,7 @@ class DigestAuth extends AbstractAuth {
*
* @return void
*/
- public function init() {
+ function init() {
$digest = $this->getDigest();
$this->digestParts = $this->parseDigest($digest);
@@ -80,7 +82,7 @@ class DigestAuth extends AbstractAuth {
* @param int $qop
* @return void
*/
- public function setQOP($qop) {
+ function setQOP($qop) {
$this->qop = $qop;
@@ -94,7 +96,7 @@ class DigestAuth extends AbstractAuth {
* @param string $A1
* @return bool
*/
- public function validateA1($A1) {
+ function validateA1($A1) {
$this->A1 = $A1;
return $this->validate();
@@ -108,7 +110,7 @@ class DigestAuth extends AbstractAuth {
* @param string $password
* @return bool
*/
- public function validatePassword($password) {
+ function validatePassword($password) {
$this->A1 = md5($this->digestParts['username'] . ':' . $this->realm . ':' . $password);
return $this->validate();
@@ -120,7 +122,7 @@ class DigestAuth extends AbstractAuth {
*
* @return string
*/
- public function getUsername() {
+ function getUsername() {
return $this->digestParts['username'];
@@ -133,14 +135,14 @@ class DigestAuth extends AbstractAuth {
*/
protected function validate() {
- $A2 = $this->httpRequest->getMethod() . ':' . $this->digestParts['uri'];
+ $A2 = $this->request->getMethod() . ':' . $this->digestParts['uri'];
- if ($this->digestParts['qop']=='auth-int') {
+ if ($this->digestParts['qop'] == 'auth-int') {
// Making sure we support this qop value
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->httpRequest->getBody(true);
- $this->httpRequest->setBody($body,true);
+ $body = $this->request->getBody($asString = true);
+ $this->request->setBody($body);
$A2 .= ':' . md5($body);
} else {
@@ -152,7 +154,7 @@ class DigestAuth extends AbstractAuth {
$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;
}
@@ -164,17 +166,23 @@ class DigestAuth extends AbstractAuth {
*
* @return void
*/
- public function requireLogin() {
+ function requireLogin() {
$qop = '';
- switch($this->qop) {
- case self::QOP_AUTH : $qop = 'auth'; break;
- case self::QOP_AUTHINT : $qop = 'auth-int'; break;
- case self::QOP_AUTH | self::QOP_AUTHINT : $qop = 'auth,auth-int'; break;
+ switch ($this->qop) {
+ case self::QOP_AUTH :
+ $qop = 'auth';
+ break;
+ case self::QOP_AUTHINT :
+ $qop = 'auth-int';
+ break;
+ case self::QOP_AUTH | self::QOP_AUTHINT :
+ $qop = 'auth,auth-int';
+ break;
}
- $this->httpResponse->setHeader('WWW-Authenticate','Digest realm="' . $this->realm . '",qop="'.$qop.'",nonce="' . $this->nonce . '",opaque="' . $this->opaque . '"');
- $this->httpResponse->sendStatus(401);
+ $this->response->addHeader('WWW-Authenticate', 'Digest realm="' . $this->realm . '",qop="' . $qop . '",nonce="' . $this->nonce . '",opaque="' . $this->opaque . '"');
+ $this->response->setStatus(401);
}
@@ -188,26 +196,9 @@ class DigestAuth extends AbstractAuth {
*
* @return mixed
*/
- public function getDigest() {
-
- // mod_php
- $digest = $this->httpRequest->getRawServerValue('PHP_AUTH_DIGEST');
- if ($digest) return $digest;
-
- // most other servers
- $digest = $this->httpRequest->getHeader('Authorization');
+ function getDigest() {
- // Apache could prefix environment variables with REDIRECT_ when urls
- // are passed through mod_rewrite
- if (!$digest) {
- $digest = $this->httpRequest->getRawServerValue('REDIRECT_HTTP_AUTHORIZATION');
- }
-
- if ($digest && strpos(strtolower($digest),'digest')===0) {
- return substr($digest,7);
- } else {
- return null;
- }
+ return $this->request->getHeader('Authorization');
}
@@ -223,8 +214,8 @@ class DigestAuth extends AbstractAuth {
protected function parseDigest($digest) {
// protect against missing data
- $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
- $data = array();
+ $needed_parts = ['nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1];
+ $data = [];
preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $digest, $matches, PREG_SET_ORDER);
diff --git a/vendor/sabre/http/lib/Client.php b/vendor/sabre/http/lib/Client.php
new file mode 100644
index 000000000..0810c4a25
--- /dev/null
+++ b/vendor/sabre/http/lib/Client.php
@@ -0,0 +1,601 @@
+<?php
+
+namespace Sabre\HTTP;
+
+use Sabre\Event\EventEmitter;
+use Sabre\Uri;
+
+/**
+ * A rudimentary HTTP client.
+ *
+ * This object wraps PHP's curl extension and provides an easy way to send it a
+ * Request object, and return a Response object.
+ *
+ * This is by no means intended as the next best HTTP client, but it does the
+ * job and provides a simple integration with the rest of sabre/http.
+ *
+ * This client emits the following events:
+ * beforeRequest(RequestInterface $request)
+ * afterRequest(RequestInterface $request, ResponseInterface $response)
+ * error(RequestInterface $request, ResponseInterface $response, bool &$retry, int $retryCount)
+ * exception(RequestInterface $request, ClientException $e, bool &$retry, int $retryCount)
+ *
+ * The beforeRequest event allows you to do some last minute changes to the
+ * request before it's done, such as adding authentication headers.
+ *
+ * The afterRequest event will be emitted after the request is completed
+ * succesfully.
+ *
+ * If a HTTP error is returned (status code higher than 399) the error event is
+ * triggered. It's possible using this event to retry the request, by setting
+ * retry to true.
+ *
+ * The amount of times a request has retried is passed as $retryCount, which
+ * can be used to avoid retrying indefinitely. The first time the event is
+ * called, this will be 0.
+ *
+ * It's also possible to intercept specific http errors, by subscribing to for
+ * example 'error:401'.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Client extends EventEmitter {
+
+ /**
+ * List of curl settings
+ *
+ * @var array
+ */
+ protected $curlSettings = [];
+
+ /**
+ * Wether or not exceptions should be thrown when a HTTP error is returned.
+ *
+ * @var bool
+ */
+ protected $throwExceptions = false;
+
+ /**
+ * The maximum number of times we'll follow a redirect.
+ *
+ * @var int
+ */
+ protected $maxRedirects = 5;
+
+ /**
+ * Initializes the client.
+ *
+ * @return void
+ */
+ function __construct() {
+
+ $this->curlSettings = [
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HEADER => true,
+ CURLOPT_NOBODY => false,
+ CURLOPT_USERAGENT => 'sabre-http/' . Version::VERSION . ' (http://sabre.io/)',
+ ];
+
+ }
+
+ /**
+ * Sends a request to a HTTP server, and returns a response.
+ *
+ * @param RequestInterface $request
+ * @return ResponseInterface
+ */
+ function send(RequestInterface $request) {
+
+ $this->emit('beforeRequest', [$request]);
+
+ $retryCount = 0;
+ $redirects = 0;
+
+ do {
+
+ $doRedirect = false;
+ $retry = false;
+
+ try {
+
+ $response = $this->doRequest($request);
+
+ $code = (int)$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) {
+
+ $oldLocation = $request->getUrl();
+
+ // Creating a new instance of the request object.
+ $request = clone $request;
+
+ // Setting the new location
+ $request->setUrl(Uri\resolve(
+ $oldLocation,
+ $response->getHeader('Location')
+ ));
+
+ $doRedirect = true;
+ $redirects++;
+
+ }
+
+ // This was a HTTP error
+ if ($code >= 400) {
+
+ $this->emit('error', [$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
+ // dealt with the problem. In this case we just re-throw the
+ // exception.
+ if (!$retry) {
+ throw $e;
+ }
+
+ }
+
+ if ($retry) {
+ $retryCount++;
+ }
+
+ } while ($retry || $doRedirect);
+
+ $this->emit('afterRequest', [$request, $response]);
+
+ if ($this->throwExceptions && $code >= 400) {
+ throw new ClientHttpException($response);
+ }
+
+ return $response;
+
+ }
+
+ /**
+ * Sends a HTTP request asynchronously.
+ *
+ * Due to the nature of PHP, you must from time to time poll to see if any
+ * new responses came in.
+ *
+ * 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) {
+
+ $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() {
+
+ // nothing to do?
+ if (!$this->curlMultiMap) {
+ return false;
+ }
+
+ do {
+ $r = curl_multi_exec(
+ $this->curlMultiHandle,
+ $stillRunning
+ );
+ } while ($r === CURLM_CALL_MULTI_PERFORM);
+
+ do {
+
+ messageQueue:
+
+ $status = curl_multi_info_read(
+ $this->curlMultiHandle,
+ $messagesInQueue
+ );
+
+ if ($status && $status['msg'] === CURLMSG_DONE) {
+
+ $resourceId = intval($status['handle']);
+ list(
+ $request,
+ $successCallback,
+ $errorCallback,
+ $retryCount,
+ ) = $this->curlMultiMap[$resourceId];
+ unset($this->curlMultiMap[$resourceId]);
+ $curlResult = $this->parseCurlResult(curl_multi_getcontent($status['handle']), $status['handle']);
+ $retry = false;
+
+ if ($curlResult['status'] === self::STATUS_CURLERROR) {
+
+ $e = new ClientException($curlResult['curl_errmsg'], $curlResult['curl_errno']);
+ $this->emit('exception', [$request, $e, &$retry, $retryCount]);
+
+ if ($retry) {
+ $retryCount++;
+ $this->sendAsyncInternal($request, $successCallback, $errorCallback, $retryCount);
+ goto messageQueue;
+ }
+
+ $curlResult['request'] = $request;
+
+ if ($errorCallback) {
+ $errorCallback($curlResult);
+ }
+
+ } elseif ($curlResult['status'] === self::STATUS_HTTPERROR) {
+
+ $this->emit('error', [$request, $curlResult['response'], &$retry, $retryCount]);
+ $this->emit('error:' . $curlResult['http_code'], [$request, $curlResult['response'], &$retry, $retryCount]);
+
+ if ($retry) {
+
+ $retryCount++;
+ $this->sendAsyncInternal($request, $successCallback, $errorCallback, $retryCount);
+ goto messageQueue;
+
+ }
+
+ $curlResult['request'] = $request;
+
+ 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() {
+
+ do {
+ curl_multi_select($this->curlMultiHandle);
+ $stillRunning = $this->poll();
+ } while ($stillRunning);
+
+ }
+
+ /**
+ * If this is set to true, the Client will automatically throw exceptions
+ * upon HTTP errors.
+ *
+ * This means that if a response came back with a status code greater than
+ * or equal to 400, we will throw a ClientHttpException.
+ *
+ * This only works for the send() method. Throwing exceptions for
+ * sendAsync() is not supported.
+ *
+ * @param bool $throwExceptions
+ * @return void
+ */
+ function setThrowExceptions($throwExceptions) {
+
+ $this->throwExceptions = $throwExceptions;
+
+ }
+
+ /**
+ * Adds a CURL setting.
+ *
+ * These settings will be included in every HTTP request.
+ *
+ * @param int $name
+ * @param mixed $value
+ * @return void
+ */
+ function addCurlSetting($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) {
+
+ $settings = $this->createCurlSettingsArray($request);
+
+ if (!$this->curlHandle) {
+ $this->curlHandle = curl_init();
+ }
+
+ curl_setopt_array($this->curlHandle, $settings);
+ $response = $this->curlExec($this->curlHandle);
+ $response = $this->parseCurlResult($response, $this->curlHandle);
+
+ if ($response['status'] === self::STATUS_CURLERROR) {
+ throw new ClientException($response['curl_errmsg'], $response['curl_errno']);
+ }
+
+ return $response['response'];
+
+ }
+
+ /**
+ * Cached curl handle.
+ *
+ * By keeping this resource around for the lifetime of this object, things
+ * like persistent connections are possible.
+ *
+ * @var resource
+ */
+ private $curlHandle;
+
+ /**
+ * Handler for curl_multi requests.
+ *
+ * The first time sendAsync is used, this will be created.
+ *
+ * @var resource
+ */
+ private $curlMultiHandle;
+
+ /**
+ * Has a list of curl handles, as well as their associated success and
+ * error callbacks.
+ *
+ * @var array
+ */
+ private $curlMultiMap = [];
+
+ /**
+ * Turns a RequestInterface object into an array with settings that can be
+ * fed to curl_setopt
+ *
+ * @param RequestInterface $request
+ * @return array
+ */
+ protected function createCurlSettingsArray(RequestInterface $request) {
+
+ $settings = $this->curlSettings;
+
+ switch ($request->getMethod()) {
+ case 'HEAD' :
+ $settings[CURLOPT_NOBODY] = true;
+ $settings[CURLOPT_CUSTOMREQUEST] = 'HEAD';
+ $settings[CURLOPT_POSTFIELDS] = '';
+ $settings[CURLOPT_PUT] = false;
+ break;
+ case 'GET' :
+ $settings[CURLOPT_CUSTOMREQUEST] = 'GET';
+ $settings[CURLOPT_POSTFIELDS] = '';
+ $settings[CURLOPT_PUT] = false;
+ break;
+ default :
+ $body = $request->getBody();
+ if (is_resource($body)) {
+ // This needs to be set to PUT, regardless of the actual
+ // method used. Without it, INFILE will be ignored for some
+ // reason.
+ $settings[CURLOPT_PUT] = true;
+ $settings[CURLOPT_INFILE] = $request->getBody();
+ } else {
+ // 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_CUSTOMREQUEST] = $request->getMethod();
+ break;
+
+ }
+
+ $nHeaders = [];
+ foreach ($request->getHeaders() as $key => $values) {
+
+ foreach ($values as $value) {
+ $nHeaders[] = $key . ': ' . $value;
+ }
+
+ }
+ $settings[CURLOPT_HTTPHEADER] = $nHeaders;
+ $settings[CURLOPT_URL] = $request->getUrl();
+ // FIXME: CURLOPT_PROTOCOLS is currently unsupported by HHVM
+ if (defined('CURLOPT_PROTOCOLS')) {
+ $settings[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
+ }
+ // FIXME: CURLOPT_REDIR_PROTOCOLS is currently unsupported by HHVM
+ if (defined('CURLOPT_REDIR_PROTOCOLS')) {
+ $settings[CURLOPT_REDIR_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
+ }
+
+ return $settings;
+
+ }
+
+ const STATUS_SUCCESS = 0;
+ const STATUS_CURLERROR = 1;
+ const STATUS_HTTPERROR = 2;
+
+ /**
+ * 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
+ *
+ * @param string $response
+ * @param resource $curlHandle
+ * @return Response
+ */
+ protected function parseCurlResult($response, $curlHandle) {
+
+ list(
+ $curlInfo,
+ $curlErrNo,
+ $curlErrMsg
+ ) = $this->curlStuff($curlHandle);
+
+ if ($curlErrNo) {
+ return [
+ 'status' => self::STATUS_CURLERROR,
+ 'curl_errno' => $curlErrNo,
+ 'curl_errmsg' => $curlErrMsg,
+ ];
+ }
+
+ $headerBlob = substr($response, 0, $curlInfo['header_size']);
+ // 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;
+
+ unset($response);
+
+ // In the case of 100 Continue, or redirects we'll have multiple lists
+ // of headers for each separate HTTP response. We can easily split this
+ // because they are separated by \r\n\r\n
+ $headerBlob = explode("\r\n\r\n", trim($headerBlob, "\r\n"));
+
+ // We only care about the last set of headers
+ $headerBlob = $headerBlob[count($headerBlob) - 1];
+
+ // 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,
+ ];
+
+ }
+
+ /**
+ * Sends an asynchronous HTTP request.
+ *
+ * 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) {
+
+ if (!$this->curlMultiHandle) {
+ $this->curlMultiHandle = curl_multi_init();
+ }
+ $curl = curl_init();
+ curl_setopt_array(
+ $curl,
+ $this->createCurlSettingsArray($request)
+ );
+ curl_multi_add_handle($this->curlMultiHandle, $curl);
+ $this->curlMultiMap[intval($curl)] = [
+ $request,
+ $success,
+ $error,
+ $retryCount
+ ];
+
+ }
+
+ // @codeCoverageIgnoreStart
+
+ /**
+ * Calls curl_exec
+ *
+ * This method exists so it can easily be overridden and mocked.
+ *
+ * @param resource $curlHandle
+ * @return string
+ */
+ protected function curlExec($curlHandle) {
+
+ return curl_exec($curlHandle);
+
+ }
+
+ /**
+ * Returns a bunch of information about a curl request.
+ *
+ * This method exists so it can easily be overridden and mocked.
+ *
+ * @param resource $curlHandle
+ * @return array
+ */
+ protected function curlStuff($curlHandle) {
+
+ return [
+ curl_getinfo($curlHandle),
+ curl_errno($curlHandle),
+ curl_error($curlHandle),
+ ];
+
+ }
+ // @codeCoverageIgnoreEnd
+
+}
diff --git a/vendor/sabre/http/lib/ClientException.php b/vendor/sabre/http/lib/ClientException.php
new file mode 100644
index 000000000..69631f44e
--- /dev/null
+++ b/vendor/sabre/http/lib/ClientException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * This exception may be emitted by the HTTP\Client class, in case there was a
+ * problem emitting the request.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ClientException extends \Exception {
+
+}
diff --git a/vendor/sabre/http/lib/ClientHttpException.php b/vendor/sabre/http/lib/ClientHttpException.php
new file mode 100644
index 000000000..2923ef3b5
--- /dev/null
+++ b/vendor/sabre/http/lib/ClientHttpException.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * This exception represents a HTTP error coming from the Client.
+ *
+ * By default the Client will not emit these, this has to be explicitly enabled
+ * with the setThrowExceptions method.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ClientHttpException extends \Exception implements HttpException {
+
+ /**
+ * Response object
+ *
+ * @var ResponseInterface
+ */
+ protected $response;
+
+ /**
+ * Constructor
+ *
+ * @param ResponseInterface $response
+ */
+ function __construct(ResponseInterface $response) {
+
+ $this->response = $response;
+ parent::__construct($response->getStatusText(), $response->getStatus());
+
+ }
+
+ /**
+ * The http status code for the error.
+ *
+ * @return int
+ */
+ function getHttpStatus() {
+
+ return $this->response->getStatus();
+
+ }
+
+ /**
+ * Returns the full response object.
+ *
+ * @return ResponseInterface
+ */
+ function getResponse() {
+
+ return $this->response;
+
+ }
+
+}
diff --git a/vendor/sabre/http/lib/HttpException.php b/vendor/sabre/http/lib/HttpException.php
new file mode 100644
index 000000000..1303dec97
--- /dev/null
+++ b/vendor/sabre/http/lib/HttpException.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * An exception representing a HTTP error.
+ *
+ * This can be used as a generic exception in your application, if you'd like
+ * to map HTTP errors to exceptions.
+ *
+ * If you'd like to use this, create a new exception class, extending Exception
+ * and implementing this interface.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface HttpException {
+
+ /**
+ * The http status code for the error.
+ *
+ * This may either be just the number, or a number and a human-readable
+ * message, separated by a space.
+ *
+ * @return string|null
+ */
+ function getHttpStatus();
+
+}
diff --git a/vendor/sabre/http/lib/Message.php b/vendor/sabre/http/lib/Message.php
new file mode 100644
index 000000000..5c6887d8a
--- /dev/null
+++ b/vendor/sabre/http/lib/Message.php
@@ -0,0 +1,314 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * This is the abstract base class for both the Request and Response objects.
+ *
+ * This object contains a few simple methods that are shared by both.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class Message implements MessageInterface {
+
+ /**
+ * Request body
+ *
+ * This should be a stream resource
+ *
+ * @var resource
+ */
+ protected $body;
+
+ /**
+ * Contains the list of HTTP headers
+ *
+ * @var array
+ */
+ protected $headers = [];
+
+ /**
+ * HTTP message version (1.0 or 1.1)
+ *
+ * @var string
+ */
+ protected $httpVersion = '1.1';
+
+ /**
+ * Returns the body as a readable stream resource.
+ *
+ * Note that the stream may not be rewindable, and therefore may only be
+ * read once.
+ *
+ * @return resource
+ */
+ function getBodyAsStream() {
+
+ $body = $this->getBody();
+ if (is_string($body) || is_null($body)) {
+ $stream = fopen('php://temp', 'r+');
+ fwrite($stream, $body);
+ rewind($stream);
+ return $stream;
+ }
+ return $body;
+
+ }
+
+ /**
+ * Returns the body as a string.
+ *
+ * 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() {
+
+ $body = $this->getBody();
+ if (is_string($body)) {
+ return $body;
+ }
+ if (is_null($body)) {
+ return '';
+ }
+ $contentLength = $this->getHeader('Content-Length');
+ if (null === $contentLength) {
+ return stream_get_contents($body);
+ } else {
+ return stream_get_contents($body, $contentLength);
+ }
+
+ }
+
+ /**
+ * Returns the message body, as it's internal representation.
+ *
+ * This could be either a string or a stream.
+ *
+ * @return resource|string
+ */
+ function getBody() {
+
+ return $this->body;
+
+ }
+
+ /**
+ * Replaces the body resource with a new stream or string.
+ *
+ * @param resource|string $body
+ */
+ 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() {
+
+ $result = [];
+ foreach ($this->headers as $headerInfo) {
+ $result[$headerInfo[0]] = $headerInfo[1];
+ }
+ return $result;
+
+ }
+
+ /**
+ * Will return true or false, depending on if a HTTP header exists.
+ *
+ * @param string $name
+ * @return bool
+ */
+ function hasHeader($name) {
+
+ return isset($this->headers[strtolower($name)]);
+
+ }
+
+ /**
+ * Returns a specific HTTP header, based on it's name.
+ *
+ * The name must be treated as case-insensitive.
+ * If the header does not exist, this method must return null.
+ *
+ * If a header appeared more than once in a HTTP request, this method will
+ * concatenate all the values with a comma.
+ *
+ * Note that this not make sense for all headers. Some, such as
+ * `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) {
+
+ $name = strtolower($name);
+
+ if (isset($this->headers[$name])) {
+ return implode(',', $this->headers[$name][1]);
+ }
+ return null;
+
+ }
+
+ /**
+ * Returns a HTTP header as an array.
+ *
+ * For every time the HTTP header appeared in the request or response, an
+ * 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) {
+
+ $name = strtolower($name);
+
+ if (isset($this->headers[$name])) {
+ return $this->headers[$name][1];
+ }
+
+ return [];
+
+ }
+
+ /**
+ * Updates a HTTP header.
+ *
+ * The case-sensitity of the name value must be retained as-is.
+ *
+ * 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];
+
+ }
+
+ /**
+ * Sets a new set of HTTP headers.
+ *
+ * The headers array should contain headernames for keys, and their value
+ * 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) {
+
+ foreach ($headers as $name => $value) {
+ $this->setHeader($name, $value);
+ }
+
+ }
+
+ /**
+ * Adds a HTTP header.
+ *
+ * This method will not overwrite any existing HTTP header, but instead add
+ * another value. Individual values can be retrieved with
+ * getHeadersAsArray.
+ *
+ * @param string $name
+ * @param string $value
+ * @return void
+ */
+ function addHeader($name, $value) {
+
+ $lName = strtolower($name);
+ if (isset($this->headers[$lName])) {
+ $this->headers[$lName][1] = array_merge(
+ $this->headers[$lName][1],
+ (array)$value
+ );
+ } else {
+ $this->headers[$lName] = [
+ $name,
+ (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) {
+
+ foreach ($headers as $name => $value) {
+ $this->addHeader($name, $value);
+ }
+
+ }
+
+
+ /**
+ * Removes a HTTP header.
+ *
+ * The specified header name must be treated as case-insenstive.
+ * This method should return true if the header was successfully deleted,
+ * and false if the header did not exist.
+ *
+ * @return bool
+ */
+ function removeHeader($name) {
+
+ $name = strtolower($name);
+ if (!isset($this->headers[$name])) {
+ return false;
+ }
+ unset($this->headers[$name]);
+ return true;
+
+ }
+
+ /**
+ * Sets the HTTP version.
+ *
+ * Should be 1.0 or 1.1.
+ *
+ * @param string $version
+ * @return void
+ */
+ function setHttpVersion($version) {
+
+ $this->httpVersion = $version;
+
+ }
+
+ /**
+ * Returns the HTTP version.
+ *
+ * @return string
+ */
+ function getHttpVersion() {
+
+ return $this->httpVersion;
+
+ }
+}
diff --git a/vendor/sabre/http/lib/MessageDecoratorTrait.php b/vendor/sabre/http/lib/MessageDecoratorTrait.php
new file mode 100644
index 000000000..f104af38d
--- /dev/null
+++ b/vendor/sabre/http/lib/MessageDecoratorTrait.php
@@ -0,0 +1,250 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * This trait contains a bunch of methods, shared by both the RequestDecorator
+ * and the ResponseDecorator.
+ *
+ * Didn't seem needed to create a full class for this, so we're just
+ * implementing it as a trait.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+trait MessageDecoratorTrait {
+
+ /**
+ * The inner request object.
+ *
+ * All method calls will be forwarded here.
+ *
+ * @var MessageInterface
+ */
+ protected $inner;
+
+ /**
+ * Returns the body as a readable stream resource.
+ *
+ * Note that the stream may not be rewindable, and therefore may only be
+ * read once.
+ *
+ * @return resource
+ */
+ function getBodyAsStream() {
+
+ return $this->inner->getBodyAsStream();
+
+ }
+
+ /**
+ * Returns the body as a string.
+ *
+ * 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() {
+
+ return $this->inner->getBodyAsString();
+
+ }
+
+ /**
+ * Returns the message body, as it's internal representation.
+ *
+ * This could be either a string or a stream.
+ *
+ * @return resource|string
+ */
+ function getBody() {
+
+ return $this->inner->getBody();
+
+ }
+
+ /**
+ * Updates the body resource with a new stream.
+ *
+ * @param resource $body
+ * @return void
+ */
+ 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() {
+
+ return $this->inner->getHeaders();
+
+ }
+
+ /**
+ * Will return true or false, depending on if a HTTP header exists.
+ *
+ * @param string $name
+ * @return bool
+ */
+ function hasHeader($name) {
+
+ return $this->inner->hasHeader($name);
+
+ }
+
+ /**
+ * Returns a specific HTTP header, based on it's name.
+ *
+ * The name must be treated as case-insensitive.
+ * If the header does not exist, this method must return null.
+ *
+ * If a header appeared more than once in a HTTP request, this method will
+ * concatenate all the values with a comma.
+ *
+ * Note that this not make sense for all headers. Some, such as
+ * `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) {
+
+ return $this->inner->getHeader($name);
+
+ }
+
+ /**
+ * Returns a HTTP header as an array.
+ *
+ * For every time the HTTP header appeared in the request or response, an
+ * 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) {
+
+ return $this->inner->getHeaderAsArray($name);
+
+ }
+
+ /**
+ * Updates a HTTP header.
+ *
+ * The case-sensitity of the name value must be retained as-is.
+ *
+ * If the header already existed, it will be overwritten.
+ *
+ * @param string $name
+ * @param string|string[] $value
+ * @return void
+ */
+ function setHeader($name, $value) {
+
+ $this->inner->setHeader($name, $value);
+
+ }
+
+ /**
+ * Sets a new set of HTTP headers.
+ *
+ * The headers array should contain headernames for keys, and their value
+ * 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) {
+
+ $this->inner->setHeaders($headers);
+
+ }
+
+ /**
+ * Adds a HTTP header.
+ *
+ * This method will not overwrite any existing HTTP header, but instead add
+ * another value. Individual values can be retrieved with
+ * getHeadersAsArray.
+ *
+ * @param string $name
+ * @param string $value
+ * @return void
+ */
+ function addHeader($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) {
+
+ $this->inner->addHeaders($headers);
+
+ }
+
+
+ /**
+ * Removes a HTTP header.
+ *
+ * The specified header name must be treated as case-insenstive.
+ * This method should return true if the header was successfully deleted,
+ * and false if the header did not exist.
+ *
+ * @return bool
+ */
+ function removeHeader($name) {
+
+ $this->inner->removeHeader($name);
+
+ }
+
+ /**
+ * Sets the HTTP version.
+ *
+ * Should be 1.0 or 1.1.
+ *
+ * @param string $version
+ * @return void
+ */
+ function setHttpVersion($version) {
+
+ $this->inner->setHttpVersion($version);
+
+ }
+
+ /**
+ * Returns the HTTP version.
+ *
+ * @return string
+ */
+ function getHttpVersion() {
+
+ return $this->inner->getHttpVersion();
+
+ }
+
+}
diff --git a/vendor/sabre/http/lib/MessageInterface.php b/vendor/sabre/http/lib/MessageInterface.php
new file mode 100644
index 000000000..55d8485c1
--- /dev/null
+++ b/vendor/sabre/http/lib/MessageInterface.php
@@ -0,0 +1,177 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * The MessageInterface is the base interface that's used by both
+ * the RequestInterface and ResponseInterface.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface MessageInterface {
+
+ /**
+ * Returns the body as a readable stream resource.
+ *
+ * Note that the stream may not be rewindable, and therefore may only be
+ * read once.
+ *
+ * @return resource
+ */
+ function getBodyAsStream();
+
+ /**
+ * Returns the body as a string.
+ *
+ * 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();
+
+ /**
+ * Returns the message body, as it's internal representation.
+ *
+ * This could be either a string or a stream.
+ *
+ * @return resource|string
+ */
+ function getBody();
+
+ /**
+ * Updates the body resource with a new stream.
+ *
+ * @param resource $body
+ * @return void
+ */
+ function 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();
+
+ /**
+ * Will return true or false, depending on if a HTTP header exists.
+ *
+ * @param string $name
+ * @return bool
+ */
+ function hasHeader($name);
+
+ /**
+ * Returns a specific HTTP header, based on it's name.
+ *
+ * The name must be treated as case-insensitive.
+ * If the header does not exist, this method must return null.
+ *
+ * If a header appeared more than once in a HTTP request, this method will
+ * concatenate all the values with a comma.
+ *
+ * Note that this not make sense for all headers. Some, such as
+ * `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);
+
+ /**
+ * Returns a HTTP header as an array.
+ *
+ * For every time the HTTP header appeared in the request or response, an
+ * 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);
+
+ /**
+ * Updates a HTTP header.
+ *
+ * The case-sensitity of the name value must be retained as-is.
+ *
+ * If the header already existed, it will be overwritten.
+ *
+ * @param string $name
+ * @param string|string[] $value
+ * @return void
+ */
+ function setHeader($name, $value);
+
+ /**
+ * Sets a new set of HTTP headers.
+ *
+ * The headers array should contain headernames for keys, and their value
+ * 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);
+
+ /**
+ * Adds a HTTP header.
+ *
+ * This method will not overwrite any existing HTTP header, but instead add
+ * another value. Individual values can be retrieved with
+ * getHeadersAsArray.
+ *
+ * @param string $name
+ * @param string $value
+ * @return void
+ */
+ function 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);
+
+ /**
+ * Removes a HTTP header.
+ *
+ * The specified header name must be treated as case-insenstive.
+ * This method should return true if the header was successfully deleted,
+ * and false if the header did not exist.
+ *
+ * @return bool
+ */
+ function removeHeader($name);
+
+ /**
+ * Sets the HTTP version.
+ *
+ * Should be 1.0 or 1.1.
+ *
+ * @param string $version
+ * @return void
+ */
+ function setHttpVersion($version);
+
+ /**
+ * Returns the HTTP version.
+ *
+ * @return string
+ */
+ function getHttpVersion();
+
+}
diff --git a/vendor/sabre/http/lib/Request.php b/vendor/sabre/http/lib/Request.php
new file mode 100644
index 000000000..8bcaf32a6
--- /dev/null
+++ b/vendor/sabre/http/lib/Request.php
@@ -0,0 +1,316 @@
+<?php
+
+namespace Sabre\HTTP;
+
+use InvalidArgumentException;
+use Sabre\Uri;
+
+/**
+ * The Request class represents a single HTTP request.
+ *
+ * You can either simply construct the object from scratch, or if you need
+ * access to the current HTTP request, use Sapi::getRequest.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Request extends Message implements RequestInterface {
+
+ /**
+ * HTTP Method
+ *
+ * @var string
+ */
+ protected $method;
+
+ /**
+ * Request Url
+ *
+ * @var string
+ */
+ protected $url;
+
+ /**
+ * Creates the request object
+ *
+ * @param string $method
+ * @param string $url
+ * @param array $headers
+ * @param resource $body
+ */
+ function __construct($method = null, $url = null, array $headers = null, $body = null) {
+
+ if (is_array($method)) {
+ throw new InvalidArgumentException('The first argument for this constructor should be a string or null, not an array. Did you upgrade from sabre/http 1.0 to 2.0?');
+ }
+ if (!is_null($method)) $this->setMethod($method);
+ if (!is_null($url)) $this->setUrl($url);
+ if (!is_null($headers)) $this->setHeaders($headers);
+ if (!is_null($body)) $this->setBody($body);
+
+ }
+
+ /**
+ * Returns the current HTTP method
+ *
+ * @return string
+ */
+ function getMethod() {
+
+ return $this->method;
+
+ }
+
+ /**
+ * Sets the HTTP method
+ *
+ * @param string $method
+ * @return void
+ */
+ function setMethod($method) {
+
+ $this->method = $method;
+
+ }
+
+ /**
+ * Returns the request url.
+ *
+ * @return string
+ */
+ function getUrl() {
+
+ return $this->url;
+
+ }
+
+ /**
+ * Sets the request url.
+ *
+ * @param string $url
+ * @return void
+ */
+ function setUrl($url) {
+
+ $this->url = $url;
+
+ }
+
+ /**
+ * Returns the list of query parameters.
+ *
+ * This is equivalent to PHP's $_GET superglobal.
+ *
+ * @return array
+ */
+ function getQueryParameters() {
+
+ $url = $this->getUrl();
+ if (($index = strpos($url, '?')) === false) {
+ return [];
+ } else {
+ parse_str(substr($url, $index + 1), $queryParams);
+ return $queryParams;
+ }
+
+ }
+
+ /**
+ * Sets the absolute url.
+ *
+ * @param string $url
+ * @return void
+ */
+ function setAbsoluteUrl($url) {
+
+ $this->absoluteUrl = $url;
+
+ }
+
+ /**
+ * Returns the absolute url.
+ *
+ * @return string
+ */
+ function getAbsoluteUrl() {
+
+ return $this->absoluteUrl;
+
+ }
+
+ /**
+ * Base url
+ *
+ * @var string
+ */
+ protected $baseUrl = '/';
+
+ /**
+ * Sets a base url.
+ *
+ * This url is used for relative path calculations.
+ *
+ * @param string $url
+ * @return void
+ */
+ function setBaseUrl($url) {
+
+ $this->baseUrl = $url;
+
+ }
+
+ /**
+ * Returns the current base url.
+ *
+ * @return string
+ */
+ function getBaseUrl() {
+
+ return $this->baseUrl;
+
+ }
+
+ /**
+ * Returns the relative path.
+ *
+ * This is being calculated using the base url. This path will not start
+ * with a slash, so it will always return something like
+ * 'example/path.html'.
+ *
+ * If the full path is equal to the base url, this method will return an
+ * empty string.
+ *
+ * This method will also urldecode the path, and if the url was incoded as
+ * 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() {
+
+ // Removing duplicated slashes.
+ $uri = str_replace('//', '/', $this->getUrl());
+
+ $uri = Uri\normalize($uri);
+ $baseUri = Uri\normalize($this->getBaseUrl());
+
+ if (strpos($uri, $baseUri) === 0) {
+
+ // We're not interested in the query part (everything after the ?).
+ list($uri) = explode('?', $uri);
+ return trim(URLUtil::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) {
+
+ return '';
+
+ }
+
+ throw new \LogicException('Requested uri (' . $this->getUrl() . ') is out of base uri (' . $this->getBaseUrl() . ')');
+ }
+
+ /**
+ * Equivalent of PHP's $_POST.
+ *
+ * @var array
+ */
+ protected $postData = [];
+
+ /**
+ * Sets the post data.
+ *
+ * This is equivalent to PHP's $_POST superglobal.
+ *
+ * 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) {
+
+ $this->postData = $postData;
+
+ }
+
+ /**
+ * Returns the POST data.
+ *
+ * This is equivalent to PHP's $_POST superglobal.
+ *
+ * @return array
+ */
+ function getPostData() {
+
+ return $this->postData;
+
+ }
+
+ /**
+ * An array containing the raw _SERVER array.
+ *
+ * @var array
+ */
+ protected $rawServerData;
+
+ /**
+ * 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) {
+
+ if (isset($this->rawServerData[$valueName])) {
+ return $this->rawServerData[$valueName];
+ }
+
+ }
+
+ /**
+ * Sets the _SERVER array.
+ *
+ * @param array $data
+ * @return void
+ */
+ 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";
+
+ foreach ($this->getHeaders() as $key => $value) {
+ foreach ($value as $v) {
+ if ($key === 'Authorization') {
+ list($v) = explode(' ', $v, 2);
+ $v .= ' REDACTED';
+ }
+ $out .= $key . ": " . $v . "\r\n";
+ }
+ }
+ $out .= "\r\n";
+ $out .= $this->getBodyAsString();
+
+ return $out;
+
+ }
+
+}
diff --git a/vendor/sabre/http/lib/RequestDecorator.php b/vendor/sabre/http/lib/RequestDecorator.php
new file mode 100644
index 000000000..7ee3f6fc8
--- /dev/null
+++ b/vendor/sabre/http/lib/RequestDecorator.php
@@ -0,0 +1,231 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * Request Decorator
+ *
+ * This helper class allows you to easily create decorators for the Request
+ * object.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class RequestDecorator implements RequestInterface {
+
+ use MessageDecoratorTrait;
+
+ /**
+ * Constructor.
+ *
+ * @param RequestInterface $inner
+ */
+ function __construct(RequestInterface $inner) {
+
+ $this->inner = $inner;
+
+ }
+
+ /**
+ * Returns the current HTTP method
+ *
+ * @return string
+ */
+ function getMethod() {
+
+ return $this->inner->getMethod();
+
+ }
+
+ /**
+ * Sets the HTTP method
+ *
+ * @param string $method
+ * @return void
+ */
+ function setMethod($method) {
+
+ $this->inner->setMethod($method);
+
+ }
+
+ /**
+ * Returns the request url.
+ *
+ * @return string
+ */
+ function getUrl() {
+
+ return $this->inner->getUrl();
+
+ }
+
+ /**
+ * Sets the request url.
+ *
+ * @param string $url
+ * @return void
+ */
+ function setUrl($url) {
+
+ $this->inner->setUrl($url);
+
+ }
+
+ /**
+ * Returns the absolute url.
+ *
+ * @return string
+ */
+ function getAbsoluteUrl() {
+
+ return $this->inner->getAbsoluteUrl();
+
+ }
+
+ /**
+ * Sets the absolute url.
+ *
+ * @param string $url
+ * @return void
+ */
+ function setAbsoluteUrl($url) {
+
+ $this->inner->setAbsoluteUrl($url);
+
+ }
+
+ /**
+ * Returns the current base url.
+ *
+ * @return string
+ */
+ function getBaseUrl() {
+
+ return $this->inner->getBaseUrl();
+
+ }
+
+ /**
+ * Sets a base url.
+ *
+ * This url is used for relative path calculations.
+ *
+ * The base url should default to /
+ *
+ * @param string $url
+ * @return void
+ */
+ function setBaseUrl($url) {
+
+ $this->inner->setBaseUrl($url);
+
+ }
+
+ /**
+ * Returns the relative path.
+ *
+ * This is being calculated using the base url. This path will not start
+ * with a slash, so it will always return something like
+ * 'example/path.html'.
+ *
+ * If the full path is equal to the base url, this method will return an
+ * empty string.
+ *
+ * This method will also urldecode the path, and if the url was incoded as
+ * 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() {
+
+ return $this->inner->getPath();
+
+ }
+
+ /**
+ * Returns the list of query parameters.
+ *
+ * This is equivalent to PHP's $_GET superglobal.
+ *
+ * @return array
+ */
+ function getQueryParameters() {
+
+ return $this->inner->getQueryParameters();
+
+ }
+
+ /**
+ * Returns the POST data.
+ *
+ * This is equivalent to PHP's $_POST superglobal.
+ *
+ * @return array
+ */
+ function getPostData() {
+
+ return $this->inner->getPostData();
+
+ }
+
+ /**
+ * Sets the post data.
+ *
+ * This is equivalent to PHP's $_POST superglobal.
+ *
+ * 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) {
+
+ $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) {
+
+ return $this->inner->getRawServerValue($valueName);
+
+ }
+
+ /**
+ * Sets the _SERVER array.
+ *
+ * @param array $data
+ * @return void
+ */
+ 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() {
+
+ return $this->inner->__toString();
+
+ }
+}
diff --git a/vendor/sabre/http/lib/RequestInterface.php b/vendor/sabre/http/lib/RequestInterface.php
new file mode 100644
index 000000000..63d9cbb51
--- /dev/null
+++ b/vendor/sabre/http/lib/RequestInterface.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * The RequestInterface represents a HTTP request.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface RequestInterface extends MessageInterface {
+
+ /**
+ * Returns the current HTTP method
+ *
+ * @return string
+ */
+ function getMethod();
+
+ /**
+ * Sets the HTTP method
+ *
+ * @param string $method
+ * @return void
+ */
+ function setMethod($method);
+
+ /**
+ * Returns the request url.
+ *
+ * @return string
+ */
+ function getUrl();
+
+ /**
+ * Sets the request url.
+ *
+ * @param string $url
+ * @return void
+ */
+ function setUrl($url);
+
+ /**
+ * Returns the absolute url.
+ *
+ * @return string
+ */
+ function getAbsoluteUrl();
+
+ /**
+ * Sets the absolute url.
+ *
+ * @param string $url
+ * @return void
+ */
+ function setAbsoluteUrl($url);
+
+ /**
+ * Returns the current base url.
+ *
+ * @return string
+ */
+ function getBaseUrl();
+
+ /**
+ * Sets a base url.
+ *
+ * This url is used for relative path calculations.
+ *
+ * The base url should default to /
+ *
+ * @param string $url
+ * @return void
+ */
+ function setBaseUrl($url);
+
+ /**
+ * Returns the relative path.
+ *
+ * This is being calculated using the base url. This path will not start
+ * with a slash, so it will always return something like
+ * 'example/path.html'.
+ *
+ * If the full path is equal to the base url, this method will return an
+ * empty string.
+ *
+ * This method will also urldecode the path, and if the url was incoded as
+ * 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();
+
+ /**
+ * Returns the list of query parameters.
+ *
+ * This is equivalent to PHP's $_GET superglobal.
+ *
+ * @return array
+ */
+ function getQueryParameters();
+
+ /**
+ * Returns the POST data.
+ *
+ * This is equivalent to PHP's $_POST superglobal.
+ *
+ * @return array
+ */
+ function getPostData();
+
+ /**
+ * Sets the post data.
+ *
+ * This is equivalent to PHP's $_POST superglobal.
+ *
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * Sets the _SERVER array.
+ *
+ * @param array $data
+ * @return void
+ */
+ function setRawServerData(array $data);
+
+
+}
diff --git a/vendor/sabre/http/lib/Response.php b/vendor/sabre/http/lib/Response.php
new file mode 100644
index 000000000..d2ba6d40d
--- /dev/null
+++ b/vendor/sabre/http/lib/Response.php
@@ -0,0 +1,194 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * This class represents a single HTTP response.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Response extends Message implements ResponseInterface {
+
+ /**
+ * This is the list of currently registered HTTP status codes.
+ *
+ * @var array
+ */
+ static $statusCodes = [
+ 100 => 'Continue',
+ 101 => 'Switching Protocols',
+ 102 => 'Processing',
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authorative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+ 207 => 'Multi-Status', // RFC 4918
+ 208 => 'Already Reported', // RFC 5842
+ 226 => 'IM Used', // RFC 3229
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 307 => 'Temporary Redirect',
+ 308 => 'Permanent Redirect',
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Timeout',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition failed',
+ 413 => 'Request Entity Too Large',
+ 414 => 'Request-URI Too Long',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Requested Range Not Satisfiable',
+ 417 => 'Expectation Failed',
+ 418 => 'I\'m a teapot', // RFC 2324
+ 421 => 'Misdirected Request', // RFC7540 (HTTP/2)
+ 422 => 'Unprocessable Entity', // RFC 4918
+ 423 => 'Locked', // RFC 4918
+ 424 => 'Failed Dependency', // RFC 4918
+ 426 => 'Upgrade Required',
+ 428 => 'Precondition Required', // RFC 6585
+ 429 => 'Too Many Requests', // RFC 6585
+ 431 => 'Request Header Fields Too Large', // RFC 6585
+ 451 => 'Unavailable For Legal Reasons', // draft-tbray-http-legally-restricted-status
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Timeout',
+ 505 => 'HTTP Version not supported',
+ 506 => 'Variant Also Negotiates',
+ 507 => 'Insufficient Storage', // RFC 4918
+ 508 => 'Loop Detected', // RFC 5842
+ 509 => 'Bandwidth Limit Exceeded', // non-standard
+ 510 => 'Not extended',
+ 511 => 'Network Authentication Required', // RFC 6585
+ ];
+
+ /**
+ * HTTP status code
+ *
+ * @var int
+ */
+ protected $status;
+
+ /**
+ * HTTP status text
+ *
+ * @var string
+ */
+ protected $statusText;
+
+ /**
+ * Creates the response object
+ *
+ * @param string|int $status
+ * @param array $headers
+ * @param resource $body
+ * @return void
+ */
+ 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);
+
+ }
+
+
+ /**
+ * Returns the current HTTP status code.
+ *
+ * @return int
+ */
+ function getStatus() {
+
+ 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() {
+
+ return $this->statusText;
+
+ }
+
+ /**
+ * Sets the HTTP status code.
+ *
+ * This can be either the full HTTP status code with human readable string,
+ * for example: "403 I can't let you do that, Dave".
+ *
+ * Or just the code, in which case the appropriate default message will be
+ * added.
+ *
+ * @param string|int $status
+ * @throws \InvalidArgumentExeption
+ * @return void
+ */
+ function setStatus($status) {
+
+ if (ctype_digit($status) || is_int($status)) {
+
+ $statusCode = $status;
+ $statusText = isset(self::$statusCodes[$status]) ? self::$statusCodes[$status] : 'Unknown';
+
+ } else {
+ list(
+ $statusCode,
+ $statusText
+ ) = explode(' ', $status, 2);
+ }
+ if ($statusCode < 100 || $statusCode > 999) {
+ throw new \InvalidArgumentException('The HTTP status code must be exactly 3 digits');
+ }
+
+ $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";
+ foreach ($this->getHeaders() as $key => $value) {
+ foreach ($value as $v) {
+ $str .= $key . ": " . $v . "\r\n";
+ }
+ }
+ $str .= "\r\n";
+ $str .= $this->getBodyAsString();
+ return $str;
+
+ }
+
+}
diff --git a/vendor/sabre/http/lib/ResponseDecorator.php b/vendor/sabre/http/lib/ResponseDecorator.php
new file mode 100644
index 000000000..db3a67507
--- /dev/null
+++ b/vendor/sabre/http/lib/ResponseDecorator.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * Response Decorator
+ *
+ * This helper class allows you to easily create decorators for the Response
+ * object.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ResponseDecorator implements ResponseInterface {
+
+ use MessageDecoratorTrait;
+
+ /**
+ * Constructor.
+ *
+ * @param ResponseInterface $inner
+ */
+ function __construct(ResponseInterface $inner) {
+
+ $this->inner = $inner;
+
+ }
+
+ /**
+ * Returns the current HTTP status code.
+ *
+ * @return int
+ */
+ function getStatus() {
+
+ 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() {
+
+ return $this->inner->getStatusText();
+
+ }
+ /**
+ * Sets the HTTP status code.
+ *
+ * This can be either the full HTTP status code with human readable string,
+ * for example: "403 I can't let you do that, Dave".
+ *
+ * Or just the code, in which case the appropriate default message will be
+ * added.
+ *
+ * @param string|int $status
+ * @return void
+ */
+ function setStatus($status) {
+
+ $this->inner->setStatus($status);
+
+ }
+
+ /**
+ * Serializes the request object as a string.
+ *
+ * This is useful for debugging purposes.
+ *
+ * @return string
+ */
+ function __toString() {
+
+ return $this->inner->__toString();
+
+ }
+}
diff --git a/vendor/sabre/http/lib/ResponseInterface.php b/vendor/sabre/http/lib/ResponseInterface.php
new file mode 100644
index 000000000..c0ecc35ae
--- /dev/null
+++ b/vendor/sabre/http/lib/ResponseInterface.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * This interface represents a HTTP response.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface ResponseInterface extends MessageInterface {
+
+ /**
+ * Returns the current HTTP status code.
+ *
+ * @return int
+ */
+ function getStatus();
+
+ /**
+ * Returns the human-readable status string.
+ *
+ * In the case of a 200, this may for example be 'OK'.
+ *
+ * @return string
+ */
+ function getStatusText();
+
+ /**
+ * Sets the HTTP status code.
+ *
+ * This can be either the full HTTP status code with human readable string,
+ * for example: "403 I can't let you do that, Dave".
+ *
+ * Or just the code, in which case the appropriate default message will be
+ * added.
+ *
+ * @param string|int $status
+ * @throws \InvalidArgumentExeption
+ * @return void
+ */
+ function setStatus($status);
+
+}
diff --git a/vendor/sabre/http/lib/Sapi.php b/vendor/sabre/http/lib/Sapi.php
new file mode 100644
index 000000000..6c83c8719
--- /dev/null
+++ b/vendor/sabre/http/lib/Sapi.php
@@ -0,0 +1,194 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * PHP SAPI
+ *
+ * This object is responsible for:
+ * 1. Constructing a Request object based on the current HTTP request sent to
+ * the PHP process.
+ * 2. Sending the Response object back to the client.
+ *
+ * It could be said that this class provides a mapping between the Request and
+ * Response objects, and php's:
+ *
+ * * $_SERVER
+ * * $_POST
+ * * $_FILES
+ * * php://input
+ * * echo()
+ * * header()
+ * * php://output
+ *
+ * You can choose to either call all these methods statically, but you can also
+ * instantiate this as an object to allow for polymorhpism.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Sapi {
+
+ /**
+ * This static method will create a new Request object, based on the
+ * current PHP request.
+ *
+ * @return Request
+ */
+ static function getRequest() {
+
+ $r = self::createFromServerArray($_SERVER);
+ $r->setBody(fopen('php://input', 'r'));
+ $r->setPostData($_POST);
+ 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());
+ foreach ($response->getHeaders() as $key => $value) {
+
+ foreach ($value as $k => $v) {
+ if ($k === 0) {
+ header($key . ': ' . $v);
+ } else {
+ header($key . ': ' . $v, false);
+ }
+ }
+
+ }
+
+ $body = $response->getBody();
+ if (is_null($body)) return;
+
+ $contentLength = $response->getHeader('Content-Length');
+ if ($contentLength !== null) {
+ $output = fopen('php://output', 'wb');
+ if (is_resource($body) && get_resource_type($body) == 'stream') {
+ stream_copy_to_stream($body, $output, $contentLength);
+ } else {
+ fwrite($output, $body, $contentLength);
+ }
+ } else {
+ file_put_contents('php://output', $body);
+ }
+
+ 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
+ */
+ static function createFromServerArray(array $serverArray) {
+
+ $headers = [];
+ $method = null;
+ $url = null;
+ $httpVersion = '1.1';
+
+ $protocol = 'http';
+ $hostName = 'localhost';
+
+ foreach ($serverArray as $key => $value) {
+
+ switch ($key) {
+
+ case 'SERVER_PROTOCOL' :
+ if ($value === 'HTTP/1.0') {
+ $httpVersion = '1.0';
+ }
+ break;
+ case 'REQUEST_METHOD' :
+ $method = $value;
+ break;
+ case 'REQUEST_URI' :
+ $url = $value;
+ break;
+
+ // These sometimes show up without a HTTP_ prefix
+ case 'CONTENT_TYPE' :
+ $headers['Content-Type'] = $value;
+ break;
+ 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' :
+ if (isset($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;
+ break;
+
+ // Apache may prefix the HTTP_AUTHORIZATION header with
+ // REDIRECT_, if mod_rewrite was used.
+ case 'REDIRECT_HTTP_AUTHORIZATION' :
+ $headers['Authorization'] = $value;
+ break;
+
+ case 'HTTP_HOST' :
+ $hostName = $value;
+ $headers['Host'] = $value;
+ break;
+
+ case 'HTTPS' :
+ if (!empty($value) && $value !== 'off') {
+ $protocol = 'https';
+ }
+ break;
+
+ default :
+ if (substr($key, 0, 5) === 'HTTP_') {
+ // It's a HTTP header
+
+ // Normalizing it to be prettier
+ $header = strtolower(substr($key, 5));
+
+ // Transforming dashes into spaces, and uppercasing
+ // every first letter.
+ $header = ucwords(str_replace('_', ' ', $header));
+
+ // Turning spaces into dashes.
+ $header = str_replace(' ', '-', $header);
+ $headers[$header] = $value;
+
+ }
+ break;
+
+
+ }
+
+ }
+
+ $r = new Request($method, $url, $headers);
+ $r->setHttpVersion($httpVersion);
+ $r->setRawServerData($serverArray);
+ $r->setAbsoluteUrl($protocol . '://' . $hostName . $url);
+ return $r;
+
+ }
+
+}
diff --git a/vendor/sabre/http/lib/URLUtil.php b/vendor/sabre/http/lib/URLUtil.php
new file mode 100644
index 000000000..474856348
--- /dev/null
+++ b/vendor/sabre/http/lib/URLUtil.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace Sabre\HTTP;
+
+use Sabre\URI;
+
+/**
+ * URL utility class
+ *
+ * Note: this class is deprecated. All its functionality moved to functions.php
+ * or sabre\uri.
+ *
+ * @deprectated
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class URLUtil {
+
+ /**
+ * Encodes the path of a url.
+ *
+ * slashes (/) are treated as path-separators.
+ *
+ * @deprecated use \Sabre\HTTP\encodePath()
+ * @param string $path
+ * @return string
+ */
+ static function encodePath($path) {
+
+ return encodePath($path);
+
+ }
+
+ /**
+ * Encodes a 1 segment of a path
+ *
+ * Slashes are considered part of the name, and are encoded as %2f
+ *
+ * @deprecated use \Sabre\HTTP\encodePathSegment()
+ * @param string $pathSegment
+ * @return string
+ */
+ static function encodePathSegment($pathSegment) {
+
+ return encodePathSegment($pathSegment);
+
+ }
+
+ /**
+ * Decodes a url-encoded path
+ *
+ * @deprecated use \Sabre\HTTP\decodePath
+ * @param string $path
+ * @return string
+ */
+ static function decodePath($path) {
+
+ return decodePath($path);
+
+ }
+
+ /**
+ * Decodes a url-encoded path segment
+ *
+ * @deprecated use \Sabre\HTTP\decodePathSegment()
+ * @param string $path
+ * @return string
+ */
+ static function decodePathSegment($path) {
+
+ return decodePathSegment($path);
+
+ }
+
+ /**
+ * Returns the 'dirname' and 'basename' for a path.
+ *
+ * @deprecated Use Sabre\Uri\split().
+ * @param string $path
+ * @return array
+ */
+ static function splitPath($path) {
+
+ return Uri\split($path);
+
+ }
+
+ /**
+ * Resolves relative urls, like a browser would.
+ *
+ * @deprecated Use Sabre\Uri\resolve().
+ * @param string $basePath
+ * @param string $newPath
+ * @return string
+ */
+ static function resolve($basePath, $newPath) {
+
+ return Uri\resolve($basePath, $newPath);
+
+ }
+
+}
diff --git a/vendor/sabre/http/lib/Util.php b/vendor/sabre/http/lib/Util.php
new file mode 100644
index 000000000..83bde50a4
--- /dev/null
+++ b/vendor/sabre/http/lib/Util.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * HTTP utility methods
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @author Paul Voegler
+ * @deprecated All these functions moved to functions.php
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Util {
+
+ /**
+ * Content negotiation
+ *
+ * @deprecated Use \Sabre\HTTP\negotiateContentType
+ * @param string|null $acceptHeaderValue
+ * @param array $availableOptions
+ * @return string|null
+ */
+ static function negotiateContentType($acceptHeaderValue, array $availableOptions) {
+
+ return negotiateContentType($acceptHeaderValue, $availableOptions);
+
+ }
+
+ /**
+ * Deprecated! Use negotiateContentType.
+ *
+ * @deprecated Use \Sabre\HTTP\NegotiateContentType
+ * @param string|null $acceptHeader
+ * @param array $availableOptions
+ * @return string|null
+ */
+ static function negotiate($acceptHeaderValue, array $availableOptions) {
+
+ return negotiateContentType($acceptHeaderValue, $availableOptions);
+
+ }
+
+ /**
+ * Parses a RFC2616-compatible date string
+ *
+ * This method returns false if the date is invalid
+ *
+ * @deprecated Use parseDate
+ * @param string $dateHeader
+ * @return bool|DateTime
+ */
+ static function parseHTTPDate($dateHeader) {
+
+ return parseDate($dateHeader);
+
+ }
+
+ /**
+ * Transforms a DateTime object to HTTP's most common date format.
+ *
+ * We're serializing it as the RFC 1123 date, which, for HTTP must be
+ * specified as GMT.
+ *
+ * @deprecated Use toDate
+ * @param \DateTime $dateTime
+ * @return string
+ */
+ static function toHTTPDate(\DateTime $dateTime) {
+
+ return toDate($dateTime);
+
+ }
+}
diff --git a/vendor/sabre/http/lib/Version.php b/vendor/sabre/http/lib/Version.php
new file mode 100644
index 000000000..789ee4543
--- /dev/null
+++ b/vendor/sabre/http/lib/Version.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * This class contains the version number for the HTTP package
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Version {
+
+ /**
+ * Full version number
+ */
+ const VERSION = '4.2.1';
+
+}
diff --git a/vendor/sabre/http/lib/functions.php b/vendor/sabre/http/lib/functions.php
new file mode 100644
index 000000000..1ec123f2e
--- /dev/null
+++ b/vendor/sabre/http/lib/functions.php
@@ -0,0 +1,445 @@
+<?php
+
+namespace Sabre\HTTP;
+
+use DateTime;
+
+/**
+ * A collection of useful helpers for parsing or generating various HTTP
+ * headers.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+
+/**
+ * Parses a HTTP date-string.
+ *
+ * This method returns false if the date is invalid.
+ *
+ * The following formats are supported:
+ * Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate
+ * Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format
+ * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+ *
+ * See:
+ * http://tools.ietf.org/html/rfc7231#section-7.1.1.1
+ *
+ * @param string $dateString
+ * @return bool|DateTime
+ */
+function parseDate($dateString) {
+
+ // Only the format is checked, valid ranges are checked by strtotime below
+ $month = '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)';
+ $weekday = '(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)';
+ $wkday = '(Mon|Tue|Wed|Thu|Fri|Sat|Sun)';
+ $time = '([0-1]\d|2[0-3])(\:[0-5]\d){2}';
+ $date3 = $month . ' ([12]\d|3[01]| [1-9])';
+ $date2 = '(0[1-9]|[12]\d|3[01])\-' . $month . '\-\d{2}';
+ // 4-digit year cannot begin with 0 - unix timestamp begins in 1970
+ $date1 = '(0[1-9]|[12]\d|3[01]) ' . $month . ' [1-9]\d{3}';
+
+ // ANSI C's asctime() format
+ // 4-digit year cannot begin with 0 - unix timestamp begins in 1970
+ $asctime_date = $wkday . ' ' . $date3 . ' ' . $time . ' [1-9]\d{3}';
+ // RFC 850, obsoleted by RFC 1036
+ $rfc850_date = $weekday . ', ' . $date2 . ' ' . $time . ' GMT';
+ // RFC 822, updated by RFC 1123
+ $rfc1123_date = $wkday . ', ' . $date1 . ' ' . $time . ' GMT';
+ // allowed date formats by RFC 2616
+ $HTTP_date = "($rfc1123_date|$rfc850_date|$asctime_date)";
+
+ // allow for space around the string and strip it
+ $dateString = trim($dateString, ' ');
+ if (!preg_match('/^' . $HTTP_date . '$/', $dateString))
+ return false;
+
+ // append implicit GMT timezone to ANSI C time format
+ if (strpos($dateString, ' GMT') === false)
+ $dateString .= ' GMT';
+
+ try {
+ return new DateTime($dateString, new \DateTimeZone('UTC'));
+ } catch (\Exception $e) {
+ return false;
+ }
+
+}
+
+/**
+ * Transforms a DateTime object to a valid HTTP/1.1 Date header value
+ *
+ * @param DateTime $dateTime
+ * @return string
+ */
+function toDate(DateTime $dateTime) {
+
+ // We need to clone it, as we don't want to affect the existing
+ // DateTime.
+ $dateTime = clone $dateTime;
+ $dateTime->setTimeZone(new \DateTimeZone('GMT'));
+ return $dateTime->format('D, d M Y H:i:s \G\M\T');
+
+}
+
+/**
+ * This function can be used to aid with content negotiation.
+ *
+ * It takes 2 arguments, the $acceptHeaderValue, which usually comes from
+ * an Accept header, and $availableOptions, which contains an array of
+ * items that the server can support.
+ *
+ * The result of this function will be the 'best possible option'. If no
+ * best possible option could be found, null is returned.
+ *
+ * When it's null you can according to the spec either return a default, or
+ * you can choose to emit 406 Not Acceptable.
+ *
+ * The method also accepts sending 'null' for the $acceptHeaderValue,
+ * implying that no accept header was sent.
+ *
+ * @param string|null $acceptHeaderValue
+ * @param array $availableOptions
+ * @return string|null
+ */
+function negotiateContentType($acceptHeaderValue, array $availableOptions) {
+
+ if (!$acceptHeaderValue) {
+ // Grabbing the first in the list.
+ return reset($availableOptions);
+ }
+
+ $proposals = array_map(
+ 'Sabre\HTTP\parseMimeType',
+ explode(',', $acceptHeaderValue)
+ );
+
+ // Ensuring array keys are reset.
+ $availableOptions = array_values($availableOptions);
+
+ $options = array_map(
+ 'Sabre\HTTP\parseMimeType',
+ $availableOptions
+ );
+
+ $lastQuality = 0;
+ $lastSpecificity = 0;
+ $lastOptionIndex = 0;
+ $lastChoice = null;
+
+ foreach ($proposals as $proposal) {
+
+ // Ignoring broken values.
+ if (is_null($proposal)) continue;
+
+ // If the quality is lower we don't have to bother comparing.
+ if ($proposal['quality'] < $lastQuality) {
+ continue;
+ }
+
+ foreach ($options as $optionIndex => $option) {
+
+ if ($proposal['type'] !== '*' && $proposal['type'] !== $option['type']) {
+ // no match on type.
+ continue;
+ }
+ if ($proposal['subType'] !== '*' && $proposal['subType'] !== $option['subType']) {
+ // no match on subtype.
+ continue;
+ }
+
+ // Any parameters appearing on the options must appear on
+ // proposals.
+ foreach ($option['parameters'] as $paramName => $paramValue) {
+ if (!array_key_exists($paramName, $proposal['parameters'])) {
+ continue 2;
+ }
+ if ($paramValue !== $proposal['parameters'][$paramName]) {
+ continue 2;
+ }
+ }
+
+ // If we got here, we have a match on parameters, type and
+ // subtype. We need to calculate a score for how specific the
+ // match was.
+ $specificity =
+ ($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;
+
+}
+
+/**
+ * Parses the Prefer header, as defined in RFC7240.
+ *
+ * Input can be given as a single header value (string) or multiple headers
+ * (array of string).
+ *
+ * This method will return a key->value array with the various Prefer
+ * parameters.
+ *
+ * Prefer: return=minimal will result in:
+ *
+ * [ 'return' => 'minimal' ]
+ *
+ * Prefer: foo, wait=10 will result in:
+ *
+ * [ 'foo' => true, 'wait' => '10']
+ *
+ * This method also supports the formats from older drafts of RFC7240, and
+ * it will automatically map them to the new values, as the older values
+ * are still pretty common.
+ *
+ * Parameters are currently discarded. There's no known prefer value that
+ * uses them.
+ *
+ * @param string|string[] $header
+ * @return array
+ */
+function parsePrefer($input) {
+
+ $token = '[!#$%&\'*+\-.^_`~A-Za-z0-9]+';
+
+ // Work in progress
+ $word = '(?: [a-zA-Z0-9]+ | "[a-zA-Z0-9]*" )';
+
+ $regex = <<<REGEX
+/
+^
+(?<name> $token) # Prefer property name
+\s* # Optional space
+(?: = \s* # Prefer property value
+ (?<value> $word)
+)?
+(?: \s* ; (?: .*))? # Prefer parameters (ignored)
+$
+/x
+REGEX;
+
+ $output = [];
+ foreach (getHeaderValues($input) as $value) {
+
+ if (!preg_match($regex, $value, $matches)) {
+ // Ignore
+ continue;
+ }
+
+ // Mapping old values to their new counterparts
+ switch ($matches['name']) {
+ case 'return-asynch' :
+ $output['respond-async'] = true;
+ break;
+ case 'return-representation' :
+ $output['return'] = 'representation';
+ break;
+ case 'return-minimal' :
+ $output['return'] = 'minimal';
+ break;
+ case 'strict' :
+ $output['handling'] = 'strict';
+ break;
+ case 'lenient' :
+ $output['handling'] = 'lenient';
+ break;
+ default :
+ if (isset($matches['value'])) {
+ $value = trim($matches['value'], '"');
+ } else {
+ $value = true;
+ }
+ $output[strtolower($matches['name'])] = empty($value) ? true : $value;
+ break;
+ }
+
+ }
+
+ return $output;
+
+}
+
+/**
+ * This method splits up headers into all their individual values.
+ *
+ * A HTTP header may have more than one header, such as this:
+ * Cache-Control: private, no-store
+ *
+ * Header values are always split with a comma.
+ *
+ * You can pass either a string, or an array. The resulting value is always
+ * an array with each spliced value.
+ *
+ * If the second headers argument is set, this value will simply be merged
+ * in. This makes it quicker to merge an old list of values with a new set.
+ *
+ * @param string|string[] $values
+ * @param string|string[] $values2
+ * @return string[]
+ */
+function getHeaderValues($values, $values2 = null) {
+
+ $values = (array)$values;
+ if ($values2) {
+ $values = array_merge($values, (array)$values2);
+ }
+ foreach ($values as $l1) {
+ foreach (explode(',', $l1) as $l2) {
+ $result[] = trim($l2);
+ }
+ }
+ return $result;
+
+}
+
+/**
+ * Parses a mime-type and splits it into:
+ *
+ * 1. type
+ * 2. subtype
+ * 3. quality
+ * 4. parameters
+ *
+ * @param string $str
+ * @return array
+ */
+function parseMimeType($str) {
+
+ $parameters = [];
+ // If no q= parameter appears, then quality = 1.
+ $quality = 1;
+
+ $parts = explode(';', $str);
+
+ // The first part is the mime-type.
+ $mimeType = array_shift($parts);
+
+ $mimeType = explode('/', trim($mimeType));
+ if (count($mimeType) !== 2) {
+ // Illegal value
+ return null;
+ }
+ list($type, $subType) = $mimeType;
+
+ foreach ($parts as $part) {
+
+ $part = trim($part);
+ if (strpos($part, '=')) {
+ list($partName, $partValue) =
+ explode('=', $part, 2);
+ } else {
+ $partName = $part;
+ $partValue = null;
+ }
+
+ // The quality parameter, if it appears, also marks the end of
+ // the parameter list. Anything after the q= counts as an
+ // 'accept extension' and could introduce new semantics in
+ // content-negotation.
+ if ($partName !== 'q') {
+ $parameters[$partName] = $part;
+ } else {
+ $quality = (float)$partValue;
+ break; // Stop parsing parts
+ }
+
+ }
+
+ return [
+ '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]));
+
+ }, $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]));
+
+ }, $pathSegment);
+}
+
+/**
+ * Decodes a url-encoded path
+ *
+ * @param string $path
+ * @return string
+ */
+function decodePath($path) {
+
+ return decodePathSegment($path);
+
+}
+
+/**
+ * Decodes a url-encoded path segment
+ *
+ * @param string $path
+ * @return string
+ */
+function decodePathSegment($path) {
+
+ $path = rawurldecode($path);
+ $encoding = mb_detect_encoding($path, ['UTF-8', 'ISO-8859-1']);
+
+ switch ($encoding) {
+
+ case 'ISO-8859-1' :
+ $path = utf8_encode($path);
+
+ }
+
+ return $path;
+
+}
diff --git a/vendor/sabre/uri/.gitignore b/vendor/sabre/uri/.gitignore
new file mode 100644
index 000000000..19d1affd4
--- /dev/null
+++ b/vendor/sabre/uri/.gitignore
@@ -0,0 +1,13 @@
+# Composer
+vendor/
+composer.lock
+
+# Tests
+tests/cov/
+
+# Composer binaries
+bin/phpunit
+bin/phpcs
+
+# Vim
+.*.swp
diff --git a/vendor/sabre/uri/.travis.yml b/vendor/sabre/uri/.travis.yml
new file mode 100644
index 000000000..f7d1a0657
--- /dev/null
+++ b/vendor/sabre/uri/.travis.yml
@@ -0,0 +1,14 @@
+language: php
+php:
+ - 5.4
+ - 5.5
+ - 5.6
+ - hhvm
+ - 7
+
+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/vendor/sabre/uri/CHANGELOG.md b/vendor/sabre/uri/CHANGELOG.md
new file mode 100644
index 000000000..9fa510dc2
--- /dev/null
+++ b/vendor/sabre/uri/CHANGELOG.md
@@ -0,0 +1,34 @@
+ChangeLog
+=========
+
+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/vendor/sabre/uri/LICENSE b/vendor/sabre/uri/LICENSE
new file mode 100644
index 000000000..9a3a91946
--- /dev/null
+++ b/vendor/sabre/uri/LICENSE
@@ -0,0 +1,27 @@
+Copyright (C) 2014-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 Sabre 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/vendor/sabre/uri/README.md b/vendor/sabre/uri/README.md
new file mode 100644
index 000000000..76f55d8e4
--- /dev/null
+++ b/vendor/sabre/uri/README.md
@@ -0,0 +1,55 @@
+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/vendor/sabre/uri/lib/Version.php b/vendor/sabre/uri/lib/Version.php
new file mode 100644
index 000000000..88678d4d5
--- /dev/null
+++ b/vendor/sabre/uri/lib/Version.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Sabre\Uri;
+
+/**
+ * This class contains the version number for this package.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/
+ */
+class Version {
+
+ /**
+ * Full version number
+ */
+ const VERSION = '1.1.0';
+
+}
diff --git a/vendor/sabre/uri/lib/functions.php b/vendor/sabre/uri/lib/functions.php
new file mode 100644
index 000000000..06181aef0
--- /dev/null
+++ b/vendor/sabre/uri/lib/functions.php
@@ -0,0 +1,282 @@
+<?php
+
+namespace Sabre\Uri;
+
+/**
+ * This file contains all the uri handling functions.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/
+ */
+
+/**
+ * Resolves relative urls, like a browser would.
+ *
+ * This function takes a basePath, which itself _may_ also be relative, and
+ * then applies the relative path on top of it.
+ *
+ * @param string $basePath
+ * @param string $newPath
+ * @return string
+ */
+function resolve($basePath, $newPath) {
+
+ $base = parse($basePath);
+ $delta = parse($newPath);
+
+ $pick = function($part) use ($base, $delta) {
+
+ if ($delta[$part]) {
+ return $delta[$part];
+ } elseif ($base[$part]) {
+ return $base[$part];
+ }
+ return null;
+
+ };
+
+ // If the new path defines a scheme, it's absolute and we can just return
+ // that.
+ if ($delta['scheme']) {
+ return build($delta);
+ }
+
+ $newParts = [];
+
+ $newParts['scheme'] = $pick('scheme');
+ $newParts['host'] = $pick('host');
+ $newParts['port'] = $pick('port');
+
+ $path = '';
+ if ($delta['path']) {
+ // If the path starts with a slash
+ 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, '/'));
+ }
+ $path .= '/' . $delta['path'];
+ }
+ } else {
+ $path = $base['path'] ?: '/';
+ }
+ // Removing .. and .
+ $pathParts = explode('/', $path);
+ $newPathParts = [];
+ foreach ($pathParts as $pathPart) {
+
+ switch ($pathPart) {
+ //case '' :
+ case '.' :
+ break;
+ case '..' :
+ array_pop($newPathParts);
+ break;
+ default :
+ $newPathParts[] = $pathPart;
+ break;
+ }
+ }
+
+ $path = implode('/', $newPathParts);
+
+ // If the source url ended with a /, we want to preserve that.
+ $newParts['path'] = $path;
+ if ($delta['query']) {
+ $newParts['query'] = $delta['query'];
+ } elseif (!empty($base['query']) && empty($delta['host']) && empty($delta['path'])) {
+ // Keep the old query if host and path didn't change
+ $newParts['query'] = $base['query'];
+ }
+ if ($delta['fragment']) {
+ $newParts['fragment'] = $delta['fragment'];
+ }
+ return build($newParts);
+
+}
+
+/**
+ * Takes a URI or partial URI as its argument, and normalizes it.
+ *
+ * After normalizing a URI, you can safely compare it to other URIs.
+ * This function will for instance convert a %7E into a tilde, according to
+ * rfc3986.
+ *
+ * It will also change a %3a into a %3A.
+ *
+ * @param string $uri
+ * @return string
+ */
+function normalize($uri) {
+
+ $parts = parse($uri);
+
+ if (!empty($parts['path'])) {
+ $pathParts = explode('/', ltrim($parts['path'], '/'));
+ $newPathParts = [];
+ foreach ($pathParts as $pathPart) {
+ switch ($pathPart) {
+ case '.':
+ // skip
+ break;
+ case '..' :
+ // One level up in the hierarchy
+ array_pop($newPathParts);
+ break;
+ default :
+ // Ensuring that everything is correctly percent-encoded.
+ $newPathParts[] = rawurlencode(rawurldecode($pathPart));
+ break;
+ }
+ }
+ $parts['path'] = '/' . implode('/', $newPathParts);
+ }
+
+ if ($parts['scheme']) {
+ $parts['scheme'] = strtolower($parts['scheme']);
+ $defaultPorts = [
+ 'http' => '80',
+ 'https' => '443',
+ ];
+
+ if (!empty($parts['port']) && isset($defaultPorts[$parts['scheme']]) && $defaultPorts[$parts['scheme']] == $parts['port']) {
+ // Removing default ports.
+ unset($parts['port']);
+ }
+ // A few HTTP specific rules.
+ switch ($parts['scheme']) {
+ case 'http' :
+ case 'https' :
+ if (empty($parts['path'])) {
+ // An empty path is equivalent to / in http.
+ $parts['path'] = '/';
+ }
+ break;
+ }
+ }
+
+ if ($parts['host']) $parts['host'] = strtolower($parts['host']);
+
+ return build($parts);
+
+}
+
+/**
+ * Parses a URI and returns its individual components.
+ *
+ * This method largely behaves the same as PHP's parse_url, except that it will
+ * return an array with all the array keys, including the ones that are not
+ * set by parse_url, which makes it a bit easier to work with.
+ *
+ * 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
+ */
+function parse($uri) {
+
+ // Normally a URI must be ASCII, however. However, often it's not and
+ // parse_url might corrupt these strings.
+ //
+ // For that reason we take any non-ascii characters from the uri and
+ // uriencode them first.
+ $uri = preg_replace_callback(
+ '/[^[:ascii:]]/u',
+ function($matches) {
+ return rawurlencode($matches[0]);
+ },
+ $uri
+ );
+
+ return
+ parse_url($uri) + [
+ '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
+ */
+function build(array $parts) {
+
+ $uri = '';
+
+ $authority = '';
+ if (!empty($parts['host'])) {
+ $authority = $parts['host'];
+ if (!empty($parts['user'])) {
+ $authority = $parts['user'] . '@' . $authority;
+ }
+ if (!empty($parts['port'])) {
+ $authority = $authority . ':' . $parts['port'];
+ }
+ }
+
+ if (!empty($parts['scheme'])) {
+ // If there's a scheme, there's also a host.
+ $uri = $parts['scheme'] . ':';
+
+ }
+ if ($authority) {
+ // No scheme, but there is a host.
+ $uri .= '//' . $authority;
+
+ }
+
+ if (!empty($parts['path'])) {
+ $uri .= $parts['path'];
+ }
+ if (!empty($parts['query'])) {
+ $uri .= '?' . $parts['query'];
+ }
+ if (!empty($parts['fragment'])) {
+ $uri .= '#' . $parts['fragment'];
+ }
+
+ return $uri;
+
+}
+
+/**
+ * Returns the 'dirname' and 'basename' for a path.
+ *
+ * The reason there is a custom function for this purpose, is because
+ * basename() is locale aware (behaviour changes if C locale or a UTF-8 locale
+ * 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.
+ *
+ * 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
+ */
+function split($path) {
+
+ $matches = [];
+ if (preg_match('/^(?:(?:(.*)(?:\/+))?([^\/]+))(?:\/?)$/u', $path, $matches)) {
+ return [$matches[1], $matches[2]];
+ }
+ return [null,null];
+
+}
diff --git a/vendor/sabre/vobject/.gitignore b/vendor/sabre/vobject/.gitignore
index a46b19e2a..95935f798 100644
--- a/vendor/sabre/vobject/.gitignore
+++ b/vendor/sabre/vobject/.gitignore
@@ -2,3 +2,20 @@
vendor/
composer.lock
tests/cov/
+tests/temp
+
+#vim
+.*.swp
+
+#binaries
+bin/phpunit
+bin/phpcs
+bin/php-cs-fixer
+bin/sabre-cs-fixer
+bin/hoa
+
+# Development stuff
+testdata/
+
+# OS X
+.DS_Store
diff --git a/vendor/sabre/vobject/.travis.yml b/vendor/sabre/vobject/.travis.yml
index 5bd15086d..06bbdf8a0 100644
--- a/vendor/sabre/vobject/.travis.yml
+++ b/vendor/sabre/vobject/.travis.yml
@@ -1,17 +1,24 @@
language: php
php:
- - 5.3
- - 5.4
- 5.5
- - 5.5.9
- - 5.5.10
- 5.6
+ - 7
+ - hhvm
+
+sudo: false
matrix:
allow_failures:
- - php: 5.5.10
- - php: 5.6
+ - php: hhvm
+
+script:
+ - phpunit --configuration tests/phpunit.xml
+ - ./bin/sabre-cs-fixer fix . --dry-run --diff
-script: phpunit --configuration tests/phpunit.xml
+before_script:
+ - phpenv config-rm xdebug.ini; true
+ - composer install
-before_script: composer install
+cache:
+ directories:
+ - $HOME/.composer/cache
diff --git a/vendor/sabre/vobject/CHANGELOG.md b/vendor/sabre/vobject/CHANGELOG.md
new file mode 100644
index 000000000..0bd7d53bd
--- /dev/null
+++ b/vendor/sabre/vobject/CHANGELOG.md
@@ -0,0 +1,739 @@
+ChangeLog
+=========
+
+
+4.1.0 (2016-04-06)
+------------------
+
+* #309: When expanding recurring events, the first event should also have a
+ `RECURRENCE-ID` property.
+* #306: iTip REPLYs to the first instance of a recurring event was not handled
+ correctly.
+* Slightly better error message during validation of `N` and `ADR` properties.
+* #312: Correctly extracing timezone in the iTip broker, even when we don't
+ have a master event. (@vkomrakov-sugar).
+* When validating a component's property that must appear once and which could
+ automatically be repaired, make sure we report the change as 'repaired'.
+* Added a PHPUnitAssertions trait. This trait makes it easy to compare two
+ vcards or iCalendar objects semantically.
+* Better error message when parsing objects with an invalid `VALUE` parameter.
+
+
+4.0.3 (2016-03-12)
+------------------
+
+* #300: Added `VCard::getByType()` to quickly get a property with a specific
+ `TYPE` parameter. (@kbond)
+* #302: `UNTIL` was not encoded correctly when converting to jCal.
+ (@GrahamLinagora)
+* #303: `COUNT` is now encoded as an int in jCal instead of a string. (@strokyl)
+* #295: `RRULE` now has more validation and repair rules.
+
+
+4.0.2 (2016-01-11)
+------------------
+
+* #288: Only decode `CHARSET` if we're reading vCard 2.1. If it appears
+ in any other document, we must ignore it.
+
+
+4.0.1 (2016-01-04)
+------------------
+
+* #284: When generating `CANCEL` iTip messages, we now include `DTEND`.
+ (@kewisch)
+
+
+4.0.0 (2015-12-11)
+------------------
+
+* #274: When creating new vCards, the default vCard version is now 4.0.
+* #275: `VEVENT`, `VTODO` and `VCARD` now automatically get a `UID` and
+ `DTSTAMP` property if this was not already specified.
+* `ParseException` now extends `\Exception`.
+* `Sabre\VObject\Reader::read` now has a `$charset` argument.
+* #272: `Sabre\VObject\Recur\EventIterator::$maxInstances` is now
+ `Sabre\VObject\Settings::$maxRecurrences` and is also honored by the
+ FreeBusyGenerator.
+* #278: `expand()` did not work correctly on events with sub-components.
+
+
+4.0.0-beta1 (2015-12-02)
+------------------------
+
+* #258: Support for expanding events that use `RDATE`. (@jabdoa2)
+* #258: Correctly support TZID for events that use `RDATE`. (@jabdoa2)
+* #240: `Component\VCalendar::expand()` now returns a new expanded `VCalendar`
+ object, instead of editing the existing `VCalendar` in-place. This is a BC
+ break.
+* #265: Using the new `InvalidDataException` in place of
+ `InvalidArgumentException` and `LogicException` in all places where we fail
+ because there was something wrong with input data.
+* #227: Always add `VALUE=URI` to `PHOTO` properties.
+* #235: Always add `VALUE=URI` to `URL` properties.
+* It's now possible to override which class is used instead of
+ `Component\VCalendar` or `Component\VCard` during parsing.
+* #263: Lots of small cleanups. (@jakobsack)
+* #220: Automatically stop recurring after 3500 recurrences.
+* #41: Allow user to set different encoding than UTF-8 when decoding vCards.
+* #41: Support the `ENCODING` parameter from vCard 2.1.
+ Both ISO-8859-1 and Windows-1252 are currently supported.
+* #185: Fix encoding/decoding of `TIME` values in jCal/jCard.
+
+
+4.0.0-alpha2 (2015-09-04)
+-------------------------
+
+* Updated windows timezone file to support new mexican timezone.
+* #239: Added a `BirthdayCalendarGenerator`. (@DominikTo)
+* #250: `isInTimeRange()` now considers the timezone for floating dates and
+ times. (@armin-hackmann)
+* Added a duplicate vcard merging tool for the command line.
+* #253: `isInTimeRange()` now correctly handles events that throw the
+ `NoInstancesException` exception. (@migrax, @DominikTo)
+* #254: The parser threw an `E_NOTICE` for certain invalid objects. It now
+ correctly throws a `ParseException`.
+
+
+4.0.0-alpha1 (2015-07-17)
+-------------------------
+
+* sabre/vobject now requires PHP 5.5.
+* #244: PHP7 support.
+* Lots of speedups and reduced memory usage!
+* #160: Support for xCal a.k.a. RFC6321! (@Hywan)
+* #192: Support for xCard a.k.a. RFC6351! (@Hywan)
+* #139: We now accept `DateTimeInterface` wherever it accepted `DateTime`
+ before in arguments. This means that either `DateTime` or
+ `DateTimeImmutable` may be used everywhere.
+* #242: Full support for the `VAVAILABILITY` component, and calculating
+ `VFREEBUSY` based on `VAVAILABILITY` data.
+* #186: Fixing conversion of `UTC-OFFSET` properties when going back and
+ forward between jCal and iCalendar.
+* Properties, Components and Parameters now implement PHP's `JsonSerializable`
+ interface.
+* #139: We now _always_ return `DateTimeImmutable` from any method. This could
+ potentially have big implications if you manipulate Date objects anywhere.
+* #161: Simplified `ElementList` by extending `ArrayIterator`.
+* Removed `RecurrenceIterator` (use Recur\EventIterator instead).
+* Now using php-cs-fixer to automatically enforce and correct CS.
+* #233: The `+00:00` timezone is now recognized as UTC. (@c960657)
+* #237: Added a `destroy()` method to all documents. This method breaks any
+ circular references, allowing PHP to free up memory.
+* #197: Made accessing properties and objects by their name a lot faster. This
+ especially helps objects that have a lot of sub-components or properties,
+ such as large iCalendar objects.
+* #197: The `$children` property on components has been changed from `public`
+ to `protected`. Use the `children()` method instead to get a flat list of
+ objects.
+* #244: The `Float` and `Integer` classes have been renamed to `FloatValue`
+ and `IntegerValue` to allow PHP 7 compatibility.
+
+
+3.5.1 (2016-04-06)
+------------------
+
+* #309: When expanding recurring events, the first event should also have a
+ `RECURRENCE-ID` property.
+* #306: iTip REPLYs to the first instance of a recurring event was not handled
+ correctly.
+
+
+3.5.0 (2016-01-11)
+------------------
+
+* This release supports PHP 7, contrary to 3.4.x versions.
+* BC Break: `Sabre\VObject\Property\Float` has been renamed to
+ `Sabre\VObject\Property\FloatValue`.
+* BC Break: `Sabre\VObject\Property\Integer` has been renamed to
+ `Sabre\VObject\Property\IntegerValue`.
+
+
+3.4.9 (2016-01-11)
+------------------
+
+* This package now specifies in composer.json that it does not support PHP 7.
+ For PHP 7, use version 3.5.x or 4.x.
+
+
+3.4.8 (2016-01-04)
+------------------
+
+* #284: When generating `CANCEL` iTip messages, we now include `DTEND`.
+ (@kewisch).
+
+
+3.4.7 (2015-09-05)
+------------------
+
+* #253: Handle `isInTimeRange` for recurring events that have 0 valid
+ instances. (@DominikTo, @migrax).
+
+
+3.4.6 (2015-08-06)
+------------------
+
+* #250: Recurring all-day events are incorrectly included in time range
+ requests when not using UTC in the time range. (@armin-hackmann)
+
+
+3.4.5 (2015-06-02)
+------------------
+
+* #229: Converting vcards from 3.0 to 4.0 that contained a `LANG` property
+ would throw an error.
+
+
+3.4.4 (2015-05-27)
+------------------
+
+* #228: Fixed a 'party crasher' bug in the iTip broker. This would break
+ scheduling in some cases.
+
+
+3.4.3 (2015-05-19)
+------------------
+
+* #219: Corrected validation of `EXDATE` properties with more than one value.
+* #212: `BYSETPOS` with values below `-1` was broken and could cause infinite
+ loops.
+* #211: Fix `BYDAY=-5TH` in recurrence iterator. (@lindquist)
+* #216: `ENCODING` parameter is now validated for all document types.
+* #217: Initializing vCard `DATE` objects with a PHP DateTime object will now
+ work correctly. (@thomascube)
+
+
+3.4.2 (2015-02-25)
+------------------
+
+* #210: iTip: Replying to an event without a master event was broken.
+
+
+3.4.1 (2015-02-24)
+------------------
+
+* A minor change to ensure that unittests work correctly in the sabre/dav
+ test-suite.
+
+
+3.4.0 (2015-02-23)
+------------------
+
+* #196: Made parsing recurrence rules a lot faster on big calendars.
+* Updated windows timezone mappings to latest unicode version.
+* #202: Support for parsing and validating `VAVAILABILITY` components. (@Hywan)
+* #195: PHP 5.3 compatibility in 'generatevcards' script. (@rickdenhaan)
+* #205: Improving handling of multiple `EXDATE` when processing iTip changes.
+ (@armin-hackmann)
+* #187: Fixed validator rules for `LAST-MODIFIED` properties.
+* #188: Retain floating times when generating instances using
+ `Recur\EventIterator`.
+* #203: Skip tests for timezones that are not supported on older PHP versions,
+ instead of a hard fail.
+* #204: Dealing a bit better with vCard date-time values that contained
+ milliseconds. (which is normally invalid). (@armin-hackmann)
+
+
+3.3.5 (2015-01-09)
+------------------
+
+* #168: Expanding calendars now removes objects with recurrence rules that
+ don't have a valid recurrence instance.
+* #177: SCHEDULE-STATUS should not contain a reason phrase, only a status
+ code.
+* #175: Parser can now read and skip the UTF-8 BOM.
+* #179: Added `isFloating` to `DATE-TIME` properties.
+* #179: Fixed jCal serialization of floating `DATE-TIME` properties.
+* #173: vCard converter failed for `X-ABDATE` properties that had no
+ `X-ABLABEL`.
+* #180: Added `PROFILE_CALDAV` and `PROFILE_CARDDAV` to enable validation rules
+ specific for CalDAV/CardDAV servers.
+* #176: A missing `UID` is no longer an error, but a warning for the vCard
+ validator, unless `PROFILE_CARDDAV` is specified.
+
+
+3.3.4 (2014-11-19)
+------------------
+
+* #154: Converting `ANNIVERSARY` to `X-ANNIVERSARY` and `X-ABDATE` and
+ vice-versa when converting to/from vCard 4.
+* #154: It's now possible to easily select all vCard properties belonging to
+ a single group with `$vcard->{'ITEM1.'}` syntax. (@armin-hackmann)
+* #156: Simpler way to check if a string is UTF-8. (@Hywan)
+* Unittest improvements.
+* #159: The recurrence iterator, freebusy generator and iCalendar DATE and
+ DATE-TIME properties can now all accept a reference timezone when working
+ floating times or all-day events.
+* #159: Master events will no longer get a `RECURRENCE-ID` when expanding.
+* #159: `RECURRENCE-ID` for all-day events will now be correct when expanding.
+* #163: Added a `getTimeZone()` method to `VTIMEZONE` components.
+
+
+3.3.3 (2014-10-09)
+------------------
+
+* #142: `CANCEL` and `REPLY` messages now include the `DTSTART` from the
+ original event.
+* #143: `SCHEDULE-AGENT` on the `ORGANIZER` property is respected.
+* #144: `PARTSTAT=NEEDS-ACTION` is now set for new invites, if no `PARTSTAT` is
+ set to support the inbox feature of iOS.
+* #147: Bugs related to scheduling all-day events.
+* #148: Ignore events that have attendees but no organizer.
+* #149: Avoiding logging errors during timezone detection. This is a workaround
+ for a PHP bug.
+* Support for "Line Islands Standard Time" windows timezone.
+* #154: Correctly work around vCard parameters that have a value but no name.
+
+
+3.3.2 (2014-09-19)
+------------------
+
+* Changed: iTip broker now sets RSVP status to false when replies are received.
+* #118: iTip Message now has a `getScheduleStatus()` method.
+* #119: Support for detecting 'significant changes'.
+* #120: Support for `SCHEDULE-FORCE-SEND`.
+* #121: iCal demands parameters containing the + sign to be quoted.
+* #122: Don't generate REPLY messages for events that have been cancelled.
+* #123: Added `SUMMARY` to iTip messages.
+* #130: Incorrect validation rules for `RELATED` (should be `RELATED-TO`).
+* #128: `ATTACH` in iCalendar is `URI` by default, not `BINARY`.
+* #131: RRULE that doesn't provide a single valid instance now throws an
+ exception.
+* #136: Validator rejects *all* control characters. We were missing a few.
+* #133: Splitter objects will throw exceptions when receiving incompatible
+ objects.
+* #127: Attendees who delete recurring event instances events they had already
+ declined earlier will no longer generate another reply.
+* #125: Send CANCEL messages when ORGANIZER property gets deleted.
+
+
+3.3.1 (2014-08-18)
+------------------
+
+* Changed: It's now possible to pass DateTime objects when using the magic
+ setters on properties. (`$event->DTSTART = new DateTime('now')`).
+* #111: iTip Broker does not process attendee adding events to EXDATE.
+* #112: EventIterator now sets TZID on RECURRENCE-ID.
+* #113: Timezone support during creation of iTip REPLY messages.
+* #114: VTIMEZONE is retained when generating new REQUEST objects.
+* #114: Support for 'MAILTO:' style email addresses (in uppercase) in the iTip
+ broker. This improves evolution support.
+* #115: Using REQUEST-STATUS from REPLY messages and now propegating that into
+ SCHEDULE-STATUS.
+
+
+3.3.0 (2014-08-07)
+------------------
+
+* We now use PSR-4 for the directory structure. This means that everything
+ that was used to be in the `lib/Sabre/VObject` directory is now moved to
+ `lib/`. If you use composer to load this library, you shouldn't have to do
+ anything about that though.
+* VEVENT now get populated with a DTSTAMP and UID property by default.
+* BC Break: Removed the 'includes.php' file. Use composer instead.
+* #103: Added support for processing [iTip][iTip] messages. This allows a user
+ to parse incoming iTip messages and apply the result on existing calendars,
+ or automatically generate invites/replies/cancellations based on changes that
+ a user made on objects.
+* #75, #58, #18: Fixes related to overriding the first event in recurrences.
+* Added: VCalendar::getBaseComponent to find the 'master' component in a
+ calendar.
+* #51: Support for iterating RDATE properties.
+* Fixed: Issue #101: RecurrenceIterator::nextMonthly() shows events that are
+ excluded events with wrong time
+
+
+3.2.4 (2014-07-14)
+------------------
+
+* Added: Issue #98. The VCardConverter now takes `X-APPLE-OMIT-YEAR` into
+ consideration when converting between vCard 3 and 4.
+* Fixed: Issue #96. Some support for Yahoo's broken vcards.
+* Fixed: PHP 5.3 support was broken in the cli tool.
+
+
+3.2.3 (2014-06-12)
+------------------
+
+* Validator now checks if DUE and DTSTART are of the same type in VTODO, and
+ ensures that DUE is always after DTSTART.
+* Removed documentation from source repository, to http://sabre.io/vobject/
+* Expanded the vobject cli tool validation output to make it easier to find
+ issues.
+* Fixed: vobject repair. It was not working for iCalendar objects.
+
+
+3.2.2 (2014-05-07)
+------------------
+
+* Minor tweak in unittests to make it run on PHP 5.5.12. Json-prettifying
+ slightly changed which caused the test to fail.
+
+
+3.2.1 (2014-05-03)
+------------------
+
+* Minor tweak to make the unittests run with the latest hhvm on travis.
+* Updated timezone definitions.
+* Updated copyright links to point to http://sabre.io/
+
+
+3.2.0 (2014-04-02)
+------------------
+
+* Now hhvm compatible!
+* The validator can now detect a _lot_ more problems. Many rules for both
+ iCalendar and vCard were added.
+* Added: bin/generate_vcards, a utility to generate random vcards for testing
+ purposes. Patches are welcome to add more data.
+* Updated: Windows timezone mapping to latest version from unicode.org
+* Changed: The timezone maps are now loaded in from external files, in
+ lib/Sabre/VObject/timezonedata.
+* Added: Fixing badly encoded URL's from google contacts vcards.
+* Fixed: Issue #68. Couldn't decode properties ending in a colon.
+* Fixed: Issue #72. RecurrenceIterator should respect timezone in the UNTIL
+ clause.
+* Fixed: Issue #67. BYMONTH limit on DAILY recurrences.
+* Fixed: Issue #26. Return a more descriptive error when coming across broken
+ BYDAY rules.
+* Fixed: Issue #28. Incorrect timezone detection for some timezones.
+* Fixed: Issue #70. Casting a parameter with a null value to string would fail.
+* Added: Support for rfc6715 and rfc6474.
+* Added: Support for DateTime objects in the VCard DATE-AND-OR-TIME property.
+* Added: UUIDUtil, for easily creating unique identifiers.
+* Fixed: Issue #83. Creating new VALUE=DATE objects using php's DateTime.
+* Fixed: Issue #86. Don't go into an infinite loop when php errors are
+ disabled and an invalid file is read.
+
+
+3.1.4 (2014-03-30)
+------------------
+
+* Fixed: Issue #87: Several compatibility fixes related to timezone handling
+ changes in PHP 5.5.10.
+
+
+3.1.3 (2013-10-02)
+------------------
+
+* Fixed: Support from properties from draft-daboo-valarm-extensions-04. Issue
+ #56.
+* Fixed: Issue #54. Parsing a stream of multiple vcards separated by more than
+ one newline. Thanks @Vedmak for the patch.
+* Fixed: Serializing vcard 2.1 parameters with no name caused a literal '1' to
+ be inserted.
+* Added: VCardConverter removed properties that are no longer supported in vCard
+ 4.0.
+* Added: vCards with a minimum number of values (such as N), but don't have that
+ many, are now automatically padded with empty components.
+* Added: The vCard validator now also checks for a minimum number of components,
+ and has the ability to repair these.
+* Added: Some support for vCard 2.1 in the VCard converter, to upgrade to vCard
+ 3.0 or 4.0.
+* Fixed: Issue 60 Use Document::$componentMap when instantiating the top-level
+ VCalendar and VCard components.
+* Fixed: Issue 62: Parsing iCalendar parameters with no value.
+* Added: --forgiving option to vobject utility.
+* Fixed: Compound properties such as ADR were not correctly split up in vCard
+ 2.1 quoted printable-encoded properties.
+* Fixed: Issue 64: Encoding of binary properties of converted vCards. Thanks
+ @DominikTo for the patch.
+
+
+3.1.2 (2013-08-13)
+------------------
+
+* Fixed: Setting correct property group on VCard conversion
+
+
+3.1.1 (2013-08-02)
+------------------
+
+* Fixed: Issue #53. A regression in RecurrenceIterator.
+
+
+3.1.0 (2013-07-27)
+------------------
+
+* Added: bad-ass new cli debugging utility (in bin/vobject).
+* Added: jCal and jCard parser.
+* Fixed: URI properties should not escape ; and ,.
+* Fixed: VCard 4 documents now correctly use URI as a default value-type for
+ PHOTO and others. BINARY no longer exists in vCard 4.
+* Added: Utility to convert between 2.1, 3.0 and 4.0 vCards.
+* Added: You can now add() multiple parameters to a property in one call.
+* Added: Parameter::has() for easily checking if a parameter value exists.
+* Added: VCard::preferred() to find a preferred email, phone number, etc for a
+ contact.
+* Changed: All $duration properties are now public.
+* Added: A few validators for iCalendar documents.
+* Fixed: Issue #50. RecurrenceIterator gives incorrect result when exception
+ events are out of order in the iCalendar file.
+* Fixed: Issue #48. Overridden events in the recurrence iterator that were past
+ the UNTIL date were ignored.
+* Added: getDuration for DURATION values such as TRIGGER. Thanks to
+ @SimonSimCity.
+* Fixed: Issue #52. vCard 2.1 parameters with no name may lose values if there's
+ more than 1. Thanks to @Vedmak.
+
+
+3.0.0 (2013-06-21)
+------------------
+
+* Fixed: includes.php file was still broken. Our tool to generate it had some
+ bugs.
+
+
+3.0.0-beta4 (2013-06-21)
+------------------------
+
+* Fixed: includes.php was no longer up to date.
+
+
+3.0.0-beta3 (2013-06-17)
+------------------------
+
+* Added: OPTION_FORGIVING now also allows slashes in property names.
+* Fixed: DateTimeParser no longer fails on dates with years < 1000 & > 4999
+* Fixed: Issue 36: Workaround for the recurrenceiterator and caldav events with
+ a missing base event.
+* Fixed: jCard encoding of TIME properties.
+* Fixed: jCal encoding of REQUEST-STATUS, GEO and PERIOD values.
+
+
+3.0.0-beta2 (2013-06-10)
+------------------------
+
+* Fixed: Corrected includes.php file.
+* Fixed: vCard date-time parser supported extended-format dates as well.
+* Changed: Properties have been moved to an ICalendar or VCard directory.
+* Fixed: Couldn't parse vCard 3 extended format dates and times.
+* Fixed: Couldn't export jCard DATE values correctly.
+* Fixed: Recursive loop in ICalendar\DateTime property.
+
+
+3.0.0-beta1 (2013-06-07)
+------------------------
+
+* Added: jsonSerialize() for creating jCal and jCard documents.
+* Added: helper method to parse vCard dates and times.
+* Added: Specialized classes for FLOAT, LANGUAGE-TAG, TIME, TIMESTAMP,
+ DATE-AND-OR-TIME, CAL-ADDRESS, UNKNOWN and UTC-OFFSET properties.
+* Removed: CommaSeparatedText property. Now included into Text.
+* Fixed: Multiple parameters with the same name are now correctly encoded.
+* Fixed: Parameter values containing a comma are now enclosed in double-quotes.
+* Fixed: Iterating parameter values should now fully work as expected.
+* Fixed: Support for vCard 2.1 nameless parameters.
+* Changed: $valueMap, $componentMap and $propertyMap now all use fully-qualified
+ class names, so they are actually overridable.
+* Fixed: Updating DATE-TIME to DATE values now behaves like expected.
+
+
+3.0.0-alpha4 (2013-05-31)
+-------------------------
+
+* Added: It's now possible to send parser options to the splitter classes.
+* Added: A few tweaks to improve component and property creation.
+
+
+3.0.0-alpha3 (2013-05-13)
+-------------------------
+
+* Changed: propertyMap, valueMap and componentMap are now static properties.
+* Changed: Component::remove() will throw an exception when trying to a node
+ that's not a child of said component.
+* Added: Splitter objects are now faster, line numbers are accurately reported
+ and use less memory.
+* Added: MimeDir parser can now continue parsing with the same stream buffer.
+* Fixed: vobjectvalidate.php is operational again.
+* Fixed: \r is properly stripped in text values.
+* Fixed: QUOTED-PRINTABLE is now correctly encoded as well as encoded, for
+ vCards 2.1.
+* Fixed: Parser assumes vCard 2.1, if no version was supplied.
+
+
+3.0.0-alpha2 (2013-05-22)
+-------------------------
+
+* Fixed: vCard URL properties were referencing a non-existant class.
+
+
+3.0.0-alpha1 (2013-05-21)
+-------------------------
+
+* Fixed: Now correctly dealing with escaping of properties. This solves the
+ problem with double-backslashes where they don't belong.
+* Added: Easy support for properties with more than one value, using setParts
+ and getParts.
+* Added: Support for broken 2.1 vCards produced by microsoft.
+* Added: Automatically decoding quoted-printable values.
+* Added: Automatically decoding base64 values.
+* Added: Decoding RFC6868 parameter values (uses ^ as an escape character).
+* Added: Fancy new MimeDir parser that can also parse streams.
+* Added: Automatically mapping many, many properties to a property-class with
+ specialized API's.
+* Added: remove() method for easily removing properties and sub-components
+ components.
+* Changed: Components, Properties and Parameters can no longer be created with
+ Component::create, Property::create and Parameter::create. They must instead
+ be created through the root component. (A VCalendar or VCard object).
+* Changed: API for DateTime properties has slightly changed.
+* Changed: the ->value property is now protected everywhere. Use getParts() and
+ getValue() instead.
+* BC Break: No support for mac newlines (\r). Never came across these anyway.
+* Added: add() method to the Property class.
+* Added: It's now possible to easy set multi-value properties as arrays.
+* Added: When setting date-time properties you can just pass PHP's DateTime
+ object.
+* Added: New components automatically get a bunch of default properties, such as
+ VERSION and CALSCALE.
+* Added: You can add new sub-components much quicker with the magic setters, and
+ add() method.
+
+
+2.1.7 (2015-01-21)
+------------------
+
+* Fixed: Issue #94, a workaround for bad escaping of ; and , in compound
+ properties. It's not a full solution, but it's an improvement for those
+ stuck in the 2.1 versions.
+
+
+2.1.6 (2014-12-10)
+------------------
+
+* Fixed: Minor change to make sure that unittests succeed on every PHP version.
+
+
+2.1.5 (2014-06-03)
+------------------
+
+* Fixed: #94: Better parameter escaping.
+* Changed: Documentation cleanups.
+
+
+2.1.4 (2014-03-30)
+------------------
+
+* Fixed: Issue #87: Several compatibility fixes related to timezone handling
+ changes in PHP 5.5.10.
+
+
+2.1.3 (2013-10-02)
+------------------
+
+* Fixed: Issue #55. \r must be stripped from property values.
+* Fixed: Issue #65. Putting quotes around parameter values that contain a colon.
+
+
+2.1.2 (2013-08-02)
+------------------
+
+* Fixed: Issue #53. A regression in RecurrenceIterator.
+
+
+2.1.1 (2013-07-27)
+------------------
+
+* Fixed: Issue #50. RecurrenceIterator gives incorrect result when exception
+ events are out of order in the iCalendar file.
+* Fixed: Issue #48. Overridden events in the recurrence iterator that were past
+ the UNTIL date were ignored.
+
+
+2.1.0 (2013-06-17)
+------------------
+
+* This version is fully backwards compatible with 2.0.\*. However, it contains a
+ few new API's that mimic the VObject 3 API. This allows it to be used a
+ 'bridge' version. Specifically, this new version exists so SabreDAV 1.7 and
+ 1.8 can run with both the 2 and 3 versions of this library.
+* Added: Property\DateTime::hasTime().
+* Added: Property\MultiDateTime::hasTime().
+* Added: Property::getValue().
+* Added: Document class.
+* Added: Document::createComponent and Document::createProperty.
+* Added: Parameter::getValue().
+
+
+2.0.7 (2013-03-05)
+------------------
+
+* Fixed: Microsoft re-uses their magic numbers for different timezones,
+ specifically id 2 for both Sarajevo and Lisbon). A workaround was added to
+ deal with this.
+
+
+2.0.6 (2013-02-17)
+------------------
+
+* Fixed: The reader now properly parses parameters without a value.
+
+
+2.0.5 (2012-11-05)
+------------------
+
+* Fixed: The FreeBusyGenerator is now properly using the factory methods for
+ creation of components and properties.
+
+
+2.0.4 (2012-11-02)
+------------------
+
+* Added: Known Lotus Notes / Domino timezone id's.
+
+
+2.0.3 (2012-10-29)
+------------------
+
+* Added: Support for 'GMT+????' format in TZID's.
+* Added: Support for formats like SystemV/EST5EDT in TZID's.
+* Fixed: RecurrenceIterator now repairs recurrence rules where UNTIL < DTSTART.
+* Added: Support for BYHOUR in FREQ=DAILY (@hollodk).
+* Added: Support for BYHOUR and BYDAY in FREQ=WEEKLY.
+
+
+2.0.2 (2012-10-06)
+------------------
+
+* Added: includes.php file, to load the entire library in one go.
+* Fixed: A problem with determining alarm triggers for TODO's.
+
+
+2.0.1 (2012-09-22)
+------------------
+
+* Removed: Element class. It wasn't used.
+* Added: Basic validation and repair methods for broken input data.
+* Fixed: RecurrenceIterator could infinitely loop when an INTERVAL of 0 was
+ specified.
+* Added: A cli script that can validate and automatically repair vcards and
+ iCalendar objects.
+* Added: A new 'Compound' property, that can automatically split up parts for
+ properties such as N, ADR, ORG and CATEGORIES.
+* Added: Splitter classes, that can split up large objects (such as exports)
+ into individual objects (thanks @DominikTo and @armin-hackmann).
+* Added: VFREEBUSY component, which allows easily checking wether timeslots are
+ available.
+* Added: The Reader class now has a 'FORGIVING' option, which allows it to parse
+ properties with incorrect characters in the name (at this time, it just allows
+ underscores).
+* Added: Also added the 'IGNORE_INVALID_LINES' option, to completely disregard
+ any invalid lines.
+* Fixed: A bug in Windows timezone-id mappings for times created in Greenlands
+ timezone (sorry Greenlanders! I do care!).
+* Fixed: DTEND was not generated correctly for VFREEBUSY reports.
+* Fixed: Parser is at least 25% faster with real-world data.
+
+
+2.0.0 (2012-08-08)
+------------------
+
+* VObject is now a separate project from SabreDAV. See the SabreDAV changelog
+ for version information before 2.0.
+* New: VObject library now uses PHP 5.3 namespaces.
+* New: It's possible to specify lists of parameters when constructing
+ properties.
+* New: made it easier to construct the FreeBusyGenerator.
+
+[iTip]: http://tools.ietf.org/html/rfc5546
diff --git a/vendor/sabre/vobject/ChangeLog b/vendor/sabre/vobject/ChangeLog
deleted file mode 100644
index 96e1fb669..000000000
--- a/vendor/sabre/vobject/ChangeLog
+++ /dev/null
@@ -1,88 +0,0 @@
-2.1.4-stable (2014-03-30)
- * Fixed: Issue #87: Several compatibility fixes related to timezone
- handling changes in PHP 5.5.10.
-
-2.1.3-stable (2013-10-02)
- * Fixed: Issue #55. \r must be stripped from property values.
- * Fixed: Issue #65. Putting quotes around parameter values that contain a
- colon.
-
-2.1.2-stable (2013-08-02)
- * Fixed: Issue #53. A regression in RecurrenceIterator.
-
-2.1.1-stable (2013-07-27)
- * Fixed: Issue #50. RecurrenceIterator gives incorrect result when
- exception events are out of order in the iCalendar file.
- * Fixed: Issue #48. Overridden events in the recurrence iterator that were
- past the UNTIL date were ignored.
-
-2.1.0-stable (2013-06-17)
- * This version is fully backwards compatible with 2.0.*. However, it
- contains a few new API's that mimic the VObject 3 API. This allows it to
- be used a 'bridge' version.
- Specifically, this new version exists so SabreDAV 1.7 and 1.8 can run with
- both the 2 and 3 versions of this library.
- * Added: Property\DateTime::hasTime().
- * Added: Property\MultiDateTime::hasTime().
- * Added: Property::getValue().
- * Added: Document class.
- * Added: Document::createComponent and Document::createProperty.
- * Added: Parameter::getValue().
-
-
-2.0.7-stable (2013-03-05)
- * Fixed: Microsoft re-uses their magic numbers for different timezones,
- specifically id 2 for both Sarajevo and Lisbon). A workaround was added
- to deal with this.
-
-2.0.6-stable (2013-02-17)
- * Fixed: The reader now properly parses parameters without a value.
-
-2.0.5-stable (2012-11-05)
- * Fixed: The FreeBusyGenerator is now properly using the factory methods
- for creation of components and properties.
-
-2.0.4-stable (2012-11-02)
- * Added: Known Lotus Notes / Domino timezone id's.
-
-2.0.3-stable (2012-10-29)
- * Added: Support for 'GMT+????' format in TZID's.
- * Added: Support for formats like SystemV/EST5EDT in TZID's.
- * Fixed: RecurrenceIterator now repairs recurrence rules where UNTIL < DTSTART.
- * Added: Support for BYHOUR in FREQ=DAILY (@hollodk).
- * Added: Support for BYHOUR and BYDAY in FREQ=WEEKLY.
-
-2.0.2-stable (2012-10-06)
- * Added: includes.php file, to load the entire library in one go.
- * Fixed: A problem with determining alarm triggers for TODO's.
-
-2.0.1-stable (2012-09-22)
- * Removed: Element class. It wasn't used.
- * Added: Basic validation and repair methods for broken input data.
- * Fixed: RecurrenceIterator could infinitely loop when an INTERVAL of 0
- was specified.
- * Added: A cli script that can validate and automatically repair vcards
- and iCalendar objects.
- * Added: A new 'Compound' property, that can automatically split up parts
- for properties such as N, ADR, ORG and CATEGORIES.
- * Added: Splitter classes, that can split up large objects (such as exports)
- into individual objects (thanks @DominikTO and @armin-hackmann).
- * Added: VFREEBUSY component, which allows easily checking wether
- timeslots are available.
- * Added: The Reader class now has a 'FORGIVING' option, which allows it to
- parse properties with incorrect characters in the name (at this time, it
- just allows underscores).
- * Added: Also added the 'IGNORE_INVALID_LINES' option, to completely
- disregard any invalid lines.
- * Fixed: A bug in Windows timezone-id mappings for times created in
- Greenlands timezone (sorry Greenlanders! I do care!).
- * Fixed: DTEND was not generated correctly for VFREEBUSY reports.
- * Fixed: Parser is at least 25% faster with real-world data.
-
-2.0.0-stable (2012-08-08)
- * VObject is now a separate project from SabreDAV. See the SabreDAV
- changelog for version information before 2.0.
- * New: VObject library now uses PHP 5.3 namespaces.
- * New: It's possible to specify lists of parameters when constructing
- properties.
- * New: made it easier to construct the FreeBusyGenerator.
diff --git a/vendor/sabre/vobject/LICENSE b/vendor/sabre/vobject/LICENSE
index 628c60c78..a99c8da19 100644
--- a/vendor/sabre/vobject/LICENSE
+++ b/vendor/sabre/vobject/LICENSE
@@ -1,4 +1,4 @@
-Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/)
+Copyright (C) 2011-2016 fruux GmbH (https://fruux.com/)
All rights reserved.
diff --git a/vendor/sabre/vobject/README.md b/vendor/sabre/vobject/README.md
index c7541eaca..0e37f1388 100644
--- a/vendor/sabre/vobject/README.md
+++ b/vendor/sabre/vobject/README.md
@@ -1,377 +1,46 @@
-SabreTooth VObject library
-==========================
-
-[![Build Status](https://secure.travis-ci.org/fruux/sabre-vobject.png?branch=master)](http://travis-ci.org/fruux/sabre-vobject)
+sabre/vobject
+=============
The VObject library allows you to easily parse and manipulate [iCalendar](https://tools.ietf.org/html/rfc5545)
and [vCard](https://tools.ietf.org/html/rfc6350) objects using PHP.
+
The goal of the VObject library is to create a very complete library, with an easy to use API.
-This project is a spin-off from [SabreDAV](http://code.google.com/p/sabredav/), where it has
-been used for several years. The VObject library has 100% unittest coverage.
Installation
------------
-VObject requires PHP 5.3, and should be installed using composer.
-The general composer instructions can be found on the [composer website](http://getcomposer.org/doc/00-intro.md composer website).
-
-After that, just declare the vobject dependency as follows:
-
-```
-"require" : {
- "sabre/vobject" : "2.0.*"
-}
-```
-
-Then, run `composer.phar update` and you should be good.
-
-Usage
------
-
-### Parsing
-
-For our example, we will be using the following vcard:
-
-```
-BEGIN:VCARD
-VERSION:3.0
-PRODID:-//Sabre//Sabre VObject 2.0//EN
-N:Planck;Max;;;
-FN:Max Planck
-EMAIL;TYPE=WORK:mplanck@example.org
-item1.TEL;TYPE=CELL:(+49)3144435678
-item1.X-ABLabel:Private cell
-item2.TEL;TYPE=WORK:(+49)5554564744
-item2.X-ABLabel:Work
-END:VCARD
-```
-
-
-If we want to just print out Max' full name, you can just use property access:
-
-
-```php
-use Sabre\VObject;
-
-$card = VObject\Reader::read($data);
-echo $card->FN;
-```
-
-### Changing properties
-
-Creating properties is pretty similar. If we like to add his birthday, we just
-set the property:
-
-```php
-$card->BDAY = '1858-04-23';
-```
-
-Note that in the previous example, we're actually updating any existing BDAY that
-may already exist. If we want to add a new property, without overwriting the previous
-we can do this with the `add` method.
-
-```php
-$card->add('EMAIL','max@example.org');
-```
-
-### Parameters
-
-If we want to also specify that this is max' home email addresses, we can do this with
-a third parameter:
-
-```
-$card->add('EMAIL', 'max@example'org', array('type' => 'HOME'));
-```
-
-If we want to read out all the email addresses and their type, this would look something
-like this:
-
-```
-foreach($card->EMAIL as $email) {
-
- echo $email['TYPE'], ' - ', $email;
-
-}
-```
-
-### Groups
-
-In our example, you can see that the TEL properties are prefixed. These are 'groups' and
-allow you to group multiple related properties together. The group can be any user-defined
-name.
-
-This particular example as generated by the OS X addressbook, which uses the `X-ABLabel`
-to allow the user to specify custom labels for properties. OS X addressbook uses groups
-to tie the label to the property.
-
-The VObject library simply ignores the group if you don't specify it, so this will work:
-
-```php
-foreach($card->TEL as $tel) {
- echo $tel, "\n";
-}
-```
-
-But if you would like to target a specific group + property, this is possible too:
-
-```php
-echo $card->{'ITEM1.TEL'};
-```
-
-So if you would like to show all the phone numbers, along with their custom label, the
-following syntax is used:
-
-```php
-foreach($card->TEL as $tel) {
-
- echo $card->{$tel->group . '.X-ABLABEL'}, ": ";
- echo $tel, "\n";
-
-}
-```
-
-### Serializing / Saving
-
-If you want to generate your updated VObject, you can simply call the serialize() method.
-
-```php
-echo $card->serialize();
-```
+Make sure you have [Composer][1] installed, and then run:
-### Components
+ composer require sabre/vobject "^4.0"
-iCalendar, unlike vCards always have sub-components. Where vCards are often just a flat
-list, iCalendar objects tend to have a tree-like structure. For the following paragraphs,
-we will use the following object as the example:
+This package requires PHP 5.5. If you need the PHP 5.3/5.4 version of this package instead, use:
-```
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Sabre//Sabre VObject 2.0//EN
-BEGIN:VEVENT
-SUMMARY:Curiosity landing
-DTSTART:20120806T051439Z
-LOCATION:Mars
-END:VEVENT
-END:VCALENDAR
-```
-Since events, tasks and journals are always in a sub component, this is also how we
-access them.
+ composer require sabre/vobject "^3.4"
-```php
-use Sabre\VObject;
-$calendar = VObject\Reader::read($data);
-echo $calendar->VEVENT->SUMMARY;
-```
-
-Adding components to a calendar is done with a factory method:
-
-```php
-$event = VObject\Component::create('VEVENT');
-$calendar->add($event);
-
-$event->SUMMARY = 'Curiosity launch';
-$event->DTSTART = '20111126T150202Z';
-$event->LOCATION = 'Cape Carnival';
-```
-
-By the way.. cloning also works as expected, as the entire structure is cloned along with it:
-
-```php
-$clonedEvent = clone $calendar->VEVENT[0];
-$calendar->add($clonedEvent);
-```
-
-### Date and time handling
-
-If you ever had to deal with iCalendar timezones, you know it can be complicated.
-The way timezones are specified is flawed, which is something I may write an essay about some
-day. VObject does its best to determine the correct timezone. Many standard formats
-have been tested and verified, and special code has been implemented for special-casing
-microsoft generated timezone information, and others.
-
-To get a real php `DateTime` object, you can request this as follows:
-
-```php
-$event = $calendar->VEVENT;
-$start = $event->DTSTART->getDateTime();
-echo $start->format(\DateTime::W3C);
-```
-
-To set the property with a DateTime object, you can use the following syntax:
-
-```php
-$dateTime = new \DateTime('2012-08-07 23:53:00', new DateTimeZone('Europe/Amsterdam'));
-$event->DTSTART->setDateTime($dateTime, VObject\Property\DateTime::DATE);
-```
-
-The second argument specifies the type of date you're setting. The following three
-options exist:
-
-1. `LOCAL` This is a floating time, with no timezone information. This basically specifies that the event happens in whatever the timezone's currently in. This would be encoded as `DTSTART;VALUE=DATE-TIME:20120807235300`
-2. `UTC` This specifies that the time should be encoded as a UTC time. This is encoded as `DTSTART;VALUE=DATE-TIME:20120807205300Z`. Note the extra Z and the fact that it's two hours 'earlier'.
-3. `LOCALTZ` specifies that it's supposed to be encoded in its local timezone. For example `DTSTART;VALUE=DATE-TIME;TZID=Europe/Amsterdam:20120807235300`.
-4. `DATE` This is a date-only, and does not contain the time. In this case this would be encoded as `DTSTART;VALUE=DATE:20120807`.
-
-A few important notes:
-
-* When a `TZID` is specified, there should also be a matching `VTIMEZONE` object with all the timezone information. VObject cannot currently automatically embed this. However, in reality other clients seem to do fine without this information. Yet, for completeness, this will be added in the future.
-* As mentioned, the timezone-determination process may sometimes fail. Report any issues you find, and I'll be quick to add workarounds!
-
-### Recurrence rules
-
-Recurrence rules allow events to recur, for example for a weekly meeting, or an anniversary.
-This is done with the `RRULE` property. The `RRULE` property allows for a LOT of different
-rules. VObject only implements the ones that actually appear in calendar software.
-
-To read more about `RRULE` and all the options, check out [RFC5545](https://tools.ietf.org/html/rfc5545#section-3.8.5).
-VObject supports the following options:
-
-1. `UNTIL` for an end date.
-2. `INTERVAL` for for example "every 2 days".
-3. `COUNT` to stop recurring after x items.
-4. `FREQ=DAILY` to recur every day, and `BYDAY` to limit it to certain days.
-5. `FREQ=WEEKLY` to recur every week, `BYDAY` to expand this to multiple weekdays in every week and `WKST` to specify on which day the week starts.
-6. `FREQ=MONTHLY` to recur every month, `BYMONTHDAY` to expand this to certain days in a month, `BYDAY` to expand it to certain weekdays occuring in a month, and `BYSETPOS` to limit the last two expansions.
-7. `FREQ=YEARLY` to recur every year, `BYMONTH` to expand that to certain months in a year, and `BYDAY` and `BYWEEKDAY` to expand the `BYMONTH` rule even further.
-
-VObject supports the `EXDATE` property for exclusions, but not yet the `RDATE` and `EXRULE`
-properties. If you're interested in this, please file a github issue, as this will put it
-on my radar.
-
-This is a bit of a complex subject to go in excruciating detail. The
-[RFC](https://tools.ietf.org/html/rfc5545#section-3.8.5) has a lot of examples though.
-
-The hard part is not to write the RRULE, it is to expand them. The most complex and
-hard-to-read code is hidden in this component. Dragons be here.
-
-So, if we have a meeting every 2nd monday of the month, this would be specified as such:
-
-```
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Sabre//Sabre VObject 2.0//EN
-BEGIN:VEVENT
-UID:1102c450-e0d7-11e1-9b23-0800200c9a66
-DTSTART:20120109T140000Z
-RRULE:FREQ=MONTHLY;BYDAY=MO;BYSETPOS=2
-END:VEVENT
-END:VCALENDAR
-```
-
-Note that normally it's not allowed to indent the object like this, but it does make
-it easier to read. This is also the first time I added in a UID, which is required
-for all VEVENT, VTODO and VJOURNAL objects!
-
-To figure out all the meetings for this year, we can use the following syntax:
-
-```php
-use Sabre\VObject;
-
-$calendar = VObject\Reader::read($data);
-$calendar->expand(new DateTime('2012-01-01'), new DateTime('2012-12-31'));
-```
-
-What the expand method does, is look at its inner events, and expand the recurring
-rule. Our calendar now contains 12 events. The first will have its RRULE stripped,
-and every subsequent VEVENT has the correct meeting date and a `RECURRENCE-ID` set.
-
-This results in something like this:
-
-```
-BEGIN:VCALENDAR
- VERSION:2.0
- PRODID:-//Sabre//Sabre VObject 2.0//EN
- BEGIN:VEVENT
- UID:1102c450-e0d7-11e1-9b23-0800200c9a66
- DTSTART:20120109T140000Z
- END:VEVENT
- BEGIN:VEVENT
- UID:1102c450-e0d7-11e1-9b23-0800200c9a66
- RECURRENCE-ID:20120213T140000Z
- DTSTART:20120213T140000Z
- END:VEVENT
- BEGIN:VEVENT
- UID:1102c450-e0d7-11e1-9b23-0800200c9a66
- RECURRENCE-ID:20120312T140000Z
- DTSTART:20120312T140000Z
- END:VEVENT
- ..etc..
-END:VCALENDAR
-```
-
-To show the list of dates, we would do this as such:
-
-```php
-foreach($calendar->VEVENT as $event) {
- echo $event->DTSTART->getDateTime()->format(\DateTime::ATOM);
-}
-```
-
-In a recurring event, single instances can also be overriden. VObject also takes these
-into consideration. The reason we needed to specify a start and end-date, is because
-some recurrence rules can be 'never ending'.
-
-You should make sure you pick a sane date-range. Because if you pick a 50 year
-time-range, for a daily recurring event; this would result in over 18K objects.
-
-Free-busy report generation
----------------------------
-
-Some calendaring software can make use of FREEBUSY reports to show when people are
-available.
+Usage
+-----
-You can automatically generate these reports from calendars using the `FreeBusyGenerator`.
+* [Working with vCards](http://sabre.io/vobject/vcard/)
+* [Working with iCalendar](http://sabre.io/vobject/icalendar/)
-Example based on our last event:
-```php
-// We're giving it the calendar object. It's also possible to specify multiple objects,
-// by setting them as an array.
-//
-// We must also specify a start and end date, because recurring events are expanded.
-$fbGenerator = new VObject\FreeBusyGenerator(
- new DateTime('2012-01-01'),
- new DateTime('2012-12-31'),
- $calendar
-);
-// Grabbing the report
-$freebusy = $fbGenerator->result();
+Build status
+------------
-// The freebusy report is another VCALENDAR object, so we can serialize it as usual:
-echo $freebusy->serialize();
-```
+| branch | status |
+| ------ | ------ |
+| master | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.svg?branch=master)](https://travis-ci.org/fruux/sabre-vobject) |
+| 3.5 | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.svg?branch=3.5)](https://travis-ci.org/fruux/sabre-vobject) |
+| 3.4 | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.svg?branch=3.4)](https://travis-ci.org/fruux/sabre-vobject) |
+| 3.1 | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.svg?branch=3.1)](https://travis-ci.org/fruux/sabre-vobject) |
+| 2.1 | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.svg?branch=2.1)](https://travis-ci.org/fruux/sabre-vobject) |
+| 2.0 | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.svg?branch=2.0)](https://travis-ci.org/fruux/sabre-vobject) |
-The output of this script will look like this:
-```
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Sabre//Sabre VObject 2.0//EN
-CALSCALE:GREGORIAN
-BEGIN:VFREEBUSY
-DTSTART;VALUE=DATE-TIME:20111231T230000Z
-DTEND;VALUE=DATE-TIME:20111231T230000Z
-DTSTAMP;VALUE=DATE-TIME:20120808T131628Z
-FREEBUSY;FBTYPE=BUSY:20120109T140000Z/20120109T140000Z
-FREEBUSY;FBTYPE=BUSY:20120213T140000Z/20120213T140000Z
-FREEBUSY;FBTYPE=BUSY:20120312T140000Z/20120312T140000Z
-FREEBUSY;FBTYPE=BUSY:20120409T140000Z/20120409T140000Z
-FREEBUSY;FBTYPE=BUSY:20120514T140000Z/20120514T140000Z
-FREEBUSY;FBTYPE=BUSY:20120611T140000Z/20120611T140000Z
-FREEBUSY;FBTYPE=BUSY:20120709T140000Z/20120709T140000Z
-FREEBUSY;FBTYPE=BUSY:20120813T140000Z/20120813T140000Z
-FREEBUSY;FBTYPE=BUSY:20120910T140000Z/20120910T140000Z
-FREEBUSY;FBTYPE=BUSY:20121008T140000Z/20121008T140000Z
-FREEBUSY;FBTYPE=BUSY:20121112T140000Z/20121112T140000Z
-FREEBUSY;FBTYPE=BUSY:20121210T140000Z/20121210T140000Z
-END:VFREEBUSY
-END:VCALENDAR
-```
Support
-------
@@ -382,3 +51,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]: https://getcomposer.org/
diff --git a/vendor/sabre/vobject/bin/bench.php b/vendor/sabre/vobject/bin/bench.php
index b949c8ea4..807b40777 100755
--- a/vendor/sabre/vobject/bin/bench.php
+++ b/vendor/sabre/vobject/bin/bench.php
@@ -5,8 +5,8 @@ include __DIR__ . '/../vendor/autoload.php';
$data = stream_get_contents(STDIN);
-$start = microtime(true);
+$start = microtime(true);
$lol = Sabre\VObject\Reader::read($data);
-echo "time: " . (microtime(true)-$start) . "\n";
+echo "time: " . (microtime(true) - $start) . "\n";
diff --git a/vendor/sabre/vobject/bin/bench_freebusygenerator.php b/vendor/sabre/vobject/bin/bench_freebusygenerator.php
new file mode 100644
index 000000000..2c51b2a32
--- /dev/null
+++ b/vendor/sabre/vobject/bin/bench_freebusygenerator.php
@@ -0,0 +1,62 @@
+<?php
+
+include __DIR__ . '/../vendor/autoload.php';
+
+if ($argc < 2) {
+ echo "sabre/vobject ", Sabre\VObject\Version::VERSION, " freebusy benchmark\n";
+ echo "\n";
+ echo "This script can be used to measure the speed of generating a\n";
+ echo "free-busy report based on a calendar.\n";
+ echo "\n";
+ echo "The process will be repeated 100 times to get accurate stats\n";
+ echo "\n";
+ echo "Usage: " . $argv[0] . " inputfile.ics\n";
+ die();
+}
+
+list(, $inputFile) = $argv;
+
+$bench = new Hoa\Bench\Bench();
+$bench->parse->start();
+
+$vcal = Sabre\VObject\Reader::read(fopen($inputFile, 'r'));
+
+$bench->parse->stop();
+
+$repeat = 100;
+$start = new \DateTime('2000-01-01');
+$end = new \DateTime('2020-01-01');
+$timeZone = new \DateTimeZone('America/Toronto');
+
+$bench->fb->start();
+
+for ($i = 0; $i < $repeat; $i++) {
+
+ $fb = new Sabre\VObject\FreeBusyGenerator($start, $end, $vcal, $timeZone);
+ $results = $fb->getResult();
+
+}
+$bench->fb->stop();
+
+
+
+echo $bench,"\n";
+
+function formatMemory($input) {
+
+ if (strlen($input) > 6) {
+
+ return round($input / (1024 * 1024)) . 'M';
+
+ } elseif (strlen($input) > 3) {
+
+ return round($input / 1024) . 'K';
+
+ }
+
+}
+
+unset($input, $splitter);
+
+echo "peak memory usage: " . formatMemory(memory_get_peak_usage()), "\n";
+echo "current memory usage: " . formatMemory(memory_get_usage()), "\n";
diff --git a/vendor/sabre/vobject/bin/bench_manipulatevcard.php b/vendor/sabre/vobject/bin/bench_manipulatevcard.php
new file mode 100644
index 000000000..adc198e9b
--- /dev/null
+++ b/vendor/sabre/vobject/bin/bench_manipulatevcard.php
@@ -0,0 +1,69 @@
+<?php
+
+include __DIR__ . '/../vendor/autoload.php';
+
+if ($argc < 2) {
+ echo "sabre/vobject ", Sabre\VObject\Version::VERSION, " manipulation benchmark\n";
+ echo "\n";
+ echo "This script can be used to measure the speed of opening a large amount of\n";
+ echo "vcards, making a few alterations and serializing them again.\n";
+ echo "system.";
+ echo "\n";
+ echo "Usage: " . $argv[0] . " inputfile.vcf\n";
+ die();
+}
+
+list(, $inputFile) = $argv;
+
+$input = file_get_contents($inputFile);
+
+$splitter = new Sabre\VObject\Splitter\VCard($input);
+
+$bench = new Hoa\Bench\Bench();
+
+while (true) {
+
+ $bench->parse->start();
+ $vcard = $splitter->getNext();
+ $bench->parse->pause();
+
+ if (!$vcard) break;
+
+ $bench->manipulate->start();
+ $vcard->{'X-FOO'} = 'Random new value!';
+ $emails = [];
+ if (isset($vcard->EMAIL)) foreach ($vcard->EMAIL as $email) {
+ $emails[] = (string)$email;
+ }
+ $bench->manipulate->pause();
+
+ $bench->serialize->start();
+ $vcard2 = $vcard->serialize();
+ $bench->serialize->pause();
+
+ $vcard->destroy();
+
+}
+
+
+
+echo $bench,"\n";
+
+function formatMemory($input) {
+
+ if (strlen($input) > 6) {
+
+ return round($input / (1024 * 1024)) . 'M';
+
+ } elseif (strlen($input) > 3) {
+
+ return round($input / 1024) . 'K';
+
+ }
+
+}
+
+unset($input, $splitter);
+
+echo "peak memory usage: " . formatMemory(memory_get_peak_usage()), "\n";
+echo "current memory usage: " . formatMemory(memory_get_usage()), "\n";
diff --git a/vendor/sabre/vobject/bin/fetch_windows_zones.php b/vendor/sabre/vobject/bin/fetch_windows_zones.php
new file mode 100755
index 000000000..1b1fdc37c
--- /dev/null
+++ b/vendor/sabre/vobject/bin/fetch_windows_zones.php
@@ -0,0 +1,51 @@
+#!/usr/bin/env php
+<?php
+
+$windowsZonesUrl = 'http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml';
+$outputFile = __DIR__ . '/../lib/timezonedata/windowszones.php';
+
+echo "Fetching timezone map from: " . $windowsZonesUrl, "\n";
+
+$data = file_get_contents($windowsZonesUrl);
+
+$xml = simplexml_load_string($data);
+
+$map = [];
+
+foreach ($xml->xpath('//mapZone') as $mapZone) {
+
+ $from = (string)$mapZone['other'];
+ $to = (string)$mapZone['type'];
+
+ list($to) = explode(' ', $to, 2);
+
+ if (!isset($map[$from])) {
+ $map[$from] = $to;
+ }
+
+}
+
+ksort($map);
+echo "Writing to: $outputFile\n";
+
+$f = fopen($outputFile, 'w');
+fwrite($f, "<?php\n\n");
+fwrite($f, "/**\n");
+fwrite($f, " * Automatically generated timezone file\n");
+fwrite($f, " *\n");
+fwrite($f, " * Last update: " . date(DATE_W3C) . "\n");
+fwrite($f, " * Source: " . $windowsZonesUrl . "\n");
+fwrite($f, " *\n");
+fwrite($f, " * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).\n");
+fwrite($f, " * @license http://sabre.io/license/ Modified BSD License\n");
+fwrite($f, " */\n");
+fwrite($f, "\n");
+fwrite($f, "return ");
+fwrite($f, var_export($map, true) . ';');
+fclose($f);
+
+echo "Formatting\n";
+
+exec(__DIR__ . '/sabre-cs-fixer fix ' . escapeshellarg($outputFile));
+
+echo "Done\n";
diff --git a/vendor/sabre/vobject/bin/generate_vcards b/vendor/sabre/vobject/bin/generate_vcards
new file mode 100755
index 000000000..4663c3c16
--- /dev/null
+++ b/vendor/sabre/vobject/bin/generate_vcards
@@ -0,0 +1,241 @@
+#!/usr/bin/env php
+<?php
+
+namespace Sabre\VObject;
+
+// This sucks.. we have to try to find the composer autoloader. But chances
+// are, we can't find it this way. So we'll do our bestest
+$paths = [
+ __DIR__ . '/../vendor/autoload.php', // In case vobject is cloned directly
+ __DIR__ . '/../../../autoload.php', // In case vobject is a composer dependency.
+];
+
+foreach($paths as $path) {
+ if (file_exists($path)) {
+ include $path;
+ break;
+ }
+}
+
+if (!class_exists('Sabre\\VObject\\Version')) {
+ fwrite(STDERR, "Composer autoloader could not be properly loaded.\n");
+ die(1);
+}
+
+if ($argc < 2) {
+
+ $version = Version::VERSION;
+
+ $help = <<<HI
+sabre/vobject $version
+Usage:
+ generate_vcards [count]
+
+Options:
+ count The number of random vcards to generate
+
+Examples:
+ generate_vcards 1000 > testdata.vcf
+
+HI;
+
+ fwrite(STDERR, $help);
+ exit(2);
+}
+
+$count = (int)$argv[1];
+if ($count < 1) {
+ fwrite(STDERR, "Count must be at least 1\n");
+ exit(2);
+}
+
+fwrite(STDERR, "sabre/vobject " . Version::VERSION . "\n");
+fwrite(STDERR, "Generating " . $count . " vcards in vCard 4.0 format\n");
+
+/**
+ * The following list is just some random data we compiled from various
+ * sources online.
+ *
+ * Very little thought went into compiling this list, and certainly nothing
+ * political or ethical.
+ *
+ * We would _love_ more additions to this to add more variation to this list.
+ *
+ * Send us PR's and don't be shy adding your own first and last name for fun.
+ */
+
+$sets = array(
+ "nl" => array(
+ "country" => "Netherlands",
+ "boys" => array(
+ "Anno",
+ "Bram",
+ "Daan",
+ "Evert",
+ "Finn",
+ "Jayden",
+ "Jens",
+ "Jesse",
+ "Levi",
+ "Lucas",
+ "Luuk",
+ "Milan",
+ "René",
+ "Sem",
+ "Sibrand",
+ "Willem",
+ ),
+ "girls" => array(
+ "Celia",
+ "Emma",
+ "Fenna",
+ "Geke",
+ "Inge",
+ "Julia",
+ "Lisa",
+ "Lotte",
+ "Mila",
+ "Sara",
+ "Sophie",
+ "Tess",
+ "Zoë",
+ ),
+ "last" => array(
+ "Bakker",
+ "Bos",
+ "De Boer",
+ "De Groot",
+ "De Jong",
+ "De Vries",
+ "Jansen",
+ "Janssen",
+ "Meyer",
+ "Mulder",
+ "Peters",
+ "Smit",
+ "Van Dijk",
+ "Van den Berg",
+ "Visser",
+ "Vos",
+ ),
+ ),
+ "us" => array(
+ "country" => "United States",
+ "boys" => array(
+ "Aiden",
+ "Alexander",
+ "Charles",
+ "David",
+ "Ethan",
+ "Jacob",
+ "James",
+ "Jayden",
+ "John",
+ "Joseph",
+ "Liam",
+ "Mason",
+ "Michael",
+ "Noah",
+ "Richard",
+ "Robert",
+ "Thomas",
+ "William",
+ ),
+ "girls" => array(
+ "Ava",
+ "Barbara",
+ "Chloe",
+ "Dorothy",
+ "Elizabeth",
+ "Emily",
+ "Emma",
+ "Isabella",
+ "Jennifer",
+ "Lily",
+ "Linda",
+ "Margaret",
+ "Maria",
+ "Mary",
+ "Mia",
+ "Olivia",
+ "Patricia",
+ "Roxy",
+ "Sophia",
+ "Susan",
+ "Zoe",
+ ),
+ "last" => array(
+ "Smith",
+ "Johnson",
+ "Williams",
+ "Jones",
+ "Brown",
+ "Davis",
+ "Miller",
+ "Wilson",
+ "Moore",
+ "Taylor",
+ "Anderson",
+ "Thomas",
+ "Jackson",
+ "White",
+ "Harris",
+ "Martin",
+ "Thompson",
+ "Garcia",
+ "Martinez",
+ "Robinson",
+ ),
+ ),
+);
+
+$current = 0;
+
+$r = function($arr) {
+
+ return $arr[mt_rand(0,count($arr)-1)];
+
+};
+
+$bdayStart = strtotime('-85 years');
+$bdayEnd = strtotime('-20 years');
+
+while($current < $count) {
+
+ $current++;
+ fwrite(STDERR, "\033[100D$current/$count");
+
+ $country = array_rand($sets);
+ $gender = mt_rand(0,1)?'girls':'boys';
+
+ $vcard = new Component\VCard(array(
+ 'VERSION' => '4.0',
+ 'FN' => $r($sets[$country][$gender]) . ' ' . $r($sets[$country]['last']),
+ 'UID' => UUIDUtil::getUUID(),
+ ));
+
+ $bdayRatio = mt_rand(0,9);
+
+ if($bdayRatio < 2) {
+ // 20% has a birthday property with a full date
+ $dt = new \DateTime('@' . mt_rand($bdayStart, $bdayEnd));
+ $vcard->add('BDAY', $dt->format('Ymd'));
+
+ } elseif ($bdayRatio < 3) {
+ // 10% we only know the month and date of
+ $dt = new \DateTime('@' . mt_rand($bdayStart, $bdayEnd));
+ $vcard->add('BDAY', '--' . $dt->format('md'));
+ }
+ if ($result = $vcard->validate()) {
+ ob_start();
+ echo "\nWe produced an invalid vcard somehow!\n";
+ foreach($result as $message) {
+ echo " " . $message['message'] . "\n";
+ }
+ fwrite(STDERR, ob_get_clean());
+ }
+ echo $vcard->serialize();
+
+}
+
+fwrite(STDERR,"\nDone.\n");
diff --git a/vendor/sabre/vobject/bin/generateicalendardata.php b/vendor/sabre/vobject/bin/generateicalendardata.php
index 92c8c106d..dfcf18780 100755
--- a/vendor/sabre/vobject/bin/generateicalendardata.php
+++ b/vendor/sabre/vobject/bin/generateicalendardata.php
@@ -3,7 +3,7 @@
use Sabre\VObject;
-if ($argc<2) {
+if ($argc < 2) {
$cmd = $argv[0];
fwrite(STDERR, <<<HI
Fruux test data generator
@@ -29,49 +29,47 @@ include __DIR__ . '/../vendor/autoload.php';
fwrite(STDERR, "Generating " . $events . " events\n");
-$currentDate = new DateTime('-' . round($events/2) . ' days');
+$currentDate = new DateTime('-' . round($events / 2) . ' days');
-$calendar = VObject\Component::create('VCALENDAR');
-$calendar->version = '2.0';
-$calendar->calscale = 'GREGORIAN';
+$calendar = new VObject\Component\VCalendar();
-$ii=0;
+$ii = 0;
-while($ii < $events) {
+while ($ii < $events) {
$ii++;
- $event = VObject\Component::create('VEVENT');
+ $event = $calendar->add('VEVENT');
$event->DTSTART = 'bla';
$event->SUMMARY = 'Event #' . $ii;
$event->UID = md5(microtime(true));
- $doctorRandom = mt_rand(1,1000);
+ $doctorRandom = mt_rand(1, 1000);
- switch($doctorRandom) {
+ switch ($doctorRandom) {
// All-day event
- case 1 :
+ case 1 :
$event->DTEND = 'bla';
$dtStart = clone $currentDate;
$dtEnd = clone $currentDate;
- $dtEnd->modify('+' . mt_rand(1,3) . ' days');
- $event->DTSTART->setDateTime($dtStart, VObject\Property\DateTime::DATE);
- $event->DTEND->setDateTime($dtEnd, VObject\Property\DateTime::DATE);
+ $dtEnd->modify('+' . mt_rand(1, 3) . ' days');
+ $event->DTSTART->setDateTime($dtStart);
+ $event->DTSTART['VALUE'] = 'DATE';
+ $event->DTEND->setDateTime($dtEnd);
break;
case 2 :
- $event->RRULE = 'FREQ=DAILY;COUNT=' . mt_rand(1,10);
+ $event->RRULE = 'FREQ=DAILY;COUNT=' . mt_rand(1, 10);
// No break intentional
default :
$dtStart = clone $currentDate;
- $dtStart->setTime(mt_rand(1,23), mt_rand(0,59), mt_rand(0,59));
- $event->DTSTART->setDateTime($dtStart, VObject\Property\DateTime::UTC);
- $event->DURATION = 'PT'.mt_rand(1,3).'H';
+ $dtStart->setTime(mt_rand(1, 23), mt_rand(0, 59), mt_rand(0, 59));
+ $event->DTSTART->setDateTime($dtStart);
+ $event->DURATION = 'PT' . mt_rand(1, 3) . 'H';
break;
}
- $calendar->add($event);
- $currentDate->modify('+ ' . mt_rand(0,3) . ' days');
+ $currentDate->modify('+ ' . mt_rand(0, 3) . ' days');
}
fwrite(STDERR, "Validating\n");
@@ -79,7 +77,7 @@ fwrite(STDERR, "Validating\n");
$result = $calendar->validate();
if ($result) {
fwrite(STDERR, "Errors!\n");
- fwrite(STDERR, print_r($result,true));
+ fwrite(STDERR, print_r($result, true));
die(-1);
}
@@ -88,4 +86,3 @@ fwrite(STDERR, "Serializing this beast\n");
echo $calendar->serialize();
fwrite(STDERR, "done.\n");
-
diff --git a/vendor/sabre/vobject/bin/mergeduplicates.php b/vendor/sabre/vobject/bin/mergeduplicates.php
new file mode 100755
index 000000000..1662e7bf3
--- /dev/null
+++ b/vendor/sabre/vobject/bin/mergeduplicates.php
@@ -0,0 +1,184 @@
+#!/usr/bin/env php
+<?php
+
+namespace Sabre\VObject;
+
+// This sucks.. we have to try to find the composer autoloader. But chances
+// are, we can't find it this way. So we'll do our bestest
+$paths = [
+ __DIR__ . '/../vendor/autoload.php', // In case vobject is cloned directly
+ __DIR__ . '/../../../autoload.php', // In case vobject is a composer dependency.
+];
+
+foreach ($paths as $path) {
+ if (file_exists($path)) {
+ include $path;
+ break;
+ }
+}
+
+if (!class_exists('Sabre\\VObject\\Version')) {
+ fwrite(STDERR, "Composer autoloader could not be loaded.\n");
+ die(1);
+}
+
+echo "sabre/vobject ", Version::VERSION, " duplicate contact merge tool\n";
+
+if ($argc < 3) {
+
+ echo "\n";
+ echo "Usage: ", $argv[0], " input.vcf output.vcf [debug.log]\n";
+ die(1);
+
+}
+
+$input = fopen($argv[1], 'r');
+$output = fopen($argv[2], 'w');
+$debug = isset($argv[3]) ? fopen($argv[3], 'w') : null;
+
+$splitter = new Splitter\VCard($input);
+
+// The following properties are ignored. If they appear in some vcards
+// but not in others, we don't consider them for the sake of finding
+// differences.
+$ignoredProperties = [
+ "PRODID",
+ "VERSION",
+ "REV",
+ "UID",
+ "X-ABLABEL",
+];
+
+
+$collectedNames = [];
+
+$stats = [
+ "Total vcards" => 0,
+ "No FN property" => 0,
+ "Ignored duplicates" => 0,
+ "Merged values" => 0,
+ "Error" => 0,
+ "Unique cards" => 0,
+ "Total written" => 0,
+];
+
+function writeStats() {
+
+ global $stats;
+ foreach ($stats as $name => $value) {
+ echo str_pad($name, 23, " ", STR_PAD_RIGHT), str_pad($value, 6, " ", STR_PAD_LEFT), "\n";
+ }
+ // Moving cursor back a few lines.
+ echo "\033[" . count($stats) . "A";
+
+}
+
+function write($vcard) {
+
+ global $stats, $output;
+
+ $stats["Total written"]++;
+ fwrite($output, $vcard->serialize() . "\n");
+
+}
+
+while ($vcard = $splitter->getNext()) {
+
+ $stats["Total vcards"]++;
+ writeStats();
+
+ $fn = isset($vcard->FN) ? (string)$vcard->FN : null;
+
+ if (empty($fn)) {
+
+ // Immediately write this vcard, we don't compare it.
+ $stats["No FN property"]++;
+ $stats['Unique cards']++;
+ write($vcard);
+ $vcard->destroy();
+ continue;
+
+ }
+
+ if (!isset($collectedNames[$fn])) {
+
+ $collectedNames[$fn] = $vcard;
+ $stats['Unique cards']++;
+ continue;
+
+ } else {
+
+ // Starting comparison for all properties. We only check if properties
+ // in the current vcard exactly appear in the earlier vcard as well.
+ foreach ($vcard->children() as $newProp) {
+
+ if (in_array($newProp->name, $ignoredProperties)) {
+ // We don't care about properties such as UID and REV.
+ continue;
+ }
+ $ok = false;
+ foreach ($collectedNames[$fn]->select($newProp->name) as $compareProp) {
+
+ if ($compareProp->serialize() === $newProp->serialize()) {
+ $ok = true;
+ break;
+ }
+ }
+
+ if (!$ok) {
+
+ if ($newProp->name === 'EMAIL' || $newProp->name === 'TEL') {
+
+ // We're going to make another attempt to find this
+ // property, this time just by value. If we find it, we
+ // consider it a success.
+ foreach ($collectedNames[$fn]->select($newProp->name) as $compareProp) {
+
+ if ($compareProp->getValue() === $newProp->getValue()) {
+ $ok = true;
+ break;
+ }
+ }
+
+ if (!$ok) {
+
+ // Merging the new value in the old vcard.
+ $collectedNames[$fn]->add(clone $newProp);
+ $ok = true;
+ $stats['Merged values']++;
+
+ }
+
+ }
+
+ }
+
+ if (!$ok) {
+
+ // echo $newProp->serialize() . " does not appear in earlier vcard!\n";
+ $stats['Error']++;
+ if ($debug) fwrite($debug, "Missing '" . $newProp->name . "' property in duplicate. Earlier vcard:\n" . $collectedNames[$fn]->serialize() . "\n\nLater:\n" . $vcard->serialize() . "\n\n");
+
+ $vcard->destroy();
+ continue 2;
+ }
+
+ }
+
+ }
+
+ $vcard->destroy();
+ $stats['Ignored duplicates']++;
+
+}
+
+foreach ($collectedNames as $vcard) {
+
+ // Overwriting any old PRODID
+ $vcard->PRODID = '-//Sabre//Sabre VObject ' . Version::VERSION . '//EN';
+ write($vcard);
+ writeStats();
+
+}
+
+echo str_repeat("\n", count($stats)), "\nDone.\n";
diff --git a/vendor/sabre/vobject/bin/rrulebench.php b/vendor/sabre/vobject/bin/rrulebench.php
new file mode 100644
index 000000000..af26b4765
--- /dev/null
+++ b/vendor/sabre/vobject/bin/rrulebench.php
@@ -0,0 +1,32 @@
+<?php
+
+include __DIR__ . '/../vendor/autoload.php';
+
+if ($argc < 4) {
+ echo "sabre/vobject ", Sabre\VObject\Version::VERSION, " RRULE benchmark\n";
+ echo "\n";
+ echo "This script can be used to measure the speed of the 'recurrence expansion'\n";
+ echo "system.";
+ echo "\n";
+ echo "Usage: " . $argv[0] . " inputfile.ics startdate enddate\n";
+ die();
+}
+
+list(, $inputFile, $startDate, $endDate) = $argv;
+
+$bench = new Hoa\Bench\Bench();
+$bench->parse->start();
+
+echo "Parsing.\n";
+$vobj = Sabre\VObject\Reader::read(fopen($inputFile, 'r'));
+
+$bench->parse->stop();
+
+echo "Expanding.\n";
+$bench->expand->start();
+
+$vobj->expand(new DateTime($startDate), new DateTime($endDate));
+
+$bench->expand->stop();
+
+echo $bench,"\n";
diff --git a/vendor/sabre/vobject/bin/vobject b/vendor/sabre/vobject/bin/vobject
new file mode 100755
index 000000000..2aca7e729
--- /dev/null
+++ b/vendor/sabre/vobject/bin/vobject
@@ -0,0 +1,27 @@
+#!/usr/bin/env php
+<?php
+
+namespace Sabre\VObject;
+
+// This sucks.. we have to try to find the composer autoloader. But chances
+// are, we can't find it this way. So we'll do our bestest
+$paths = [
+ __DIR__ . '/../vendor/autoload.php', // In case vobject is cloned directly
+ __DIR__ . '/../../../autoload.php', // In case vobject is a composer dependency.
+];
+
+foreach($paths as $path) {
+ if (file_exists($path)) {
+ include $path;
+ break;
+ }
+}
+
+if (!class_exists('Sabre\\VObject\\Version')) {
+ fwrite(STDERR, "Composer autoloader could not be loaded.\n");
+ die(1);
+}
+
+$cli = new Cli();
+exit($cli->main($argv));
+
diff --git a/vendor/sabre/vobject/bin/vobjectvalidate.php b/vendor/sabre/vobject/bin/vobjectvalidate.php
deleted file mode 100755
index e0b2a479f..000000000
--- a/vendor/sabre/vobject/bin/vobjectvalidate.php
+++ /dev/null
@@ -1,139 +0,0 @@
-#!/usr/bin/env php
-<?php
-
-namespace Sabre\VObject;
-
-// This sucks.. we have to try to find the composer autoloader. But chances
-// are, we can't find it this way. So we'll do our bestest
-$paths = array(
- __DIR__ . '/../vendor/autoload.php', // In case vobject is cloned directly
- __DIR__ . '/../../../autoload.php', // In case vobject is a composer dependency.
-);
-
-foreach($paths as $path) {
- if (file_exists($path)) {
- include $path;
- break;
- }
-}
-
-if (!class_exists('Sabre\\VObject\\Version')) {
- fwrite(STDERR, "Composer autoloader could not be properly loaded.\n");
- die(1);
-}
-
-fwrite(STDERR, "SabreTooth VObject validator " . Version::VERSION . "\n");
-
-
-$repair = false;
-$posArgs = array();
-
-// Argument parsing:
-foreach($argv as $k=>$v) {
-
- if ($k===0) {
- continue;
- }
- if (substr($v,0,2)==='--') {
- switch($v) {
- case '--repair' :
- $repair = true;
- break;
- default :
- throw new InvalidArgumentException('Unknown option: ' . $v);
- break;
- }
- continue;
- }
- $posArgs[] = $v;
-
-}
-
-function help() {
-
- global $argv;
-
- fwrite(STDERR, <<<HELP
-Usage instructions:
-
- {$argv[0]} [--repair] inputfile [outputfile]
-
- inputfile Input .vcf or .ics file.
- outputfile Output .vcf or .ics file. This is only used with --repair.
- --repair Attempt to automatically repair broken files.
-
-For both the output- and inputfile "-" can be specified, to use STDIN and STDOUT
-respectively.
-
-All other output from this script is sent to STDERR.
-
-https://github.com/fruux/sabre-vobject
-
-HELP
-);
-
-}
-
-if (count($posArgs) < 1) {
- help();
- die();
-}
-
-if ($posArgs[0]!=='-') {
- $input = fopen($posArgs[0],'r');
-} else {
- $input = STDIN;
-}
-
-if (isset($posArgs[1]) && $posArgs[1]!=='-') {
- $output = fopen($posArgs[1],'w');
-} else {
- $output = STDOUT;
-}
-
-// This is a bit of a hack to easily support multiple objects in a single file.
-$inputStr = "BEGIN:X-SABRE-WRAPPER\r\n" . stream_get_contents($input);
-
-$inputStr = rtrim($inputStr, "\r\n") . "\r\nEND:X-SABRE-WRAPPER\r\n";
-
-// Now the actual work.
-$vObj = Reader::read($inputStr);
-
-$objects = $vObj->children();
-
-foreach($objects as $child) {
-
- switch($child->name) {
- case 'VCALENDAR' :
- fwrite(STDERR, "iCalendar: " . (string)$child->VERSION . "\n");
- break;
- case 'VCARD' :
- fwrite(STDERR, "vCard: " . (string)$child->VERSION . "\n");
- break;
- default :
- fwrite(STDERR, "This was an unknown object, but it did parse. It's likely that validation will give you unexpected results.\n");
- break;
- }
-
- $options = 0;
- if ($repair) $options |= Node::REPAIR;
-
- $warnings = $child->validate($options);
-
- if (!count($warnings)) {
- fwrite(STDERR, "[GOOD NEWS] No warnings!\n");
- } else {
- foreach($warnings as $warn) {
-
- fwrite(STDERR, $warn['message'] . "\n");
-
- }
-
- }
-
- if ($repair) {
- fwrite($output, $child->serialize());
- }
-
-}
-
diff --git a/vendor/sabre/vobject/lib/BirthdayCalendarGenerator.php b/vendor/sabre/vobject/lib/BirthdayCalendarGenerator.php
new file mode 100644
index 000000000..afa41ab1c
--- /dev/null
+++ b/vendor/sabre/vobject/lib/BirthdayCalendarGenerator.php
@@ -0,0 +1,191 @@
+<?php
+
+namespace Sabre\VObject;
+
+use Sabre\VObject\Component\VCalendar;
+
+/**
+ * This class generates birthday calendars.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Dominik Tobschall (http://tobschall.de/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class BirthdayCalendarGenerator {
+
+ /**
+ * Input objects.
+ *
+ * @var array
+ */
+ protected $objects = [];
+
+ /**
+ * Default year.
+ * Used for dates without a year.
+ */
+ const DEFAULT_YEAR = 2000;
+
+ /**
+ * Output format for the SUMMARY.
+ *
+ * @var string
+ */
+ protected $format = '%1$s\'s Birthday';
+
+ /**
+ * Creates the generator.
+ *
+ * Check the setTimeRange and setObjects methods for details about the
+ * arguments.
+ *
+ * @param mixed $objects
+ */
+ function __construct($objects = null) {
+
+ if ($objects) {
+ $this->setObjects($objects);
+ }
+
+ }
+
+ /**
+ * Sets the input objects.
+ *
+ * You must either supply a vCard as a string or as a Component/VCard object.
+ * It's also possible to supply an array of strings or objects.
+ *
+ * @param mixed $objects
+ *
+ * @return void
+ */
+ function setObjects($objects) {
+
+ if (!is_array($objects)) {
+ $objects = [$objects];
+ }
+
+ $this->objects = [];
+ foreach ($objects as $object) {
+
+ if (is_string($object)) {
+
+ $vObj = Reader::read($object);
+ if (!$vObj instanceof Component\VCard) {
+ throw new \InvalidArgumentException('String could not be parsed as \\Sabre\\VObject\\Component\\VCard by setObjects');
+ }
+
+ $this->objects[] = $vObj;
+
+ } elseif ($object instanceof Component\VCard) {
+
+ $this->objects[] = $object;
+
+ } else {
+
+ throw new \InvalidArgumentException('You can only pass strings or \\Sabre\\VObject\\Component\\VCard arguments to setObjects');
+
+ }
+
+ }
+
+ }
+
+ /**
+ * Sets the output format for the SUMMARY
+ *
+ * @param string $format
+ *
+ * @return void
+ */
+ function setFormat($format) {
+
+ $this->format = $format;
+
+ }
+
+ /**
+ * Parses the input data and returns a VCALENDAR.
+ *
+ * @return Component/VCalendar
+ */
+ function getResult() {
+
+ $calendar = new VCalendar();
+
+ foreach ($this->objects as $object) {
+
+ // Skip if there is no BDAY property.
+ if (!$object->select('BDAY')) {
+ continue;
+ }
+
+ // We've seen clients (ez-vcard) putting "BDAY:" properties
+ // without a value into vCards. If we come across those, we'll
+ // skip them.
+ if (empty($object->BDAY->getValue())) {
+ continue;
+ }
+
+ // We're always converting to vCard 4.0 so we can rely on the
+ // VCardConverter handling the X-APPLE-OMIT-YEAR property for us.
+ $object = $object->convert(Document::VCARD40);
+
+ // Skip if the card has no FN property.
+ if (!isset($object->FN)) {
+ continue;
+ }
+
+ // Skip if the BDAY property is not of the right type.
+ if (!$object->BDAY instanceof Property\VCard\DateAndOrTime) {
+ continue;
+ }
+
+ // Skip if we can't parse the BDAY value.
+ try {
+ $dateParts = DateTimeParser::parseVCardDateTime($object->BDAY->getValue());
+ } catch (InvalidDataException $e) {
+ continue;
+ }
+
+ // Set a year if it's not set.
+ $unknownYear = false;
+
+ if (!$dateParts['year']) {
+ $object->BDAY = self::DEFAULT_YEAR . '-' . $dateParts['month'] . '-' . $dateParts['date'];
+
+ $unknownYear = true;
+ }
+
+ // Create event.
+ $event = $calendar->add('VEVENT', [
+ 'SUMMARY' => sprintf($this->format, $object->FN->getValue()),
+ 'DTSTART' => new \DateTime($object->BDAY->getValue()),
+ 'RRULE' => 'FREQ=YEARLY',
+ 'TRANSP' => 'TRANSPARENT',
+ ]);
+
+ // add VALUE=date
+ $event->DTSTART['VALUE'] = 'DATE';
+
+ // Add X-SABRE-BDAY property.
+ if ($unknownYear) {
+ $event->add('X-SABRE-BDAY', 'BDAY', [
+ 'X-SABRE-VCARD-UID' => $object->UID->getValue(),
+ 'X-SABRE-VCARD-FN' => $object->FN->getValue(),
+ 'X-SABRE-OMIT-YEAR' => self::DEFAULT_YEAR,
+ ]);
+ } else {
+ $event->add('X-SABRE-BDAY', 'BDAY', [
+ 'X-SABRE-VCARD-UID' => $object->UID->getValue(),
+ 'X-SABRE-VCARD-FN' => $object->FN->getValue(),
+ ]);
+ }
+
+ }
+
+ return $calendar;
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Cli.php b/vendor/sabre/vobject/lib/Cli.php
new file mode 100644
index 000000000..df7ac22f3
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Cli.php
@@ -0,0 +1,771 @@
+<?php
+
+namespace Sabre\VObject;
+
+use
+ InvalidArgumentException;
+
+/**
+ * This is the CLI interface for sabre-vobject.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Cli {
+
+ /**
+ * No output.
+ *
+ * @var bool
+ */
+ protected $quiet = false;
+
+ /**
+ * Help display.
+ *
+ * @var bool
+ */
+ protected $showHelp = false;
+
+ /**
+ * Wether to spit out 'mimedir' or 'json' format.
+ *
+ * @var string
+ */
+ protected $format;
+
+ /**
+ * JSON pretty print.
+ *
+ * @var bool
+ */
+ protected $pretty;
+
+ /**
+ * Source file.
+ *
+ * @var string
+ */
+ protected $inputPath;
+
+ /**
+ * Destination file.
+ *
+ * @var string
+ */
+ protected $outputPath;
+
+ /**
+ * output stream.
+ *
+ * @var resource
+ */
+ protected $stdout;
+
+ /**
+ * stdin.
+ *
+ * @var resource
+ */
+ protected $stdin;
+
+ /**
+ * stderr.
+ *
+ * @var resource
+ */
+ protected $stderr;
+
+ /**
+ * Input format (one of json or mimedir).
+ *
+ * @var string
+ */
+ protected $inputFormat;
+
+ /**
+ * Makes the parser less strict.
+ *
+ * @var bool
+ */
+ protected $forgiving = false;
+
+ /**
+ * Main function.
+ *
+ * @return int
+ */
+ function main(array $argv) {
+
+ // @codeCoverageIgnoreStart
+ // We cannot easily test this, so we'll skip it. Pretty basic anyway.
+
+ if (!$this->stderr) {
+ $this->stderr = fopen('php://stderr', 'w');
+ }
+ if (!$this->stdout) {
+ $this->stdout = fopen('php://stdout', 'w');
+ }
+ if (!$this->stdin) {
+ $this->stdin = fopen('php://stdin', 'r');
+ }
+
+ // @codeCoverageIgnoreEnd
+
+
+ try {
+
+ list($options, $positional) = $this->parseArguments($argv);
+
+ if (isset($options['q'])) {
+ $this->quiet = true;
+ }
+ $this->log($this->colorize('green', "sabre/vobject ") . $this->colorize('yellow', Version::VERSION));
+
+ foreach ($options as $name => $value) {
+
+ switch ($name) {
+
+ case 'q' :
+ // Already handled earlier.
+ break;
+ case 'h' :
+ case 'help' :
+ $this->showHelp();
+ return 0;
+ break;
+ case 'format' :
+ switch ($value) {
+
+ // jcard/jcal documents
+ case 'jcard' :
+ case 'jcal' :
+
+ // specific document versions
+ case 'vcard21' :
+ case 'vcard30' :
+ case 'vcard40' :
+ case 'icalendar20' :
+
+ // specific formats
+ case 'json' :
+ case 'mimedir' :
+
+ // icalendar/vcad
+ case 'icalendar' :
+ case 'vcard' :
+ $this->format = $value;
+ break;
+
+ default :
+ throw new InvalidArgumentException('Unknown format: ' . $value);
+
+ }
+ break;
+ case 'pretty' :
+ if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
+ $this->pretty = true;
+ }
+ break;
+ case 'forgiving' :
+ $this->forgiving = true;
+ break;
+ case 'inputformat' :
+ switch ($value) {
+ // json formats
+ case 'jcard' :
+ case 'jcal' :
+ case 'json' :
+ $this->inputFormat = 'json';
+ break;
+
+ // mimedir formats
+ case 'mimedir' :
+ case 'icalendar' :
+ case 'vcard' :
+ case 'vcard21' :
+ case 'vcard30' :
+ case 'vcard40' :
+ case 'icalendar20' :
+
+ $this->inputFormat = 'mimedir';
+ break;
+
+ default :
+ throw new InvalidArgumentException('Unknown format: ' . $value);
+
+ }
+ break;
+ default :
+ throw new InvalidArgumentException('Unknown option: ' . $name);
+
+ }
+
+ }
+
+ if (count($positional) === 0) {
+ $this->showHelp();
+ return 1;
+ }
+
+ if (count($positional) === 1) {
+ throw new InvalidArgumentException('Inputfile is a required argument');
+ }
+
+ if (count($positional) > 3) {
+ throw new InvalidArgumentException('Too many arguments');
+ }
+
+ if (!in_array($positional[0], ['validate', 'repair', 'convert', 'color'])) {
+ throw new InvalidArgumentException('Uknown command: ' . $positional[0]);
+ }
+
+ } catch (InvalidArgumentException $e) {
+ $this->showHelp();
+ $this->log('Error: ' . $e->getMessage(), 'red');
+ return 1;
+ }
+
+ $command = $positional[0];
+
+ $this->inputPath = $positional[1];
+ $this->outputPath = isset($positional[2]) ? $positional[2] : '-';
+
+ if ($this->outputPath !== '-') {
+ $this->stdout = fopen($this->outputPath, 'w');
+ }
+
+ if (!$this->inputFormat) {
+ if (substr($this->inputPath, -5) === '.json') {
+ $this->inputFormat = 'json';
+ } else {
+ $this->inputFormat = 'mimedir';
+ }
+ }
+ if (!$this->format) {
+ if (substr($this->outputPath, -5) === '.json') {
+ $this->format = 'json';
+ } else {
+ $this->format = 'mimedir';
+ }
+ }
+
+
+ $realCode = 0;
+
+ try {
+
+ while ($input = $this->readInput()) {
+
+ $returnCode = $this->$command($input);
+ if ($returnCode !== 0) $realCode = $returnCode;
+
+ }
+
+ } catch (EofException $e) {
+ // end of file
+ } catch (\Exception $e) {
+ $this->log('Error: ' . $e->getMessage(), 'red');
+ return 2;
+ }
+
+ return $realCode;
+
+ }
+
+ /**
+ * Shows the help message.
+ *
+ * @return void
+ */
+ protected function showHelp() {
+
+ $this->log('Usage:', 'yellow');
+ $this->log(" vobject [options] command [arguments]");
+ $this->log('');
+ $this->log('Options:', 'yellow');
+ $this->log($this->colorize('green', ' -q ') . "Don't output anything.");
+ $this->log($this->colorize('green', ' -help -h ') . "Display this help message.");
+ $this->log($this->colorize('green', ' --format ') . "Convert to a specific format. Must be one of: vcard, vcard21,");
+ $this->log($this->colorize('green', ' --forgiving ') . "Makes the parser less strict.");
+ $this->log(" vcard30, vcard40, icalendar20, jcal, jcard, json, mimedir.");
+ $this->log($this->colorize('green', ' --inputformat ') . "If the input format cannot be guessed from the extension, it");
+ $this->log(" must be specified here.");
+ // Only PHP 5.4 and up
+ if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
+ $this->log($this->colorize('green', ' --pretty ') . "json pretty-print.");
+ }
+ $this->log('');
+ $this->log('Commands:', 'yellow');
+ $this->log($this->colorize('green', ' validate') . ' source_file Validates a file for correctness.');
+ $this->log($this->colorize('green', ' repair') . ' source_file [output_file] Repairs a file.');
+ $this->log($this->colorize('green', ' convert') . ' source_file [output_file] Converts a file.');
+ $this->log($this->colorize('green', ' color') . ' source_file Colorize a file, useful for debbugging.');
+ $this->log(
+ <<<HELP
+
+If source_file is set as '-', STDIN will be used.
+If output_file is omitted, STDOUT will be used.
+All other output is sent to STDERR.
+
+HELP
+ );
+
+ $this->log('Examples:', 'yellow');
+ $this->log(' vobject convert contact.vcf contact.json');
+ $this->log(' vobject convert --format=vcard40 old.vcf new.vcf');
+ $this->log(' vobject convert --inputformat=json --format=mimedir - -');
+ $this->log(' vobject color calendar.ics');
+ $this->log('');
+ $this->log('https://github.com/fruux/sabre-vobject', 'purple');
+
+ }
+
+ /**
+ * Validates a VObject file.
+ *
+ * @param Component $vObj
+ *
+ * @return int
+ */
+ protected function validate(Component $vObj) {
+
+ $returnCode = 0;
+
+ switch ($vObj->name) {
+ case 'VCALENDAR' :
+ $this->log("iCalendar: " . (string)$vObj->VERSION);
+ break;
+ case 'VCARD' :
+ $this->log("vCard: " . (string)$vObj->VERSION);
+ break;
+ }
+
+ $warnings = $vObj->validate();
+ if (!count($warnings)) {
+ $this->log(" No warnings!");
+ } else {
+
+ $levels = [
+ 1 => 'REPAIRED',
+ 2 => 'WARNING',
+ 3 => 'ERROR',
+ ];
+ $returnCode = 2;
+ foreach ($warnings as $warn) {
+
+ $extra = '';
+ if ($warn['node'] instanceof Property) {
+ $extra = ' (property: "' . $warn['node']->name . '")';
+ }
+ $this->log(" [" . $levels[$warn['level']] . '] ' . $warn['message'] . $extra);
+
+ }
+
+ }
+
+ return $returnCode;
+
+ }
+
+ /**
+ * Repairs a VObject file.
+ *
+ * @param Component $vObj
+ *
+ * @return int
+ */
+ protected function repair(Component $vObj) {
+
+ $returnCode = 0;
+
+ switch ($vObj->name) {
+ case 'VCALENDAR' :
+ $this->log("iCalendar: " . (string)$vObj->VERSION);
+ break;
+ case 'VCARD' :
+ $this->log("vCard: " . (string)$vObj->VERSION);
+ break;
+ }
+
+ $warnings = $vObj->validate(Node::REPAIR);
+ if (!count($warnings)) {
+ $this->log(" No warnings!");
+ } else {
+
+ $levels = [
+ 1 => 'REPAIRED',
+ 2 => 'WARNING',
+ 3 => 'ERROR',
+ ];
+ $returnCode = 2;
+ foreach ($warnings as $warn) {
+
+ $extra = '';
+ if ($warn['node'] instanceof Property) {
+ $extra = ' (property: "' . $warn['node']->name . '")';
+ }
+ $this->log(" [" . $levels[$warn['level']] . '] ' . $warn['message'] . $extra);
+
+ }
+
+ }
+ fwrite($this->stdout, $vObj->serialize());
+
+ return $returnCode;
+
+ }
+
+ /**
+ * Converts a vObject file to a new format.
+ *
+ * @param Component $vObj
+ *
+ * @return int
+ */
+ protected function convert($vObj) {
+
+ $json = false;
+ $convertVersion = null;
+ $forceInput = null;
+
+ switch ($this->format) {
+ case 'json' :
+ $json = true;
+ if ($vObj->name === 'VCARD') {
+ $convertVersion = Document::VCARD40;
+ }
+ break;
+ case 'jcard' :
+ $json = true;
+ $forceInput = 'VCARD';
+ $convertVersion = Document::VCARD40;
+ break;
+ case 'jcal' :
+ $json = true;
+ $forceInput = 'VCALENDAR';
+ break;
+ case 'mimedir' :
+ case 'icalendar' :
+ case 'icalendar20' :
+ case 'vcard' :
+ break;
+ case 'vcard21' :
+ $convertVersion = Document::VCARD21;
+ break;
+ case 'vcard30' :
+ $convertVersion = Document::VCARD30;
+ break;
+ case 'vcard40' :
+ $convertVersion = Document::VCARD40;
+ break;
+
+ }
+
+ if ($forceInput && $vObj->name !== $forceInput) {
+ throw new \Exception('You cannot convert a ' . strtolower($vObj->name) . ' to ' . $this->format);
+ }
+ if ($convertVersion) {
+ $vObj = $vObj->convert($convertVersion);
+ }
+ if ($json) {
+ $jsonOptions = 0;
+ if ($this->pretty) {
+ $jsonOptions = JSON_PRETTY_PRINT;
+ }
+ fwrite($this->stdout, json_encode($vObj->jsonSerialize(), $jsonOptions));
+ } else {
+ fwrite($this->stdout, $vObj->serialize());
+ }
+
+ return 0;
+
+ }
+
+ /**
+ * Colorizes a file.
+ *
+ * @param Component $vObj
+ *
+ * @return int
+ */
+ protected function color($vObj) {
+
+ fwrite($this->stdout, $this->serializeComponent($vObj));
+
+ }
+
+ /**
+ * Returns an ansi color string for a color name.
+ *
+ * @param string $color
+ *
+ * @return string
+ */
+ protected function colorize($color, $str, $resetTo = 'default') {
+
+ $colors = [
+ 'cyan' => '1;36',
+ 'red' => '1;31',
+ 'yellow' => '1;33',
+ 'blue' => '0;34',
+ 'green' => '0;32',
+ 'default' => '0',
+ 'purple' => '0;35',
+ ];
+ return "\033[" . $colors[$color] . 'm' . $str . "\033[" . $colors[$resetTo] . "m";
+
+ }
+
+ /**
+ * Writes out a string in specific color.
+ *
+ * @param string $color
+ * @param string $str
+ *
+ * @return void
+ */
+ protected function cWrite($color, $str) {
+
+ fwrite($this->stdout, $this->colorize($color, $str));
+
+ }
+
+ protected function serializeComponent(Component $vObj) {
+
+ $this->cWrite('cyan', 'BEGIN');
+ $this->cWrite('red', ':');
+ $this->cWrite('yellow', $vObj->name . "\n");
+
+ /**
+ * Gives a component a 'score' for sorting purposes.
+ *
+ * This is solely used by the childrenSort method.
+ *
+ * A higher score means the item will be lower in the list.
+ * To avoid score collisions, each "score category" has a reasonable
+ * space to accomodate elements. The $key is added to the $score to
+ * preserve the original relative order of elements.
+ *
+ * @param int $key
+ * @param array $array
+ *
+ * @return int
+ */
+ $sortScore = function($key, $array) {
+
+ if ($array[$key] instanceof Component) {
+
+ // We want to encode VTIMEZONE first, this is a personal
+ // preference.
+ if ($array[$key]->name === 'VTIMEZONE') {
+ $score = 300000000;
+ return $score + $key;
+ } else {
+ $score = 400000000;
+ return $score + $key;
+ }
+ } else {
+ // Properties get encoded first
+ // VCARD version 4.0 wants the VERSION property to appear first
+ if ($array[$key] instanceof Property) {
+ if ($array[$key]->name === 'VERSION') {
+ $score = 100000000;
+ return $score + $key;
+ } else {
+ // All other properties
+ $score = 200000000;
+ return $score + $key;
+ }
+ }
+ }
+
+ };
+
+ $children = $vObj->children();
+ $tmp = $children;
+ uksort(
+ $children,
+ function($a, $b) use ($sortScore, $tmp) {
+
+ $sA = $sortScore($a, $tmp);
+ $sB = $sortScore($b, $tmp);
+
+ return $sA - $sB;
+
+ }
+ );
+
+ foreach ($children as $child) {
+ if ($child instanceof Component) {
+ $this->serializeComponent($child);
+ } else {
+ $this->serializeProperty($child);
+ }
+ }
+
+ $this->cWrite('cyan', 'END');
+ $this->cWrite('red', ':');
+ $this->cWrite('yellow', $vObj->name . "\n");
+
+ }
+
+ /**
+ * Colorizes a property.
+ *
+ * @param Property $property
+ *
+ * @return void
+ */
+ protected function serializeProperty(Property $property) {
+
+ if ($property->group) {
+ $this->cWrite('default', $property->group);
+ $this->cWrite('red', '.');
+ }
+
+ $this->cWrite('yellow', $property->name);
+
+ foreach ($property->parameters as $param) {
+
+ $this->cWrite('red', ';');
+ $this->cWrite('blue', $param->serialize());
+
+ }
+ $this->cWrite('red', ':');
+
+ if ($property instanceof Property\Binary) {
+
+ $this->cWrite('default', 'embedded binary stripped. (' . strlen($property->getValue()) . ' bytes)');
+
+ } else {
+
+ $parts = $property->getParts();
+ $first1 = true;
+ // Looping through property values
+ foreach ($parts as $part) {
+ if ($first1) {
+ $first1 = false;
+ } else {
+ $this->cWrite('red', $property->delimiter);
+ }
+ $first2 = true;
+ // Looping through property sub-values
+ foreach ((array)$part as $subPart) {
+ if ($first2) {
+ $first2 = false;
+ } else {
+ // The sub-value delimiter is always comma
+ $this->cWrite('red', ',');
+ }
+
+ $subPart = strtr(
+ $subPart,
+ [
+ '\\' => $this->colorize('purple', '\\\\', 'green'),
+ ';' => $this->colorize('purple', '\;', 'green'),
+ ',' => $this->colorize('purple', '\,', 'green'),
+ "\n" => $this->colorize('purple', "\\n\n\t", 'green'),
+ "\r" => "",
+ ]
+ );
+
+ $this->cWrite('green', $subPart);
+ }
+ }
+
+ }
+ $this->cWrite("default", "\n");
+
+ }
+
+ /**
+ * Parses the list of arguments.
+ *
+ * @param array $argv
+ *
+ * @return void
+ */
+ protected function parseArguments(array $argv) {
+
+ $positional = [];
+ $options = [];
+
+ for ($ii = 0; $ii < count($argv); $ii++) {
+
+ // Skipping the first argument.
+ if ($ii === 0) continue;
+
+ $v = $argv[$ii];
+
+ if (substr($v, 0, 2) === '--') {
+ // This is a long-form option.
+ $optionName = substr($v, 2);
+ $optionValue = true;
+ if (strpos($optionName, '=')) {
+ list($optionName, $optionValue) = explode('=', $optionName);
+ }
+ $options[$optionName] = $optionValue;
+ } elseif (substr($v, 0, 1) === '-' && strlen($v) > 1) {
+ // This is a short-form option.
+ foreach (str_split(substr($v, 1)) as $option) {
+ $options[$option] = true;
+ }
+
+ } else {
+
+ $positional[] = $v;
+
+ }
+
+ }
+
+ return [$options, $positional];
+
+ }
+
+ protected $parser;
+
+ /**
+ * Reads the input file.
+ *
+ * @return Component
+ */
+ protected function readInput() {
+
+ if (!$this->parser) {
+ if ($this->inputPath !== '-') {
+ $this->stdin = fopen($this->inputPath, 'r');
+ }
+
+ if ($this->inputFormat === 'mimedir') {
+ $this->parser = new Parser\MimeDir($this->stdin, ($this->forgiving ? Reader::OPTION_FORGIVING : 0));
+ } else {
+ $this->parser = new Parser\Json($this->stdin, ($this->forgiving ? Reader::OPTION_FORGIVING : 0));
+ }
+ }
+
+ return $this->parser->parse();
+
+ }
+
+ /**
+ * Sends a message to STDERR.
+ *
+ * @param string $msg
+ *
+ * @return void
+ */
+ protected function log($msg, $color = 'default') {
+
+ if (!$this->quiet) {
+ if ($color !== 'default') {
+ $msg = $this->colorize($color, $msg);
+ }
+ fwrite($this->stderr, $msg . "\n");
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Component.php b/vendor/sabre/vobject/lib/Component.php
new file mode 100644
index 000000000..9a10ed3f8
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component.php
@@ -0,0 +1,700 @@
+<?php
+
+namespace Sabre\VObject;
+
+use Sabre\Xml;
+
+/**
+ * Component.
+ *
+ * A component represents a group of properties, such as VCALENDAR, VEVENT, or
+ * VCARD.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Component extends Node {
+
+ /**
+ * Component name.
+ *
+ * This will contain a string such as VEVENT, VTODO, VCALENDAR, VCARD.
+ *
+ * @var string
+ */
+ public $name;
+
+ /**
+ * A list of properties and/or sub-components.
+ *
+ * @var array
+ */
+ protected $children = [];
+
+ /**
+ * Creates a new component.
+ *
+ * You can specify the children either in key=>value syntax, in which case
+ * properties will automatically be created, or you can just pass a list of
+ * Component and Property object.
+ *
+ * By default, a set of sensible values will be added to the component. For
+ * an iCalendar object, this may be something like CALSCALE:GREGORIAN. To
+ * ensure that this does not happen, set $defaults to false.
+ *
+ * @param Document $root
+ * @param string $name such as VCALENDAR, VEVENT.
+ * @param array $children
+ * @param bool $defaults
+ *
+ * @return void
+ */
+ function __construct(Document $root, $name, array $children = [], $defaults = true) {
+
+ $this->name = strtoupper($name);
+ $this->root = $root;
+
+ if ($defaults) {
+ // This is a terribly convoluted way to do this, but this ensures
+ // that the order of properties as they are specified in both
+ // defaults and the childrens list, are inserted in the object in a
+ // natural way.
+ $list = $this->getDefaults();
+ $nodes = [];
+ foreach ($children as $key => $value) {
+ if ($value instanceof Node) {
+ if (isset($list[$value->name])) {
+ unset($list[$value->name]);
+ }
+ $nodes[] = $value;
+ } else {
+ $list[$key] = $value;
+ }
+ }
+ foreach ($list as $key => $value) {
+ $this->add($key, $value);
+ }
+ foreach ($nodes as $node) {
+ $this->add($node);
+ }
+ } else {
+ foreach ($children as $k => $child) {
+ if ($child instanceof Node) {
+ // Component or Property
+ $this->add($child);
+ } else {
+
+ // Property key=>value
+ $this->add($k, $child);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Adds a new property or component, and returns the new item.
+ *
+ * This method has 3 possible signatures:
+ *
+ * add(Component $comp) // Adds a new component
+ * add(Property $prop) // Adds a new property
+ * add($name, $value, array $parameters = []) // Adds a new property
+ * add($name, array $children = []) // Adds a new component
+ * by name.
+ *
+ * @return Node
+ */
+ function add() {
+
+ $arguments = func_get_args();
+
+ if ($arguments[0] instanceof Node) {
+ if (isset($arguments[1])) {
+ throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject Node');
+ }
+ $arguments[0]->parent = $this;
+ $newNode = $arguments[0];
+
+ } elseif (is_string($arguments[0])) {
+
+ $newNode = call_user_func_array([$this->root, 'create'], $arguments);
+
+ } else {
+
+ throw new \InvalidArgumentException('The first argument must either be a \\Sabre\\VObject\\Node or a string');
+
+ }
+
+ $name = $newNode->name;
+ if (isset($this->children[$name])) {
+ $this->children[$name][] = $newNode;
+ } else {
+ $this->children[$name] = [$newNode];
+ }
+ return $newNode;
+
+ }
+
+ /**
+ * This method removes a component or property from this component.
+ *
+ * You can either specify the item by name (like DTSTART), in which case
+ * all properties/components with that name will be removed, or you can
+ * pass an instance of a property or component, in which case only that
+ * exact item will be removed.
+ *
+ * @param string|Property|Component $item
+ * @return void
+ */
+ function remove($item) {
+
+ if (is_string($item)) {
+ // If there's no dot in the name, it's an exact property name and
+ // we can just wipe out all those properties.
+ //
+ if (strpos($item, '.') === false) {
+ unset($this->children[strtoupper($item)]);
+ return;
+ }
+ // If there was a dot, we need to ask select() to help us out and
+ // then we just call remove recursively.
+ foreach ($this->select($item) as $child) {
+
+ $this->remove($child);
+
+ }
+ } else {
+ foreach ($this->select($item->name) as $k => $child) {
+ if ($child === $item) {
+ unset($this->children[$item->name][$k]);
+ return;
+ }
+ }
+ }
+
+ throw new \InvalidArgumentException('The item you passed to remove() was not a child of this component');
+
+ }
+
+ /**
+ * Returns a flat list of all the properties and components in this
+ * component.
+ *
+ * @return array
+ */
+ function children() {
+
+ $result = [];
+ foreach ($this->children as $childGroup) {
+ $result = array_merge($result, $childGroup);
+ }
+ return $result;
+
+ }
+
+ /**
+ * This method only returns a list of sub-components. Properties are
+ * ignored.
+ *
+ * @return array
+ */
+ function getComponents() {
+
+ $result = [];
+
+ foreach ($this->children as $childGroup) {
+ foreach ($childGroup as $child) {
+ if ($child instanceof self) {
+ $result[] = $child;
+ }
+ }
+ }
+ return $result;
+
+ }
+
+ /**
+ * Returns an array with elements that match the specified name.
+ *
+ * This function is also aware of MIME-Directory groups (as they appear in
+ * vcards). This means that if a property is grouped as "HOME.EMAIL", it
+ * will also be returned when searching for just "EMAIL". If you want to
+ * search for a property in a specific group, you can select on the entire
+ * string ("HOME.EMAIL"). If you want to search on a specific property that
+ * has not been assigned a group, specify ".EMAIL".
+ *
+ * @param string $name
+ * @return array
+ */
+ function select($name) {
+
+ $group = null;
+ $name = strtoupper($name);
+ if (strpos($name, '.') !== false) {
+ list($group, $name) = explode('.', $name, 2);
+ }
+ if ($name === '') $name = null;
+
+ if (!is_null($name)) {
+
+ $result = isset($this->children[$name]) ? $this->children[$name] : [];
+
+ if (is_null($group)) {
+ return $result;
+ } else {
+ // If we have a group filter as well, we need to narrow it down
+ // more.
+ return array_filter(
+ $result,
+ function($child) use ($group) {
+
+ return $child instanceof Property && strtoupper($child->group) === $group;
+
+ }
+ );
+ }
+
+ }
+
+ // If we got to this point, it means there was no 'name' specified for
+ // searching, implying that this is a group-only search.
+ $result = [];
+ foreach ($this->children as $childGroup) {
+
+ foreach ($childGroup as $child) {
+
+ if ($child instanceof Property && strtoupper($child->group) === $group) {
+ $result[] = $child;
+ }
+
+ }
+
+ }
+ return $result;
+
+ }
+
+ /**
+ * Turns the object back into a serialized blob.
+ *
+ * @return string
+ */
+ function serialize() {
+
+ $str = "BEGIN:" . $this->name . "\r\n";
+
+ /**
+ * Gives a component a 'score' for sorting purposes.
+ *
+ * This is solely used by the childrenSort method.
+ *
+ * A higher score means the item will be lower in the list.
+ * To avoid score collisions, each "score category" has a reasonable
+ * space to accomodate elements. The $key is added to the $score to
+ * preserve the original relative order of elements.
+ *
+ * @param int $key
+ * @param array $array
+ *
+ * @return int
+ */
+ $sortScore = function($key, $array) {
+
+ if ($array[$key] instanceof Component) {
+
+ // We want to encode VTIMEZONE first, this is a personal
+ // preference.
+ if ($array[$key]->name === 'VTIMEZONE') {
+ $score = 300000000;
+ return $score + $key;
+ } else {
+ $score = 400000000;
+ return $score + $key;
+ }
+ } else {
+ // Properties get encoded first
+ // VCARD version 4.0 wants the VERSION property to appear first
+ if ($array[$key] instanceof Property) {
+ if ($array[$key]->name === 'VERSION') {
+ $score = 100000000;
+ return $score + $key;
+ } else {
+ // All other properties
+ $score = 200000000;
+ return $score + $key;
+ }
+ }
+ }
+
+ };
+
+ $children = $this->children();
+ $tmp = $children;
+ uksort(
+ $children,
+ function($a, $b) use ($sortScore, $tmp) {
+
+ $sA = $sortScore($a, $tmp);
+ $sB = $sortScore($b, $tmp);
+
+ return $sA - $sB;
+
+ }
+ );
+
+ foreach ($children as $child) $str .= $child->serialize();
+ $str .= "END:" . $this->name . "\r\n";
+
+ return $str;
+
+ }
+
+ /**
+ * This method returns an array, with the representation as it should be
+ * encoded in JSON. This is used to create jCard or jCal documents.
+ *
+ * @return array
+ */
+ function jsonSerialize() {
+
+ $components = [];
+ $properties = [];
+
+ foreach ($this->children as $childGroup) {
+ foreach ($childGroup as $child) {
+ if ($child instanceof self) {
+ $components[] = $child->jsonSerialize();
+ } else {
+ $properties[] = $child->jsonSerialize();
+ }
+ }
+ }
+
+ return [
+ strtolower($this->name),
+ $properties,
+ $components
+ ];
+
+ }
+
+ /**
+ * This method serializes the data into XML. This is used to create xCard or
+ * xCal documents.
+ *
+ * @param Xml\Writer $writer XML writer.
+ *
+ * @return void
+ */
+ function xmlSerialize(Xml\Writer $writer) {
+
+ $components = [];
+ $properties = [];
+
+ foreach ($this->children as $childGroup) {
+ foreach ($childGroup as $child) {
+ if ($child instanceof self) {
+ $components[] = $child;
+ } else {
+ $properties[] = $child;
+ }
+ }
+ }
+
+ $writer->startElement(strtolower($this->name));
+
+ if (!empty($properties)) {
+
+ $writer->startElement('properties');
+
+ foreach ($properties as $property) {
+ $property->xmlSerialize($writer);
+ }
+
+ $writer->endElement();
+
+ }
+
+ if (!empty($components)) {
+
+ $writer->startElement('components');
+
+ foreach ($components as $component) {
+ $component->xmlSerialize($writer);
+ }
+
+ $writer->endElement();
+ }
+
+ $writer->endElement();
+
+ }
+
+ /**
+ * This method should return a list of default property values.
+ *
+ * @return array
+ */
+ protected function getDefaults() {
+
+ return [];
+
+ }
+
+ /* Magic property accessors {{{ */
+
+ /**
+ * Using 'get' you will either get a property or component.
+ *
+ * If there were no child-elements found with the specified name,
+ * null is returned.
+ *
+ * To use this, this may look something like this:
+ *
+ * $event = $calendar->VEVENT;
+ *
+ * @param string $name
+ *
+ * @return Property
+ */
+ function __get($name) {
+
+ if ($name === 'children') {
+
+ throw new \RuntimeException('Starting sabre/vobject 4.0 the children property is now protected. You should use the children() method instead');
+
+ }
+
+ $matches = $this->select($name);
+ if (count($matches) === 0) {
+ return;
+ } else {
+ $firstMatch = current($matches);
+ /** @var $firstMatch Property */
+ $firstMatch->setIterator(new ElementList(array_values($matches)));
+ return $firstMatch;
+ }
+
+ }
+
+ /**
+ * This method checks if a sub-element with the specified name exists.
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ function __isset($name) {
+
+ $matches = $this->select($name);
+ return count($matches) > 0;
+
+ }
+
+ /**
+ * Using the setter method you can add properties or subcomponents.
+ *
+ * You can either pass a Component, Property
+ * object, or a string to automatically create a Property.
+ *
+ * If the item already exists, it will be removed. If you want to add
+ * a new item with the same name, always use the add() method.
+ *
+ * @param string $name
+ * @param mixed $value
+ *
+ * @return void
+ */
+ function __set($name, $value) {
+
+ $name = strtoupper($name);
+ $this->remove($name);
+ if ($value instanceof self || $value instanceof Property) {
+ $this->add($value);
+ } else {
+ $this->add($name, $value);
+ }
+ }
+
+ /**
+ * Removes all properties and components within this component with the
+ * specified name.
+ *
+ * @param string $name
+ *
+ * @return void
+ */
+ function __unset($name) {
+
+ $this->remove($name);
+
+ }
+
+ /* }}} */
+
+ /**
+ * This method is automatically called when the object is cloned.
+ * Specifically, this will ensure all child elements are also cloned.
+ *
+ * @return void
+ */
+ function __clone() {
+
+ foreach ($this->children as $childName => $childGroup) {
+ foreach ($childGroup as $key => $child) {
+ $clonedChild = clone $child;
+ $clonedChild->parent = $this;
+ $clonedChild->root = $this->root;
+ $this->children[$childName][$key] = $clonedChild;
+ }
+ }
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * It is also possible to specify defaults and severity levels for
+ * violating the rule.
+ *
+ * See the VEVENT implementation for getValidationRules for a more complex
+ * example.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [];
+
+ }
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ * Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
+ * Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
+ *
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on).
+ * 2 - A warning.
+ * 3 - An error.
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $rules = $this->getValidationRules();
+ $defaults = $this->getDefaults();
+
+ $propertyCounters = [];
+
+ $messages = [];
+
+ foreach ($this->children() as $child) {
+ $name = strtoupper($child->name);
+ if (!isset($propertyCounters[$name])) {
+ $propertyCounters[$name] = 1;
+ } else {
+ $propertyCounters[$name]++;
+ }
+ $messages = array_merge($messages, $child->validate($options));
+ }
+
+ foreach ($rules as $propName => $rule) {
+
+ switch ($rule) {
+ case '0' :
+ if (isset($propertyCounters[$propName])) {
+ $messages[] = [
+ 'level' => 3,
+ 'message' => $propName . ' MUST NOT appear in a ' . $this->name . ' component',
+ 'node' => $this,
+ ];
+ }
+ break;
+ case '1' :
+ if (!isset($propertyCounters[$propName]) || $propertyCounters[$propName] !== 1) {
+ $repaired = false;
+ if ($options & self::REPAIR && isset($defaults[$propName])) {
+ $this->add($propName, $defaults[$propName]);
+ $repaired = true;
+ }
+ $messages[] = [
+ 'level' => $repaired ? 1 : 3,
+ 'message' => $propName . ' MUST appear exactly once in a ' . $this->name . ' component',
+ 'node' => $this,
+ ];
+ }
+ break;
+ case '+' :
+ if (!isset($propertyCounters[$propName]) || $propertyCounters[$propName] < 1) {
+ $messages[] = [
+ 'level' => 3,
+ 'message' => $propName . ' MUST appear at least once in a ' . $this->name . ' component',
+ 'node' => $this,
+ ];
+ }
+ break;
+ case '*' :
+ break;
+ case '?' :
+ if (isset($propertyCounters[$propName]) && $propertyCounters[$propName] > 1) {
+ $messages[] = [
+ 'level' => 3,
+ 'message' => $propName . ' MUST NOT appear more than once in a ' . $this->name . ' component',
+ 'node' => $this,
+ ];
+ }
+ break;
+
+ }
+
+ }
+ return $messages;
+
+ }
+
+ /**
+ * Call this method on a document if you're done using it.
+ *
+ * It's intended to remove all circular references, so PHP can easily clean
+ * it up.
+ *
+ * @return void
+ */
+ function destroy() {
+
+ parent::destroy();
+ foreach ($this->children as $childGroup) {
+ foreach ($childGroup as $child) {
+ $child->destroy();
+ }
+ }
+ $this->children = [];
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Component/Available.php b/vendor/sabre/vobject/lib/Component/Available.php
new file mode 100644
index 000000000..b3aaf08af
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/Available.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+
+/**
+ * The Available sub-component.
+ *
+ * This component adds functionality to a component, specific for AVAILABLE
+ * components.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Ivan Enderlin
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Available extends VObject\Component {
+
+ /**
+ * Returns the 'effective start' and 'effective end' of this VAVAILABILITY
+ * component.
+ *
+ * We use the DTSTART and DTEND or DURATION to determine this.
+ *
+ * The returned value is an array containing DateTimeImmutable instances.
+ * If either the start or end is 'unbounded' its value will be null
+ * instead.
+ *
+ * @return array
+ */
+ function getEffectiveStartEnd() {
+
+ $effectiveStart = $this->DTSTART->getDateTime();
+ if (isset($this->DTEND)) {
+ $effectiveEnd = $this->DTEND->getDateTime();
+ } else {
+ $effectiveEnd = $effectiveStart->add(VObject\DateTimeParser::parseDuration($this->DURATION));
+ }
+
+ return [$effectiveStart, $effectiveEnd];
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'UID' => 1,
+ 'DTSTART' => 1,
+ 'DTSTAMP' => 1,
+
+ 'DTEND' => '?',
+ 'DURATION' => '?',
+
+ 'CREATED' => '?',
+ 'DESCRIPTION' => '?',
+ 'LAST-MODIFIED' => '?',
+ 'RECURRENCE-ID' => '?',
+ 'RRULE' => '?',
+ 'SUMMARY' => '?',
+
+ 'CATEGORIES' => '*',
+ 'COMMENT' => '*',
+ 'CONTACT' => '*',
+ 'EXDATE' => '*',
+ 'RDATE' => '*',
+
+ 'AVAILABLE' => '*',
+ ];
+
+ }
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ * Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
+ * Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
+ *
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on).
+ * 2 - A warning.
+ * 3 - An error.
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $result = parent::validate($options);
+
+ if (isset($this->DTEND) && isset($this->DURATION)) {
+ $result[] = [
+ 'level' => 3,
+ 'message' => 'DTEND and DURATION cannot both be present',
+ 'node' => $this
+ ];
+ }
+
+ return $result;
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Component/VAlarm.php b/vendor/sabre/vobject/lib/Component/VAlarm.php
index 2f86c44fd..8cbd572e6 100644
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Component/VAlarm.php
+++ b/vendor/sabre/vobject/lib/Component/VAlarm.php
@@ -1,16 +1,20 @@
<?php
namespace Sabre\VObject\Component;
+
use Sabre\VObject;
+use Sabre\VObject\InvalidDataException;
+use DateTimeInterface;
+use DateTimeImmutable;
/**
- * VAlarm component
+ * VAlarm component.
*
* This component contains some additional functionality specific for VALARMs.
*
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ * @license http://sabre.io/license/ Modified BSD License
*/
class VAlarm extends VObject\Component {
@@ -19,12 +23,12 @@ class VAlarm extends VObject\Component {
*
* This ignores repeated alarm, only the first trigger is returned.
*
- * @return DateTime
+ * @return DateTimeImmutable
*/
- public function getEffectiveTriggerTime() {
+ function getEffectiveTriggerTime() {
$trigger = $this->TRIGGER;
- if(!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') {
+ if (!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') {
$triggerDuration = VObject\DateTimeParser::parseDuration($this->TRIGGER);
$related = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'END' : 'START';
@@ -37,28 +41,28 @@ class VAlarm extends VObject\Component {
$propName = 'DTSTART';
}
- $effectiveTrigger = clone $parentComponent->$propName->getDateTime();
- $effectiveTrigger->add($triggerDuration);
+ $effectiveTrigger = $parentComponent->$propName->getDateTime();
+ $effectiveTrigger = $effectiveTrigger->add($triggerDuration);
} else {
if ($parentComponent->name === 'VTODO') {
$endProp = 'DUE';
} elseif ($parentComponent->name === 'VEVENT') {
$endProp = 'DTEND';
} else {
- throw new \LogicException('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT');
+ throw new InvalidDataException('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT');
}
if (isset($parentComponent->$endProp)) {
- $effectiveTrigger = clone $parentComponent->$endProp->getDateTime();
- $effectiveTrigger->add($triggerDuration);
+ $effectiveTrigger = $parentComponent->$endProp->getDateTime();
+ $effectiveTrigger = $effectiveTrigger->add($triggerDuration);
} elseif (isset($parentComponent->DURATION)) {
- $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime();
+ $effectiveTrigger = $parentComponent->DTSTART->getDateTime();
$duration = VObject\DateTimeParser::parseDuration($parentComponent->DURATION);
- $effectiveTrigger->add($duration);
- $effectiveTrigger->add($triggerDuration);
+ $effectiveTrigger = $effectiveTrigger->add($duration);
+ $effectiveTrigger = $effectiveTrigger->add($triggerDuration);
} else {
- $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime();
- $effectiveTrigger->add($triggerDuration);
+ $effectiveTrigger = $parentComponent->DTSTART->getDateTime();
+ $effectiveTrigger = $effectiveTrigger->add($triggerDuration);
}
}
} else {
@@ -75,24 +79,25 @@ class VAlarm extends VObject\Component {
* The rules used to determine if an event falls within the specified
* time-range is based on the CalDAV specification.
*
- * @param \DateTime $start
- * @param \DateTime $end
+ * @param DateTime $start
+ * @param DateTime $end
+ *
* @return bool
*/
- public function isInTimeRange(\DateTime $start, \DateTime $end) {
+ function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) {
$effectiveTrigger = $this->getEffectiveTriggerTime();
if (isset($this->DURATION)) {
$duration = VObject\DateTimeParser::parseDuration($this->DURATION);
- $repeat = (string)$this->repeat;
+ $repeat = (string)$this->REPEAT;
if (!$repeat) {
$repeat = 1;
}
$period = new \DatePeriod($effectiveTrigger, $duration, (int)$repeat);
- foreach($period as $occurrence) {
+ foreach ($period as $occurrence) {
if ($start <= $occurrence && $end > $occurrence) {
return true;
@@ -105,4 +110,33 @@ class VAlarm extends VObject\Component {
}
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'ACTION' => 1,
+ 'TRIGGER' => 1,
+
+ 'DURATION' => '?',
+ 'REPEAT' => '?',
+
+ 'ATTACH' => '?',
+ ];
+
+ }
+
}
diff --git a/vendor/sabre/vobject/lib/Component/VAvailability.php b/vendor/sabre/vobject/lib/Component/VAvailability.php
new file mode 100644
index 000000000..66b3310c5
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VAvailability.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use DateTimeInterface;
+use Sabre\VObject;
+
+/**
+ * The VAvailability component.
+ *
+ * This component adds functionality to a component, specific for VAVAILABILITY
+ * components.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Ivan Enderlin
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VAvailability extends VObject\Component {
+
+ /**
+ * Returns true or false depending on if the event falls in the specified
+ * time-range. This is used for filtering purposes.
+ *
+ * The rules used to determine if an event falls within the specified
+ * time-range is based on:
+ *
+ * https://tools.ietf.org/html/draft-daboo-calendar-availability-05#section-3.1
+ *
+ * @param DateTimeInterface $start
+ * @param DateTimeInterface $end
+ *
+ * @return bool
+ */
+ function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) {
+
+ list($effectiveStart, $effectiveEnd) = $this->getEffectiveStartEnd();
+ return (
+ (is_null($effectiveStart) || $start < $effectiveEnd) &&
+ (is_null($effectiveEnd) || $end > $effectiveStart)
+ );
+
+ }
+
+ /**
+ * Returns the 'effective start' and 'effective end' of this VAVAILABILITY
+ * component.
+ *
+ * We use the DTSTART and DTEND or DURATION to determine this.
+ *
+ * The returned value is an array containing DateTimeImmutable instances.
+ * If either the start or end is 'unbounded' its value will be null
+ * instead.
+ *
+ * @return array
+ */
+ function getEffectiveStartEnd() {
+
+ $effectiveStart = null;
+ $effectiveEnd = null;
+
+ if (isset($this->DTSTART)) {
+ $effectiveStart = $this->DTSTART->getDateTime();
+ }
+ if (isset($this->DTEND)) {
+ $effectiveEnd = $this->DTEND->getDateTime();
+ } elseif ($effectiveStart && isset($this->DURATION)) {
+ $effectiveEnd = $effectiveStart->add(VObject\DateTimeParser::parseDuration($this->DURATION));
+ }
+
+ return [$effectiveStart, $effectiveEnd];
+
+ }
+
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'UID' => 1,
+ 'DTSTAMP' => 1,
+
+ 'BUSYTYPE' => '?',
+ 'CLASS' => '?',
+ 'CREATED' => '?',
+ 'DESCRIPTION' => '?',
+ 'DTSTART' => '?',
+ 'LAST-MODIFIED' => '?',
+ 'ORGANIZER' => '?',
+ 'PRIORITY' => '?',
+ 'SEQUENCE' => '?',
+ 'SUMMARY' => '?',
+ 'URL' => '?',
+ 'DTEND' => '?',
+ 'DURATION' => '?',
+
+ 'CATEGORIES' => '*',
+ 'COMMENT' => '*',
+ 'CONTACT' => '*',
+ ];
+
+ }
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ * Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
+ * Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
+ *
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on).
+ * 2 - A warning.
+ * 3 - An error.
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $result = parent::validate($options);
+
+ if (isset($this->DTEND) && isset($this->DURATION)) {
+ $result[] = [
+ 'level' => 3,
+ 'message' => 'DTEND and DURATION cannot both be present',
+ 'node' => $this
+ ];
+ }
+
+ return $result;
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Component/VCalendar.php b/vendor/sabre/vobject/lib/Component/VCalendar.php
new file mode 100644
index 000000000..988db9dc2
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VCalendar.php
@@ -0,0 +1,561 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use DateTimeInterface;
+use DateTimeZone;
+use Sabre\VObject;
+use Sabre\VObject\Component;
+use Sabre\VObject\Property;
+use Sabre\VObject\Recur\EventIterator;
+use Sabre\VObject\Recur\NoInstancesException;
+use Sabre\VObject\InvalidDataException;
+
+/**
+ * The VCalendar component.
+ *
+ * This component adds functionality to a component, specific for a VCALENDAR.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VCalendar extends VObject\Document {
+
+ /**
+ * The default name for this component.
+ *
+ * This should be 'VCALENDAR' or 'VCARD'.
+ *
+ * @var string
+ */
+ static $defaultName = 'VCALENDAR';
+
+ /**
+ * This is a list of components, and which classes they should map to.
+ *
+ * @var array
+ */
+ static $componentMap = [
+ 'VCALENDAR' => 'Sabre\\VObject\\Component\\VCalendar',
+ 'VALARM' => 'Sabre\\VObject\\Component\\VAlarm',
+ 'VEVENT' => 'Sabre\\VObject\\Component\\VEvent',
+ 'VFREEBUSY' => 'Sabre\\VObject\\Component\\VFreeBusy',
+ 'VAVAILABILITY' => 'Sabre\\VObject\\Component\\VAvailability',
+ 'AVAILABLE' => 'Sabre\\VObject\\Component\\Available',
+ 'VJOURNAL' => 'Sabre\\VObject\\Component\\VJournal',
+ 'VTIMEZONE' => 'Sabre\\VObject\\Component\\VTimeZone',
+ 'VTODO' => 'Sabre\\VObject\\Component\\VTodo',
+ ];
+
+ /**
+ * List of value-types, and which classes they map to.
+ *
+ * @var array
+ */
+ static $valueMap = [
+ 'BINARY' => 'Sabre\\VObject\\Property\\Binary',
+ 'BOOLEAN' => 'Sabre\\VObject\\Property\\Boolean',
+ 'CAL-ADDRESS' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
+ 'DATE' => 'Sabre\\VObject\\Property\\ICalendar\\Date',
+ 'DATE-TIME' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'DURATION' => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
+ 'FLOAT' => 'Sabre\\VObject\\Property\\FloatValue',
+ 'INTEGER' => 'Sabre\\VObject\\Property\\IntegerValue',
+ 'PERIOD' => 'Sabre\\VObject\\Property\\ICalendar\\Period',
+ 'RECUR' => 'Sabre\\VObject\\Property\\ICalendar\\Recur',
+ 'TEXT' => 'Sabre\\VObject\\Property\\Text',
+ 'TIME' => 'Sabre\\VObject\\Property\\Time',
+ 'UNKNOWN' => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only.
+ 'URI' => 'Sabre\\VObject\\Property\\Uri',
+ 'UTC-OFFSET' => 'Sabre\\VObject\\Property\\UtcOffset',
+ ];
+
+ /**
+ * List of properties, and which classes they map to.
+ *
+ * @var array
+ */
+ static $propertyMap = [
+ // Calendar properties
+ 'CALSCALE' => 'Sabre\\VObject\\Property\\FlatText',
+ 'METHOD' => 'Sabre\\VObject\\Property\\FlatText',
+ 'PRODID' => 'Sabre\\VObject\\Property\\FlatText',
+ 'VERSION' => 'Sabre\\VObject\\Property\\FlatText',
+
+ // Component properties
+ 'ATTACH' => 'Sabre\\VObject\\Property\\Uri',
+ 'CATEGORIES' => 'Sabre\\VObject\\Property\\Text',
+ 'CLASS' => 'Sabre\\VObject\\Property\\FlatText',
+ 'COMMENT' => 'Sabre\\VObject\\Property\\FlatText',
+ 'DESCRIPTION' => 'Sabre\\VObject\\Property\\FlatText',
+ 'GEO' => 'Sabre\\VObject\\Property\\FloatValue',
+ 'LOCATION' => 'Sabre\\VObject\\Property\\FlatText',
+ 'PERCENT-COMPLETE' => 'Sabre\\VObject\\Property\\IntegerValue',
+ 'PRIORITY' => 'Sabre\\VObject\\Property\\IntegerValue',
+ 'RESOURCES' => 'Sabre\\VObject\\Property\\Text',
+ 'STATUS' => 'Sabre\\VObject\\Property\\FlatText',
+ 'SUMMARY' => 'Sabre\\VObject\\Property\\FlatText',
+
+ // Date and Time Component Properties
+ 'COMPLETED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'DTEND' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'DUE' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'DTSTART' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'DURATION' => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
+ 'FREEBUSY' => 'Sabre\\VObject\\Property\\ICalendar\\Period',
+ 'TRANSP' => 'Sabre\\VObject\\Property\\FlatText',
+
+ // Time Zone Component Properties
+ 'TZID' => 'Sabre\\VObject\\Property\\FlatText',
+ 'TZNAME' => 'Sabre\\VObject\\Property\\FlatText',
+ 'TZOFFSETFROM' => 'Sabre\\VObject\\Property\\UtcOffset',
+ 'TZOFFSETTO' => 'Sabre\\VObject\\Property\\UtcOffset',
+ 'TZURL' => 'Sabre\\VObject\\Property\\Uri',
+
+ // Relationship Component Properties
+ 'ATTENDEE' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
+ 'CONTACT' => 'Sabre\\VObject\\Property\\FlatText',
+ 'ORGANIZER' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
+ 'RECURRENCE-ID' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'RELATED-TO' => 'Sabre\\VObject\\Property\\FlatText',
+ 'URL' => 'Sabre\\VObject\\Property\\Uri',
+ 'UID' => 'Sabre\\VObject\\Property\\FlatText',
+
+ // Recurrence Component Properties
+ 'EXDATE' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'RDATE' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'RRULE' => 'Sabre\\VObject\\Property\\ICalendar\\Recur',
+ 'EXRULE' => 'Sabre\\VObject\\Property\\ICalendar\\Recur', // Deprecated since rfc5545
+
+ // Alarm Component Properties
+ 'ACTION' => 'Sabre\\VObject\\Property\\FlatText',
+ 'REPEAT' => 'Sabre\\VObject\\Property\\IntegerValue',
+ 'TRIGGER' => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
+
+ // Change Management Component Properties
+ 'CREATED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'DTSTAMP' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'LAST-MODIFIED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'SEQUENCE' => 'Sabre\\VObject\\Property\\IntegerValue',
+
+ // Request Status
+ 'REQUEST-STATUS' => 'Sabre\\VObject\\Property\\Text',
+
+ // Additions from draft-daboo-valarm-extensions-04
+ 'ALARM-AGENT' => 'Sabre\\VObject\\Property\\Text',
+ 'ACKNOWLEDGED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'PROXIMITY' => 'Sabre\\VObject\\Property\\Text',
+ 'DEFAULT-ALARM' => 'Sabre\\VObject\\Property\\Boolean',
+
+ // Additions from draft-daboo-calendar-availability-05
+ 'BUSYTYPE' => 'Sabre\\VObject\\Property\\Text',
+
+ ];
+
+ /**
+ * Returns the current document type.
+ *
+ * @return int
+ */
+ function getDocumentType() {
+
+ return self::ICALENDAR20;
+
+ }
+
+ /**
+ * Returns a list of all 'base components'. For instance, if an Event has
+ * a recurrence rule, and one instance is overridden, the overridden event
+ * will have the same UID, but will be excluded from this list.
+ *
+ * VTIMEZONE components will always be excluded.
+ *
+ * @param string $componentName filter by component name
+ *
+ * @return VObject\Component[]
+ */
+ function getBaseComponents($componentName = null) {
+
+ $isBaseComponent = function($component) {
+
+ if (!$component instanceof VObject\Component) {
+ return false;
+ }
+ if ($component->name === 'VTIMEZONE') {
+ return false;
+ }
+ if (isset($component->{'RECURRENCE-ID'})) {
+ return false;
+ }
+ return true;
+
+ };
+
+ if ($componentName) {
+ // Early exit
+ return array_filter(
+ $this->select($componentName),
+ $isBaseComponent
+ );
+ }
+
+ $components = [];
+ foreach ($this->children as $childGroup) {
+
+ foreach ($childGroup as $child) {
+
+ if (!$child instanceof Component) {
+ // If one child is not a component, they all are so we skip
+ // the entire group.
+ continue 2;
+ }
+ if ($isBaseComponent($child)) {
+ $components[] = $child;
+ }
+
+ }
+
+ }
+ return $components;
+
+ }
+
+ /**
+ * Returns the first component that is not a VTIMEZONE, and does not have
+ * an RECURRENCE-ID.
+ *
+ * If there is no such component, null will be returned.
+ *
+ * @param string $componentName filter by component name
+ *
+ * @return VObject\Component|null
+ */
+ function getBaseComponent($componentName = null) {
+
+ $isBaseComponent = function($component) {
+
+ if (!$component instanceof VObject\Component) {
+ return false;
+ }
+ if ($component->name === 'VTIMEZONE') {
+ return false;
+ }
+ if (isset($component->{'RECURRENCE-ID'})) {
+ return false;
+ }
+ return true;
+
+ };
+
+ if ($componentName) {
+ foreach ($this->select($componentName) as $child) {
+ if ($isBaseComponent($child)) {
+ return $child;
+ }
+ }
+ return null;
+ }
+
+ // Searching all components
+ foreach ($this->children as $childGroup) {
+ foreach ($childGroup as $child) {
+ if ($isBaseComponent($child)) {
+ return $child;
+ }
+ }
+
+ }
+ return null;
+
+ }
+
+ /**
+ * Expand all events in this VCalendar object and return a new VCalendar
+ * with the expanded events.
+ *
+ * If this calendar object, has events with recurrence rules, this method
+ * can be used to expand the event into multiple sub-events.
+ *
+ * Each event will be stripped from it's recurrence information, and only
+ * the instances of the event in the specified timerange will be left
+ * alone.
+ *
+ * In addition, this method will cause timezone information to be stripped,
+ * and normalized to UTC.
+ *
+ * @param DateTimeInterface $start
+ * @param DateTimeInterface $end
+ * @param DateTimeZone $timeZone reference timezone for floating dates and
+ * times.
+ * @return VCalendar
+ */
+ function expand(DateTimeInterface $start, DateTimeInterface $end, DateTimeZone $timeZone = null) {
+
+ $newChildren = [];
+ $recurringEvents = [];
+
+ if (!$timeZone) {
+ $timeZone = new DateTimeZone('UTC');
+ }
+
+ $stripTimezones = function(Component $component) use ($timeZone, &$stripTimezones) {
+
+ foreach ($component->children() as $componentChild) {
+ if ($componentChild instanceof Property\ICalendar\DateTime && $componentChild->hasTime()) {
+
+ $dt = $componentChild->getDateTimes($timeZone);
+ // We only need to update the first timezone, because
+ // setDateTimes will match all other timezones to the
+ // first.
+ $dt[0] = $dt[0]->setTimeZone(new DateTimeZone('UTC'));
+ $componentChild->setDateTimes($dt);
+ } elseif ($componentChild instanceof Component) {
+ $stripTimezones($componentChild);
+ }
+
+ }
+ return $component;
+
+ };
+
+ foreach ($this->children() as $child) {
+
+ if ($child instanceof Property && $child->name !== 'PRODID') {
+ // We explictly want to ignore PRODID, because we want to
+ // overwrite it with our own.
+ $newChildren[] = clone $child;
+ } elseif ($child instanceof Component && $child->name !== 'VTIMEZONE') {
+
+ // We're also stripping all VTIMEZONE objects because we're
+ // converting everything to UTC.
+ if ($child->name === 'VEVENT' && (isset($child->{'RECURRENCE-ID'}) || isset($child->RRULE) || isset($child->RDATE))) {
+ // Handle these a bit later.
+ $uid = (string)$child->UID;
+ if (!$uid) {
+ throw new InvalidDataException('Every VEVENT object must have a UID property');
+ }
+ if (isset($recurringEvents[$uid])) {
+ $recurringEvents[$uid][] = clone $child;
+ } else {
+ $recurringEvents[$uid] = [clone $child];
+ }
+ } elseif ($child->name === 'VEVENT' && $child->isInTimeRange($start, $end)) {
+ $newChildren[] = $stripTimezones(clone $child);
+ }
+
+ }
+
+ }
+
+ foreach ($recurringEvents as $events) {
+
+ try {
+ $it = new EventIterator($events, $timeZone);
+
+ } catch (NoInstancesException $e) {
+ // This event is recurring, but it doesn't have a single
+ // instance. We are skipping this event from the output
+ // entirely.
+ continue;
+ }
+ $it->fastForward($start);
+
+ while ($it->valid() && $it->getDTStart() < $end) {
+
+ if ($it->getDTEnd() > $start) {
+
+ $newChildren[] = $stripTimezones($it->getEventObject());
+
+ }
+ $it->next();
+
+ }
+
+ }
+
+ return new self($newChildren);
+
+ }
+
+ /**
+ * This method should return a list of default property values.
+ *
+ * @return array
+ */
+ protected function getDefaults() {
+
+ return [
+ 'VERSION' => '2.0',
+ 'PRODID' => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN',
+ 'CALSCALE' => 'GREGORIAN',
+ ];
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'PRODID' => 1,
+ 'VERSION' => 1,
+
+ 'CALSCALE' => '?',
+ 'METHOD' => '?',
+ ];
+
+ }
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ * Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
+ * Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
+ *
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on).
+ * 2 - A warning.
+ * 3 - An error.
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $warnings = parent::validate($options);
+
+ if ($ver = $this->VERSION) {
+ if ((string)$ver !== '2.0') {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.',
+ 'node' => $this,
+ ];
+ }
+
+ }
+
+ $uidList = [];
+ $componentsFound = 0;
+ $componentTypes = [];
+
+ foreach ($this->children() as $child) {
+ if ($child instanceof Component) {
+ $componentsFound++;
+
+ if (!in_array($child->name, ['VEVENT', 'VTODO', 'VJOURNAL'])) {
+ continue;
+ }
+ $componentTypes[] = $child->name;
+
+ $uid = (string)$child->UID;
+ $isMaster = isset($child->{'RECURRENCE-ID'}) ? 0 : 1;
+ if (isset($uidList[$uid])) {
+ $uidList[$uid]['count']++;
+ if ($isMaster && $uidList[$uid]['hasMaster']) {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'More than one master object was found for the object with UID ' . $uid,
+ 'node' => $this,
+ ];
+ }
+ $uidList[$uid]['hasMaster'] += $isMaster;
+ } else {
+ $uidList[$uid] = [
+ 'count' => 1,
+ 'hasMaster' => $isMaster,
+ ];
+ }
+
+ }
+ }
+
+ if ($componentsFound === 0) {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'An iCalendar object must have at least 1 component.',
+ 'node' => $this,
+ ];
+ }
+
+ if ($options & self::PROFILE_CALDAV) {
+ if (count($uidList) > 1) {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'A calendar object on a CalDAV server may only have components with the same UID.',
+ 'node' => $this,
+ ];
+ }
+ if (count($componentTypes) === 0) {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'A calendar object on a CalDAV server must have at least 1 component (VTODO, VEVENT, VJOURNAL).',
+ 'node' => $this,
+ ];
+ }
+ if (count(array_unique($componentTypes)) > 1) {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'A calendar object on a CalDAV server may only have 1 type of component (VEVENT, VTODO or VJOURNAL).',
+ 'node' => $this,
+ ];
+ }
+
+ if (isset($this->METHOD)) {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'A calendar object on a CalDAV server MUST NOT have a METHOD property.',
+ 'node' => $this,
+ ];
+ }
+ }
+
+ return $warnings;
+
+ }
+
+ /**
+ * Returns all components with a specific UID value.
+ *
+ * @return array
+ */
+ function getByUID($uid) {
+
+ return array_filter($this->getComponents(), function($item) use ($uid) {
+
+ if (!$itemUid = $item->select('UID')) {
+ return false;
+ }
+ $itemUid = current($itemUid)->getValue();
+ return $uid === $itemUid;
+
+ });
+
+ }
+
+
+}
diff --git a/vendor/sabre/vobject/lib/Component/VCard.php b/vendor/sabre/vobject/lib/Component/VCard.php
new file mode 100644
index 000000000..3c05191a9
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VCard.php
@@ -0,0 +1,553 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+use Sabre\Xml;
+
+/**
+ * The VCard component.
+ *
+ * This component represents the BEGIN:VCARD and END:VCARD found in every
+ * vcard.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VCard extends VObject\Document {
+
+ /**
+ * The default name for this component.
+ *
+ * This should be 'VCALENDAR' or 'VCARD'.
+ *
+ * @var string
+ */
+ static $defaultName = 'VCARD';
+
+ /**
+ * Caching the version number.
+ *
+ * @var int
+ */
+ private $version = null;
+
+ /**
+ * This is a list of components, and which classes they should map to.
+ *
+ * @var array
+ */
+ static $componentMap = [
+ 'VCARD' => 'Sabre\\VObject\\Component\\VCard',
+ ];
+
+ /**
+ * List of value-types, and which classes they map to.
+ *
+ * @var array
+ */
+ static $valueMap = [
+ 'BINARY' => 'Sabre\\VObject\\Property\\Binary',
+ 'BOOLEAN' => 'Sabre\\VObject\\Property\\Boolean',
+ 'CONTENT-ID' => 'Sabre\\VObject\\Property\\FlatText', // vCard 2.1 only
+ 'DATE' => 'Sabre\\VObject\\Property\\VCard\\Date',
+ 'DATE-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateTime',
+ 'DATE-AND-OR-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', // vCard only
+ 'FLOAT' => 'Sabre\\VObject\\Property\\FloatValue',
+ 'INTEGER' => 'Sabre\\VObject\\Property\\IntegerValue',
+ 'LANGUAGE-TAG' => 'Sabre\\VObject\\Property\\VCard\\LanguageTag',
+ 'TIMESTAMP' => 'Sabre\\VObject\\Property\\VCard\\TimeStamp',
+ 'TEXT' => 'Sabre\\VObject\\Property\\Text',
+ 'TIME' => 'Sabre\\VObject\\Property\\Time',
+ 'UNKNOWN' => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only.
+ 'URI' => 'Sabre\\VObject\\Property\\Uri',
+ 'URL' => 'Sabre\\VObject\\Property\\Uri', // vCard 2.1 only
+ 'UTC-OFFSET' => 'Sabre\\VObject\\Property\\UtcOffset',
+ ];
+
+ /**
+ * List of properties, and which classes they map to.
+ *
+ * @var array
+ */
+ static $propertyMap = [
+
+ // vCard 2.1 properties and up
+ 'N' => 'Sabre\\VObject\\Property\\Text',
+ 'FN' => 'Sabre\\VObject\\Property\\FlatText',
+ 'PHOTO' => 'Sabre\\VObject\\Property\\Binary',
+ 'BDAY' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
+ 'ADR' => 'Sabre\\VObject\\Property\\Text',
+ 'LABEL' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
+ 'TEL' => 'Sabre\\VObject\\Property\\FlatText',
+ 'EMAIL' => 'Sabre\\VObject\\Property\\FlatText',
+ 'MAILER' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
+ 'GEO' => 'Sabre\\VObject\\Property\\FlatText',
+ 'TITLE' => 'Sabre\\VObject\\Property\\FlatText',
+ 'ROLE' => 'Sabre\\VObject\\Property\\FlatText',
+ 'LOGO' => 'Sabre\\VObject\\Property\\Binary',
+ // 'AGENT' => 'Sabre\\VObject\\Property\\', // Todo: is an embedded vCard. Probably rare, so
+ // not supported at the moment
+ 'ORG' => 'Sabre\\VObject\\Property\\Text',
+ 'NOTE' => 'Sabre\\VObject\\Property\\FlatText',
+ 'REV' => 'Sabre\\VObject\\Property\\VCard\\TimeStamp',
+ 'SOUND' => 'Sabre\\VObject\\Property\\FlatText',
+ 'URL' => 'Sabre\\VObject\\Property\\Uri',
+ 'UID' => 'Sabre\\VObject\\Property\\FlatText',
+ 'VERSION' => 'Sabre\\VObject\\Property\\FlatText',
+ 'KEY' => 'Sabre\\VObject\\Property\\FlatText',
+ 'TZ' => 'Sabre\\VObject\\Property\\Text',
+
+ // vCard 3.0 properties
+ 'CATEGORIES' => 'Sabre\\VObject\\Property\\Text',
+ 'SORT-STRING' => 'Sabre\\VObject\\Property\\FlatText',
+ 'PRODID' => 'Sabre\\VObject\\Property\\FlatText',
+ 'NICKNAME' => 'Sabre\\VObject\\Property\\Text',
+ 'CLASS' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
+
+ // rfc2739 properties
+ 'FBURL' => 'Sabre\\VObject\\Property\\Uri',
+ 'CAPURI' => 'Sabre\\VObject\\Property\\Uri',
+ 'CALURI' => 'Sabre\\VObject\\Property\\Uri',
+ 'CALADRURI' => 'Sabre\\VObject\\Property\\Uri',
+
+ // rfc4770 properties
+ 'IMPP' => 'Sabre\\VObject\\Property\\Uri',
+
+ // vCard 4.0 properties
+ 'SOURCE' => 'Sabre\\VObject\\Property\\Uri',
+ 'XML' => 'Sabre\\VObject\\Property\\FlatText',
+ 'ANNIVERSARY' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
+ 'CLIENTPIDMAP' => 'Sabre\\VObject\\Property\\Text',
+ 'LANG' => 'Sabre\\VObject\\Property\\VCard\\LanguageTag',
+ 'GENDER' => 'Sabre\\VObject\\Property\\Text',
+ 'KIND' => 'Sabre\\VObject\\Property\\FlatText',
+ 'MEMBER' => 'Sabre\\VObject\\Property\\Uri',
+ 'RELATED' => 'Sabre\\VObject\\Property\\Uri',
+
+ // rfc6474 properties
+ 'BIRTHPLACE' => 'Sabre\\VObject\\Property\\FlatText',
+ 'DEATHPLACE' => 'Sabre\\VObject\\Property\\FlatText',
+ 'DEATHDATE' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
+
+ // rfc6715 properties
+ 'EXPERTISE' => 'Sabre\\VObject\\Property\\FlatText',
+ 'HOBBY' => 'Sabre\\VObject\\Property\\FlatText',
+ 'INTEREST' => 'Sabre\\VObject\\Property\\FlatText',
+ 'ORG-DIRECTORY' => 'Sabre\\VObject\\Property\\FlatText',
+
+ ];
+
+ /**
+ * Returns the current document type.
+ *
+ * @return int
+ */
+ function getDocumentType() {
+
+ if (!$this->version) {
+
+ $version = (string)$this->VERSION;
+
+ switch ($version) {
+ case '2.1' :
+ $this->version = self::VCARD21;
+ break;
+ case '3.0' :
+ $this->version = self::VCARD30;
+ break;
+ case '4.0' :
+ $this->version = self::VCARD40;
+ break;
+ default :
+ // We don't want to cache the version if it's unknown,
+ // because we might get a version property in a bit.
+ return self::UNKNOWN;
+ }
+ }
+
+ return $this->version;
+
+ }
+
+ /**
+ * Converts the document to a different vcard version.
+ *
+ * Use one of the VCARD constants for the target. This method will return
+ * a copy of the vcard in the new version.
+ *
+ * At the moment the only supported conversion is from 3.0 to 4.0.
+ *
+ * If input and output version are identical, a clone is returned.
+ *
+ * @param int $target
+ *
+ * @return VCard
+ */
+ function convert($target) {
+
+ $converter = new VObject\VCardConverter();
+ return $converter->convert($this, $target);
+
+ }
+
+ /**
+ * VCards with version 2.1, 3.0 and 4.0 are found.
+ *
+ * If the VCARD doesn't know its version, 2.1 is assumed.
+ */
+ const DEFAULT_VERSION = self::VCARD21;
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ *
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on)
+ * 2 - An inconsequential issue
+ * 3 - A severe issue.
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $warnings = [];
+
+ $versionMap = [
+ self::VCARD21 => '2.1',
+ self::VCARD30 => '3.0',
+ self::VCARD40 => '4.0',
+ ];
+
+ $version = $this->select('VERSION');
+ if (count($version) === 1) {
+ $version = (string)$this->VERSION;
+ if ($version !== '2.1' && $version !== '3.0' && $version !== '4.0') {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.',
+ 'node' => $this,
+ ];
+ if ($options & self::REPAIR) {
+ $this->VERSION = $versionMap[self::DEFAULT_VERSION];
+ }
+ }
+ if ($version === '2.1' && ($options & self::PROFILE_CARDDAV)) {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'CardDAV servers are not allowed to accept vCard 2.1.',
+ 'node' => $this,
+ ];
+ }
+
+ }
+ $uid = $this->select('UID');
+ if (count($uid) === 0) {
+ if ($options & self::PROFILE_CARDDAV) {
+ // Required for CardDAV
+ $warningLevel = 3;
+ $message = 'vCards on CardDAV servers MUST have a UID property.';
+ } else {
+ // Not required for regular vcards
+ $warningLevel = 2;
+ $message = 'Adding a UID to a vCard property is recommended.';
+ }
+ if ($options & self::REPAIR) {
+ $this->UID = VObject\UUIDUtil::getUUID();
+ $warningLevel = 1;
+ }
+ $warnings[] = [
+ 'level' => $warningLevel,
+ 'message' => $message,
+ 'node' => $this,
+ ];
+ }
+
+ $fn = $this->select('FN');
+ if (count($fn) !== 1) {
+
+ $repaired = false;
+ if (($options & self::REPAIR) && count($fn) === 0) {
+ // We're going to try to see if we can use the contents of the
+ // N property.
+ if (isset($this->N)) {
+ $value = explode(';', (string)$this->N);
+ if (isset($value[1]) && $value[1]) {
+ $this->FN = $value[1] . ' ' . $value[0];
+ } else {
+ $this->FN = $value[0];
+ }
+ $repaired = true;
+
+ // Otherwise, the ORG property may work
+ } elseif (isset($this->ORG)) {
+ $this->FN = (string)$this->ORG;
+ $repaired = true;
+ }
+
+ }
+ $warnings[] = [
+ 'level' => $repaired ? 1 : 3,
+ 'message' => 'The FN property must appear in the VCARD component exactly 1 time',
+ 'node' => $this,
+ ];
+ }
+
+ return array_merge(
+ parent::validate($options),
+ $warnings
+ );
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'ADR' => '*',
+ 'ANNIVERSARY' => '?',
+ 'BDAY' => '?',
+ 'CALADRURI' => '*',
+ 'CALURI' => '*',
+ 'CATEGORIES' => '*',
+ 'CLIENTPIDMAP' => '*',
+ 'EMAIL' => '*',
+ 'FBURL' => '*',
+ 'IMPP' => '*',
+ 'GENDER' => '?',
+ 'GEO' => '*',
+ 'KEY' => '*',
+ 'KIND' => '?',
+ 'LANG' => '*',
+ 'LOGO' => '*',
+ 'MEMBER' => '*',
+ 'N' => '?',
+ 'NICKNAME' => '*',
+ 'NOTE' => '*',
+ 'ORG' => '*',
+ 'PHOTO' => '*',
+ 'PRODID' => '?',
+ 'RELATED' => '*',
+ 'REV' => '?',
+ 'ROLE' => '*',
+ 'SOUND' => '*',
+ 'SOURCE' => '*',
+ 'TEL' => '*',
+ 'TITLE' => '*',
+ 'TZ' => '*',
+ 'URL' => '*',
+ 'VERSION' => '1',
+ 'XML' => '*',
+
+ // FN is commented out, because it's already handled by the
+ // validate function, which may also try to repair it.
+ // 'FN' => '+',
+ 'UID' => '?',
+ ];
+
+ }
+
+ /**
+ * Returns a preferred field.
+ *
+ * VCards can indicate wether a field such as ADR, TEL or EMAIL is
+ * preferred by specifying TYPE=PREF (vcard 2.1, 3) or PREF=x (vcard 4, x
+ * being a number between 1 and 100).
+ *
+ * If neither of those parameters are specified, the first is returned, if
+ * a field with that name does not exist, null is returned.
+ *
+ * @param string $fieldName
+ *
+ * @return VObject\Property|null
+ */
+ function preferred($propertyName) {
+
+ $preferred = null;
+ $lastPref = 101;
+ foreach ($this->select($propertyName) as $field) {
+
+ $pref = 101;
+ if (isset($field['TYPE']) && $field['TYPE']->has('PREF')) {
+ $pref = 1;
+ } elseif (isset($field['PREF'])) {
+ $pref = $field['PREF']->getValue();
+ }
+
+ if ($pref < $lastPref || is_null($preferred)) {
+ $preferred = $field;
+ $lastPref = $pref;
+ }
+
+ }
+ return $preferred;
+
+ }
+
+ /**
+ * Returns a property with a specific TYPE value (ADR, TEL, or EMAIL).
+ *
+ * This function will return null if the property does not exist. If there are
+ * multiple properties with the same TYPE value, only one will be returned.
+ *
+ * @param string $propertyName
+ * @param string $type
+ *
+ * @return VObject\Property|null
+ */
+ function getByType($propertyName, $type) {
+ foreach ($this->select($propertyName) as $field) {
+ if (isset($field['TYPE']) && $field['TYPE']->has($type)) {
+ return $field;
+ }
+ }
+ }
+
+ /**
+ * This method should return a list of default property values.
+ *
+ * @return array
+ */
+ protected function getDefaults() {
+
+ return [
+ 'VERSION' => '4.0',
+ 'PRODID' => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN',
+ 'UID' => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(),
+ ];
+
+ }
+
+ /**
+ * This method returns an array, with the representation as it should be
+ * encoded in json. This is used to create jCard or jCal documents.
+ *
+ * @return array
+ */
+ function jsonSerialize() {
+
+ // A vcard does not have sub-components, so we're overriding this
+ // method to remove that array element.
+ $properties = [];
+
+ foreach ($this->children() as $child) {
+ $properties[] = $child->jsonSerialize();
+ }
+
+ return [
+ strtolower($this->name),
+ $properties,
+ ];
+
+ }
+
+ /**
+ * This method serializes the data into XML. This is used to create xCard or
+ * xCal documents.
+ *
+ * @param Xml\Writer $writer XML writer.
+ *
+ * @return void
+ */
+ function xmlSerialize(Xml\Writer $writer) {
+
+ $propertiesByGroup = [];
+
+ foreach ($this->children() as $property) {
+
+ $group = $property->group;
+
+ if (!isset($propertiesByGroup[$group])) {
+ $propertiesByGroup[$group] = [];
+ }
+
+ $propertiesByGroup[$group][] = $property;
+
+ }
+
+ $writer->startElement(strtolower($this->name));
+
+ foreach ($propertiesByGroup as $group => $properties) {
+
+ if (!empty($group)) {
+
+ $writer->startElement('group');
+ $writer->writeAttribute('name', strtolower($group));
+
+ }
+
+ foreach ($properties as $property) {
+ switch ($property->name) {
+
+ case 'VERSION':
+ continue;
+
+ case 'XML':
+ $value = $property->getParts();
+ $fragment = new Xml\Element\XmlFragment($value[0]);
+ $writer->write($fragment);
+ break;
+
+ default:
+ $property->xmlSerialize($writer);
+ break;
+
+ }
+ }
+
+ if (!empty($group)) {
+ $writer->endElement();
+ }
+
+ }
+
+ $writer->endElement();
+
+ }
+
+ /**
+ * Returns the default class for a property name.
+ *
+ * @param string $propertyName
+ *
+ * @return string
+ */
+ function getClassNameForPropertyName($propertyName) {
+
+ $className = parent::getClassNameForPropertyName($propertyName);
+
+ // In vCard 4, BINARY no longer exists, and we need URI instead.
+ if ($className == 'Sabre\\VObject\\Property\\Binary' && $this->getDocumentType() === self::VCARD40) {
+ return 'Sabre\\VObject\\Property\\Uri';
+ }
+ return $className;
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Component/VEvent.php b/vendor/sabre/vobject/lib/Component/VEvent.php
new file mode 100644
index 000000000..7f6861190
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VEvent.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use DateTimeInterface;
+use Sabre\VObject;
+use Sabre\VObject\Recur\EventIterator;
+use Sabre\VObject\Recur\NoInstancesException;
+
+/**
+ * VEvent component.
+ *
+ * This component contains some additional functionality specific for VEVENT's.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VEvent extends VObject\Component {
+
+ /**
+ * Returns true or false depending on if the event falls in the specified
+ * time-range. This is used for filtering purposes.
+ *
+ * The rules used to determine if an event falls within the specified
+ * time-range is based on the CalDAV specification.
+ *
+ * @param DateTimeInterface $start
+ * @param DateTimeInterface $end
+ *
+ * @return bool
+ */
+ function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) {
+
+ if ($this->RRULE) {
+
+ try {
+
+ $it = new EventIterator($this, null, $start->getTimezone());
+
+ } catch (NoInstancesException $e) {
+
+ // If we've catched this exception, there are no instances
+ // for the event that fall into the specified time-range.
+ return false;
+
+ }
+
+ $it->fastForward($start);
+
+ // We fast-forwarded to a spot where the end-time of the
+ // recurrence instance exceeded the start of the requested
+ // time-range.
+ //
+ // If the starttime of the recurrence did not exceed the
+ // end of the time range as well, we have a match.
+ return ($it->getDTStart() < $end && $it->getDTEnd() > $start);
+
+ }
+
+ $effectiveStart = $this->DTSTART->getDateTime($start->getTimezone());
+ if (isset($this->DTEND)) {
+
+ // The DTEND property is considered non inclusive. So for a 3 day
+ // event in july, dtstart and dtend would have to be July 1st and
+ // July 4th respectively.
+ //
+ // See:
+ // http://tools.ietf.org/html/rfc5545#page-54
+ $effectiveEnd = $this->DTEND->getDateTime($end->getTimezone());
+
+ } elseif (isset($this->DURATION)) {
+ $effectiveEnd = $effectiveStart->add(VObject\DateTimeParser::parseDuration($this->DURATION));
+ } elseif (!$this->DTSTART->hasTime()) {
+ $effectiveEnd = $effectiveStart->modify('+1 day');
+ } else {
+ $effectiveEnd = $effectiveStart;
+ }
+ return (
+ ($start < $effectiveEnd) && ($end > $effectiveStart)
+ );
+
+ }
+
+ /**
+ * This method should return a list of default property values.
+ *
+ * @return array
+ */
+ protected function getDefaults() {
+
+ return [
+ 'UID' => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(),
+ 'DTSTAMP' => date('Ymd\\THis\\Z'),
+ ];
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ $hasMethod = isset($this->parent->METHOD);
+ return [
+ 'UID' => 1,
+ 'DTSTAMP' => 1,
+ 'DTSTART' => $hasMethod ? '?' : '1',
+ 'CLASS' => '?',
+ 'CREATED' => '?',
+ 'DESCRIPTION' => '?',
+ 'GEO' => '?',
+ 'LAST-MODIFIED' => '?',
+ 'LOCATION' => '?',
+ 'ORGANIZER' => '?',
+ 'PRIORITY' => '?',
+ 'SEQUENCE' => '?',
+ 'STATUS' => '?',
+ 'SUMMARY' => '?',
+ 'TRANSP' => '?',
+ 'URL' => '?',
+ 'RECURRENCE-ID' => '?',
+ 'RRULE' => '?',
+ 'DTEND' => '?',
+ 'DURATION' => '?',
+
+ 'ATTACH' => '*',
+ 'ATTENDEE' => '*',
+ 'CATEGORIES' => '*',
+ 'COMMENT' => '*',
+ 'CONTACT' => '*',
+ 'EXDATE' => '*',
+ 'REQUEST-STATUS' => '*',
+ 'RELATED-TO' => '*',
+ 'RESOURCES' => '*',
+ 'RDATE' => '*',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Component/VFreeBusy.php b/vendor/sabre/vobject/lib/Component/VFreeBusy.php
new file mode 100644
index 000000000..72294cc9f
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VFreeBusy.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use DateTimeInterface;
+use Sabre\VObject;
+
+/**
+ * The VFreeBusy component.
+ *
+ * This component adds functionality to a component, specific for VFREEBUSY
+ * components.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VFreeBusy extends VObject\Component {
+
+ /**
+ * Checks based on the contained FREEBUSY information, if a timeslot is
+ * available.
+ *
+ * @param DateTimeInterface $start
+ * @param DateTimeInterface $end
+ *
+ * @return bool
+ */
+ function isFree(DateTimeInterface $start, DatetimeInterface $end) {
+
+ foreach ($this->select('FREEBUSY') as $freebusy) {
+
+ // We are only interested in FBTYPE=BUSY (the default),
+ // FBTYPE=BUSY-TENTATIVE or FBTYPE=BUSY-UNAVAILABLE.
+ if (isset($freebusy['FBTYPE']) && strtoupper(substr((string)$freebusy['FBTYPE'], 0, 4)) !== 'BUSY') {
+ continue;
+ }
+
+ // The freebusy component can hold more than 1 value, separated by
+ // commas.
+ $periods = explode(',', (string)$freebusy);
+
+ foreach ($periods as $period) {
+ // Every period is formatted as [start]/[end]. The start is an
+ // absolute UTC time, the end may be an absolute UTC time, or
+ // duration (relative) value.
+ list($busyStart, $busyEnd) = explode('/', $period);
+
+ $busyStart = VObject\DateTimeParser::parse($busyStart);
+ $busyEnd = VObject\DateTimeParser::parse($busyEnd);
+ if ($busyEnd instanceof \DateInterval) {
+ $busyEnd = $busyStart->add($busyEnd);
+ }
+
+ if ($start < $busyEnd && $end > $busyStart) {
+ return false;
+ }
+
+ }
+
+ }
+
+ return true;
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'UID' => 1,
+ 'DTSTAMP' => 1,
+
+ 'CONTACT' => '?',
+ 'DTSTART' => '?',
+ 'DTEND' => '?',
+ 'ORGANIZER' => '?',
+ 'URL' => '?',
+
+ 'ATTENDEE' => '*',
+ 'COMMENT' => '*',
+ 'FREEBUSY' => '*',
+ 'REQUEST-STATUS' => '*',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Component/VJournal.php b/vendor/sabre/vobject/lib/Component/VJournal.php
new file mode 100644
index 000000000..a1b1a863d
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VJournal.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use DateTimeInterface;
+use Sabre\VObject;
+
+/**
+ * VJournal component.
+ *
+ * This component contains some additional functionality specific for VJOURNALs.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VJournal extends VObject\Component {
+
+ /**
+ * Returns true or false depending on if the event falls in the specified
+ * time-range. This is used for filtering purposes.
+ *
+ * The rules used to determine if an event falls within the specified
+ * time-range is based on the CalDAV specification.
+ *
+ * @param DateTimeInterface $start
+ * @param DateTimeInterface $end
+ *
+ * @return bool
+ */
+ function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) {
+
+ $dtstart = isset($this->DTSTART) ? $this->DTSTART->getDateTime() : null;
+ if ($dtstart) {
+ $effectiveEnd = $dtstart;
+ if (!$this->DTSTART->hasTime()) {
+ $effectiveEnd = $effectiveEnd->modify('+1 day');
+ }
+
+ return ($start <= $effectiveEnd && $end > $dtstart);
+
+ }
+ return false;
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'UID' => 1,
+ 'DTSTAMP' => 1,
+
+ 'CLASS' => '?',
+ 'CREATED' => '?',
+ 'DTSTART' => '?',
+ 'LAST-MODIFIED' => '?',
+ 'ORGANIZER' => '?',
+ 'RECURRENCE-ID' => '?',
+ 'SEQUENCE' => '?',
+ 'STATUS' => '?',
+ 'SUMMARY' => '?',
+ 'URL' => '?',
+
+ 'RRULE' => '?',
+
+ 'ATTACH' => '*',
+ 'ATTENDEE' => '*',
+ 'CATEGORIES' => '*',
+ 'COMMENT' => '*',
+ 'CONTACT' => '*',
+ 'DESCRIPTION' => '*',
+ 'EXDATE' => '*',
+ 'RELATED-TO' => '*',
+ 'RDATE' => '*',
+ ];
+
+ }
+
+ /**
+ * This method should return a list of default property values.
+ *
+ * @return array
+ */
+ protected function getDefaults() {
+
+ return [
+ 'UID' => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(),
+ 'DTSTAMP' => date('Ymd\\THis\\Z'),
+ ];
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Component/VTimeZone.php b/vendor/sabre/vobject/lib/Component/VTimeZone.php
new file mode 100644
index 000000000..d5d886947
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VTimeZone.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+
+/**
+ * The VTimeZone component.
+ *
+ * This component adds functionality to a component, specific for VTIMEZONE
+ * components.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VTimeZone extends VObject\Component {
+
+ /**
+ * Returns the PHP DateTimeZone for this VTIMEZONE component.
+ *
+ * If we can't accurately determine the timezone, this method will return
+ * UTC.
+ *
+ * @return \DateTimeZone
+ */
+ function getTimeZone() {
+
+ return VObject\TimeZoneUtil::getTimeZone((string)$this->TZID, $this->root);
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'TZID' => 1,
+
+ 'LAST-MODIFIED' => '?',
+ 'TZURL' => '?',
+
+ // At least 1 STANDARD or DAYLIGHT must appear, or more. But both
+ // cannot appear in the same VTIMEZONE.
+ //
+ // The validator is not specific yet to pick this up, so these
+ // rules are too loose.
+ 'STANDARD' => '*',
+ 'DAYLIGHT' => '*',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Component/VTodo.php b/vendor/sabre/vobject/lib/Component/VTodo.php
new file mode 100644
index 000000000..144ced694
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VTodo.php
@@ -0,0 +1,193 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use DateTimeInterface;
+use Sabre\VObject;
+
+/**
+ * VTodo component.
+ *
+ * This component contains some additional functionality specific for VTODOs.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VTodo extends VObject\Component {
+
+ /**
+ * Returns true or false depending on if the event falls in the specified
+ * time-range. This is used for filtering purposes.
+ *
+ * The rules used to determine if an event falls within the specified
+ * time-range is based on the CalDAV specification.
+ *
+ * @param DateTimeInterface $start
+ * @param DateTimeInterface $end
+ *
+ * @return bool
+ */
+ function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) {
+
+ $dtstart = isset($this->DTSTART) ? $this->DTSTART->getDateTime() : null;
+ $duration = isset($this->DURATION) ? VObject\DateTimeParser::parseDuration($this->DURATION) : null;
+ $due = isset($this->DUE) ? $this->DUE->getDateTime() : null;
+ $completed = isset($this->COMPLETED) ? $this->COMPLETED->getDateTime() : null;
+ $created = isset($this->CREATED) ? $this->CREATED->getDateTime() : null;
+
+ if ($dtstart) {
+ if ($duration) {
+ $effectiveEnd = $dtstart->add($duration);
+ return $start <= $effectiveEnd && $end > $dtstart;
+ } elseif ($due) {
+ return
+ ($start < $due || $start <= $dtstart) &&
+ ($end > $dtstart || $end >= $due);
+ } else {
+ return $start <= $dtstart && $end > $dtstart;
+ }
+ }
+ if ($due) {
+ return ($start < $due && $end >= $due);
+ }
+ if ($completed && $created) {
+ return
+ ($start <= $created || $start <= $completed) &&
+ ($end >= $created || $end >= $completed);
+ }
+ if ($completed) {
+ return ($start <= $completed && $end >= $completed);
+ }
+ if ($created) {
+ return ($end > $created);
+ }
+ return true;
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'UID' => 1,
+ 'DTSTAMP' => 1,
+
+ 'CLASS' => '?',
+ 'COMPLETED' => '?',
+ 'CREATED' => '?',
+ 'DESCRIPTION' => '?',
+ 'DTSTART' => '?',
+ 'GEO' => '?',
+ 'LAST-MODIFIED' => '?',
+ 'LOCATION' => '?',
+ 'ORGANIZER' => '?',
+ 'PERCENT' => '?',
+ 'PRIORITY' => '?',
+ 'RECURRENCE-ID' => '?',
+ 'SEQUENCE' => '?',
+ 'STATUS' => '?',
+ 'SUMMARY' => '?',
+ 'URL' => '?',
+
+ 'RRULE' => '?',
+ 'DUE' => '?',
+ 'DURATION' => '?',
+
+ 'ATTACH' => '*',
+ 'ATTENDEE' => '*',
+ 'CATEGORIES' => '*',
+ 'COMMENT' => '*',
+ 'CONTACT' => '*',
+ 'EXDATE' => '*',
+ 'REQUEST-STATUS' => '*',
+ 'RELATED-TO' => '*',
+ 'RESOURCES' => '*',
+ 'RDATE' => '*',
+ ];
+
+ }
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ *
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on)
+ * 2 - An inconsequential issue
+ * 3 - A severe issue.
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $result = parent::validate($options);
+ if (isset($this->DUE) && isset($this->DTSTART)) {
+
+ $due = $this->DUE;
+ $dtStart = $this->DTSTART;
+
+ if ($due->getValueType() !== $dtStart->getValueType()) {
+
+ $result[] = [
+ 'level' => 3,
+ 'message' => 'The value type (DATE or DATE-TIME) must be identical for DUE and DTSTART',
+ 'node' => $due,
+ ];
+
+ } elseif ($due->getDateTime() < $dtStart->getDateTime()) {
+
+ $result[] = [
+ 'level' => 3,
+ 'message' => 'DUE must occur after DTSTART',
+ 'node' => $due,
+ ];
+
+ }
+
+ }
+
+ return $result;
+
+ }
+
+ /**
+ * This method should return a list of default property values.
+ *
+ * @return array
+ */
+ protected function getDefaults() {
+
+ return [
+ 'UID' => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(),
+ 'DTSTAMP' => date('Ymd\\THis\\Z'),
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/DateTimeParser.php b/vendor/sabre/vobject/lib/DateTimeParser.php
new file mode 100644
index 000000000..fc568abb0
--- /dev/null
+++ b/vendor/sabre/vobject/lib/DateTimeParser.php
@@ -0,0 +1,571 @@
+<?php
+
+namespace Sabre\VObject;
+
+use DateTimeImmutable;
+use DateTimeZone;
+use DateInterval;
+
+/**
+ * DateTimeParser.
+ *
+ * This class is responsible for parsing the several different date and time
+ * formats iCalendar and vCards have.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class DateTimeParser {
+
+ /**
+ * Parses an iCalendar (rfc5545) formatted datetime and returns a
+ * DateTimeImmutable object.
+ *
+ * Specifying a reference timezone is optional. It will only be used
+ * if the non-UTC format is used. The argument is used as a reference, the
+ * returned DateTimeImmutable object will still be in the UTC timezone.
+ *
+ * @param string $dt
+ * @param DateTimeZone $tz
+ *
+ * @return DateTimeImmutable
+ */
+ static function parseDateTime($dt, DateTimeZone $tz = null) {
+
+ // Format is YYYYMMDD + "T" + hhmmss
+ $result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/', $dt, $matches);
+
+ if (!$result) {
+ throw new InvalidDataException('The supplied iCalendar datetime value is incorrect: ' . $dt);
+ }
+
+ if ($matches[7] === 'Z' || is_null($tz)) {
+ $tz = new DateTimeZone('UTC');
+ }
+ $date = new DateTimeImmutable($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] . ':' . $matches[6], $tz);
+
+ return $date;
+
+ }
+
+ /**
+ * Parses an iCalendar (rfc5545) formatted date and returns a DateTimeImmutable object.
+ *
+ * @param string $date
+ * @param DateTimeZone $tz
+ *
+ * @return DateTimeImmutable
+ */
+ static function parseDate($date, DateTimeZone $tz = null) {
+
+ // Format is YYYYMMDD
+ $result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])$/', $date, $matches);
+
+ if (!$result) {
+ throw new InvalidDataException('The supplied iCalendar date value is incorrect: ' . $date);
+ }
+
+ if (is_null($tz)) {
+ $tz = new DateTimeZone('UTC');
+ }
+
+ $date = new DateTimeImmutable($matches[1] . '-' . $matches[2] . '-' . $matches[3], $tz);
+
+ return $date;
+
+ }
+
+ /**
+ * Parses an iCalendar (RFC5545) formatted duration value.
+ *
+ * This method will either return a DateTimeInterval object, or a string
+ * suitable for strtotime or DateTime::modify.
+ *
+ * @param string $duration
+ * @param bool $asString
+ *
+ * @return DateInterval|string
+ */
+ static function parseDuration($duration, $asString = false) {
+
+ $result = preg_match('/^(?<plusminus>\+|-)?P((?<week>\d+)W)?((?<day>\d+)D)?(T((?<hour>\d+)H)?((?<minute>\d+)M)?((?<second>\d+)S)?)?$/', $duration, $matches);
+ if (!$result) {
+ throw new InvalidDataException('The supplied iCalendar duration value is incorrect: ' . $duration);
+ }
+
+ if (!$asString) {
+
+ $invert = false;
+
+ if ($matches['plusminus'] === '-') {
+ $invert = true;
+ }
+
+ $parts = [
+ 'week',
+ 'day',
+ 'hour',
+ 'minute',
+ 'second',
+ ];
+
+ foreach ($parts as $part) {
+ $matches[$part] = isset($matches[$part]) && $matches[$part] ? (int)$matches[$part] : 0;
+ }
+
+ // We need to re-construct the $duration string, because weeks and
+ // days are not supported by DateInterval in the same string.
+ $duration = 'P';
+ $days = $matches['day'];
+
+ if ($matches['week']) {
+ $days += $matches['week'] * 7;
+ }
+
+ if ($days) {
+ $duration .= $days . 'D';
+ }
+
+ if ($matches['minute'] || $matches['second'] || $matches['hour']) {
+
+ $duration .= 'T';
+
+ if ($matches['hour']) {
+ $duration .= $matches['hour'] . 'H';
+ }
+
+ if ($matches['minute']) {
+ $duration .= $matches['minute'] . 'M';
+ }
+
+ if ($matches['second']) {
+ $duration .= $matches['second'] . 'S';
+ }
+
+ }
+
+ if ($duration === 'P') {
+ $duration = 'PT0S';
+ }
+
+ $iv = new DateInterval($duration);
+
+ if ($invert) {
+ $iv->invert = true;
+ }
+
+ return $iv;
+
+ }
+
+ $parts = [
+ 'week',
+ 'day',
+ 'hour',
+ 'minute',
+ 'second',
+ ];
+
+ $newDur = '';
+
+ foreach ($parts as $part) {
+ if (isset($matches[$part]) && $matches[$part]) {
+ $newDur .= ' ' . $matches[$part] . ' ' . $part . 's';
+ }
+ }
+
+ $newDur = ($matches['plusminus'] === '-' ? '-' : '+') . trim($newDur);
+
+ if ($newDur === '+') {
+ $newDur = '+0 seconds';
+ };
+
+ return $newDur;
+
+ }
+
+ /**
+ * Parses either a Date or DateTime, or Duration value.
+ *
+ * @param string $date
+ * @param DateTimeZone|string $referenceTz
+ *
+ * @return DateTimeImmutable|DateInterval
+ */
+ static function parse($date, $referenceTz = null) {
+
+ if ($date[0] === 'P' || ($date[0] === '-' && $date[1] === 'P')) {
+ return self::parseDuration($date);
+ } elseif (strlen($date) === 8) {
+ return self::parseDate($date, $referenceTz);
+ } else {
+ return self::parseDateTime($date, $referenceTz);
+ }
+
+ }
+
+ /**
+ * This method parses a vCard date and or time value.
+ *
+ * This can be used for the DATE, DATE-TIME, TIMESTAMP and
+ * DATE-AND-OR-TIME value.
+ *
+ * This method returns an array, not a DateTime value.
+ *
+ * The elements in the array are in the following order:
+ * year, month, date, hour, minute, second, timezone
+ *
+ * Almost any part of the string may be omitted. It's for example legal to
+ * just specify seconds, leave out the year, etc.
+ *
+ * Timezone is either returned as 'Z' or as '+0800'
+ *
+ * For any non-specified values null is returned.
+ *
+ * List of date formats that are supported:
+ * YYYY
+ * YYYY-MM
+ * YYYYMMDD
+ * --MMDD
+ * ---DD
+ *
+ * YYYY-MM-DD
+ * --MM-DD
+ * ---DD
+ *
+ * List of supported time formats:
+ *
+ * HH
+ * HHMM
+ * HHMMSS
+ * -MMSS
+ * --SS
+ *
+ * HH
+ * HH:MM
+ * HH:MM:SS
+ * -MM:SS
+ * --SS
+ *
+ * A full basic-format date-time string looks like :
+ * 20130603T133901
+ *
+ * A full extended-format date-time string looks like :
+ * 2013-06-03T13:39:01
+ *
+ * Times may be postfixed by a timezone offset. This can be either 'Z' for
+ * UTC, or a string like -0500 or +1100.
+ *
+ * @param string $date
+ *
+ * @return array
+ */
+ static function parseVCardDateTime($date) {
+
+ $regex = '/^
+ (?: # date part
+ (?:
+ (?: (?<year> [0-9]{4}) (?: -)?| --)
+ (?<month> [0-9]{2})?
+ |---)
+ (?<date> [0-9]{2})?
+ )?
+ (?:T # time part
+ (?<hour> [0-9]{2} | -)
+ (?<minute> [0-9]{2} | -)?
+ (?<second> [0-9]{2})?
+
+ (?: \.[0-9]{3})? # milliseconds
+ (?P<timezone> # timezone offset
+
+ Z | (?: \+|-)(?: [0-9]{4})
+
+ )?
+
+ )?
+ $/x';
+
+ if (!preg_match($regex, $date, $matches)) {
+
+ // Attempting to parse the extended format.
+ $regex = '/^
+ (?: # date part
+ (?: (?<year> [0-9]{4}) - | -- )
+ (?<month> [0-9]{2}) -
+ (?<date> [0-9]{2})
+ )?
+ (?:T # time part
+
+ (?: (?<hour> [0-9]{2}) : | -)
+ (?: (?<minute> [0-9]{2}) : | -)?
+ (?<second> [0-9]{2})?
+
+ (?: \.[0-9]{3})? # milliseconds
+ (?P<timezone> # timezone offset
+
+ Z | (?: \+|-)(?: [0-9]{2}:[0-9]{2})
+
+ )?
+
+ )?
+ $/x';
+
+ if (!preg_match($regex, $date, $matches)) {
+ throw new InvalidDataException('Invalid vCard date-time string: ' . $date);
+ }
+
+ }
+ $parts = [
+ 'year',
+ 'month',
+ 'date',
+ 'hour',
+ 'minute',
+ 'second',
+ 'timezone'
+ ];
+
+ $result = [];
+ foreach ($parts as $part) {
+
+ if (empty($matches[$part])) {
+ $result[$part] = null;
+ } elseif ($matches[$part] === '-' || $matches[$part] === '--') {
+ $result[$part] = null;
+ } else {
+ $result[$part] = $matches[$part];
+ }
+
+ }
+
+ return $result;
+
+ }
+
+ /**
+ * This method parses a vCard TIME value.
+ *
+ * This method returns an array, not a DateTime value.
+ *
+ * The elements in the array are in the following order:
+ * hour, minute, second, timezone
+ *
+ * Almost any part of the string may be omitted. It's for example legal to
+ * just specify seconds, leave out the hour etc.
+ *
+ * Timezone is either returned as 'Z' or as '+08:00'
+ *
+ * For any non-specified values null is returned.
+ *
+ * List of supported time formats:
+ *
+ * HH
+ * HHMM
+ * HHMMSS
+ * -MMSS
+ * --SS
+ *
+ * HH
+ * HH:MM
+ * HH:MM:SS
+ * -MM:SS
+ * --SS
+ *
+ * A full basic-format time string looks like :
+ * 133901
+ *
+ * A full extended-format time string looks like :
+ * 13:39:01
+ *
+ * Times may be postfixed by a timezone offset. This can be either 'Z' for
+ * UTC, or a string like -0500 or +11:00.
+ *
+ * @param string $date
+ *
+ * @return array
+ */
+ static function parseVCardTime($date) {
+
+ $regex = '/^
+ (?<hour> [0-9]{2} | -)
+ (?<minute> [0-9]{2} | -)?
+ (?<second> [0-9]{2})?
+
+ (?: \.[0-9]{3})? # milliseconds
+ (?P<timezone> # timezone offset
+
+ Z | (?: \+|-)(?: [0-9]{4})
+
+ )?
+ $/x';
+
+
+ if (!preg_match($regex, $date, $matches)) {
+
+ // Attempting to parse the extended format.
+ $regex = '/^
+ (?: (?<hour> [0-9]{2}) : | -)
+ (?: (?<minute> [0-9]{2}) : | -)?
+ (?<second> [0-9]{2})?
+
+ (?: \.[0-9]{3})? # milliseconds
+ (?P<timezone> # timezone offset
+
+ Z | (?: \+|-)(?: [0-9]{2}:[0-9]{2})
+
+ )?
+ $/x';
+
+ if (!preg_match($regex, $date, $matches)) {
+ throw new InvalidDataException('Invalid vCard time string: ' . $date);
+ }
+
+ }
+ $parts = [
+ 'hour',
+ 'minute',
+ 'second',
+ 'timezone'
+ ];
+
+ $result = [];
+ foreach ($parts as $part) {
+
+ if (empty($matches[$part])) {
+ $result[$part] = null;
+ } elseif ($matches[$part] === '-') {
+ $result[$part] = null;
+ } else {
+ $result[$part] = $matches[$part];
+ }
+
+ }
+
+ return $result;
+
+ }
+
+ /**
+ * This method parses a vCard date and or time value.
+ *
+ * This can be used for the DATE, DATE-TIME and
+ * DATE-AND-OR-TIME value.
+ *
+ * This method returns an array, not a DateTime value.
+ * The elements in the array are in the following order:
+ * year, month, date, hour, minute, second, timezone
+ * Almost any part of the string may be omitted. It's for example legal to
+ * just specify seconds, leave out the year, etc.
+ *
+ * Timezone is either returned as 'Z' or as '+0800'
+ *
+ * For any non-specified values null is returned.
+ *
+ * List of date formats that are supported:
+ * 20150128
+ * 2015-01
+ * --01
+ * --0128
+ * ---28
+ *
+ * List of supported time formats:
+ * 13
+ * 1353
+ * 135301
+ * -53
+ * -5301
+ * --01 (unreachable, see the tests)
+ * --01Z
+ * --01+1234
+ *
+ * List of supported date-time formats:
+ * 20150128T13
+ * --0128T13
+ * ---28T13
+ * ---28T1353
+ * ---28T135301
+ * ---28T13Z
+ * ---28T13+1234
+ *
+ * See the regular expressions for all the possible patterns.
+ *
+ * Times may be postfixed by a timezone offset. This can be either 'Z' for
+ * UTC, or a string like -0500 or +1100.
+ *
+ * @param string $date
+ *
+ * @return array
+ */
+ static function parseVCardDateAndOrTime($date) {
+
+ // \d{8}|\d{4}-\d\d|--\d\d(\d\d)?|---\d\d
+ $valueDate = '/^(?J)(?:' .
+ '(?<year>\d{4})(?<month>\d\d)(?<date>\d\d)' .
+ '|(?<year>\d{4})-(?<month>\d\d)' .
+ '|--(?<month>\d\d)(?<date>\d\d)?' .
+ '|---(?<date>\d\d)' .
+ ')$/';
+
+ // (\d\d(\d\d(\d\d)?)?|-\d\d(\d\d)?|--\d\d)(Z|[+\-]\d\d(\d\d)?)?
+ $valueTime = '/^(?J)(?:' .
+ '((?<hour>\d\d)((?<minute>\d\d)(?<second>\d\d)?)?' .
+ '|-(?<minute>\d\d)(?<second>\d\d)?' .
+ '|--(?<second>\d\d))' .
+ '(?<timezone>(Z|[+\-]\d\d(\d\d)?))?' .
+ ')$/';
+
+ // (\d{8}|--\d{4}|---\d\d)T\d\d(\d\d(\d\d)?)?(Z|[+\-]\d\d(\d\d?)?
+ $valueDateTime = '/^(?:' .
+ '((?<year0>\d{4})(?<month0>\d\d)(?<date0>\d\d)' .
+ '|--(?<month1>\d\d)(?<date1>\d\d)' .
+ '|---(?<date2>\d\d))' .
+ 'T' .
+ '(?<hour>\d\d)((?<minute>\d\d)(?<second>\d\d)?)?' .
+ '(?<timezone>(Z|[+\-]\d\d(\d\d?)))?' .
+ ')$/';
+
+ // date-and-or-time is date | date-time | time
+ // in this strict order.
+
+ if (0 === preg_match($valueDate, $date, $matches)
+ && 0 === preg_match($valueDateTime, $date, $matches)
+ && 0 === preg_match($valueTime, $date, $matches)) {
+ throw new InvalidDataException('Invalid vCard date-time string: ' . $date);
+ }
+
+ $parts = [
+ 'year' => null,
+ 'month' => null,
+ 'date' => null,
+ 'hour' => null,
+ 'minute' => null,
+ 'second' => null,
+ 'timezone' => null
+ ];
+
+ // The $valueDateTime expression has a bug with (?J) so we simulate it.
+ $parts['date0'] = &$parts['date'];
+ $parts['date1'] = &$parts['date'];
+ $parts['date2'] = &$parts['date'];
+ $parts['month0'] = &$parts['month'];
+ $parts['month1'] = &$parts['month'];
+ $parts['year0'] = &$parts['year'];
+
+ foreach ($parts as $part => &$value) {
+ if (!empty($matches[$part])) {
+ $value = $matches[$part];
+ }
+ }
+
+ unset($parts['date0']);
+ unset($parts['date1']);
+ unset($parts['date2']);
+ unset($parts['month0']);
+ unset($parts['month1']);
+ unset($parts['year0']);
+
+ return $parts;
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Document.php b/vendor/sabre/vobject/lib/Document.php
new file mode 100644
index 000000000..03252ab06
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Document.php
@@ -0,0 +1,270 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * Document.
+ *
+ * A document is just like a component, except that it's also the top level
+ * element.
+ *
+ * Both a VCALENDAR and a VCARD are considered documents.
+ *
+ * This class also provides a registry for document types.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class Document extends Component {
+
+ /**
+ * Unknown document type.
+ */
+ const UNKNOWN = 1;
+
+ /**
+ * vCalendar 1.0.
+ */
+ const VCALENDAR10 = 2;
+
+ /**
+ * iCalendar 2.0.
+ */
+ const ICALENDAR20 = 3;
+
+ /**
+ * vCard 2.1.
+ */
+ const VCARD21 = 4;
+
+ /**
+ * vCard 3.0.
+ */
+ const VCARD30 = 5;
+
+ /**
+ * vCard 4.0.
+ */
+ const VCARD40 = 6;
+
+ /**
+ * The default name for this component.
+ *
+ * This should be 'VCALENDAR' or 'VCARD'.
+ *
+ * @var string
+ */
+ static $defaultName;
+
+ /**
+ * List of properties, and which classes they map to.
+ *
+ * @var array
+ */
+ static $propertyMap = [];
+
+ /**
+ * List of components, along with which classes they map to.
+ *
+ * @var array
+ */
+ static $componentMap = [];
+
+ /**
+ * List of value-types, and which classes they map to.
+ *
+ * @var array
+ */
+ static $valueMap = [];
+
+ /**
+ * Creates a new document.
+ *
+ * We're changing the default behavior slightly here. First, we don't want
+ * to have to specify a name (we already know it), and we want to allow
+ * children to be specified in the first argument.
+ *
+ * But, the default behavior also works.
+ *
+ * So the two sigs:
+ *
+ * new Document(array $children = [], $defaults = true);
+ * new Document(string $name, array $children = [], $defaults = true)
+ *
+ * @return void
+ */
+ function __construct() {
+
+ $args = func_get_args();
+ if (count($args) === 0 || is_array($args[0])) {
+ array_unshift($args, $this, static::$defaultName);
+ call_user_func_array(['parent', '__construct'], $args);
+ } else {
+ array_unshift($args, $this);
+ call_user_func_array(['parent', '__construct'], $args);
+ }
+
+ }
+
+ /**
+ * Returns the current document type.
+ *
+ * @return int
+ */
+ function getDocumentType() {
+
+ return self::UNKNOWN;
+
+ }
+
+ /**
+ * Creates a new component or property.
+ *
+ * If it's a known component, we will automatically call createComponent.
+ * otherwise, we'll assume it's a property and call createProperty instead.
+ *
+ * @param string $name
+ * @param string $arg1,... Unlimited number of args
+ *
+ * @return mixed
+ */
+ function create($name) {
+
+ if (isset(static::$componentMap[strtoupper($name)])) {
+
+ return call_user_func_array([$this, 'createComponent'], func_get_args());
+
+ } else {
+
+ return call_user_func_array([$this, 'createProperty'], func_get_args());
+
+ }
+
+ }
+
+ /**
+ * Creates a new component.
+ *
+ * This method automatically searches for the correct component class, based
+ * on its name.
+ *
+ * You can specify the children either in key=>value syntax, in which case
+ * properties will automatically be created, or you can just pass a list of
+ * Component and Property object.
+ *
+ * By default, a set of sensible values will be added to the component. For
+ * an iCalendar object, this may be something like CALSCALE:GREGORIAN. To
+ * ensure that this does not happen, set $defaults to false.
+ *
+ * @param string $name
+ * @param array $children
+ * @param bool $defaults
+ *
+ * @return Component
+ */
+ function createComponent($name, array $children = null, $defaults = true) {
+
+ $name = strtoupper($name);
+ $class = 'Sabre\\VObject\\Component';
+
+ if (isset(static::$componentMap[$name])) {
+ $class = static::$componentMap[$name];
+ }
+ if (is_null($children)) $children = [];
+ return new $class($this, $name, $children, $defaults);
+
+ }
+
+ /**
+ * Factory method for creating new properties.
+ *
+ * This method automatically searches for the correct property class, based
+ * on its name.
+ *
+ * You can specify the parameters either in key=>value syntax, in which case
+ * parameters will automatically be created, or you can just pass a list of
+ * Parameter objects.
+ *
+ * @param string $name
+ * @param mixed $value
+ * @param array $parameters
+ * @param string $valueType Force a specific valuetype, such as URI or TEXT
+ *
+ * @return Property
+ */
+ function createProperty($name, $value = null, array $parameters = null, $valueType = null) {
+
+ // If there's a . in the name, it means it's prefixed by a groupname.
+ if (($i = strpos($name, '.')) !== false) {
+ $group = substr($name, 0, $i);
+ $name = strtoupper(substr($name, $i + 1));
+ } else {
+ $name = strtoupper($name);
+ $group = null;
+ }
+
+ $class = null;
+
+ if ($valueType) {
+ // The valueType argument comes first to figure out the correct
+ // class.
+ $class = $this->getClassNameForPropertyValue($valueType);
+ }
+
+ if (is_null($class)) {
+ // If a VALUE parameter is supplied, we should use that.
+ if (isset($parameters['VALUE'])) {
+ $class = $this->getClassNameForPropertyValue($parameters['VALUE']);
+ if (is_null($class)) {
+ throw new InvalidDataException('Unsupported VALUE parameter for ' . $name . ' property. You supplied "' . $parameters['VALUE'] . '"');
+ }
+ }
+ else {
+ $class = $this->getClassNameForPropertyName($name);
+ }
+ }
+ if (is_null($parameters)) $parameters = [];
+
+ return new $class($this, $name, $value, $parameters, $group);
+
+ }
+
+ /**
+ * This method returns a full class-name for a value parameter.
+ *
+ * For instance, DTSTART may have VALUE=DATE. In that case we will look in
+ * our valueMap table and return the appropriate class name.
+ *
+ * This method returns null if we don't have a specialized class.
+ *
+ * @param string $valueParam
+ * @return string|null
+ */
+ function getClassNameForPropertyValue($valueParam) {
+
+ $valueParam = strtoupper($valueParam);
+ if (isset(static::$valueMap[$valueParam])) {
+ return static::$valueMap[$valueParam];
+ }
+
+ }
+
+ /**
+ * Returns the default class for a property name.
+ *
+ * @param string $propertyName
+ *
+ * @return string
+ */
+ function getClassNameForPropertyName($propertyName) {
+
+ if (isset(static::$propertyMap[$propertyName])) {
+ return static::$propertyMap[$propertyName];
+ } else {
+ return 'Sabre\\VObject\\Property\\Unknown';
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/ElementList.php b/vendor/sabre/vobject/lib/ElementList.php
new file mode 100644
index 000000000..959249247
--- /dev/null
+++ b/vendor/sabre/vobject/lib/ElementList.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Sabre\VObject;
+
+use ArrayIterator;
+use LogicException;
+
+/**
+ * VObject ElementList.
+ *
+ * This class represents a list of elements. Lists are the result of queries,
+ * such as doing $vcalendar->vevent where there's multiple VEVENT objects.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ElementList extends ArrayIterator {
+
+
+ /* {{{ ArrayAccess Interface */
+
+ /**
+ * Sets an item through ArrayAccess.
+ *
+ * @param int $offset
+ * @param mixed $value
+ *
+ * @return void
+ */
+ function offsetSet($offset, $value) {
+
+ throw new LogicException('You can not add new objects to an ElementList');
+
+ }
+
+ /**
+ * Sets an item through ArrayAccess.
+ *
+ * This method just forwards the request to the inner iterator
+ *
+ * @param int $offset
+ *
+ * @return void
+ */
+ function offsetUnset($offset) {
+
+ throw new LogicException('You can not remove objects from an ElementList');
+
+ }
+
+ /* }}} */
+
+}
diff --git a/vendor/sabre/vobject/lib/EofException.php b/vendor/sabre/vobject/lib/EofException.php
new file mode 100644
index 000000000..e9bd55878
--- /dev/null
+++ b/vendor/sabre/vobject/lib/EofException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * Exception thrown by parser when the end of the stream has been reached,
+ * before this was expected.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class EofException extends ParseException {
+
+}
diff --git a/vendor/sabre/vobject/lib/FreeBusyData.php b/vendor/sabre/vobject/lib/FreeBusyData.php
new file mode 100644
index 000000000..0a6c72bb2
--- /dev/null
+++ b/vendor/sabre/vobject/lib/FreeBusyData.php
@@ -0,0 +1,193 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * FreeBusyData is a helper class that manages freebusy information.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class FreeBusyData {
+
+ /**
+ * Start timestamp
+ *
+ * @var int
+ */
+ protected $start;
+
+ /**
+ * End timestamp
+ *
+ * @var int
+ */
+ protected $end;
+
+ /**
+ * A list of free-busy times.
+ *
+ * @var array
+ */
+ protected $data;
+
+ function __construct($start, $end) {
+
+ $this->start = $start;
+ $this->end = $end;
+ $this->data = [];
+
+ $this->data[] = [
+ 'start' => $this->start,
+ 'end' => $this->end,
+ 'type' => 'FREE',
+ ];
+
+ }
+
+ /**
+ * Adds free or busytime to the data.
+ *
+ * @param int $start
+ * @param int $end
+ * @param string $type FREE, BUSY, BUSY-UNAVAILABLE or BUSY-TENTATIVE
+ * @return void
+ */
+ function add($start, $end, $type) {
+
+ if ($start > $this->end || $end < $this->start) {
+
+ // This new data is outside our timerange.
+ return;
+
+ }
+
+ if ($start < $this->start) {
+ // The item starts before our requested time range
+ $start = $this->start;
+ }
+ if ($end > $this->end) {
+ // The item ends after our requested time range
+ $end = $this->end;
+ }
+
+ // Finding out where we need to insert the new item.
+ $currentIndex = 0;
+ while ($start > $this->data[$currentIndex]['end']) {
+ $currentIndex++;
+ }
+
+ // The standard insertion point will be one _after_ the first
+ // overlapping item.
+ $insertStartIndex = $currentIndex + 1;
+
+ $newItem = [
+ 'start' => $start,
+ 'end' => $end,
+ 'type' => $type,
+ ];
+
+ $preceedingItem = $this->data[$insertStartIndex - 1];
+ if ($this->data[$insertStartIndex - 1]['start'] === $start) {
+ // The old item starts at the exact same point as the new item.
+ $insertStartIndex--;
+ }
+
+ // Now we know where to insert the item, we need to know where it
+ // starts overlapping with items on the tail end. We need to start
+ // looking one item before the insertStartIndex, because it's possible
+ // that the new item 'sits inside' the previous old item.
+ if ($insertStartIndex > 0) {
+ $currentIndex = $insertStartIndex - 1;
+ } else {
+ $currentIndex = 0;
+ }
+
+ while ($end > $this->data[$currentIndex]['end']) {
+
+ $currentIndex++;
+
+ }
+
+ // What we are about to insert into the array
+ $newItems = [
+ $newItem
+ ];
+
+ // This is the amount of items that are completely overwritten by the
+ // new item.
+ $itemsToDelete = $currentIndex - $insertStartIndex;
+ if ($this->data[$currentIndex]['end'] <= $end) $itemsToDelete++;
+
+ // If itemsToDelete was -1, it means that the newly inserted item is
+ // actually sitting inside an existing one. This means we need to split
+ // the item at the current position in two and insert the new item in
+ // between.
+ if ($itemsToDelete === -1) {
+ $itemsToDelete = 0;
+ if ($newItem['end'] < $preceedingItem['end']) {
+ $newItems[] = [
+ 'start' => $newItem['end'] + 1,
+ 'end' => $preceedingItem['end'],
+ 'type' => $preceedingItem['type']
+ ];
+ }
+ }
+
+ array_splice(
+ $this->data,
+ $insertStartIndex,
+ $itemsToDelete,
+ $newItems
+ );
+
+ $doMerge = false;
+ $mergeOffset = $insertStartIndex;
+ $mergeItem = $newItem;
+ $mergeDelete = 1;
+
+ if (isset($this->data[$insertStartIndex - 1])) {
+ // Updating the start time of the previous item.
+ $this->data[$insertStartIndex - 1]['end'] = $start;
+
+ // If the previous and the current are of the same type, we can
+ // merge them into one item.
+ if ($this->data[$insertStartIndex - 1]['type'] === $this->data[$insertStartIndex]['type']) {
+ $doMerge = true;
+ $mergeOffset--;
+ $mergeDelete++;
+ $mergeItem['start'] = $this->data[$insertStartIndex - 1]['start'];
+ }
+ }
+ if (isset($this->data[$insertStartIndex + 1])) {
+ // Updating the start time of the next item.
+ $this->data[$insertStartIndex + 1]['start'] = $end;
+
+ // If the next and the current are of the same type, we can
+ // merge them into one item.
+ if ($this->data[$insertStartIndex + 1]['type'] === $this->data[$insertStartIndex]['type']) {
+ $doMerge = true;
+ $mergeDelete++;
+ $mergeItem['end'] = $this->data[$insertStartIndex + 1]['end'];
+ }
+
+ }
+ if ($doMerge) {
+ array_splice(
+ $this->data,
+ $mergeOffset,
+ $mergeDelete,
+ [$mergeItem]
+ );
+ }
+
+ }
+
+ function getData() {
+
+ return $this->data;
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/FreeBusyGenerator.php b/vendor/sabre/vobject/lib/FreeBusyGenerator.php
new file mode 100644
index 000000000..e33621090
--- /dev/null
+++ b/vendor/sabre/vobject/lib/FreeBusyGenerator.php
@@ -0,0 +1,604 @@
+<?php
+
+namespace Sabre\VObject;
+
+use DateTimeInterface;
+use DateTimeImmutable;
+use DateTimeZone;
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Recur\EventIterator;
+use Sabre\VObject\Recur\NoInstancesException;
+
+/**
+ * This class helps with generating FREEBUSY reports based on existing sets of
+ * objects.
+ *
+ * It only looks at VEVENT and VFREEBUSY objects from the sourcedata, and
+ * generates a single VFREEBUSY object.
+ *
+ * VFREEBUSY components are described in RFC5545, The rules for what should
+ * go in a single freebusy report is taken from RFC4791, section 7.10.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class FreeBusyGenerator {
+
+ /**
+ * Input objects.
+ *
+ * @var array
+ */
+ protected $objects = [];
+
+ /**
+ * Start of range.
+ *
+ * @var DateTimeInterface|null
+ */
+ protected $start;
+
+ /**
+ * End of range.
+ *
+ * @var DateTimeInterface|null
+ */
+ protected $end;
+
+ /**
+ * VCALENDAR object.
+ *
+ * @var Document
+ */
+ protected $baseObject;
+
+ /**
+ * Reference timezone.
+ *
+ * When we are calculating busy times, and we come across so-called
+ * floating times (times without a timezone), we use the reference timezone
+ * instead.
+ *
+ * This is also used for all-day events.
+ *
+ * This defaults to UTC.
+ *
+ * @var DateTimeZone
+ */
+ protected $timeZone;
+
+ /**
+ * A VAVAILABILITY document.
+ *
+ * If this is set, it's information will be included when calculating
+ * freebusy time.
+ *
+ * @var Document
+ */
+ protected $vavailability;
+
+ /**
+ * Creates the generator.
+ *
+ * Check the setTimeRange and setObjects methods for details about the
+ * arguments.
+ *
+ * @param DateTimeInterface $start
+ * @param DateTimeInterface $end
+ * @param mixed $objects
+ * @param DateTimeZone $timeZone
+ */
+ function __construct(DateTimeInterface $start = null, DateTimeInterface $end = null, $objects = null, DateTimeZone $timeZone = null) {
+
+ $this->setTimeRange($start, $end);
+
+ if ($objects) {
+ $this->setObjects($objects);
+ }
+ if (is_null($timeZone)) {
+ $timeZone = new DateTimeZone('UTC');
+ }
+ $this->setTimeZone($timeZone);
+
+ }
+
+ /**
+ * Sets the VCALENDAR object.
+ *
+ * If this is set, it will not be generated for you. You are responsible
+ * for setting things like the METHOD, CALSCALE, VERSION, etc..
+ *
+ * The VFREEBUSY object will be automatically added though.
+ *
+ * @param Document $vcalendar
+ * @return void
+ */
+ function setBaseObject(Document $vcalendar) {
+
+ $this->baseObject = $vcalendar;
+
+ }
+
+ /**
+ * Sets a VAVAILABILITY document.
+ *
+ * @param Document $vcalendar
+ * @return void
+ */
+ function setVAvailability(Document $vcalendar) {
+
+ $this->vavailability = $vcalendar;
+
+ }
+
+ /**
+ * Sets the input objects.
+ *
+ * You must either specify a valendar object as a string, or as the parse
+ * Component.
+ * It's also possible to specify multiple objects as an array.
+ *
+ * @param mixed $objects
+ *
+ * @return void
+ */
+ function setObjects($objects) {
+
+ if (!is_array($objects)) {
+ $objects = [$objects];
+ }
+
+ $this->objects = [];
+ foreach ($objects as $object) {
+
+ if (is_string($object)) {
+ $this->objects[] = Reader::read($object);
+ } elseif ($object instanceof Component) {
+ $this->objects[] = $object;
+ } else {
+ throw new \InvalidArgumentException('You can only pass strings or \\Sabre\\VObject\\Component arguments to setObjects');
+ }
+
+ }
+
+ }
+
+ /**
+ * Sets the time range.
+ *
+ * Any freebusy object falling outside of this time range will be ignored.
+ *
+ * @param DateTimeInterface $start
+ * @param DateTimeInterface $end
+ *
+ * @return void
+ */
+ function setTimeRange(DateTimeInterface $start = null, DateTimeInterface $end = null) {
+
+ if (!$start) {
+ $start = new DateTimeImmutable(Settings::$minDate);
+ }
+ if (!$end) {
+ $end = new DateTimeImmutable(Settings::$maxDate);
+ }
+ $this->start = $start;
+ $this->end = $end;
+
+ }
+
+ /**
+ * Sets the reference timezone for floating times.
+ *
+ * @param DateTimeZone $timeZone
+ *
+ * @return void
+ */
+ function setTimeZone(DateTimeZone $timeZone) {
+
+ $this->timeZone = $timeZone;
+
+ }
+
+ /**
+ * Parses the input data and returns a correct VFREEBUSY object, wrapped in
+ * a VCALENDAR.
+ *
+ * @return Component
+ */
+ function getResult() {
+
+ $fbData = new FreeBusyData(
+ $this->start->getTimeStamp(),
+ $this->end->getTimeStamp()
+ );
+ if ($this->vavailability) {
+
+ $this->calculateAvailability($fbData, $this->vavailability);
+
+ }
+
+ $this->calculateBusy($fbData, $this->objects);
+
+ return $this->generateFreeBusyCalendar($fbData);
+
+
+ }
+
+ /**
+ * This method takes a VAVAILABILITY component and figures out all the
+ * available times.
+ *
+ * @param FreeBusyData $fbData
+ * @param VCalendar $vavailability
+ * @return void
+ */
+ protected function calculateAvailability(FreeBusyData $fbData, VCalendar $vavailability) {
+
+ $vavailComps = iterator_to_array($vavailability->VAVAILABILITY);
+ usort(
+ $vavailComps,
+ function($a, $b) {
+
+ // We need to order the components by priority. Priority 1
+ // comes first, up until priority 9. Priority 0 comes after
+ // priority 9. No priority implies priority 0.
+ //
+ // Yes, I'm serious.
+ $priorityA = isset($a->PRIORITY) ? (int)$a->PRIORITY->getValue() : 0;
+ $priorityB = isset($b->PRIORITY) ? (int)$b->PRIORITY->getValue() : 0;
+
+ if ($priorityA === 0) $priorityA = 10;
+ if ($priorityB === 0) $priorityB = 10;
+
+ return $priorityA - $priorityB;
+
+ }
+ );
+
+ // Now we go over all the VAVAILABILITY components and figure if
+ // there's any we don't need to consider.
+ //
+ // This is can be because of one of two reasons: either the
+ // VAVAILABILITY component falls outside the time we are interested in,
+ // or a different VAVAILABILITY component with a higher priority has
+ // already completely covered the time-range.
+ $old = $vavailComps;
+ $new = [];
+
+ foreach ($old as $vavail) {
+
+ list($compStart, $compEnd) = $vavail->getEffectiveStartEnd();
+
+ // We don't care about datetimes that are earlier or later than the
+ // start and end of the freebusy report, so this gets normalized
+ // first.
+ if (is_null($compStart) || $compStart < $this->start) {
+ $compStart = $this->start;
+ }
+ if (is_null($compEnd) || $compEnd > $this->end) {
+ $compEnd = $this->end;
+ }
+
+ // If the item fell out of the timerange, we can just skip it.
+ if ($compStart > $this->end || $compEnd < $this->start) {
+ continue;
+ }
+
+ // Going through our existing list of components to see if there's
+ // a higher priority component that already fully covers this one.
+ foreach ($new as $higherVavail) {
+
+ list($higherStart, $higherEnd) = $higherVavail->getEffectiveStartEnd();
+ if (
+ (is_null($higherStart) || $higherStart < $compStart) &&
+ (is_null($higherEnd) || $higherEnd > $compEnd)
+ ) {
+
+ // Component is fully covered by a higher priority
+ // component. We can skip this component.
+ continue 2;
+
+ }
+
+ }
+
+ // We're keeping it!
+ $new[] = $vavail;
+
+ }
+
+ // Lastly, we need to traverse the remaining components and fill in the
+ // freebusydata slots.
+ //
+ // We traverse the components in reverse, because we want the higher
+ // priority components to override the lower ones.
+ foreach (array_reverse($new) as $vavail) {
+
+ $busyType = isset($vavail->BUSYTYPE) ? strtoupper($vavail->BUSYTYPE) : 'BUSY-UNAVAILABLE';
+ list($vavailStart, $vavailEnd) = $vavail->getEffectiveStartEnd();
+
+ // Making the component size no larger than the requested free-busy
+ // report range.
+ if (!$vavailStart || $vavailStart < $this->start) {
+ $vavailStart = $this->start;
+ }
+ if (!$vavailEnd || $vavailEnd > $this->end) {
+ $vavailEnd = $this->end;
+ }
+
+ // Marking the entire time range of the VAVAILABILITY component as
+ // busy.
+ $fbData->add(
+ $vavailStart->getTimeStamp(),
+ $vavailEnd->getTimeStamp(),
+ $busyType
+ );
+
+ // Looping over the AVAILABLE components.
+ if (isset($vavail->AVAILABLE)) foreach ($vavail->AVAILABLE as $available) {
+
+ list($availStart, $availEnd) = $available->getEffectiveStartEnd();
+ $fbData->add(
+ $availStart->getTimeStamp(),
+ $availEnd->getTimeStamp(),
+ 'FREE'
+ );
+
+ if ($available->RRULE) {
+ // Our favourite thing: recurrence!!
+
+ $rruleIterator = new Recur\RRuleIterator(
+ $available->RRULE->getValue(),
+ $availStart
+ );
+ $rruleIterator->fastForward($vavailStart);
+
+ $startEndDiff = $availStart->diff($availEnd);
+
+ while ($rruleIterator->valid()) {
+
+ $recurStart = $rruleIterator->current();
+ $recurEnd = $recurStart->add($startEndDiff);
+
+ if ($recurStart > $vavailEnd) {
+ // We're beyond the legal timerange.
+ break;
+ }
+
+ if ($recurEnd > $vavailEnd) {
+ // Truncating the end if it exceeds the
+ // VAVAILABILITY end.
+ $recurEnd = $vavailEnd;
+ }
+
+ $fbData->add(
+ $recurStart->getTimeStamp(),
+ $recurEnd->getTimeStamp(),
+ 'FREE'
+ );
+
+ $rruleIterator->next();
+
+ }
+ }
+
+ }
+
+ }
+
+ }
+
+ /**
+ * This method takes an array of iCalendar objects and applies its busy
+ * times on fbData.
+ *
+ * @param FreeBusyData $fbData
+ * @param VCalendar[] $objects
+ */
+ protected function calculateBusy(FreeBusyData $fbData, array $objects) {
+
+ foreach ($objects as $key => $object) {
+
+ foreach ($object->getBaseComponents() as $component) {
+
+ switch ($component->name) {
+
+ case 'VEVENT' :
+
+ $FBTYPE = 'BUSY';
+ if (isset($component->TRANSP) && (strtoupper($component->TRANSP) === 'TRANSPARENT')) {
+ break;
+ }
+ if (isset($component->STATUS)) {
+ $status = strtoupper($component->STATUS);
+ if ($status === 'CANCELLED') {
+ break;
+ }
+ if ($status === 'TENTATIVE') {
+ $FBTYPE = 'BUSY-TENTATIVE';
+ }
+ }
+
+ $times = [];
+
+ if ($component->RRULE) {
+ try {
+ $iterator = new EventIterator($object, (string)$component->UID, $this->timeZone);
+ } catch (NoInstancesException $e) {
+ // This event is recurring, but it doesn't have a single
+ // instance. We are skipping this event from the output
+ // entirely.
+ unset($this->objects[$key]);
+ continue;
+ }
+
+ if ($this->start) {
+ $iterator->fastForward($this->start);
+ }
+
+ $maxRecurrences = Settings::$maxRecurrences;
+
+ while ($iterator->valid() && --$maxRecurrences) {
+
+ $startTime = $iterator->getDTStart();
+ if ($this->end && $startTime > $this->end) {
+ break;
+ }
+ $times[] = [
+ $iterator->getDTStart(),
+ $iterator->getDTEnd(),
+ ];
+
+ $iterator->next();
+
+ }
+
+ } else {
+
+ $startTime = $component->DTSTART->getDateTime($this->timeZone);
+ if ($this->end && $startTime > $this->end) {
+ break;
+ }
+ $endTime = null;
+ if (isset($component->DTEND)) {
+ $endTime = $component->DTEND->getDateTime($this->timeZone);
+ } elseif (isset($component->DURATION)) {
+ $duration = DateTimeParser::parseDuration((string)$component->DURATION);
+ $endTime = clone $startTime;
+ $endTime = $endTime->add($duration);
+ } elseif (!$component->DTSTART->hasTime()) {
+ $endTime = clone $startTime;
+ $endTime = $endTime->modify('+1 day');
+ } else {
+ // The event had no duration (0 seconds)
+ break;
+ }
+
+ $times[] = [$startTime, $endTime];
+
+ }
+
+ foreach ($times as $time) {
+
+ if ($this->end && $time[0] > $this->end) break;
+ if ($this->start && $time[1] < $this->start) break;
+
+ $fbData->add(
+ $time[0]->getTimeStamp(),
+ $time[1]->getTimeStamp(),
+ $FBTYPE
+ );
+ }
+ break;
+
+ case 'VFREEBUSY' :
+ foreach ($component->FREEBUSY as $freebusy) {
+
+ $fbType = isset($freebusy['FBTYPE']) ? strtoupper($freebusy['FBTYPE']) : 'BUSY';
+
+ // Skipping intervals marked as 'free'
+ if ($fbType === 'FREE')
+ continue;
+
+ $values = explode(',', $freebusy);
+ foreach ($values as $value) {
+ list($startTime, $endTime) = explode('/', $value);
+ $startTime = DateTimeParser::parseDateTime($startTime);
+
+ if (substr($endTime, 0, 1) === 'P' || substr($endTime, 0, 2) === '-P') {
+ $duration = DateTimeParser::parseDuration($endTime);
+ $endTime = clone $startTime;
+ $endTime = $endTime->add($duration);
+ } else {
+ $endTime = DateTimeParser::parseDateTime($endTime);
+ }
+
+ if ($this->start && $this->start > $endTime) continue;
+ if ($this->end && $this->end < $startTime) continue;
+ $fbData->add(
+ $startTime->getTimeStamp(),
+ $endTime->getTimeStamp(),
+ $fbType
+ );
+
+ }
+
+
+ }
+ break;
+
+ }
+
+
+ }
+
+ }
+
+ }
+
+ /**
+ * This method takes a FreeBusyData object and generates the VCALENDAR
+ * object associated with it.
+ *
+ * @return VCalendar
+ */
+ protected function generateFreeBusyCalendar(FreeBusyData $fbData) {
+
+ if ($this->baseObject) {
+ $calendar = $this->baseObject;
+ } else {
+ $calendar = new VCalendar();
+ }
+
+ $vfreebusy = $calendar->createComponent('VFREEBUSY');
+ $calendar->add($vfreebusy);
+
+ if ($this->start) {
+ $dtstart = $calendar->createProperty('DTSTART');
+ $dtstart->setDateTime($this->start);
+ $vfreebusy->add($dtstart);
+ }
+ if ($this->end) {
+ $dtend = $calendar->createProperty('DTEND');
+ $dtend->setDateTime($this->end);
+ $vfreebusy->add($dtend);
+ }
+
+ $tz = new \DateTimeZone('UTC');
+ $dtstamp = $calendar->createProperty('DTSTAMP');
+ $dtstamp->setDateTime(new DateTimeImmutable('now', $tz));
+ $vfreebusy->add($dtstamp);
+
+ foreach ($fbData->getData() as $busyTime) {
+
+ $busyType = strtoupper($busyTime['type']);
+
+ // Ignoring all the FREE parts, because those are already assumed.
+ if ($busyType === 'FREE') {
+ continue;
+ }
+
+ $busyTime[0] = new \DateTimeImmutable('@' . $busyTime['start'], $tz);
+ $busyTime[1] = new \DateTimeImmutable('@' . $busyTime['end'], $tz);
+
+ $prop = $calendar->createProperty(
+ 'FREEBUSY',
+ $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z')
+ );
+
+ // Only setting FBTYPE if it's not BUSY, because BUSY is the
+ // default anyway.
+ if ($busyType !== 'BUSY') {
+ $prop['FBTYPE'] = $busyType;
+ }
+ $vfreebusy->add($prop);
+
+ }
+
+ return $calendar;
+
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/ITip/Broker.php b/vendor/sabre/vobject/lib/ITip/Broker.php
new file mode 100644
index 000000000..effa74317
--- /dev/null
+++ b/vendor/sabre/vobject/lib/ITip/Broker.php
@@ -0,0 +1,989 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\DateTimeParser;
+use Sabre\VObject\Reader;
+use Sabre\VObject\Recur\EventIterator;
+
+/**
+ * The ITip\Broker class is a utility class that helps with processing
+ * so-called iTip messages.
+ *
+ * iTip is defined in rfc5546, stands for iCalendar Transport-Independent
+ * Interoperability Protocol, and describes the underlying mechanism for
+ * using iCalendar for scheduling for for example through email (also known as
+ * IMip) and CalDAV Scheduling.
+ *
+ * This class helps by:
+ *
+ * 1. Creating individual invites based on an iCalendar event for each
+ * attendee.
+ * 2. Generating invite updates based on an iCalendar update. This may result
+ * in new invites, updates and cancellations for attendees, if that list
+ * changed.
+ * 3. On the receiving end, it can create a local iCalendar event based on
+ * a received invite.
+ * 4. It can also process an invite update on a local event, ensuring that any
+ * overridden properties from attendees are retained.
+ * 5. It can create a accepted or declined iTip reply based on an invite.
+ * 6. It can process a reply from an invite and update an events attendee
+ * status based on a reply.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Broker {
+
+ /**
+ * This setting determines whether the rules for the SCHEDULE-AGENT
+ * parameter should be followed.
+ *
+ * This is a parameter defined on ATTENDEE properties, introduced by RFC
+ * 6638. This parameter allows a caldav client to tell the server 'Don't do
+ * any scheduling operations'.
+ *
+ * If this setting is turned on, any attendees with SCHEDULE-AGENT set to
+ * CLIENT will be ignored. This is the desired behavior for a CalDAV
+ * server, but if you're writing an iTip application that doesn't deal with
+ * CalDAV, you may want to ignore this parameter.
+ *
+ * @var bool
+ */
+ public $scheduleAgentServerRules = true;
+
+ /**
+ * The broker will try during 'parseEvent' figure out whether the change
+ * was significant.
+ *
+ * It uses a few different ways to do this. One of these ways is seeing if
+ * certain properties changed values. This list of specified here.
+ *
+ * This list is taken from:
+ * * http://tools.ietf.org/html/rfc5546#section-2.1.4
+ *
+ * @var string[]
+ */
+ public $significantChangeProperties = [
+ 'DTSTART',
+ 'DTEND',
+ 'DURATION',
+ 'DUE',
+ 'RRULE',
+ 'RDATE',
+ 'EXDATE',
+ 'STATUS',
+ ];
+
+ /**
+ * This method is used to process an incoming itip message.
+ *
+ * Examples:
+ *
+ * 1. A user is an attendee to an event. The organizer sends an updated
+ * meeting using a new iTip message with METHOD:REQUEST. This function
+ * will process the message and update the attendee's event accordingly.
+ *
+ * 2. The organizer cancelled the event using METHOD:CANCEL. We will update
+ * the users event to state STATUS:CANCELLED.
+ *
+ * 3. An attendee sent a reply to an invite using METHOD:REPLY. We can
+ * update the organizers event to update the ATTENDEE with its correct
+ * PARTSTAT.
+ *
+ * The $existingObject is updated in-place. If there is no existing object
+ * (because it's a new invite for example) a new object will be created.
+ *
+ * If an existing object does not exist, and the method was CANCEL or
+ * REPLY, the message effectively gets ignored, and no 'existingObject'
+ * will be created.
+ *
+ * The updated $existingObject is also returned from this function.
+ *
+ * If the iTip message was not supported, we will always return false.
+ *
+ * @param Message $itipMessage
+ * @param VCalendar $existingObject
+ *
+ * @return VCalendar|null
+ */
+ function processMessage(Message $itipMessage, VCalendar $existingObject = null) {
+
+ // We only support events at the moment.
+ if ($itipMessage->component !== 'VEVENT') {
+ return false;
+ }
+
+ switch ($itipMessage->method) {
+
+ case 'REQUEST' :
+ return $this->processMessageRequest($itipMessage, $existingObject);
+
+ case 'CANCEL' :
+ return $this->processMessageCancel($itipMessage, $existingObject);
+
+ case 'REPLY' :
+ return $this->processMessageReply($itipMessage, $existingObject);
+
+ default :
+ // Unsupported iTip message
+ return;
+
+ }
+
+ return $existingObject;
+
+ }
+
+ /**
+ * This function parses a VCALENDAR object and figure out if any messages
+ * need to be sent.
+ *
+ * A VCALENDAR object will be created from the perspective of either an
+ * attendee, or an organizer. You must pass a string identifying the
+ * current user, so we can figure out who in the list of attendees or the
+ * organizer we are sending this message on behalf of.
+ *
+ * It's possible to specify the current user as an array, in case the user
+ * has more than one identifying href (such as multiple emails).
+ *
+ * It $oldCalendar is specified, it is assumed that the operation is
+ * updating an existing event, which means that we need to look at the
+ * differences between events, and potentially send old attendees
+ * cancellations, and current attendees updates.
+ *
+ * If $calendar is null, but $oldCalendar is specified, we treat the
+ * operation as if the user has deleted an event. If the user was an
+ * organizer, this means that we need to send cancellation notices to
+ * people. If the user was an attendee, we need to make sure that the
+ * organizer gets the 'declined' message.
+ *
+ * @param VCalendar|string $calendar
+ * @param string|array $userHref
+ * @param VCalendar|string $oldCalendar
+ *
+ * @return array
+ */
+ function parseEvent($calendar = null, $userHref, $oldCalendar = null) {
+
+ if ($oldCalendar) {
+ if (is_string($oldCalendar)) {
+ $oldCalendar = Reader::read($oldCalendar);
+ }
+ if (!isset($oldCalendar->VEVENT)) {
+ // We only support events at the moment
+ return [];
+ }
+
+ $oldEventInfo = $this->parseEventInfo($oldCalendar);
+ } else {
+ $oldEventInfo = [
+ 'organizer' => null,
+ 'significantChangeHash' => '',
+ 'attendees' => [],
+ ];
+ }
+
+ $userHref = (array)$userHref;
+
+ if (!is_null($calendar)) {
+
+ if (is_string($calendar)) {
+ $calendar = Reader::read($calendar);
+ }
+ if (!isset($calendar->VEVENT)) {
+ // We only support events at the moment
+ return [];
+ }
+ $eventInfo = $this->parseEventInfo($calendar);
+ if (!$eventInfo['attendees'] && !$oldEventInfo['attendees']) {
+ // If there were no attendees on either side of the equation,
+ // we don't need to do anything.
+ return [];
+ }
+ if (!$eventInfo['organizer'] && !$oldEventInfo['organizer']) {
+ // There was no organizer before or after the change.
+ return [];
+ }
+
+ $baseCalendar = $calendar;
+
+ // If the new object didn't have an organizer, the organizer
+ // changed the object from a scheduling object to a non-scheduling
+ // object. We just copy the info from the old object.
+ if (!$eventInfo['organizer'] && $oldEventInfo['organizer']) {
+ $eventInfo['organizer'] = $oldEventInfo['organizer'];
+ $eventInfo['organizerName'] = $oldEventInfo['organizerName'];
+ }
+
+ } else {
+ // The calendar object got deleted, we need to process this as a
+ // cancellation / decline.
+ if (!$oldCalendar) {
+ // No old and no new calendar, there's no thing to do.
+ return [];
+ }
+
+ $eventInfo = $oldEventInfo;
+
+ if (in_array($eventInfo['organizer'], $userHref)) {
+ // This is an organizer deleting the event.
+ $eventInfo['attendees'] = [];
+ // Increasing the sequence, but only if the organizer deleted
+ // the event.
+ $eventInfo['sequence']++;
+ } else {
+ // This is an attendee deleting the event.
+ foreach ($eventInfo['attendees'] as $key => $attendee) {
+ if (in_array($attendee['href'], $userHref)) {
+ $eventInfo['attendees'][$key]['instances'] = ['master' =>
+ ['id' => 'master', 'partstat' => 'DECLINED']
+ ];
+ }
+ }
+ }
+ $baseCalendar = $oldCalendar;
+
+ }
+
+ if (in_array($eventInfo['organizer'], $userHref)) {
+ return $this->parseEventForOrganizer($baseCalendar, $eventInfo, $oldEventInfo);
+ } elseif ($oldCalendar) {
+ // We need to figure out if the user is an attendee, but we're only
+ // doing so if there's an oldCalendar, because we only want to
+ // process updates, not creation of new events.
+ foreach ($eventInfo['attendees'] as $attendee) {
+ if (in_array($attendee['href'], $userHref)) {
+ return $this->parseEventForAttendee($baseCalendar, $eventInfo, $oldEventInfo, $attendee['href']);
+ }
+ }
+ }
+ return [];
+
+ }
+
+ /**
+ * Processes incoming REQUEST messages.
+ *
+ * This is message from an organizer, and is either a new event
+ * invite, or an update to an existing one.
+ *
+ *
+ * @param Message $itipMessage
+ * @param VCalendar $existingObject
+ *
+ * @return VCalendar|null
+ */
+ protected function processMessageRequest(Message $itipMessage, VCalendar $existingObject = null) {
+
+ if (!$existingObject) {
+ // This is a new invite, and we're just going to copy over
+ // all the components from the invite.
+ $existingObject = new VCalendar();
+ foreach ($itipMessage->message->getComponents() as $component) {
+ $existingObject->add(clone $component);
+ }
+ } else {
+ // We need to update an existing object with all the new
+ // information. We can just remove all existing components
+ // and create new ones.
+ foreach ($existingObject->getComponents() as $component) {
+ $existingObject->remove($component);
+ }
+ foreach ($itipMessage->message->getComponents() as $component) {
+ $existingObject->add(clone $component);
+ }
+ }
+ return $existingObject;
+
+ }
+
+ /**
+ * Processes incoming CANCEL messages.
+ *
+ * This is a message from an organizer, and means that either an
+ * attendee got removed from an event, or an event got cancelled
+ * altogether.
+ *
+ * @param Message $itipMessage
+ * @param VCalendar $existingObject
+ *
+ * @return VCalendar|null
+ */
+ protected function processMessageCancel(Message $itipMessage, VCalendar $existingObject = null) {
+
+ if (!$existingObject) {
+ // The event didn't exist in the first place, so we're just
+ // ignoring this message.
+ } else {
+ foreach ($existingObject->VEVENT as $vevent) {
+ $vevent->STATUS = 'CANCELLED';
+ $vevent->SEQUENCE = $itipMessage->sequence;
+ }
+ }
+ return $existingObject;
+
+ }
+
+ /**
+ * Processes incoming REPLY messages.
+ *
+ * The message is a reply. This is for example an attendee telling
+ * an organizer he accepted the invite, or declined it.
+ *
+ * @param Message $itipMessage
+ * @param VCalendar $existingObject
+ *
+ * @return VCalendar|null
+ */
+ protected function processMessageReply(Message $itipMessage, VCalendar $existingObject = null) {
+
+ // A reply can only be processed based on an existing object.
+ // If the object is not available, the reply is ignored.
+ if (!$existingObject) {
+ return;
+ }
+ $instances = [];
+ $requestStatus = '2.0';
+
+ // Finding all the instances the attendee replied to.
+ foreach ($itipMessage->message->VEVENT as $vevent) {
+ $recurId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->getValue() : 'master';
+ $attendee = $vevent->ATTENDEE;
+ $instances[$recurId] = $attendee['PARTSTAT']->getValue();
+ if (isset($vevent->{'REQUEST-STATUS'})) {
+ $requestStatus = $vevent->{'REQUEST-STATUS'}->getValue();
+ list($requestStatus) = explode(';', $requestStatus);
+ }
+ }
+
+ // Now we need to loop through the original organizer event, to find
+ // all the instances where we have a reply for.
+ $masterObject = null;
+ foreach ($existingObject->VEVENT as $vevent) {
+ $recurId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->getValue() : 'master';
+ if ($recurId === 'master') {
+ $masterObject = $vevent;
+ }
+ if (isset($instances[$recurId])) {
+ $attendeeFound = false;
+ if (isset($vevent->ATTENDEE)) {
+ foreach ($vevent->ATTENDEE as $attendee) {
+ if ($attendee->getValue() === $itipMessage->sender) {
+ $attendeeFound = true;
+ $attendee['PARTSTAT'] = $instances[$recurId];
+ $attendee['SCHEDULE-STATUS'] = $requestStatus;
+ // Un-setting the RSVP status, because we now know
+ // that the attendee already replied.
+ unset($attendee['RSVP']);
+ break;
+ }
+ }
+ }
+ if (!$attendeeFound) {
+ // Adding a new attendee. The iTip documentation calls this
+ // a party crasher.
+ $attendee = $vevent->add('ATTENDEE', $itipMessage->sender, [
+ 'PARTSTAT' => $instances[$recurId]
+ ]);
+ if ($itipMessage->senderName) $attendee['CN'] = $itipMessage->senderName;
+ }
+ unset($instances[$recurId]);
+ }
+ }
+
+ if (!$masterObject) {
+ // No master object, we can't add new instances.
+ return;
+ }
+ // If we got replies to instances that did not exist in the
+ // original list, it means that new exceptions must be created.
+ foreach ($instances as $recurId => $partstat) {
+
+ $recurrenceIterator = new EventIterator($existingObject, $itipMessage->uid);
+ $found = false;
+ $iterations = 1000;
+ do {
+
+ $newObject = $recurrenceIterator->getEventObject();
+ $recurrenceIterator->next();
+
+ if (isset($newObject->{'RECURRENCE-ID'}) && $newObject->{'RECURRENCE-ID'}->getValue() === $recurId) {
+ $found = true;
+ }
+ $iterations--;
+
+ } while ($recurrenceIterator->valid() && !$found && $iterations);
+
+ // Invalid recurrence id. Skipping this object.
+ if (!$found) continue;
+
+ unset(
+ $newObject->RRULE,
+ $newObject->EXDATE,
+ $newObject->RDATE
+ );
+ $attendeeFound = false;
+ if (isset($newObject->ATTENDEE)) {
+ foreach ($newObject->ATTENDEE as $attendee) {
+ if ($attendee->getValue() === $itipMessage->sender) {
+ $attendeeFound = true;
+ $attendee['PARTSTAT'] = $partstat;
+ break;
+ }
+ }
+ }
+ if (!$attendeeFound) {
+ // Adding a new attendee
+ $attendee = $newObject->add('ATTENDEE', $itipMessage->sender, [
+ 'PARTSTAT' => $partstat
+ ]);
+ if ($itipMessage->senderName) {
+ $attendee['CN'] = $itipMessage->senderName;
+ }
+ }
+ $existingObject->add($newObject);
+
+ }
+ return $existingObject;
+
+ }
+
+ /**
+ * This method is used in cases where an event got updated, and we
+ * potentially need to send emails to attendees to let them know of updates
+ * in the events.
+ *
+ * We will detect which attendees got added, which got removed and create
+ * specific messages for these situations.
+ *
+ * @param VCalendar $calendar
+ * @param array $eventInfo
+ * @param array $oldEventInfo
+ *
+ * @return array
+ */
+ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo, array $oldEventInfo) {
+
+ // Merging attendee lists.
+ $attendees = [];
+ foreach ($oldEventInfo['attendees'] as $attendee) {
+ $attendees[$attendee['href']] = [
+ 'href' => $attendee['href'],
+ 'oldInstances' => $attendee['instances'],
+ 'newInstances' => [],
+ 'name' => $attendee['name'],
+ 'forceSend' => null,
+ ];
+ }
+ foreach ($eventInfo['attendees'] as $attendee) {
+ if (isset($attendees[$attendee['href']])) {
+ $attendees[$attendee['href']]['name'] = $attendee['name'];
+ $attendees[$attendee['href']]['newInstances'] = $attendee['instances'];
+ $attendees[$attendee['href']]['forceSend'] = $attendee['forceSend'];
+ } else {
+ $attendees[$attendee['href']] = [
+ 'href' => $attendee['href'],
+ 'oldInstances' => [],
+ 'newInstances' => $attendee['instances'],
+ 'name' => $attendee['name'],
+ 'forceSend' => $attendee['forceSend'],
+ ];
+ }
+ }
+
+ $messages = [];
+
+ foreach ($attendees as $attendee) {
+
+ // An organizer can also be an attendee. We should not generate any
+ // messages for those.
+ if ($attendee['href'] === $eventInfo['organizer']) {
+ continue;
+ }
+
+ $message = new Message();
+ $message->uid = $eventInfo['uid'];
+ $message->component = 'VEVENT';
+ $message->sequence = $eventInfo['sequence'];
+ $message->sender = $eventInfo['organizer'];
+ $message->senderName = $eventInfo['organizerName'];
+ $message->recipient = $attendee['href'];
+ $message->recipientName = $attendee['name'];
+
+ if (!$attendee['newInstances']) {
+
+ // If there are no instances the attendee is a part of, it
+ // means the attendee was removed and we need to send him a
+ // CANCEL.
+ $message->method = 'CANCEL';
+
+ // Creating the new iCalendar body.
+ $icalMsg = new VCalendar();
+ $icalMsg->METHOD = $message->method;
+ $event = $icalMsg->add('VEVENT', [
+ 'UID' => $message->uid,
+ 'SEQUENCE' => $message->sequence,
+ ]);
+ if (isset($calendar->VEVENT->SUMMARY)) {
+ $event->add('SUMMARY', $calendar->VEVENT->SUMMARY->getValue());
+ }
+ $event->add(clone $calendar->VEVENT->DTSTART);
+ if (isset($calendar->VEVENT->DTEND)) {
+ $event->add(clone $calendar->VEVENT->DTEND);
+ } elseif (isset($calendar->VEVENT->DURATION)) {
+ $event->add(clone $calendar->VEVENT->DURATION);
+ }
+ $org = $event->add('ORGANIZER', $eventInfo['organizer']);
+ if ($eventInfo['organizerName']) $org['CN'] = $eventInfo['organizerName'];
+ $event->add('ATTENDEE', $attendee['href'], [
+ 'CN' => $attendee['name'],
+ ]);
+ $message->significantChange = true;
+
+ } else {
+
+ // The attendee gets the updated event body
+ $message->method = 'REQUEST';
+
+ // Creating the new iCalendar body.
+ $icalMsg = new VCalendar();
+ $icalMsg->METHOD = $message->method;
+
+ foreach ($calendar->select('VTIMEZONE') as $timezone) {
+ $icalMsg->add(clone $timezone);
+ }
+
+ // We need to find out that this change is significant. If it's
+ // not, systems may opt to not send messages.
+ //
+ // We do this based on the 'significantChangeHash' which is
+ // some value that changes if there's a certain set of
+ // properties changed in the event, or simply if there's a
+ // difference in instances that the attendee is invited to.
+
+ $message->significantChange =
+ $attendee['forceSend'] === 'REQUEST' ||
+ array_keys($attendee['oldInstances']) != array_keys($attendee['newInstances']) ||
+ $oldEventInfo['significantChangeHash'] !== $eventInfo['significantChangeHash'];
+
+ foreach ($attendee['newInstances'] as $instanceId => $instanceInfo) {
+
+ $currentEvent = clone $eventInfo['instances'][$instanceId];
+ if ($instanceId === 'master') {
+
+ // We need to find a list of events that the attendee
+ // is not a part of to add to the list of exceptions.
+ $exceptions = [];
+ foreach ($eventInfo['instances'] as $instanceId => $vevent) {
+ if (!isset($attendee['newInstances'][$instanceId])) {
+ $exceptions[] = $instanceId;
+ }
+ }
+
+ // If there were exceptions, we need to add it to an
+ // existing EXDATE property, if it exists.
+ if ($exceptions) {
+ if (isset($currentEvent->EXDATE)) {
+ $currentEvent->EXDATE->setParts(array_merge(
+ $currentEvent->EXDATE->getParts(),
+ $exceptions
+ ));
+ } else {
+ $currentEvent->EXDATE = $exceptions;
+ }
+ }
+
+ // Cleaning up any scheduling information that
+ // shouldn't be sent along.
+ unset($currentEvent->ORGANIZER['SCHEDULE-FORCE-SEND']);
+ unset($currentEvent->ORGANIZER['SCHEDULE-STATUS']);
+
+ foreach ($currentEvent->ATTENDEE as $attendee) {
+ unset($attendee['SCHEDULE-FORCE-SEND']);
+ unset($attendee['SCHEDULE-STATUS']);
+
+ // We're adding PARTSTAT=NEEDS-ACTION to ensure that
+ // iOS shows an "Inbox Item"
+ if (!isset($attendee['PARTSTAT'])) {
+ $attendee['PARTSTAT'] = 'NEEDS-ACTION';
+ }
+
+ }
+
+ }
+
+ $icalMsg->add($currentEvent);
+
+ }
+
+ }
+
+ $message->message = $icalMsg;
+ $messages[] = $message;
+
+ }
+
+ return $messages;
+
+ }
+
+ /**
+ * Parse an event update for an attendee.
+ *
+ * This function figures out if we need to send a reply to an organizer.
+ *
+ * @param VCalendar $calendar
+ * @param array $eventInfo
+ * @param array $oldEventInfo
+ * @param string $attendee
+ *
+ * @return Message[]
+ */
+ protected function parseEventForAttendee(VCalendar $calendar, array $eventInfo, array $oldEventInfo, $attendee) {
+
+ if ($this->scheduleAgentServerRules && $eventInfo['organizerScheduleAgent'] === 'CLIENT') {
+ return [];
+ }
+
+ // Don't bother generating messages for events that have already been
+ // cancelled.
+ if ($eventInfo['status'] === 'CANCELLED') {
+ return [];
+ }
+
+ $oldInstances = !empty($oldEventInfo['attendees'][$attendee]['instances']) ?
+ $oldEventInfo['attendees'][$attendee]['instances'] :
+ [];
+
+ $instances = [];
+ foreach ($oldInstances as $instance) {
+
+ $instances[$instance['id']] = [
+ 'id' => $instance['id'],
+ 'oldstatus' => $instance['partstat'],
+ 'newstatus' => null,
+ ];
+
+ }
+ foreach ($eventInfo['attendees'][$attendee]['instances'] as $instance) {
+
+ if (isset($instances[$instance['id']])) {
+ $instances[$instance['id']]['newstatus'] = $instance['partstat'];
+ } else {
+ $instances[$instance['id']] = [
+ 'id' => $instance['id'],
+ 'oldstatus' => null,
+ 'newstatus' => $instance['partstat'],
+ ];
+ }
+
+ }
+
+ // We need to also look for differences in EXDATE. If there are new
+ // items in EXDATE, it means that an attendee deleted instances of an
+ // event, which means we need to send DECLINED specifically for those
+ // instances.
+ // We only need to do that though, if the master event is not declined.
+ if (isset($instances['master']) && $instances['master']['newstatus'] !== 'DECLINED') {
+ foreach ($eventInfo['exdate'] as $exDate) {
+
+ if (!in_array($exDate, $oldEventInfo['exdate'])) {
+ if (isset($instances[$exDate])) {
+ $instances[$exDate]['newstatus'] = 'DECLINED';
+ } else {
+ $instances[$exDate] = [
+ 'id' => $exDate,
+ 'oldstatus' => null,
+ 'newstatus' => 'DECLINED',
+ ];
+ }
+ }
+
+ }
+ }
+
+ // Gathering a few extra properties for each instance.
+ foreach ($instances as $recurId => $instanceInfo) {
+
+ if (isset($eventInfo['instances'][$recurId])) {
+ $instances[$recurId]['dtstart'] = clone $eventInfo['instances'][$recurId]->DTSTART;
+ } else {
+ $instances[$recurId]['dtstart'] = $recurId;
+ }
+
+ }
+
+ $message = new Message();
+ $message->uid = $eventInfo['uid'];
+ $message->method = 'REPLY';
+ $message->component = 'VEVENT';
+ $message->sequence = $eventInfo['sequence'];
+ $message->sender = $attendee;
+ $message->senderName = $eventInfo['attendees'][$attendee]['name'];
+ $message->recipient = $eventInfo['organizer'];
+ $message->recipientName = $eventInfo['organizerName'];
+
+ $icalMsg = new VCalendar();
+ $icalMsg->METHOD = 'REPLY';
+
+ $hasReply = false;
+
+ foreach ($instances as $instance) {
+
+ if ($instance['oldstatus'] == $instance['newstatus'] && $eventInfo['organizerForceSend'] !== 'REPLY') {
+ // Skip
+ continue;
+ }
+
+ $event = $icalMsg->add('VEVENT', [
+ 'UID' => $message->uid,
+ 'SEQUENCE' => $message->sequence,
+ ]);
+ $summary = isset($calendar->VEVENT->SUMMARY) ? $calendar->VEVENT->SUMMARY->getValue() : '';
+ // Adding properties from the correct source instance
+ if (isset($eventInfo['instances'][$instance['id']])) {
+ $instanceObj = $eventInfo['instances'][$instance['id']];
+ $event->add(clone $instanceObj->DTSTART);
+ if (isset($instanceObj->DTEND)) {
+ $event->add(clone $instanceObj->DTEND);
+ } elseif (isset($instanceObj->DURATION)) {
+ $event->add(clone $instanceObj->DURATION);
+ }
+ if (isset($instanceObj->SUMMARY)) {
+ $event->add('SUMMARY', $instanceObj->SUMMARY->getValue());
+ } elseif ($summary) {
+ $event->add('SUMMARY', $summary);
+ }
+ } else {
+ // This branch of the code is reached, when a reply is
+ // generated for an instance of a recurring event, through the
+ // fact that the instance has disappeared by showing up in
+ // EXDATE
+ $dt = DateTimeParser::parse($instance['id'], $eventInfo['timezone']);
+ // Treat is as a DATE field
+ if (strlen($instance['id']) <= 8) {
+ $event->add('DTSTART', $dt, ['VALUE' => 'DATE']);
+ } else {
+ $event->add('DTSTART', $dt);
+ }
+ if ($summary) {
+ $event->add('SUMMARY', $summary);
+ }
+ }
+ if ($instance['id'] !== 'master') {
+ $dt = DateTimeParser::parse($instance['id'], $eventInfo['timezone']);
+ // Treat is as a DATE field
+ if (strlen($instance['id']) <= 8) {
+ $event->add('RECURRENCE-ID', $dt, ['VALUE' => 'DATE']);
+ } else {
+ $event->add('RECURRENCE-ID', $dt);
+ }
+ }
+ $organizer = $event->add('ORGANIZER', $message->recipient);
+ if ($message->recipientName) {
+ $organizer['CN'] = $message->recipientName;
+ }
+ $attendee = $event->add('ATTENDEE', $message->sender, [
+ 'PARTSTAT' => $instance['newstatus']
+ ]);
+ if ($message->senderName) {
+ $attendee['CN'] = $message->senderName;
+ }
+ $hasReply = true;
+
+ }
+
+ if ($hasReply) {
+ $message->message = $icalMsg;
+ return [$message];
+ } else {
+ return [];
+ }
+
+ }
+
+ /**
+ * Returns attendee information and information about instances of an
+ * event.
+ *
+ * Returns an array with the following keys:
+ *
+ * 1. uid
+ * 2. organizer
+ * 3. organizerName
+ * 4. organizerScheduleAgent
+ * 5. organizerForceSend
+ * 6. instances
+ * 7. attendees
+ * 8. sequence
+ * 9. exdate
+ * 10. timezone - strictly the timezone on which the recurrence rule is
+ * based on.
+ * 11. significantChangeHash
+ * 12. status
+ * @param VCalendar $calendar
+ *
+ * @return array
+ */
+ protected function parseEventInfo(VCalendar $calendar = null) {
+
+ $uid = null;
+ $organizer = null;
+ $organizerName = null;
+ $organizerForceSend = null;
+ $sequence = null;
+ $timezone = null;
+ $status = null;
+ $organizerScheduleAgent = 'SERVER';
+
+ $significantChangeHash = '';
+
+ // Now we need to collect a list of attendees, and which instances they
+ // are a part of.
+ $attendees = [];
+
+ $instances = [];
+ $exdate = [];
+
+ foreach ($calendar->VEVENT as $vevent) {
+
+ if (is_null($uid)) {
+ $uid = $vevent->UID->getValue();
+ } else {
+ if ($uid !== $vevent->UID->getValue()) {
+ throw new ITipException('If a calendar contained more than one event, they must have the same UID.');
+ }
+ }
+
+ if (!isset($vevent->DTSTART)) {
+ throw new ITipException('An event MUST have a DTSTART property.');
+ }
+
+ if (isset($vevent->ORGANIZER)) {
+ if (is_null($organizer)) {
+ $organizer = $vevent->ORGANIZER->getNormalizedValue();
+ $organizerName = isset($vevent->ORGANIZER['CN']) ? $vevent->ORGANIZER['CN'] : null;
+ } else {
+ if ($organizer !== $vevent->ORGANIZER->getNormalizedValue()) {
+ throw new SameOrganizerForAllComponentsException('Every instance of the event must have the same organizer.');
+ }
+ }
+ $organizerForceSend =
+ isset($vevent->ORGANIZER['SCHEDULE-FORCE-SEND']) ?
+ strtoupper($vevent->ORGANIZER['SCHEDULE-FORCE-SEND']) :
+ null;
+ $organizerScheduleAgent =
+ isset($vevent->ORGANIZER['SCHEDULE-AGENT']) ?
+ strtoupper((string)$vevent->ORGANIZER['SCHEDULE-AGENT']) :
+ 'SERVER';
+ }
+ if (is_null($sequence) && isset($vevent->SEQUENCE)) {
+ $sequence = $vevent->SEQUENCE->getValue();
+ }
+ if (isset($vevent->EXDATE)) {
+ foreach ($vevent->select('EXDATE') as $val) {
+ $exdate = array_merge($exdate, $val->getParts());
+ }
+ sort($exdate);
+ }
+ if (isset($vevent->STATUS)) {
+ $status = strtoupper($vevent->STATUS->getValue());
+ }
+
+ $recurId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->getValue() : 'master';
+ if (is_null($timezone)) {
+ if ($recurId === 'master') {
+ $timezone = $vevent->DTSTART->getDateTime()->getTimeZone();
+ } else {
+ $timezone = $vevent->{'RECURRENCE-ID'}->getDateTime()->getTimeZone();
+ }
+ }
+ if (isset($vevent->ATTENDEE)) {
+ foreach ($vevent->ATTENDEE as $attendee) {
+
+ if ($this->scheduleAgentServerRules &&
+ isset($attendee['SCHEDULE-AGENT']) &&
+ strtoupper($attendee['SCHEDULE-AGENT']->getValue()) === 'CLIENT'
+ ) {
+ continue;
+ }
+ $partStat =
+ isset($attendee['PARTSTAT']) ?
+ strtoupper($attendee['PARTSTAT']) :
+ 'NEEDS-ACTION';
+
+ $forceSend =
+ isset($attendee['SCHEDULE-FORCE-SEND']) ?
+ strtoupper($attendee['SCHEDULE-FORCE-SEND']) :
+ null;
+
+
+ if (isset($attendees[$attendee->getNormalizedValue()])) {
+ $attendees[$attendee->getNormalizedValue()]['instances'][$recurId] = [
+ 'id' => $recurId,
+ 'partstat' => $partStat,
+ 'force-send' => $forceSend,
+ ];
+ } else {
+ $attendees[$attendee->getNormalizedValue()] = [
+ 'href' => $attendee->getNormalizedValue(),
+ 'instances' => [
+ $recurId => [
+ 'id' => $recurId,
+ 'partstat' => $partStat,
+ ],
+ ],
+ 'name' => isset($attendee['CN']) ? (string)$attendee['CN'] : null,
+ 'forceSend' => $forceSend,
+ ];
+ }
+
+ }
+ $instances[$recurId] = $vevent;
+
+ }
+
+ foreach ($this->significantChangeProperties as $prop) {
+ if (isset($vevent->$prop)) {
+ $propertyValues = $vevent->select($prop);
+
+ $significantChangeHash .= $prop . ':';
+
+ if ($prop === 'EXDATE') {
+
+ $significantChangeHash .= implode(',', $exdate) . ';';
+
+ } else {
+
+ foreach ($propertyValues as $val) {
+ $significantChangeHash .= $val->getValue() . ';';
+ }
+
+ }
+ }
+ }
+
+ }
+ $significantChangeHash = md5($significantChangeHash);
+
+ return compact(
+ 'uid',
+ 'organizer',
+ 'organizerName',
+ 'organizerScheduleAgent',
+ 'organizerForceSend',
+ 'instances',
+ 'attendees',
+ 'sequence',
+ 'exdate',
+ 'timezone',
+ 'significantChangeHash',
+ 'status'
+ );
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/ITip/ITipException.php b/vendor/sabre/vobject/lib/ITip/ITipException.php
new file mode 100644
index 000000000..ad5e53ab4
--- /dev/null
+++ b/vendor/sabre/vobject/lib/ITip/ITipException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+use Exception;
+
+/**
+ * This message is emitted in case of serious problems with iTip messages.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ITipException extends Exception {
+}
diff --git a/vendor/sabre/vobject/lib/ITip/Message.php b/vendor/sabre/vobject/lib/ITip/Message.php
new file mode 100644
index 000000000..bebe2e4fc
--- /dev/null
+++ b/vendor/sabre/vobject/lib/ITip/Message.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+/**
+ * This class represents an iTip message.
+ *
+ * A message holds all the information relevant to the message, including the
+ * object itself.
+ *
+ * It should for the most part be treated as immutable.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Message {
+
+ /**
+ * The object's UID.
+ *
+ * @var string
+ */
+ public $uid;
+
+ /**
+ * The component type, such as VEVENT.
+ *
+ * @var string
+ */
+ public $component;
+
+ /**
+ * Contains the ITip method, which is something like REQUEST, REPLY or
+ * CANCEL.
+ *
+ * @var string
+ */
+ public $method;
+
+ /**
+ * The current sequence number for the event.
+ *
+ * @var int
+ */
+ public $sequence;
+
+ /**
+ * The senders' email address.
+ *
+ * Note that this does not imply that this has to be used in a From: field
+ * if the message is sent by email. It may also be populated in Reply-To:
+ * or not at all.
+ *
+ * @var string
+ */
+ public $sender;
+
+ /**
+ * The name of the sender. This is often populated from a CN parameter from
+ * either the ORGANIZER or ATTENDEE, depending on the message.
+ *
+ * @var string|null
+ */
+ public $senderName;
+
+ /**
+ * The recipient's email address.
+ *
+ * @var string
+ */
+ public $recipient;
+
+ /**
+ * The name of the recipient. This is usually populated with the CN
+ * parameter from the ATTENDEE or ORGANIZER property, if it's available.
+ *
+ * @var string|null
+ */
+ public $recipientName;
+
+ /**
+ * After the message has been delivered, this should contain a string such
+ * as : 1.1;Sent or 1.2;Delivered.
+ *
+ * In case of a failure, this will hold the error status code.
+ *
+ * See:
+ * http://tools.ietf.org/html/rfc6638#section-7.3
+ *
+ * @var string
+ */
+ public $scheduleStatus;
+
+ /**
+ * The iCalendar / iTip body.
+ *
+ * @var \Sabre\VObject\Component\VCalendar
+ */
+ public $message;
+
+ /**
+ * This will be set to true, if the iTip broker considers the change
+ * 'significant'.
+ *
+ * In practice, this means that we'll only mark it true, if for instance
+ * DTSTART changed. This allows systems to only send iTip messages when
+ * significant changes happened. This is especially useful for iMip, as
+ * normally a ton of messages may be generated for normal calendar use.
+ *
+ * To see the list of properties that are considered 'significant', check
+ * out Sabre\VObject\ITip\Broker::$significantChangeProperties.
+ *
+ * @var bool
+ */
+ public $significantChange = true;
+
+ /**
+ * Returns the schedule status as a string.
+ *
+ * For example:
+ * 1.2
+ *
+ * @return mixed bool|string
+ */
+ function getScheduleStatus() {
+
+ if (!$this->scheduleStatus) {
+
+ return false;
+
+ } else {
+
+ list($scheduleStatus) = explode(';', $this->scheduleStatus);
+ return $scheduleStatus;
+
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/ITip/SameOrganizerForAllComponentsException.php b/vendor/sabre/vobject/lib/ITip/SameOrganizerForAllComponentsException.php
new file mode 100644
index 000000000..423b39831
--- /dev/null
+++ b/vendor/sabre/vobject/lib/ITip/SameOrganizerForAllComponentsException.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+/**
+ * SameOrganizerForAllComponentsException.
+ *
+ * This exception is emitted when an event is encountered with more than one
+ * component (e.g.: exceptions), but the organizer is not identical in every
+ * component.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class SameOrganizerForAllComponentsException extends ITipException {
+
+}
diff --git a/vendor/sabre/vobject/lib/InvalidDataException.php b/vendor/sabre/vobject/lib/InvalidDataException.php
new file mode 100644
index 000000000..50ebc0f49
--- /dev/null
+++ b/vendor/sabre/vobject/lib/InvalidDataException.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * This exception is thrown whenever an invalid value is found anywhere in a
+ * iCalendar or vCard object.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class InvalidDataException extends \Exception {
+}
diff --git a/vendor/sabre/vobject/lib/Node.php b/vendor/sabre/vobject/lib/Node.php
new file mode 100644
index 000000000..e2845da75
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Node.php
@@ -0,0 +1,265 @@
+<?php
+
+namespace Sabre\VObject;
+
+use Sabre\Xml;
+
+/**
+ * A node is the root class for every element in an iCalendar of vCard object.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class Node
+ implements \IteratorAggregate,
+ \ArrayAccess,
+ \Countable,
+ \JsonSerializable,
+ Xml\XmlSerializable {
+
+ /**
+ * The following constants are used by the validate() method.
+ *
+ * If REPAIR is set, the validator will attempt to repair any broken data
+ * (if possible).
+ */
+ const REPAIR = 1;
+
+ /**
+ * If this option is set, the validator will operate on the vcards on the
+ * assumption that the vcards need to be valid for CardDAV.
+ *
+ * This means for example that the UID is required, whereas it is not for
+ * regular vcards.
+ */
+ const PROFILE_CARDDAV = 2;
+
+ /**
+ * If this option is set, the validator will operate on iCalendar objects
+ * on the assumption that the vcards need to be valid for CalDAV.
+ *
+ * This means for example that calendars can only contain objects with
+ * identical component types and UIDs.
+ */
+ const PROFILE_CALDAV = 4;
+
+ /**
+ * Reference to the parent object, if this is not the top object.
+ *
+ * @var Node
+ */
+ public $parent;
+
+ /**
+ * Iterator override.
+ *
+ * @var ElementList
+ */
+ protected $iterator = null;
+
+ /**
+ * The root document.
+ *
+ * @var Component
+ */
+ protected $root;
+
+ /**
+ * Serializes the node into a mimedir format.
+ *
+ * @return string
+ */
+ abstract function serialize();
+
+ /**
+ * This method returns an array, with the representation as it should be
+ * encoded in JSON. This is used to create jCard or jCal documents.
+ *
+ * @return array
+ */
+ abstract function jsonSerialize();
+
+ /**
+ * This method serializes the data into XML. This is used to create xCard or
+ * xCal documents.
+ *
+ * @param Xml\Writer $writer XML writer.
+ *
+ * @return void
+ */
+ abstract function xmlSerialize(Xml\Writer $writer);
+
+ /**
+ * Call this method on a document if you're done using it.
+ *
+ * It's intended to remove all circular references, so PHP can easily clean
+ * it up.
+ *
+ * @return void
+ */
+ function destroy() {
+
+ $this->parent = null;
+ $this->root = null;
+
+ }
+
+ /* {{{ IteratorAggregator interface */
+
+ /**
+ * Returns the iterator for this object.
+ *
+ * @return ElementList
+ */
+ function getIterator() {
+
+ if (!is_null($this->iterator)) {
+ return $this->iterator;
+ }
+
+ return new ElementList([$this]);
+
+ }
+
+ /**
+ * Sets the overridden iterator.
+ *
+ * Note that this is not actually part of the iterator interface
+ *
+ * @param ElementList $iterator
+ *
+ * @return void
+ */
+ function setIterator(ElementList $iterator) {
+
+ $this->iterator = $iterator;
+
+ }
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ *
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on)
+ * 2 - An inconsequential issue
+ * 3 - A severe issue.
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ return [];
+
+ }
+
+ /* }}} */
+
+ /* {{{ Countable interface */
+
+ /**
+ * Returns the number of elements.
+ *
+ * @return int
+ */
+ function count() {
+
+ $it = $this->getIterator();
+ return $it->count();
+
+ }
+
+ /* }}} */
+
+ /* {{{ ArrayAccess Interface */
+
+
+ /**
+ * Checks if an item exists through ArrayAccess.
+ *
+ * This method just forwards the request to the inner iterator
+ *
+ * @param int $offset
+ *
+ * @return bool
+ */
+ function offsetExists($offset) {
+
+ $iterator = $this->getIterator();
+ return $iterator->offsetExists($offset);
+
+ }
+
+ /**
+ * Gets an item through ArrayAccess.
+ *
+ * This method just forwards the request to the inner iterator
+ *
+ * @param int $offset
+ *
+ * @return mixed
+ */
+ function offsetGet($offset) {
+
+ $iterator = $this->getIterator();
+ return $iterator->offsetGet($offset);
+
+ }
+
+ /**
+ * Sets an item through ArrayAccess.
+ *
+ * This method just forwards the request to the inner iterator
+ *
+ * @param int $offset
+ * @param mixed $value
+ *
+ * @return void
+ */
+ function offsetSet($offset, $value) {
+
+ $iterator = $this->getIterator();
+ $iterator->offsetSet($offset, $value);
+
+ // @codeCoverageIgnoreStart
+ //
+ // This method always throws an exception, so we ignore the closing
+ // brace
+ }
+ // @codeCoverageIgnoreEnd
+
+ /**
+ * Sets an item through ArrayAccess.
+ *
+ * This method just forwards the request to the inner iterator
+ *
+ * @param int $offset
+ *
+ * @return void
+ */
+ function offsetUnset($offset) {
+
+ $iterator = $this->getIterator();
+ $iterator->offsetUnset($offset);
+
+ // @codeCoverageIgnoreStart
+ //
+ // This method always throws an exception, so we ignore the closing
+ // brace
+ }
+ // @codeCoverageIgnoreEnd
+
+ /* }}} */
+}
diff --git a/vendor/sabre/vobject/lib/PHPUnitAssertions.php b/vendor/sabre/vobject/lib/PHPUnitAssertions.php
new file mode 100644
index 000000000..87ec75e8f
--- /dev/null
+++ b/vendor/sabre/vobject/lib/PHPUnitAssertions.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * PHPUnit Assertions
+ *
+ * This trait can be added to your unittest to make it easier to test iCalendar
+ * and/or vCards.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+trait PHPUnitAssertions {
+
+ /**
+ * This method tests wether two vcards or icalendar objects are
+ * semantically identical.
+ *
+ * It supports objects being supplied as strings, streams or
+ * Sabre\VObject\Component instances.
+ *
+ * PRODID is removed from both objects as this is often changes and would
+ * just get in the way.
+ *
+ * CALSCALE will automatically get removed if it's set to GREGORIAN.
+ *
+ * Any property that has the value **ANY** will be treated as a wildcard.
+ *
+ * @param resource|string|Component $expected
+ * @param resource|string|Component $actual
+ * @param string $message
+ */
+ function assertVObjectEqualsVObject($expected, $actual, $message = '') {
+
+ $self = $this;
+ $getObj = function($input) use ($self) {
+
+ if (is_resource($input)) {
+ $input = stream_get_contents($input);
+ }
+ if (is_string($input)) {
+ $input = Reader::read($input);
+ }
+ if (!$input instanceof Component) {
+ $this->fail('Input must be a string, stream or VObject component');
+ }
+ unset($input->PRODID);
+ if ($input instanceof Component\VCalendar && (string)$input->CALSCALE === 'GREGORIAN') {
+ unset($input->CALSCALE);
+ }
+ return $input;
+
+ };
+
+ $expected = $getObj($expected)->serialize();
+ $actual = $getObj($actual)->serialize();
+
+ // Finding wildcards in expected.
+ preg_match_all('|^([A-Z]+):\\*\\*ANY\\*\\*\r$|m', $expected, $matches, PREG_SET_ORDER);
+
+ foreach ($matches as $match) {
+
+ $actual = preg_replace(
+ '|^' . preg_quote($match[1], '|') . ':(.*)\r$|m',
+ $match[1] . ':**ANY**' . "\r",
+ $actual
+ );
+
+ }
+
+ $this->assertEquals(
+ $expected,
+ $actual,
+ $message
+ );
+
+ }
+
+
+}
diff --git a/vendor/sabre/vobject/lib/Parameter.php b/vendor/sabre/vobject/lib/Parameter.php
new file mode 100644
index 000000000..270643eab
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Parameter.php
@@ -0,0 +1,394 @@
+<?php
+
+namespace Sabre\VObject;
+
+use Sabre\Xml;
+use ArrayIterator;
+
+/**
+ * VObject Parameter.
+ *
+ * This class represents a parameter. A parameter is always tied to a property.
+ * In the case of:
+ * DTSTART;VALUE=DATE:20101108
+ * VALUE=DATE would be the parameter name and value.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Parameter extends Node {
+
+ /**
+ * Parameter name.
+ *
+ * @var string
+ */
+ public $name;
+
+ /**
+ * vCard 2.1 allows parameters to be encoded without a name.
+ *
+ * We can deduce the parameter name based on it's value.
+ *
+ * @var bool
+ */
+ public $noName = false;
+
+ /**
+ * Parameter value.
+ *
+ * @var string
+ */
+ protected $value;
+
+ /**
+ * Sets up the object.
+ *
+ * It's recommended to use the create:: factory method instead.
+ *
+ * @param string $name
+ * @param string $value
+ */
+ function __construct(Document $root, $name, $value = null) {
+
+ $this->name = strtoupper($name);
+ $this->root = $root;
+ if (is_null($name)) {
+ $this->noName = true;
+ $this->name = static::guessParameterNameByValue($value);
+ }
+
+ // If guessParameterNameByValue() returns an empty string
+ // above, we're actually dealing with a parameter that has no value.
+ // In that case we have to move the value to the name.
+ if ($this->name === '') {
+ $this->noName = false;
+ $this->name = strtoupper($value);
+ } else {
+ $this->setValue($value);
+ }
+
+ }
+
+ /**
+ * Try to guess property name by value, can be used for vCard 2.1 nameless parameters.
+ *
+ * Figuring out what the name should have been. Note that a ton of
+ * these are rather silly in 2014 and would probably rarely be
+ * used, but we like to be complete.
+ *
+ * @param string $value
+ *
+ * @return string
+ */
+ static function guessParameterNameByValue($value) {
+ switch (strtoupper($value)) {
+
+ // Encodings
+ case '7-BIT' :
+ case 'QUOTED-PRINTABLE' :
+ case 'BASE64' :
+ $name = 'ENCODING';
+ break;
+
+ // Common types
+ case 'WORK' :
+ case 'HOME' :
+ case 'PREF' :
+
+ // Delivery Label Type
+ case 'DOM' :
+ case 'INTL' :
+ case 'POSTAL' :
+ case 'PARCEL' :
+
+ // Telephone types
+ case 'VOICE' :
+ case 'FAX' :
+ case 'MSG' :
+ case 'CELL' :
+ case 'PAGER' :
+ case 'BBS' :
+ case 'MODEM' :
+ case 'CAR' :
+ case 'ISDN' :
+ case 'VIDEO' :
+
+ // EMAIL types (lol)
+ case 'AOL' :
+ case 'APPLELINK' :
+ case 'ATTMAIL' :
+ case 'CIS' :
+ case 'EWORLD' :
+ case 'INTERNET' :
+ case 'IBMMAIL' :
+ case 'MCIMAIL' :
+ case 'POWERSHARE' :
+ case 'PRODIGY' :
+ case 'TLX' :
+ case 'X400' :
+
+ // Photo / Logo format types
+ case 'GIF' :
+ case 'CGM' :
+ case 'WMF' :
+ case 'BMP' :
+ case 'DIB' :
+ case 'PICT' :
+ case 'TIFF' :
+ case 'PDF' :
+ case 'PS' :
+ case 'JPEG' :
+ case 'MPEG' :
+ case 'MPEG2' :
+ case 'AVI' :
+ case 'QTIME' :
+
+ // Sound Digital Audio Type
+ case 'WAVE' :
+ case 'PCM' :
+ case 'AIFF' :
+
+ // Key types
+ case 'X509' :
+ case 'PGP' :
+ $name = 'TYPE';
+ break;
+
+ // Value types
+ case 'INLINE' :
+ case 'URL' :
+ case 'CONTENT-ID' :
+ case 'CID' :
+ $name = 'VALUE';
+ break;
+
+ default:
+ $name = '';
+ }
+
+ return $name;
+ }
+
+ /**
+ * Updates the current value.
+ *
+ * This may be either a single, or multiple strings in an array.
+ *
+ * @param string|array $value
+ *
+ * @return void
+ */
+ function setValue($value) {
+
+ $this->value = $value;
+
+ }
+
+ /**
+ * Returns the current value.
+ *
+ * This method will always return a string, or null. If there were multiple
+ * values, it will automatically concatenate them (separated by comma).
+ *
+ * @return string|null
+ */
+ function getValue() {
+
+ if (is_array($this->value)) {
+ return implode(',', $this->value);
+ } else {
+ return $this->value;
+ }
+
+ }
+
+ /**
+ * Sets multiple values for this parameter.
+ *
+ * @param array $value
+ *
+ * @return void
+ */
+ function setParts(array $value) {
+
+ $this->value = $value;
+
+ }
+
+ /**
+ * Returns all values for this parameter.
+ *
+ * If there were no values, an empty array will be returned.
+ *
+ * @return array
+ */
+ function getParts() {
+
+ if (is_array($this->value)) {
+ return $this->value;
+ } elseif (is_null($this->value)) {
+ return [];
+ } else {
+ return [$this->value];
+ }
+
+ }
+
+ /**
+ * Adds a value to this parameter.
+ *
+ * If the argument is specified as an array, all items will be added to the
+ * parameter value list.
+ *
+ * @param string|array $part
+ *
+ * @return void
+ */
+ function addValue($part) {
+
+ if (is_null($this->value)) {
+ $this->value = $part;
+ } else {
+ $this->value = array_merge((array)$this->value, (array)$part);
+ }
+
+ }
+
+ /**
+ * Checks if this parameter contains the specified value.
+ *
+ * This is a case-insensitive match. It makes sense to call this for for
+ * instance the TYPE parameter, to see if it contains a keyword such as
+ * 'WORK' or 'FAX'.
+ *
+ * @param string $value
+ *
+ * @return bool
+ */
+ function has($value) {
+
+ return in_array(
+ strtolower($value),
+ array_map('strtolower', (array)$this->value)
+ );
+
+ }
+
+ /**
+ * Turns the object back into a serialized blob.
+ *
+ * @return string
+ */
+ function serialize() {
+
+ $value = $this->getParts();
+
+ if (count($value) === 0) {
+ return $this->name . '=';
+ }
+
+ if ($this->root->getDocumentType() === Document::VCARD21 && $this->noName) {
+
+ return implode(';', $value);
+
+ }
+
+ return $this->name . '=' . array_reduce(
+ $value,
+ function($out, $item) {
+
+ if (!is_null($out)) $out .= ',';
+
+ // If there's no special characters in the string, we'll use the simple
+ // format.
+ //
+ // The list of special characters is defined as:
+ //
+ // Any character except CONTROL, DQUOTE, ";", ":", ","
+ //
+ // by the iCalendar spec:
+ // https://tools.ietf.org/html/rfc5545#section-3.1
+ //
+ // And we add ^ to that because of:
+ // https://tools.ietf.org/html/rfc6868
+ //
+ // But we've found that iCal (7.0, shipped with OSX 10.9)
+ // severaly trips on + characters not being quoted, so we
+ // added + as well.
+ if (!preg_match('#(?: [\n":;\^,\+] )#x', $item)) {
+ return $out . $item;
+ } else {
+ // Enclosing in double-quotes, and using RFC6868 for encoding any
+ // special characters
+ $out .= '"' . strtr(
+ $item,
+ [
+ '^' => '^^',
+ "\n" => '^n',
+ '"' => '^\'',
+ ]
+ ) . '"';
+ return $out;
+ }
+
+ }
+ );
+
+ }
+
+ /**
+ * This method returns an array, with the representation as it should be
+ * encoded in JSON. This is used to create jCard or jCal documents.
+ *
+ * @return array
+ */
+ function jsonSerialize() {
+
+ return $this->value;
+
+ }
+
+ /**
+ * This method serializes the data into XML. This is used to create xCard or
+ * xCal documents.
+ *
+ * @param Xml\Writer $writer XML writer.
+ *
+ * @return void
+ */
+ function xmlSerialize(Xml\Writer $writer) {
+
+ foreach (explode(',', $this->value) as $value) {
+ $writer->writeElement('text', $value);
+ }
+
+ }
+
+ /**
+ * Called when this object is being cast to a string.
+ *
+ * @return string
+ */
+ function __toString() {
+
+ return (string)$this->getValue();
+
+ }
+
+ /**
+ * Returns the iterator for this object.
+ *
+ * @return ElementList
+ */
+ function getIterator() {
+
+ if (!is_null($this->iterator))
+ return $this->iterator;
+
+ return $this->iterator = new ArrayIterator((array)$this->value);
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/ParseException.php b/vendor/sabre/vobject/lib/ParseException.php
new file mode 100644
index 000000000..d96d20720
--- /dev/null
+++ b/vendor/sabre/vobject/lib/ParseException.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * Exception thrown by Reader if an invalid object was attempted to be parsed.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ParseException extends \Exception {
+}
diff --git a/vendor/sabre/vobject/lib/Parser/Json.php b/vendor/sabre/vobject/lib/Parser/Json.php
new file mode 100644
index 000000000..370c981a8
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Parser/Json.php
@@ -0,0 +1,197 @@
+<?php
+
+namespace Sabre\VObject\Parser;
+
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Component\VCard;
+use Sabre\VObject\ParseException;
+use Sabre\VObject\EofException;
+
+/**
+ * Json Parser.
+ *
+ * This parser parses both the jCal and jCard formats.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Json extends Parser {
+
+ /**
+ * The input data.
+ *
+ * @var array
+ */
+ protected $input;
+
+ /**
+ * Root component.
+ *
+ * @var Document
+ */
+ protected $root;
+
+ /**
+ * This method starts the parsing process.
+ *
+ * If the input was not supplied during construction, it's possible to pass
+ * it here instead.
+ *
+ * If either input or options are not supplied, the defaults will be used.
+ *
+ * @param resource|string|array|null $input
+ * @param int $options
+ *
+ * @return Sabre\VObject\Document
+ */
+ function parse($input = null, $options = 0) {
+
+ if (!is_null($input)) {
+ $this->setInput($input);
+ }
+ if (is_null($this->input)) {
+ throw new EofException('End of input stream, or no input supplied');
+ }
+
+ if (0 !== $options) {
+ $this->options = $options;
+ }
+
+ switch ($this->input[0]) {
+ case 'vcalendar' :
+ $this->root = new VCalendar([], false);
+ break;
+ case 'vcard' :
+ $this->root = new VCard([], false);
+ break;
+ default :
+ throw new ParseException('The root component must either be a vcalendar, or a vcard');
+
+ }
+ foreach ($this->input[1] as $prop) {
+ $this->root->add($this->parseProperty($prop));
+ }
+ if (isset($this->input[2])) foreach ($this->input[2] as $comp) {
+ $this->root->add($this->parseComponent($comp));
+ }
+
+ // Resetting the input so we can throw an feof exception the next time.
+ $this->input = null;
+
+ return $this->root;
+
+ }
+
+ /**
+ * Parses a component.
+ *
+ * @param array $jComp
+ *
+ * @return \Sabre\VObject\Component
+ */
+ function parseComponent(array $jComp) {
+
+ // We can remove $self from PHP 5.4 onward.
+ $self = $this;
+
+ $properties = array_map(
+ function($jProp) use ($self) {
+ return $self->parseProperty($jProp);
+ },
+ $jComp[1]
+ );
+
+ if (isset($jComp[2])) {
+
+ $components = array_map(
+ function($jComp) use ($self) {
+ return $self->parseComponent($jComp);
+ },
+ $jComp[2]
+ );
+
+ } else $components = [];
+
+ return $this->root->createComponent(
+ $jComp[0],
+ array_merge($properties, $components),
+ $defaults = false
+ );
+
+ }
+
+ /**
+ * Parses properties.
+ *
+ * @param array $jProp
+ *
+ * @return \Sabre\VObject\Property
+ */
+ function parseProperty(array $jProp) {
+
+ list(
+ $propertyName,
+ $parameters,
+ $valueType
+ ) = $jProp;
+
+ $propertyName = strtoupper($propertyName);
+
+ // This is the default class we would be using if we didn't know the
+ // value type. We're using this value later in this function.
+ $defaultPropertyClass = $this->root->getClassNameForPropertyName($propertyName);
+
+ $parameters = (array)$parameters;
+
+ $value = array_slice($jProp, 3);
+
+ $valueType = strtoupper($valueType);
+
+ if (isset($parameters['group'])) {
+ $propertyName = $parameters['group'] . '.' . $propertyName;
+ unset($parameters['group']);
+ }
+
+ $prop = $this->root->createProperty($propertyName, null, $parameters, $valueType);
+ $prop->setJsonValue($value);
+
+ // We have to do something awkward here. FlatText as well as Text
+ // represents TEXT values. We have to normalize these here. In the
+ // future we can get rid of FlatText once we're allowed to break BC
+ // again.
+ if ($defaultPropertyClass === 'Sabre\VObject\Property\FlatText') {
+ $defaultPropertyClass = 'Sabre\VObject\Property\Text';
+ }
+
+ // If the value type we received (e.g.: TEXT) was not the default value
+ // type for the given property (e.g.: BDAY), we need to add a VALUE=
+ // parameter.
+ if ($defaultPropertyClass !== get_class($prop)) {
+ $prop["VALUE"] = $valueType;
+ }
+
+ return $prop;
+
+ }
+
+ /**
+ * Sets the input data.
+ *
+ * @param resource|string|array $input
+ *
+ * @return void
+ */
+ function setInput($input) {
+
+ if (is_resource($input)) {
+ $input = stream_get_contents($input);
+ }
+ if (is_string($input)) {
+ $input = json_decode($input);
+ }
+ $this->input = $input;
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Parser/MimeDir.php b/vendor/sabre/vobject/lib/Parser/MimeDir.php
new file mode 100644
index 000000000..6a7f9317b
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Parser/MimeDir.php
@@ -0,0 +1,696 @@
+<?php
+
+namespace Sabre\VObject\Parser;
+
+use Sabre\VObject\ParseException;
+use Sabre\VObject\EofException;
+use Sabre\VObject\Component;
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Component\VCard;
+use Sabre\VObject\Document;
+
+/**
+ * MimeDir parser.
+ *
+ * This class parses iCalendar 2.0 and vCard 2.1, 3.0 and 4.0 files. This
+ * parser will return one of the following two objects from the parse method:
+ *
+ * Sabre\VObject\Component\VCalendar
+ * Sabre\VObject\Component\VCard
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class MimeDir extends Parser {
+
+ /**
+ * The input stream.
+ *
+ * @var resource
+ */
+ protected $input;
+
+ /**
+ * Root component.
+ *
+ * @var Component
+ */
+ protected $root;
+
+ /**
+ * By default all input will be assumed to be UTF-8.
+ *
+ * However, both iCalendar and vCard might be encoded using different
+ * character sets. The character set is usually set in the mime-type.
+ *
+ * If this is the case, use setEncoding to specify that a different
+ * encoding will be used. If this is set, the parser will automatically
+ * convert all incoming data to UTF-8.
+ *
+ * @var string
+ */
+ protected $charset = 'UTF-8';
+
+ /**
+ * The list of character sets we support when decoding.
+ *
+ * This would be a const expression but for now we need to support PHP 5.5
+ */
+ protected static $SUPPORTED_CHARSETS = [
+ 'UTF-8',
+ 'ISO-8859-1',
+ 'Windows-1252',
+ ];
+
+ /**
+ * Parses an iCalendar or vCard file.
+ *
+ * Pass a stream or a string. If null is parsed, the existing buffer is
+ * used.
+ *
+ * @param string|resource|null $input
+ * @param int $options
+ *
+ * @return Sabre\VObject\Document
+ */
+ function parse($input = null, $options = 0) {
+
+ $this->root = null;
+
+ if (!is_null($input)) {
+ $this->setInput($input);
+ }
+
+ if (0 !== $options) {
+ $this->options = $options;
+ }
+
+ $this->parseDocument();
+
+ return $this->root;
+
+ }
+
+ /**
+ * By default all input will be assumed to be UTF-8.
+ *
+ * However, both iCalendar and vCard might be encoded using different
+ * character sets. The character set is usually set in the mime-type.
+ *
+ * If this is the case, use setEncoding to specify that a different
+ * encoding will be used. If this is set, the parser will automatically
+ * convert all incoming data to UTF-8.
+ *
+ * @param string $charset
+ */
+ function setCharset($charset) {
+
+ if (!in_array($charset, self::$SUPPORTED_CHARSETS)) {
+ throw new \InvalidArgumentException('Unsupported encoding. (Supported encodings: ' . implode(', ', self::$SUPPORTED_CHARSETS) . ')');
+ }
+ $this->charset = $charset;
+
+ }
+
+ /**
+ * Sets the input buffer. Must be a string or stream.
+ *
+ * @param resource|string $input
+ *
+ * @return void
+ */
+ function setInput($input) {
+
+ // Resetting the parser
+ $this->lineIndex = 0;
+ $this->startLine = 0;
+
+ if (is_string($input)) {
+ // Convering to a stream.
+ $stream = fopen('php://temp', 'r+');
+ fwrite($stream, $input);
+ rewind($stream);
+ $this->input = $stream;
+ } elseif (is_resource($input)) {
+ $this->input = $input;
+ } else {
+ throw new \InvalidArgumentException('This parser can only read from strings or streams.');
+ }
+
+ }
+
+ /**
+ * Parses an entire document.
+ *
+ * @return void
+ */
+ protected function parseDocument() {
+
+ $line = $this->readLine();
+
+ // BOM is ZERO WIDTH NO-BREAK SPACE (U+FEFF).
+ // It's 0xEF 0xBB 0xBF in UTF-8 hex.
+ if (3 <= strlen($line)
+ && ord($line[0]) === 0xef
+ && ord($line[1]) === 0xbb
+ && ord($line[2]) === 0xbf) {
+ $line = substr($line, 3);
+ }
+
+ switch (strtoupper($line)) {
+ case 'BEGIN:VCALENDAR' :
+ $class = VCalendar::$componentMap['VCALENDAR'];
+ break;
+ case 'BEGIN:VCARD' :
+ $class = VCard::$componentMap['VCARD'];
+ break;
+ default :
+ throw new ParseException('This parser only supports VCARD and VCALENDAR files');
+ }
+
+ $this->root = new $class([], false);
+
+ while (true) {
+
+ // Reading until we hit END:
+ $line = $this->readLine();
+ if (strtoupper(substr($line, 0, 4)) === 'END:') {
+ break;
+ }
+ $result = $this->parseLine($line);
+ if ($result) {
+ $this->root->add($result);
+ }
+
+ }
+
+ $name = strtoupper(substr($line, 4));
+ if ($name !== $this->root->name) {
+ throw new ParseException('Invalid MimeDir file. expected: "END:' . $this->root->name . '" got: "END:' . $name . '"');
+ }
+
+ }
+
+ /**
+ * Parses a line, and if it hits a component, it will also attempt to parse
+ * the entire component.
+ *
+ * @param string $line Unfolded line
+ *
+ * @return Node
+ */
+ protected function parseLine($line) {
+
+ // Start of a new component
+ if (strtoupper(substr($line, 0, 6)) === 'BEGIN:') {
+
+ $component = $this->root->createComponent(substr($line, 6), [], false);
+
+ while (true) {
+
+ // Reading until we hit END:
+ $line = $this->readLine();
+ if (strtoupper(substr($line, 0, 4)) === 'END:') {
+ break;
+ }
+ $result = $this->parseLine($line);
+ if ($result) {
+ $component->add($result);
+ }
+
+ }
+
+ $name = strtoupper(substr($line, 4));
+ if ($name !== $component->name) {
+ throw new ParseException('Invalid MimeDir file. expected: "END:' . $component->name . '" got: "END:' . $name . '"');
+ }
+
+ return $component;
+
+ } else {
+
+ // Property reader
+ $property = $this->readProperty($line);
+ if (!$property) {
+ // Ignored line
+ return false;
+ }
+ return $property;
+
+ }
+
+ }
+
+ /**
+ * We need to look ahead 1 line every time to see if we need to 'unfold'
+ * the next line.
+ *
+ * If that was not the case, we store it here.
+ *
+ * @var null|string
+ */
+ protected $lineBuffer;
+
+ /**
+ * The real current line number.
+ */
+ protected $lineIndex = 0;
+
+ /**
+ * In the case of unfolded lines, this property holds the line number for
+ * the start of the line.
+ *
+ * @var int
+ */
+ protected $startLine = 0;
+
+ /**
+ * Contains a 'raw' representation of the current line.
+ *
+ * @var string
+ */
+ protected $rawLine;
+
+ /**
+ * Reads a single line from the buffer.
+ *
+ * This method strips any newlines and also takes care of unfolding.
+ *
+ * @throws \Sabre\VObject\EofException
+ *
+ * @return string
+ */
+ protected function readLine() {
+
+ if (!is_null($this->lineBuffer)) {
+ $rawLine = $this->lineBuffer;
+ $this->lineBuffer = null;
+ } else {
+ do {
+ $eof = feof($this->input);
+
+ $rawLine = fgets($this->input);
+
+ if ($eof || (feof($this->input) && $rawLine === false)) {
+ throw new EofException('End of document reached prematurely');
+ }
+ if ($rawLine === false) {
+ throw new ParseException('Error reading from input stream');
+ }
+ $rawLine = rtrim($rawLine, "\r\n");
+ } while ($rawLine === ''); // Skipping empty lines
+ $this->lineIndex++;
+ }
+ $line = $rawLine;
+
+ $this->startLine = $this->lineIndex;
+
+ // Looking ahead for folded lines.
+ while (true) {
+
+ $nextLine = rtrim(fgets($this->input), "\r\n");
+ $this->lineIndex++;
+ if (!$nextLine) {
+ break;
+ }
+ if ($nextLine[0] === "\t" || $nextLine[0] === " ") {
+ $line .= substr($nextLine, 1);
+ $rawLine .= "\n " . substr($nextLine, 1);
+ } else {
+ $this->lineBuffer = $nextLine;
+ break;
+ }
+
+ }
+ $this->rawLine = $rawLine;
+ return $line;
+
+ }
+
+ /**
+ * Reads a property or component from a line.
+ *
+ * @return void
+ */
+ protected function readProperty($line) {
+
+ if ($this->options & self::OPTION_FORGIVING) {
+ $propNameToken = 'A-Z0-9\-\._\\/';
+ } else {
+ $propNameToken = 'A-Z0-9\-\.';
+ }
+
+ $paramNameToken = 'A-Z0-9\-';
+ $safeChar = '^";:,';
+ $qSafeChar = '^"';
+
+ $regex = "/
+ ^(?P<name> [$propNameToken]+ ) (?=[;:]) # property name
+ |
+ (?<=:)(?P<propValue> .+)$ # property value
+ |
+ ;(?P<paramName> [$paramNameToken]+) (?=[=;:]) # parameter name
+ |
+ (=|,)(?P<paramValue> # parameter value
+ (?: [$safeChar]*) |
+ \"(?: [$qSafeChar]+)\"
+ ) (?=[;:,])
+ /xi";
+
+ //echo $regex, "\n"; die();
+ preg_match_all($regex, $line, $matches, PREG_SET_ORDER);
+
+ $property = [
+ 'name' => null,
+ 'parameters' => [],
+ 'value' => null
+ ];
+
+ $lastParam = null;
+
+ /**
+ * Looping through all the tokens.
+ *
+ * Note that we are looping through them in reverse order, because if a
+ * sub-pattern matched, the subsequent named patterns will not show up
+ * in the result.
+ */
+ foreach ($matches as $match) {
+
+ if (isset($match['paramValue'])) {
+ if ($match['paramValue'] && $match['paramValue'][0] === '"') {
+ $value = substr($match['paramValue'], 1, -1);
+ } else {
+ $value = $match['paramValue'];
+ }
+
+ $value = $this->unescapeParam($value);
+
+ if (is_null($lastParam)) {
+ throw new ParseException('Invalid Mimedir file. Line starting at ' . $this->startLine . ' did not follow iCalendar/vCard conventions');
+ }
+ if (is_null($property['parameters'][$lastParam])) {
+ $property['parameters'][$lastParam] = $value;
+ } elseif (is_array($property['parameters'][$lastParam])) {
+ $property['parameters'][$lastParam][] = $value;
+ } else {
+ $property['parameters'][$lastParam] = [
+ $property['parameters'][$lastParam],
+ $value
+ ];
+ }
+ continue;
+ }
+ if (isset($match['paramName'])) {
+ $lastParam = strtoupper($match['paramName']);
+ if (!isset($property['parameters'][$lastParam])) {
+ $property['parameters'][$lastParam] = null;
+ }
+ continue;
+ }
+ if (isset($match['propValue'])) {
+ $property['value'] = $match['propValue'];
+ continue;
+ }
+ if (isset($match['name']) && $match['name']) {
+ $property['name'] = strtoupper($match['name']);
+ continue;
+ }
+
+ // @codeCoverageIgnoreStart
+ throw new \LogicException('This code should not be reachable');
+ // @codeCoverageIgnoreEnd
+
+ }
+
+ if (is_null($property['value'])) {
+ $property['value'] = '';
+ }
+ if (!$property['name']) {
+ if ($this->options & self::OPTION_IGNORE_INVALID_LINES) {
+ return false;
+ }
+ throw new ParseException('Invalid Mimedir file. Line starting at ' . $this->startLine . ' did not follow iCalendar/vCard conventions');
+ }
+
+ // vCard 2.1 states that parameters may appear without a name, and only
+ // a value. We can deduce the value based on it's name.
+ //
+ // Our parser will get those as parameters without a value instead, so
+ // we're filtering these parameters out first.
+ $namedParameters = [];
+ $namelessParameters = [];
+
+ foreach ($property['parameters'] as $name => $value) {
+ if (!is_null($value)) {
+ $namedParameters[$name] = $value;
+ } else {
+ $namelessParameters[] = $name;
+ }
+ }
+
+ $propObj = $this->root->createProperty($property['name'], null, $namedParameters);
+
+ foreach ($namelessParameters as $namelessParameter) {
+ $propObj->add(null, $namelessParameter);
+ }
+
+ if (strtoupper($propObj['ENCODING']) === 'QUOTED-PRINTABLE') {
+ $propObj->setQuotedPrintableValue($this->extractQuotedPrintableValue());
+ } else {
+ $charset = $this->charset;
+ if ($this->root->getDocumentType() === Document::VCARD21 && isset($propObj['CHARSET'])) {
+ // vCard 2.1 allows the character set to be specified per property.
+ $charset = (string)$propObj['CHARSET'];
+ }
+ switch ($charset) {
+ case 'UTF-8' :
+ break;
+ case 'ISO-8859-1' :
+ $property['value'] = utf8_encode($property['value']);
+ break;
+ case 'Windows-1252' :
+ $property['value'] = mb_convert_encoding($property['value'], 'UTF-8', $charset);
+ break;
+ default :
+ throw new ParseException('Unsupported CHARSET: ' . $propObj['CHARSET']);
+ }
+ $propObj->setRawMimeDirValue($property['value']);
+ }
+
+ return $propObj;
+
+ }
+
+ /**
+ * Unescapes a property value.
+ *
+ * vCard 2.1 says:
+ * * Semi-colons must be escaped in some property values, specifically
+ * ADR, ORG and N.
+ * * Semi-colons must be escaped in parameter values, because semi-colons
+ * are also use to separate values.
+ * * No mention of escaping backslashes with another backslash.
+ * * newlines are not escaped either, instead QUOTED-PRINTABLE is used to
+ * span values over more than 1 line.
+ *
+ * vCard 3.0 says:
+ * * (rfc2425) Backslashes, newlines (\n or \N) and comma's must be
+ * escaped, all time time.
+ * * Comma's are used for delimeters in multiple values
+ * * (rfc2426) Adds to to this that the semi-colon MUST also be escaped,
+ * as in some properties semi-colon is used for separators.
+ * * Properties using semi-colons: N, ADR, GEO, ORG
+ * * Both ADR and N's individual parts may be broken up further with a
+ * comma.
+ * * Properties using commas: NICKNAME, CATEGORIES
+ *
+ * vCard 4.0 (rfc6350) says:
+ * * Commas must be escaped.
+ * * Semi-colons may be escaped, an unescaped semi-colon _may_ be a
+ * delimiter, depending on the property.
+ * * Backslashes must be escaped
+ * * Newlines must be escaped as either \N or \n.
+ * * Some compound properties may contain multiple parts themselves, so a
+ * comma within a semi-colon delimited property may also be unescaped
+ * to denote multiple parts _within_ the compound property.
+ * * Text-properties using semi-colons: N, ADR, ORG, CLIENTPIDMAP.
+ * * Text-properties using commas: NICKNAME, RELATED, CATEGORIES, PID.
+ *
+ * Even though the spec says that commas must always be escaped, the
+ * example for GEO in Section 6.5.2 seems to violate this.
+ *
+ * iCalendar 2.0 (rfc5545) says:
+ * * Commas or semi-colons may be used as delimiters, depending on the
+ * property.
+ * * Commas, semi-colons, backslashes, newline (\N or \n) are always
+ * escaped, unless they are delimiters.
+ * * Colons shall not be escaped.
+ * * Commas can be considered the 'default delimiter' and is described as
+ * the delimiter in cases where the order of the multiple values is
+ * insignificant.
+ * * Semi-colons are described as the delimiter for 'structured values'.
+ * They are specifically used in Semi-colons are used as a delimiter in
+ * REQUEST-STATUS, RRULE, GEO and EXRULE. EXRULE is deprecated however.
+ *
+ * Now for the parameters
+ *
+ * If delimiter is not set (null) this method will just return a string.
+ * If it's a comma or a semi-colon the string will be split on those
+ * characters, and always return an array.
+ *
+ * @param string $input
+ * @param string $delimiter
+ *
+ * @return string|string[]
+ */
+ static function unescapeValue($input, $delimiter = ';') {
+
+ $regex = '# (?: (\\\\ (?: \\\\ | N | n | ; | , ) )';
+ if ($delimiter) {
+ $regex .= ' | (' . $delimiter . ')';
+ }
+ $regex .= ') #x';
+
+ $matches = preg_split($regex, $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+
+ $resultArray = [];
+ $result = '';
+
+ foreach ($matches as $match) {
+
+ switch ($match) {
+ case '\\\\' :
+ $result .= '\\';
+ break;
+ case '\N' :
+ case '\n' :
+ $result .= "\n";
+ break;
+ case '\;' :
+ $result .= ';';
+ break;
+ case '\,' :
+ $result .= ',';
+ break;
+ case $delimiter :
+ $resultArray[] = $result;
+ $result = '';
+ break;
+ default :
+ $result .= $match;
+ break;
+
+ }
+
+ }
+
+ $resultArray[] = $result;
+ return $delimiter ? $resultArray : $result;
+
+ }
+
+ /**
+ * Unescapes a parameter value.
+ *
+ * vCard 2.1:
+ * * Does not mention a mechanism for this. In addition, double quotes
+ * are never used to wrap values.
+ * * This means that parameters can simply not contain colons or
+ * semi-colons.
+ *
+ * vCard 3.0 (rfc2425, rfc2426):
+ * * Parameters _may_ be surrounded by double quotes.
+ * * If this is not the case, semi-colon, colon and comma may simply not
+ * occur (the comma used for multiple parameter values though).
+ * * If it is surrounded by double-quotes, it may simply not contain
+ * double-quotes.
+ * * This means that a parameter can in no case encode double-quotes, or
+ * newlines.
+ *
+ * vCard 4.0 (rfc6350)
+ * * Behavior seems to be identical to vCard 3.0
+ *
+ * iCalendar 2.0 (rfc5545)
+ * * Behavior seems to be identical to vCard 3.0
+ *
+ * Parameter escaping mechanism (rfc6868) :
+ * * This rfc describes a new way to escape parameter values.
+ * * New-line is encoded as ^n
+ * * ^ is encoded as ^^.
+ * * " is encoded as ^'
+ *
+ * @param string $input
+ *
+ * @return void
+ */
+ private function unescapeParam($input) {
+
+ return
+ preg_replace_callback(
+ '#(\^(\^|n|\'))#',
+ function($matches) {
+ switch ($matches[2]) {
+ case 'n' :
+ return "\n";
+ case '^' :
+ return '^';
+ case '\'' :
+ return '"';
+
+ // @codeCoverageIgnoreStart
+ }
+ // @codeCoverageIgnoreEnd
+ },
+ $input
+ );
+ }
+
+ /**
+ * Gets the full quoted printable value.
+ *
+ * We need a special method for this, because newlines have both a meaning
+ * in vCards, and in QuotedPrintable.
+ *
+ * This method does not do any decoding.
+ *
+ * @return string
+ */
+ private function extractQuotedPrintableValue() {
+
+ // We need to parse the raw line again to get the start of the value.
+ //
+ // We are basically looking for the first colon (:), but we need to
+ // skip over the parameters first, as they may contain one.
+ $regex = '/^
+ (?: [^:])+ # Anything but a colon
+ (?: "[^"]")* # A parameter in double quotes
+ : # start of the value we really care about
+ (.*)$
+ /xs';
+
+ preg_match($regex, $this->rawLine, $matches);
+
+ $value = $matches[1];
+ // Removing the first whitespace character from every line. Kind of
+ // like unfolding, but we keep the newline.
+ $value = str_replace("\n ", "\n", $value);
+
+ // Microsoft products don't always correctly fold lines, they may be
+ // missing a whitespace. So if 'forgiving' is turned on, we will take
+ // those as well.
+ if ($this->options & self::OPTION_FORGIVING) {
+ while (substr($value, -1) === '=') {
+ // Reading the line
+ $this->readLine();
+ // Grabbing the raw form
+ $value .= "\n" . $this->rawLine;
+ }
+ }
+
+ return $value;
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Parser/Parser.php b/vendor/sabre/vobject/lib/Parser/Parser.php
new file mode 100644
index 000000000..ca8bc0add
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Parser/Parser.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Sabre\VObject\Parser;
+
+/**
+ * Abstract parser.
+ *
+ * This class serves as a base-class for the different parsers.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class Parser {
+
+ /**
+ * Turning on this option makes the parser more forgiving.
+ *
+ * In the case of the MimeDir parser, this means that the parser will
+ * accept slashes and underscores in property names, and it will also
+ * attempt to fix Microsoft vCard 2.1's broken line folding.
+ */
+ const OPTION_FORGIVING = 1;
+
+ /**
+ * If this option is turned on, any lines we cannot parse will be ignored
+ * by the reader.
+ */
+ const OPTION_IGNORE_INVALID_LINES = 2;
+
+ /**
+ * Bitmask of parser options.
+ *
+ * @var int
+ */
+ protected $options;
+
+ /**
+ * Creates the parser.
+ *
+ * Optionally, it's possible to parse the input stream here.
+ *
+ * @param mixed $input
+ * @param int $options Any parser options (OPTION constants).
+ *
+ * @return void
+ */
+ function __construct($input = null, $options = 0) {
+
+ if (!is_null($input)) {
+ $this->setInput($input);
+ }
+ $this->options = $options;
+ }
+
+ /**
+ * This method starts the parsing process.
+ *
+ * If the input was not supplied during construction, it's possible to pass
+ * it here instead.
+ *
+ * If either input or options are not supplied, the defaults will be used.
+ *
+ * @param mixed $input
+ * @param int $options
+ *
+ * @return array
+ */
+ abstract function parse($input = null, $options = 0);
+
+ /**
+ * Sets the input data.
+ *
+ * @param mixed $input
+ *
+ * @return void
+ */
+ abstract function setInput($input);
+
+}
diff --git a/vendor/sabre/vobject/lib/Parser/XML.php b/vendor/sabre/vobject/lib/Parser/XML.php
new file mode 100644
index 000000000..060a7fe2e
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Parser/XML.php
@@ -0,0 +1,428 @@
+<?php
+
+namespace Sabre\VObject\Parser;
+
+use Sabre\VObject\Component;
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Component\VCard;
+use Sabre\VObject\EofException;
+use Sabre\VObject\ParseException;
+use Sabre\Xml as SabreXml;
+
+/**
+ * XML Parser.
+ *
+ * This parser parses both the xCal and xCard formats.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Ivan Enderlin
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class XML extends Parser {
+
+ const XCAL_NAMESPACE = 'urn:ietf:params:xml:ns:icalendar-2.0';
+ const XCARD_NAMESPACE = 'urn:ietf:params:xml:ns:vcard-4.0';
+
+ /**
+ * The input data.
+ *
+ * @var array
+ */
+ protected $input;
+
+ /**
+ * A pointer/reference to the input.
+ *
+ * @var array
+ */
+ private $pointer;
+
+ /**
+ * Document, root component.
+ *
+ * @var Sabre\VObject\Document
+ */
+ protected $root;
+
+ /**
+ * Creates the parser.
+ *
+ * Optionally, it's possible to parse the input stream here.
+ *
+ * @param mixed $input
+ * @param int $options Any parser options (OPTION constants).
+ *
+ * @return void
+ */
+ function __construct($input = null, $options = 0) {
+
+ if (0 === $options) {
+ $options = parent::OPTION_FORGIVING;
+ }
+
+ parent::__construct($input, $options);
+
+ }
+
+ /**
+ * Parse xCal or xCard.
+ *
+ * @param resource|string $input
+ * @param int $options
+ *
+ * @throws \Exception
+ *
+ * @return Sabre\VObject\Document
+ */
+ function parse($input = null, $options = 0) {
+
+ if (!is_null($input)) {
+ $this->setInput($input);
+ }
+
+ if (0 !== $options) {
+ $this->options = $options;
+ }
+
+ if (is_null($this->input)) {
+ throw new EofException('End of input stream, or no input supplied');
+ }
+
+ switch ($this->input['name']) {
+
+ case '{' . self::XCAL_NAMESPACE . '}icalendar':
+ $this->root = new VCalendar([], false);
+ $this->pointer = &$this->input['value'][0];
+ $this->parseVCalendarComponents($this->root);
+ break;
+
+ case '{' . self::XCARD_NAMESPACE . '}vcards':
+ foreach ($this->input['value'] as &$vCard) {
+
+ $this->root = new VCard(['version' => '4.0'], false);
+ $this->pointer = &$vCard;
+ $this->parseVCardComponents($this->root);
+
+ // We just parse the first <vcard /> element.
+ break;
+
+ }
+ break;
+
+ default:
+ throw new ParseException('Unsupported XML standard');
+
+ }
+
+ return $this->root;
+ }
+
+ /**
+ * Parse a xCalendar component.
+ *
+ * @param Component $parentComponent
+ *
+ * @return void
+ */
+ protected function parseVCalendarComponents(Component $parentComponent) {
+
+ foreach ($this->pointer['value'] ?: [] as $children) {
+
+ switch (static::getTagName($children['name'])) {
+
+ case 'properties':
+ $this->pointer = &$children['value'];
+ $this->parseProperties($parentComponent);
+ break;
+
+ case 'components':
+ $this->pointer = &$children;
+ $this->parseComponent($parentComponent);
+ break;
+ }
+ }
+
+ }
+
+ /**
+ * Parse a xCard component.
+ *
+ * @param Component $parentComponent
+ *
+ * @return void
+ */
+ protected function parseVCardComponents(Component $parentComponent) {
+
+ $this->pointer = &$this->pointer['value'];
+ $this->parseProperties($parentComponent);
+
+ }
+
+ /**
+ * Parse xCalendar and xCard properties.
+ *
+ * @param Component $parentComponent
+ * @param string $propertyNamePrefix
+ *
+ * @return void
+ */
+ protected function parseProperties(Component $parentComponent, $propertyNamePrefix = '') {
+
+ foreach ($this->pointer ?: [] as $xmlProperty) {
+
+ list($namespace, $tagName) = SabreXml\Service::parseClarkNotation($xmlProperty['name']);
+
+ $propertyName = $tagName;
+ $propertyValue = [];
+ $propertyParameters = [];
+ $propertyType = 'text';
+
+ // A property which is not part of the standard.
+ if ($namespace !== self::XCAL_NAMESPACE
+ && $namespace !== self::XCARD_NAMESPACE) {
+
+ $propertyName = 'xml';
+ $value = '<' . $tagName . ' xmlns="' . $namespace . '"';
+
+ foreach ($xmlProperty['attributes'] as $attributeName => $attributeValue) {
+ $value .= ' ' . $attributeName . '="' . str_replace('"', '\"', $attributeValue) . '"';
+ }
+
+ $value .= '>' . $xmlProperty['value'] . '</' . $tagName . '>';
+
+ $propertyValue = [$value];
+
+ $this->createProperty(
+ $parentComponent,
+ $propertyName,
+ $propertyParameters,
+ $propertyType,
+ $propertyValue
+ );
+
+ continue;
+ }
+
+ // xCard group.
+ if ($propertyName === 'group') {
+
+ if (!isset($xmlProperty['attributes']['name'])) {
+ continue;
+ }
+
+ $this->pointer = &$xmlProperty['value'];
+ $this->parseProperties(
+ $parentComponent,
+ strtoupper($xmlProperty['attributes']['name']) . '.'
+ );
+
+ continue;
+
+ }
+
+ // Collect parameters.
+ foreach ($xmlProperty['value'] as $i => $xmlPropertyChild) {
+
+ if (!is_array($xmlPropertyChild)
+ || 'parameters' !== static::getTagName($xmlPropertyChild['name']))
+ continue;
+
+ $xmlParameters = $xmlPropertyChild['value'];
+
+ foreach ($xmlParameters as $xmlParameter) {
+
+ $propertyParameterValues = [];
+
+ foreach ($xmlParameter['value'] as $xmlParameterValues) {
+ $propertyParameterValues[] = $xmlParameterValues['value'];
+ }
+
+ $propertyParameters[static::getTagName($xmlParameter['name'])]
+ = implode(',', $propertyParameterValues);
+
+ }
+
+ array_splice($xmlProperty['value'], $i, 1);
+
+ }
+
+ $propertyNameExtended = ($this->root instanceof VCalendar
+ ? 'xcal'
+ : 'xcard') . ':' . $propertyName;
+
+ switch ($propertyNameExtended) {
+
+ case 'xcal:geo':
+ $propertyType = 'float';
+ $propertyValue['latitude'] = 0;
+ $propertyValue['longitude'] = 0;
+
+ foreach ($xmlProperty['value'] as $xmlRequestChild) {
+ $propertyValue[static::getTagName($xmlRequestChild['name'])]
+ = $xmlRequestChild['value'];
+ }
+ break;
+
+ case 'xcal:request-status':
+ $propertyType = 'text';
+
+ foreach ($xmlProperty['value'] as $xmlRequestChild) {
+ $propertyValue[static::getTagName($xmlRequestChild['name'])]
+ = $xmlRequestChild['value'];
+ }
+ break;
+
+ case 'xcal:freebusy':
+ $propertyType = 'freebusy';
+ // We don't break because we only want to set
+ // another property type.
+
+ case 'xcal:categories':
+ case 'xcal:resources':
+ case 'xcal:exdate':
+ foreach ($xmlProperty['value'] as $specialChild) {
+ $propertyValue[static::getTagName($specialChild['name'])]
+ = $specialChild['value'];
+ }
+ break;
+
+ case 'xcal:rdate':
+ $propertyType = 'date-time';
+
+ foreach ($xmlProperty['value'] as $specialChild) {
+
+ $tagName = static::getTagName($specialChild['name']);
+
+ if ('period' === $tagName) {
+
+ $propertyParameters['value'] = 'PERIOD';
+ $propertyValue[] = implode('/', $specialChild['value']);
+
+ }
+ else {
+ $propertyValue[] = $specialChild['value'];
+ }
+ }
+ break;
+
+ default:
+ $propertyType = static::getTagName($xmlProperty['value'][0]['name']);
+
+ foreach ($xmlProperty['value'] as $value) {
+ $propertyValue[] = $value['value'];
+ }
+
+ if ('date' === $propertyType) {
+ $propertyParameters['value'] = 'DATE';
+ }
+ break;
+ }
+
+ $this->createProperty(
+ $parentComponent,
+ $propertyNamePrefix . $propertyName,
+ $propertyParameters,
+ $propertyType,
+ $propertyValue
+ );
+
+ }
+
+ }
+
+ /**
+ * Parse a component.
+ *
+ * @param Component $parentComponent
+ *
+ * @return void
+ */
+ protected function parseComponent(Component $parentComponent) {
+
+ $components = $this->pointer['value'] ?: [];
+
+ foreach ($components as $component) {
+
+ $componentName = static::getTagName($component['name']);
+ $currentComponent = $this->root->createComponent(
+ $componentName,
+ null,
+ false
+ );
+
+ $this->pointer = &$component;
+ $this->parseVCalendarComponents($currentComponent);
+
+ $parentComponent->add($currentComponent);
+
+ }
+
+ }
+
+ /**
+ * Create a property.
+ *
+ * @param Component $parentComponent
+ * @param string $name
+ * @param array $parameters
+ * @param string $type
+ * @param mixed $value
+ *
+ * @return void
+ */
+ protected function createProperty(Component $parentComponent, $name, $parameters, $type, $value) {
+
+ $property = $this->root->createProperty(
+ $name,
+ null,
+ $parameters,
+ $type
+ );
+ $parentComponent->add($property);
+ $property->setXmlValue($value);
+
+ }
+
+ /**
+ * Sets the input data.
+ *
+ * @param resource|string $input
+ *
+ * @return void
+ */
+ function setInput($input) {
+
+ if (is_resource($input)) {
+ $input = stream_get_contents($input);
+ }
+
+ if (is_string($input)) {
+
+ $reader = new SabreXml\Reader();
+ $reader->elementMap['{' . self::XCAL_NAMESPACE . '}period']
+ = 'Sabre\VObject\Parser\XML\Element\KeyValue';
+ $reader->elementMap['{' . self::XCAL_NAMESPACE . '}recur']
+ = 'Sabre\VObject\Parser\XML\Element\KeyValue';
+ $reader->xml($input);
+ $input = $reader->parse();
+
+ }
+
+ $this->input = $input;
+
+ }
+
+ /**
+ * Get tag name from a Clark notation.
+ *
+ * @param string $clarkedTagName
+ *
+ * @return string
+ */
+ protected static function getTagName($clarkedTagName) {
+
+ list(, $tagName) = SabreXml\Service::parseClarkNotation($clarkedTagName);
+ return $tagName;
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Parser/XML/Element/KeyValue.php b/vendor/sabre/vobject/lib/Parser/XML/Element/KeyValue.php
new file mode 100644
index 000000000..14d798433
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Parser/XML/Element/KeyValue.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Sabre\VObject\Parser\XML\Element;
+
+use Sabre\Xml as SabreXml;
+
+/**
+ * Our own sabre/xml key-value element.
+ *
+ * It just removes the clark notation.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Ivan Enderlin
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class KeyValue extends SabreXml\Element\KeyValue {
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called staticly, 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
+ * free to return other data as well.
+ *
+ * Important note 2: You are responsible for advancing the reader to the
+ * next element. Not doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param XML\Reader $reader
+ *
+ * @return mixed
+ */
+ static function xmlDeserialize(SabreXml\Reader $reader) {
+
+ // If there's no children, we don't do anything.
+ if ($reader->isEmptyElement) {
+ $reader->next();
+ return [];
+ }
+
+ $values = [];
+ $reader->read();
+
+ do {
+
+ if ($reader->nodeType === SabreXml\Reader::ELEMENT) {
+
+ $name = $reader->localName;
+ $values[$name] = $reader->parseCurrentElement()['value'];
+
+ } else {
+ $reader->read();
+ }
+
+ } while ($reader->nodeType !== SabreXml\Reader::END_ELEMENT);
+
+ $reader->read();
+
+ return $values;
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Property.php b/vendor/sabre/vobject/lib/Property.php
new file mode 100644
index 000000000..112775131
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property.php
@@ -0,0 +1,662 @@
+<?php
+
+namespace Sabre\VObject;
+
+use Sabre\Xml;
+
+/**
+ * Property.
+ *
+ * A property is always in a KEY:VALUE structure, and may optionally contain
+ * parameters.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class Property extends Node {
+
+ /**
+ * Property name.
+ *
+ * This will contain a string such as DTSTART, SUMMARY, FN.
+ *
+ * @var string
+ */
+ public $name;
+
+ /**
+ * Property group.
+ *
+ * This is only used in vcards
+ *
+ * @var string
+ */
+ public $group;
+
+ /**
+ * List of parameters.
+ *
+ * @var array
+ */
+ public $parameters = [];
+
+ /**
+ * Current value.
+ *
+ * @var mixed
+ */
+ protected $value;
+
+ /**
+ * In case this is a multi-value property. This string will be used as a
+ * delimiter.
+ *
+ * @var string|null
+ */
+ public $delimiter = ';';
+
+ /**
+ * Creates the generic property.
+ *
+ * Parameters must be specified in key=>value syntax.
+ *
+ * @param Component $root The root document
+ * @param string $name
+ * @param string|array|null $value
+ * @param array $parameters List of parameters
+ * @param string $group The vcard property group
+ *
+ * @return void
+ */
+ function __construct(Component $root, $name, $value = null, array $parameters = [], $group = null) {
+
+ $this->name = $name;
+ $this->group = $group;
+
+ $this->root = $root;
+
+ foreach ($parameters as $k => $v) {
+ $this->add($k, $v);
+ }
+
+ if (!is_null($value)) {
+ $this->setValue($value);
+ }
+
+ }
+
+ /**
+ * Updates the current value.
+ *
+ * This may be either a single, or multiple strings in an array.
+ *
+ * @param string|array $value
+ *
+ * @return void
+ */
+ function setValue($value) {
+
+ $this->value = $value;
+
+ }
+
+ /**
+ * Returns the current value.
+ *
+ * This method will always return a singular value. If this was a
+ * multi-value object, some decision will be made first on how to represent
+ * it as a string.
+ *
+ * To get the correct multi-value version, use getParts.
+ *
+ * @return string
+ */
+ function getValue() {
+
+ if (is_array($this->value)) {
+ if (count($this->value) == 0) {
+ return;
+ } elseif (count($this->value) === 1) {
+ return $this->value[0];
+ } else {
+ return $this->getRawMimeDirValue();
+ }
+ } else {
+ return $this->value;
+ }
+
+ }
+
+ /**
+ * Sets a multi-valued property.
+ *
+ * @param array $parts
+ *
+ * @return void
+ */
+ function setParts(array $parts) {
+
+ $this->value = $parts;
+
+ }
+
+ /**
+ * Returns a multi-valued property.
+ *
+ * This method always returns an array, if there was only a single value,
+ * it will still be wrapped in an array.
+ *
+ * @return array
+ */
+ function getParts() {
+
+ if (is_null($this->value)) {
+ return [];
+ } elseif (is_array($this->value)) {
+ return $this->value;
+ } else {
+ return [$this->value];
+ }
+
+ }
+
+ /**
+ * Adds a new parameter.
+ *
+ * If a parameter with same name already existed, the values will be
+ * combined.
+ * If nameless parameter is added, we try to guess it's name.
+ *
+ * @param string $name
+ * @param string|null|array $value
+ */
+ function add($name, $value = null) {
+ $noName = false;
+ if ($name === null) {
+ $name = Parameter::guessParameterNameByValue($value);
+ $noName = true;
+ }
+
+ if (isset($this->parameters[strtoupper($name)])) {
+ $this->parameters[strtoupper($name)]->addValue($value);
+ }
+ else {
+ $param = new Parameter($this->root, $name, $value);
+ $param->noName = $noName;
+ $this->parameters[$param->name] = $param;
+ }
+ }
+
+ /**
+ * Returns an iterable list of children.
+ *
+ * @return array
+ */
+ function parameters() {
+
+ return $this->parameters;
+
+ }
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ abstract function getValueType();
+
+ /**
+ * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+ *
+ * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+ * not yet done, but parameters are not included.
+ *
+ * @param string $val
+ *
+ * @return void
+ */
+ abstract function setRawMimeDirValue($val);
+
+ /**
+ * Returns a raw mime-dir representation of the value.
+ *
+ * @return string
+ */
+ abstract function getRawMimeDirValue();
+
+ /**
+ * Turns the object back into a serialized blob.
+ *
+ * @return string
+ */
+ function serialize() {
+
+ $str = $this->name;
+ if ($this->group) $str = $this->group . '.' . $this->name;
+
+ foreach ($this->parameters() as $param) {
+
+ $str .= ';' . $param->serialize();
+
+ }
+
+ $str .= ':' . $this->getRawMimeDirValue();
+
+ $out = '';
+ while (strlen($str) > 0) {
+ if (strlen($str) > 75) {
+ $out .= mb_strcut($str, 0, 75, 'utf-8') . "\r\n";
+ $str = ' ' . mb_strcut($str, 75, strlen($str), 'utf-8');
+ } else {
+ $out .= $str . "\r\n";
+ $str = '';
+ break;
+ }
+ }
+
+ return $out;
+
+ }
+
+ /**
+ * Returns the value, in the format it should be encoded for JSON.
+ *
+ * This method must always return an array.
+ *
+ * @return array
+ */
+ function getJsonValue() {
+
+ return $this->getParts();
+
+ }
+
+ /**
+ * Sets the JSON value, as it would appear in a jCard or jCal object.
+ *
+ * The value must always be an array.
+ *
+ * @param array $value
+ *
+ * @return void
+ */
+ function setJsonValue(array $value) {
+
+ if (count($value) === 1) {
+ $this->setValue(reset($value));
+ } else {
+ $this->setValue($value);
+ }
+
+ }
+
+ /**
+ * This method returns an array, with the representation as it should be
+ * encoded in JSON. This is used to create jCard or jCal documents.
+ *
+ * @return array
+ */
+ function jsonSerialize() {
+
+ $parameters = [];
+
+ foreach ($this->parameters as $parameter) {
+ if ($parameter->name === 'VALUE') {
+ continue;
+ }
+ $parameters[strtolower($parameter->name)] = $parameter->jsonSerialize();
+ }
+ // In jCard, we need to encode the property-group as a separate 'group'
+ // parameter.
+ if ($this->group) {
+ $parameters['group'] = $this->group;
+ }
+
+ return array_merge(
+ [
+ strtolower($this->name),
+ (object)$parameters,
+ strtolower($this->getValueType()),
+ ],
+ $this->getJsonValue()
+ );
+ }
+
+ /**
+ * Hydrate data from a XML subtree, as it would appear in a xCard or xCal
+ * object.
+ *
+ * @param array $value
+ *
+ * @return void
+ */
+ function setXmlValue(array $value) {
+
+ $this->setJsonValue($value);
+
+ }
+
+ /**
+ * This method serializes the data into XML. This is used to create xCard or
+ * xCal documents.
+ *
+ * @param Xml\Writer $writer XML writer.
+ *
+ * @return void
+ */
+ function xmlSerialize(Xml\Writer $writer) {
+
+ $parameters = [];
+
+ foreach ($this->parameters as $parameter) {
+
+ if ($parameter->name === 'VALUE') {
+ continue;
+ }
+
+ $parameters[] = $parameter;
+
+ }
+
+ $writer->startElement(strtolower($this->name));
+
+ if (!empty($parameters)) {
+
+ $writer->startElement('parameters');
+
+ foreach ($parameters as $parameter) {
+
+ $writer->startElement(strtolower($parameter->name));
+ $writer->write($parameter);
+ $writer->endElement();
+
+ }
+
+ $writer->endElement();
+
+ }
+
+ $this->xmlSerializeValue($writer);
+ $writer->endElement();
+
+ }
+
+ /**
+ * This method serializes only the value of a property. This is used to
+ * create xCard or xCal documents.
+ *
+ * @param Xml\Writer $writer XML writer.
+ *
+ * @return void
+ */
+ protected function xmlSerializeValue(Xml\Writer $writer) {
+
+ $valueType = strtolower($this->getValueType());
+
+ foreach ($this->getJsonValue() as $values) {
+ foreach ((array)$values as $value) {
+ $writer->writeElement($valueType, $value);
+ }
+ }
+
+ }
+
+ /**
+ * Called when this object is being cast to a string.
+ *
+ * If the property only had a single value, you will get just that. In the
+ * case the property had multiple values, the contents will be escaped and
+ * combined with ,.
+ *
+ * @return string
+ */
+ function __toString() {
+
+ return (string)$this->getValue();
+
+ }
+
+ /* ArrayAccess interface {{{ */
+
+ /**
+ * Checks if an array element exists.
+ *
+ * @param mixed $name
+ *
+ * @return bool
+ */
+ function offsetExists($name) {
+
+ if (is_int($name)) return parent::offsetExists($name);
+
+ $name = strtoupper($name);
+
+ foreach ($this->parameters as $parameter) {
+ if ($parameter->name == $name) return true;
+ }
+ return false;
+
+ }
+
+ /**
+ * Returns a parameter.
+ *
+ * If the parameter does not exist, null is returned.
+ *
+ * @param string $name
+ *
+ * @return Node
+ */
+ function offsetGet($name) {
+
+ if (is_int($name)) return parent::offsetGet($name);
+ $name = strtoupper($name);
+
+ if (!isset($this->parameters[$name])) {
+ return;
+ }
+
+ return $this->parameters[$name];
+
+ }
+
+ /**
+ * Creates a new parameter.
+ *
+ * @param string $name
+ * @param mixed $value
+ *
+ * @return void
+ */
+ function offsetSet($name, $value) {
+
+ if (is_int($name)) {
+ parent::offsetSet($name, $value);
+ // @codeCoverageIgnoreStart
+ // This will never be reached, because an exception is always
+ // thrown.
+ return;
+ // @codeCoverageIgnoreEnd
+ }
+
+ $param = new Parameter($this->root, $name, $value);
+ $this->parameters[$param->name] = $param;
+
+ }
+
+ /**
+ * Removes one or more parameters with the specified name.
+ *
+ * @param string $name
+ *
+ * @return void
+ */
+ function offsetUnset($name) {
+
+ if (is_int($name)) {
+ parent::offsetUnset($name);
+ // @codeCoverageIgnoreStart
+ // This will never be reached, because an exception is always
+ // thrown.
+ return;
+ // @codeCoverageIgnoreEnd
+ }
+
+ unset($this->parameters[strtoupper($name)]);
+
+ }
+ /* }}} */
+
+ /**
+ * This method is automatically called when the object is cloned.
+ * Specifically, this will ensure all child elements are also cloned.
+ *
+ * @return void
+ */
+ function __clone() {
+
+ foreach ($this->parameters as $key => $child) {
+ $this->parameters[$key] = clone $child;
+ $this->parameters[$key]->parent = $this;
+ }
+
+ }
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * - Node::REPAIR - If something is broken, and automatic repair may
+ * be attempted.
+ *
+ * An array is returned with warnings.
+ *
+ * Every item in the array has the following properties:
+ * * level - (number between 1 and 3 with severity information)
+ * * message - (human readable message)
+ * * node - (reference to the offending node)
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $warnings = [];
+
+ // Checking if our value is UTF-8
+ if (!StringUtil::isUTF8($this->getRawMimeDirValue())) {
+
+ $oldValue = $this->getRawMimeDirValue();
+ $level = 3;
+ if ($options & self::REPAIR) {
+ $newValue = StringUtil::convertToUTF8($oldValue);
+ if (true || StringUtil::isUTF8($newValue)) {
+ $this->setRawMimeDirValue($newValue);
+ $level = 1;
+ }
+
+ }
+
+
+ if (preg_match('%([\x00-\x08\x0B-\x0C\x0E-\x1F\x7F])%', $oldValue, $matches)) {
+ $message = 'Property contained a control character (0x' . bin2hex($matches[1]) . ')';
+ } else {
+ $message = 'Property is not valid UTF-8! ' . $oldValue;
+ }
+
+ $warnings[] = [
+ 'level' => $level,
+ 'message' => $message,
+ 'node' => $this,
+ ];
+ }
+
+ // Checking if the propertyname does not contain any invalid bytes.
+ if (!preg_match('/^([A-Z0-9-]+)$/', $this->name)) {
+ $warnings[] = [
+ 'level' => 1,
+ 'message' => 'The propertyname: ' . $this->name . ' contains invalid characters. Only A-Z, 0-9 and - are allowed',
+ 'node' => $this,
+ ];
+ if ($options & self::REPAIR) {
+ // Uppercasing and converting underscores to dashes.
+ $this->name = strtoupper(
+ str_replace('_', '-', $this->name)
+ );
+ // Removing every other invalid character
+ $this->name = preg_replace('/([^A-Z0-9-])/u', '', $this->name);
+
+ }
+
+ }
+
+ if ($encoding = $this->offsetGet('ENCODING')) {
+
+ if ($this->root->getDocumentType() === Document::VCARD40) {
+ $warnings[] = [
+ 'level' => 1,
+ 'message' => 'ENCODING parameter is not valid in vCard 4.',
+ 'node' => $this
+ ];
+ } else {
+
+ $encoding = (string)$encoding;
+
+ $allowedEncoding = [];
+
+ switch ($this->root->getDocumentType()) {
+ case Document::ICALENDAR20 :
+ $allowedEncoding = ['8BIT', 'BASE64'];
+ break;
+ case Document::VCARD21 :
+ $allowedEncoding = ['QUOTED-PRINTABLE', 'BASE64', '8BIT'];
+ break;
+ case Document::VCARD30 :
+ $allowedEncoding = ['B'];
+ break;
+
+ }
+ if ($allowedEncoding && !in_array(strtoupper($encoding), $allowedEncoding)) {
+ $warnings[] = [
+ 'level' => 1,
+ 'message' => 'ENCODING=' . strtoupper($encoding) . ' is not valid for this document type.',
+ 'node' => $this
+ ];
+ }
+ }
+
+ }
+
+ // Validating inner parameters
+ foreach ($this->parameters as $param) {
+ $warnings = array_merge($warnings, $param->validate($options));
+ }
+
+ return $warnings;
+
+ }
+
+ /**
+ * Call this method on a document if you're done using it.
+ *
+ * It's intended to remove all circular references, so PHP can easily clean
+ * it up.
+ *
+ * @return void
+ */
+ function destroy() {
+
+ parent::destroy();
+ foreach ($this->parameters as $param) {
+ $param->destroy();
+ }
+ $this->parameters = [];
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Property/Binary.php b/vendor/sabre/vobject/lib/Property/Binary.php
new file mode 100644
index 000000000..d54cae25d
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/Binary.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use Sabre\VObject\Property;
+
+/**
+ * BINARY property.
+ *
+ * This object represents BINARY values.
+ *
+ * Binary values are most commonly used by the iCalendar ATTACH property, and
+ * the vCard PHOTO property.
+ *
+ * This property will transparently encode and decode to base64.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Binary extends Property {
+
+ /**
+ * In case this is a multi-value property. This string will be used as a
+ * delimiter.
+ *
+ * @var string|null
+ */
+ public $delimiter = null;
+
+ /**
+ * Updates the current value.
+ *
+ * This may be either a single, or multiple strings in an array.
+ *
+ * @param string|array $value
+ *
+ * @return void
+ */
+ function setValue($value) {
+
+ if (is_array($value)) {
+
+ if (count($value) === 1) {
+ $this->value = $value[0];
+ } else {
+ throw new \InvalidArgumentException('The argument must either be a string or an array with only one child');
+ }
+
+ } else {
+
+ $this->value = $value;
+
+ }
+
+ }
+
+ /**
+ * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+ *
+ * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+ * not yet done, but parameters are not included.
+ *
+ * @param string $val
+ *
+ * @return void
+ */
+ function setRawMimeDirValue($val) {
+
+ $this->value = base64_decode($val);
+
+ }
+
+ /**
+ * Returns a raw mime-dir representation of the value.
+ *
+ * @return string
+ */
+ function getRawMimeDirValue() {
+
+ return base64_encode($this->value);
+
+ }
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'BINARY';
+
+ }
+
+ /**
+ * Returns the value, in the format it should be encoded for json.
+ *
+ * This method must always return an array.
+ *
+ * @return array
+ */
+ function getJsonValue() {
+
+ return [base64_encode($this->getValue())];
+
+ }
+
+ /**
+ * Sets the json value, as it would appear in a jCard or jCal object.
+ *
+ * The value must always be an array.
+ *
+ * @param array $value
+ *
+ * @return void
+ */
+ function setJsonValue(array $value) {
+
+ $value = array_map('base64_decode', $value);
+ parent::setJsonValue($value);
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Property/Boolean.php b/vendor/sabre/vobject/lib/Property/Boolean.php
new file mode 100644
index 000000000..6f5887e25
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/Boolean.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use
+ Sabre\VObject\Property;
+
+/**
+ * Boolean property.
+ *
+ * This object represents BOOLEAN values. These are always the case-insenstive
+ * string TRUE or FALSE.
+ *
+ * Automatic conversion to PHP's true and false are done.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Boolean extends Property {
+
+ /**
+ * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+ *
+ * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+ * not yet done, but parameters are not included.
+ *
+ * @param string $val
+ *
+ * @return void
+ */
+ function setRawMimeDirValue($val) {
+
+ $val = strtoupper($val) === 'TRUE' ? true : false;
+ $this->setValue($val);
+
+ }
+
+ /**
+ * Returns a raw mime-dir representation of the value.
+ *
+ * @return string
+ */
+ function getRawMimeDirValue() {
+
+ return $this->value ? 'TRUE' : 'FALSE';
+
+ }
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'BOOLEAN';
+
+ }
+
+ /**
+ * Hydrate data from a XML subtree, as it would appear in a xCard or xCal
+ * object.
+ *
+ * @param array $value
+ *
+ * @return void
+ */
+ function setXmlValue(array $value) {
+
+ $value = array_map(
+ function($value) {
+ return 'true' === $value;
+ },
+ $value
+ );
+ parent::setXmlValue($value);
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Property/FlatText.php b/vendor/sabre/vobject/lib/Property/FlatText.php
new file mode 100644
index 000000000..2c7b43c29
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/FlatText.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+/**
+ * FlatText property.
+ *
+ * This object represents certain TEXT values.
+ *
+ * Specifically, this property is used for text values where there is only 1
+ * part. Semi-colons and colons will be de-escaped when deserializing, but if
+ * any semi-colons or commas appear without a backslash, we will not assume
+ * that they are delimiters.
+ *
+ * vCard 2.1 specifically has a whole bunch of properties where this may
+ * happen, as it only defines a delimiter for a few properties.
+ *
+ * vCard 4.0 states something similar. An unescaped semi-colon _may_ be a
+ * delimiter, depending on the property.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class FlatText extends Text {
+
+ /**
+ * Field separator.
+ *
+ * @var string
+ */
+ public $delimiter = ',';
+
+ /**
+ * Sets the value as a quoted-printable encoded string.
+ *
+ * Overriding this so we're not splitting on a ; delimiter.
+ *
+ * @param string $val
+ *
+ * @return void
+ */
+ function setQuotedPrintableValue($val) {
+
+ $val = quoted_printable_decode($val);
+ $this->setValue($val);
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Property/FloatValue.php b/vendor/sabre/vobject/lib/Property/FloatValue.php
new file mode 100644
index 000000000..15b119549
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/FloatValue.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use Sabre\VObject\Property;
+use Sabre\Xml;
+
+/**
+ * Float property.
+ *
+ * This object represents FLOAT values. These can be 1 or more floating-point
+ * numbers.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class FloatValue extends Property {
+
+ /**
+ * In case this is a multi-value property. This string will be used as a
+ * delimiter.
+ *
+ * @var string|null
+ */
+ public $delimiter = ';';
+
+ /**
+ * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+ *
+ * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+ * not yet done, but parameters are not included.
+ *
+ * @param string $val
+ *
+ * @return void
+ */
+ function setRawMimeDirValue($val) {
+
+ $val = explode($this->delimiter, $val);
+ foreach ($val as &$item) {
+ $item = (float)$item;
+ }
+ $this->setParts($val);
+
+ }
+
+ /**
+ * Returns a raw mime-dir representation of the value.
+ *
+ * @return string
+ */
+ function getRawMimeDirValue() {
+
+ return implode(
+ $this->delimiter,
+ $this->getParts()
+ );
+
+ }
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'FLOAT';
+
+ }
+
+ /**
+ * Returns the value, in the format it should be encoded for JSON.
+ *
+ * This method must always return an array.
+ *
+ * @return array
+ */
+ function getJsonValue() {
+
+ $val = array_map('floatval', $this->getParts());
+
+ // Special-casing the GEO property.
+ //
+ // See:
+ // http://tools.ietf.org/html/draft-ietf-jcardcal-jcal-04#section-3.4.1.2
+ if ($this->name === 'GEO') {
+ return [$val];
+ }
+
+ return $val;
+
+ }
+
+ /**
+ * Hydrate data from a XML subtree, as it would appear in a xCard or xCal
+ * object.
+ *
+ * @param array $value
+ *
+ * @return void
+ */
+ function setXmlValue(array $value) {
+
+ $value = array_map('floatval', $value);
+ parent::setXmlValue($value);
+
+ }
+
+ /**
+ * This method serializes only the value of a property. This is used to
+ * create xCard or xCal documents.
+ *
+ * @param Xml\Writer $writer XML writer.
+ *
+ * @return void
+ */
+ protected function xmlSerializeValue(Xml\Writer $writer) {
+
+ // Special-casing the GEO property.
+ //
+ // See:
+ // http://tools.ietf.org/html/rfc6321#section-3.4.1.2
+ if ($this->name === 'GEO') {
+
+ $value = array_map('floatval', $this->getParts());
+
+ $writer->writeElement('latitude', $value[0]);
+ $writer->writeElement('longitude', $value[1]);
+
+ }
+ else {
+ parent::xmlSerializeValue($writer);
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Property/ICalendar/CalAddress.php b/vendor/sabre/vobject/lib/Property/ICalendar/CalAddress.php
new file mode 100644
index 000000000..a0c4a9b9a
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/ICalendar/CalAddress.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+use
+ Sabre\VObject\Property\Text;
+
+/**
+ * CalAddress property.
+ *
+ * This object encodes CAL-ADDRESS values, as defined in rfc5545
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class CalAddress extends Text {
+
+ /**
+ * In case this is a multi-value property. This string will be used as a
+ * delimiter.
+ *
+ * @var string|null
+ */
+ public $delimiter = null;
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'CAL-ADDRESS';
+
+ }
+
+ /**
+ * This returns a normalized form of the value.
+ *
+ * This is primarily used right now to turn mixed-cased schemes in user
+ * uris to lower-case.
+ *
+ * Evolution in particular tends to encode mailto: as MAILTO:.
+ *
+ * @return string
+ */
+ function getNormalizedValue() {
+
+ $input = $this->getValue();
+ if (!strpos($input, ':')) {
+ return $input;
+ }
+ list($schema, $everythingElse) = explode(':', $input, 2);
+ return strtolower($schema) . ':' . $everythingElse;
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Property/ICalendar/Date.php b/vendor/sabre/vobject/lib/Property/ICalendar/Date.php
new file mode 100644
index 000000000..378a0d60a
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/ICalendar/Date.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+/**
+ * DateTime property.
+ *
+ * This object represents DATE values, as defined here:
+ *
+ * http://tools.ietf.org/html/rfc5545#section-3.3.5
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Date extends DateTime {
+
+}
diff --git a/vendor/sabre/vobject/lib/Property/ICalendar/DateTime.php b/vendor/sabre/vobject/lib/Property/ICalendar/DateTime.php
new file mode 100644
index 000000000..d580d4f68
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/ICalendar/DateTime.php
@@ -0,0 +1,404 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+use DateTimeInterface;
+use DateTimeZone;
+use Sabre\VObject\DateTimeParser;
+use Sabre\VObject\InvalidDataException;
+use Sabre\VObject\Property;
+use Sabre\VObject\TimeZoneUtil;
+
+/**
+ * DateTime property.
+ *
+ * This object represents DATE-TIME values, as defined here:
+ *
+ * http://tools.ietf.org/html/rfc5545#section-3.3.4
+ *
+ * This particular object has a bit of hackish magic that it may also in some
+ * cases represent a DATE value. This is because it's a common usecase to be
+ * able to change a DATE-TIME into a DATE.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class DateTime extends Property {
+
+ /**
+ * In case this is a multi-value property. This string will be used as a
+ * delimiter.
+ *
+ * @var string|null
+ */
+ public $delimiter = ',';
+
+ /**
+ * Sets a multi-valued property.
+ *
+ * You may also specify DateTime objects here.
+ *
+ * @param array $parts
+ *
+ * @return void
+ */
+ function setParts(array $parts) {
+
+ if (isset($parts[0]) && $parts[0] instanceof DateTimeInterface) {
+ $this->setDateTimes($parts);
+ } else {
+ parent::setParts($parts);
+ }
+
+ }
+
+ /**
+ * Updates the current value.
+ *
+ * This may be either a single, or multiple strings in an array.
+ *
+ * Instead of strings, you may also use DateTime here.
+ *
+ * @param string|array|DateTimeInterface $value
+ *
+ * @return void
+ */
+ function setValue($value) {
+
+ if (is_array($value) && isset($value[0]) && $value[0] instanceof DateTimeInterface) {
+ $this->setDateTimes($value);
+ } elseif ($value instanceof DateTimeInterface) {
+ $this->setDateTimes([$value]);
+ } else {
+ parent::setValue($value);
+ }
+
+ }
+
+ /**
+ * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+ *
+ * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+ * not yet done, but parameters are not included.
+ *
+ * @param string $val
+ *
+ * @return void
+ */
+ function setRawMimeDirValue($val) {
+
+ $this->setValue(explode($this->delimiter, $val));
+
+ }
+
+ /**
+ * Returns a raw mime-dir representation of the value.
+ *
+ * @return string
+ */
+ function getRawMimeDirValue() {
+
+ return implode($this->delimiter, $this->getParts());
+
+ }
+
+ /**
+ * Returns true if this is a DATE-TIME value, false if it's a DATE.
+ *
+ * @return bool
+ */
+ function hasTime() {
+
+ return strtoupper((string)$this['VALUE']) !== 'DATE';
+
+ }
+
+ /**
+ * Returns true if this is a floating DATE or DATE-TIME.
+ *
+ * Note that DATE is always floating.
+ */
+ function isFloating() {
+
+ return
+ !$this->hasTime() ||
+ (
+ !isset($this['TZID']) &&
+ strpos($this->getValue(), 'Z') === false
+ );
+
+ }
+
+ /**
+ * Returns a date-time value.
+ *
+ * Note that if this property contained more than 1 date-time, only the
+ * first will be returned. To get an array with multiple values, call
+ * getDateTimes.
+ *
+ * If no timezone information is known, because it's either an all-day
+ * property or floating time, we will use the DateTimeZone argument to
+ * figure out the exact date.
+ *
+ * @param DateTimeZone $timeZone
+ *
+ * @return DateTimeImmutable
+ */
+ function getDateTime(DateTimeZone $timeZone = null) {
+
+ $dt = $this->getDateTimes($timeZone);
+ if (!$dt) return;
+
+ return $dt[0];
+
+ }
+
+ /**
+ * Returns multiple date-time values.
+ *
+ * If no timezone information is known, because it's either an all-day
+ * property or floating time, we will use the DateTimeZone argument to
+ * figure out the exact date.
+ *
+ * @param DateTimeZone $timeZone
+ *
+ * @return DateTimeImmutable[]
+ * @return \DateTime[]
+ */
+ function getDateTimes(DateTimeZone $timeZone = null) {
+
+ // Does the property have a TZID?
+ $tzid = $this['TZID'];
+
+ if ($tzid) {
+ $timeZone = TimeZoneUtil::getTimeZone((string)$tzid, $this->root);
+ }
+
+ $dts = [];
+ foreach ($this->getParts() as $part) {
+ $dts[] = DateTimeParser::parse($part, $timeZone);
+ }
+ return $dts;
+
+ }
+
+ /**
+ * Sets the property as a DateTime object.
+ *
+ * @param DateTimeInterface $dt
+ * @param bool isFloating If set to true, timezones will be ignored.
+ *
+ * @return void
+ */
+ function setDateTime(DateTimeInterface $dt, $isFloating = false) {
+
+ $this->setDateTimes([$dt], $isFloating);
+
+ }
+
+ /**
+ * Sets the property as multiple date-time objects.
+ *
+ * The first value will be used as a reference for the timezones, and all
+ * the otehr values will be adjusted for that timezone
+ *
+ * @param DateTimeInterface[] $dt
+ * @param bool isFloating If set to true, timezones will be ignored.
+ *
+ * @return void
+ */
+ function setDateTimes(array $dt, $isFloating = false) {
+
+ $values = [];
+
+ if ($this->hasTime()) {
+
+ $tz = null;
+ $isUtc = false;
+
+ foreach ($dt as $d) {
+
+ if ($isFloating) {
+ $values[] = $d->format('Ymd\\THis');
+ continue;
+ }
+ if (is_null($tz)) {
+ $tz = $d->getTimeZone();
+ $isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z', '+00:00']);
+ if (!$isUtc) {
+ $this->offsetSet('TZID', $tz->getName());
+ }
+ } else {
+ $d = $d->setTimeZone($tz);
+ }
+
+ if ($isUtc) {
+ $values[] = $d->format('Ymd\\THis\\Z');
+ } else {
+ $values[] = $d->format('Ymd\\THis');
+ }
+
+ }
+ if ($isUtc || $isFloating) {
+ $this->offsetUnset('TZID');
+ }
+
+ } else {
+
+ foreach ($dt as $d) {
+
+ $values[] = $d->format('Ymd');
+
+ }
+ $this->offsetUnset('TZID');
+
+ }
+
+ $this->value = $values;
+
+ }
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return $this->hasTime() ? 'DATE-TIME' : 'DATE';
+
+ }
+
+ /**
+ * Returns the value, in the format it should be encoded for JSON.
+ *
+ * This method must always return an array.
+ *
+ * @return array
+ */
+ function getJsonValue() {
+
+ $dts = $this->getDateTimes();
+ $hasTime = $this->hasTime();
+ $isFloating = $this->isFloating();
+
+ $tz = $dts[0]->getTimeZone();
+ $isUtc = $isFloating ? false : in_array($tz->getName(), ['UTC', 'GMT', 'Z']);
+
+ return array_map(
+ function(DateTimeInterface $dt) use ($hasTime, $isUtc) {
+
+ if ($hasTime) {
+ return $dt->format('Y-m-d\\TH:i:s') . ($isUtc ? 'Z' : '');
+ } else {
+ return $dt->format('Y-m-d');
+ }
+
+ },
+ $dts
+ );
+
+ }
+
+ /**
+ * Sets the json value, as it would appear in a jCard or jCal object.
+ *
+ * The value must always be an array.
+ *
+ * @param array $value
+ *
+ * @return void
+ */
+ function setJsonValue(array $value) {
+
+ // dates and times in jCal have one difference to dates and times in
+ // iCalendar. In jCal date-parts are separated by dashes, and
+ // time-parts are separated by colons. It makes sense to just remove
+ // those.
+ $this->setValue(
+ array_map(
+ function($item) {
+
+ return strtr($item, [':' => '', '-' => '']);
+
+ },
+ $value
+ )
+ );
+
+ }
+
+ /**
+ * We need to intercept offsetSet, because it may be used to alter the
+ * VALUE from DATE-TIME to DATE or vice-versa.
+ *
+ * @param string $name
+ * @param mixed $value
+ *
+ * @return void
+ */
+ function offsetSet($name, $value) {
+
+ parent::offsetSet($name, $value);
+ if (strtoupper($name) !== 'VALUE') {
+ return;
+ }
+
+ // This will ensure that dates are correctly encoded.
+ $this->setDateTimes($this->getDateTimes());
+
+ }
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ *
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on)
+ * 2 - An inconsequential issue
+ * 3 - A severe issue.
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $messages = parent::validate($options);
+ $valueType = $this->getValueType();
+ $values = $this->getParts();
+ try {
+ foreach ($values as $value) {
+ switch ($valueType) {
+ case 'DATE' :
+ DateTimeParser::parseDate($value);
+ break;
+ case 'DATE-TIME' :
+ DateTimeParser::parseDateTime($value);
+ break;
+ }
+ }
+ } catch (InvalidDataException $e) {
+ $messages[] = [
+ 'level' => 3,
+ 'message' => 'The supplied value (' . $value . ') is not a correct ' . $valueType,
+ 'node' => $this,
+ ];
+ }
+ return $messages;
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Property/ICalendar/Duration.php b/vendor/sabre/vobject/lib/Property/ICalendar/Duration.php
new file mode 100644
index 000000000..66d366960
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/ICalendar/Duration.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+use Sabre\VObject\Property;
+use Sabre\VObject\DateTimeParser;
+
+/**
+ * Duration property.
+ *
+ * This object represents DURATION values, as defined here:
+ *
+ * http://tools.ietf.org/html/rfc5545#section-3.3.6
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Duration extends Property {
+
+ /**
+ * In case this is a multi-value property. This string will be used as a
+ * delimiter.
+ *
+ * @var string|null
+ */
+ public $delimiter = ',';
+
+ /**
+ * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+ *
+ * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+ * not yet done, but parameters are not included.
+ *
+ * @param string $val
+ *
+ * @return void
+ */
+ function setRawMimeDirValue($val) {
+
+ $this->setValue(explode($this->delimiter, $val));
+
+ }
+
+ /**
+ * Returns a raw mime-dir representation of the value.
+ *
+ * @return string
+ */
+ function getRawMimeDirValue() {
+
+ return implode($this->delimiter, $this->getParts());
+
+ }
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'DURATION';
+
+ }
+
+ /**
+ * Returns a DateInterval representation of the Duration property.
+ *
+ * If the property has more than one value, only the first is returned.
+ *
+ * @return \DateInterval
+ */
+ function getDateInterval() {
+
+ $parts = $this->getParts();
+ $value = $parts[0];
+ return DateTimeParser::parseDuration($value);
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Property/ICalendar/Period.php b/vendor/sabre/vobject/lib/Property/ICalendar/Period.php
new file mode 100644
index 000000000..a4561d929
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/ICalendar/Period.php
@@ -0,0 +1,155 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+use Sabre\VObject\Property;
+use Sabre\VObject\DateTimeParser;
+use Sabre\Xml;
+
+/**
+ * Period property.
+ *
+ * This object represents PERIOD values, as defined here:
+ *
+ * http://tools.ietf.org/html/rfc5545#section-3.8.2.6
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Period extends Property {
+
+ /**
+ * In case this is a multi-value property. This string will be used as a
+ * delimiter.
+ *
+ * @var string|null
+ */
+ public $delimiter = ',';
+
+ /**
+ * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+ *
+ * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+ * not yet done, but parameters are not included.
+ *
+ * @param string $val
+ *
+ * @return void
+ */
+ function setRawMimeDirValue($val) {
+
+ $this->setValue(explode($this->delimiter, $val));
+
+ }
+
+ /**
+ * Returns a raw mime-dir representation of the value.
+ *
+ * @return string
+ */
+ function getRawMimeDirValue() {
+
+ return implode($this->delimiter, $this->getParts());
+
+ }
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'PERIOD';
+
+ }
+
+ /**
+ * Sets the json value, as it would appear in a jCard or jCal object.
+ *
+ * The value must always be an array.
+ *
+ * @param array $value
+ *
+ * @return void
+ */
+ function setJsonValue(array $value) {
+
+ $value = array_map(
+ function($item) {
+
+ return strtr(implode('/', $item), [':' => '', '-' => '']);
+
+ },
+ $value
+ );
+ parent::setJsonValue($value);
+
+ }
+
+ /**
+ * Returns the value, in the format it should be encoded for json.
+ *
+ * This method must always return an array.
+ *
+ * @return array
+ */
+ function getJsonValue() {
+
+ $return = [];
+ foreach ($this->getParts() as $item) {
+
+ list($start, $end) = explode('/', $item, 2);
+
+ $start = DateTimeParser::parseDateTime($start);
+
+ // This is a duration value.
+ if ($end[0] === 'P') {
+ $return[] = [
+ $start->format('Y-m-d\\TH:i:s'),
+ $end
+ ];
+ } else {
+ $end = DateTimeParser::parseDateTime($end);
+ $return[] = [
+ $start->format('Y-m-d\\TH:i:s'),
+ $end->format('Y-m-d\\TH:i:s'),
+ ];
+ }
+
+ }
+
+ return $return;
+
+ }
+
+ /**
+ * This method serializes only the value of a property. This is used to
+ * create xCard or xCal documents.
+ *
+ * @param Xml\Writer $writer XML writer.
+ *
+ * @return void
+ */
+ protected function xmlSerializeValue(Xml\Writer $writer) {
+
+ $writer->startElement(strtolower($this->getValueType()));
+ $value = $this->getJsonValue();
+ $writer->writeElement('start', $value[0][0]);
+
+ if ($value[0][1][0] === 'P') {
+ $writer->writeElement('duration', $value[0][1]);
+ }
+ else {
+ $writer->writeElement('end', $value[0][1]);
+ }
+
+ $writer->endElement();
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Property/ICalendar/Recur.php b/vendor/sabre/vobject/lib/Property/ICalendar/Recur.php
new file mode 100644
index 000000000..a3c36dc64
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/ICalendar/Recur.php
@@ -0,0 +1,293 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+use Sabre\VObject\Property;
+use Sabre\Xml;
+
+/**
+ * Recur property.
+ *
+ * This object represents RECUR properties.
+ * These values are just used for RRULE and the now deprecated EXRULE.
+ *
+ * The RRULE property may look something like this:
+ *
+ * RRULE:FREQ=MONTHLY;BYDAY=1,2,3;BYHOUR=5.
+ *
+ * This property exposes this as a key=>value array that is accessible using
+ * getParts, and may be set using setParts.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Recur extends Property {
+
+ /**
+ * Updates the current value.
+ *
+ * This may be either a single, or multiple strings in an array.
+ *
+ * @param string|array $value
+ *
+ * @return void
+ */
+ function setValue($value) {
+
+ // If we're getting the data from json, we'll be receiving an object
+ if ($value instanceof \StdClass) {
+ $value = (array)$value;
+ }
+
+ if (is_array($value)) {
+ $newVal = [];
+ foreach ($value as $k => $v) {
+
+ if (is_string($v)) {
+ $v = strtoupper($v);
+
+ // The value had multiple sub-values
+ if (strpos($v, ',') !== false) {
+ $v = explode(',', $v);
+ }
+ if (strcmp($k, 'until') === 0) {
+ $v = strtr($v, [':' => '', '-' => '']);
+ }
+ } elseif (is_array($v)) {
+ $v = array_map('strtoupper', $v);
+ }
+
+ $newVal[strtoupper($k)] = $v;
+ }
+ $this->value = $newVal;
+ } elseif (is_string($value)) {
+ $this->value = self::stringToArray($value);
+ } else {
+ throw new \InvalidArgumentException('You must either pass a string, or a key=>value array');
+ }
+
+ }
+
+ /**
+ * Returns the current value.
+ *
+ * This method will always return a singular value. If this was a
+ * multi-value object, some decision will be made first on how to represent
+ * it as a string.
+ *
+ * To get the correct multi-value version, use getParts.
+ *
+ * @return string
+ */
+ function getValue() {
+
+ $out = [];
+ foreach ($this->value as $key => $value) {
+ $out[] = $key . '=' . (is_array($value) ? implode(',', $value) : $value);
+ }
+ return strtoupper(implode(';', $out));
+
+ }
+
+ /**
+ * Sets a multi-valued property.
+ *
+ * @param array $parts
+ * @return void
+ */
+ function setParts(array $parts) {
+
+ $this->setValue($parts);
+
+ }
+
+ /**
+ * Returns a multi-valued property.
+ *
+ * This method always returns an array, if there was only a single value,
+ * it will still be wrapped in an array.
+ *
+ * @return array
+ */
+ function getParts() {
+
+ return $this->value;
+
+ }
+
+ /**
+ * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+ *
+ * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+ * not yet done, but parameters are not included.
+ *
+ * @param string $val
+ *
+ * @return void
+ */
+ function setRawMimeDirValue($val) {
+
+ $this->setValue($val);
+
+ }
+
+ /**
+ * Returns a raw mime-dir representation of the value.
+ *
+ * @return string
+ */
+ function getRawMimeDirValue() {
+
+ return $this->getValue();
+
+ }
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'RECUR';
+
+ }
+
+ /**
+ * Returns the value, in the format it should be encoded for json.
+ *
+ * This method must always return an array.
+ *
+ * @return array
+ */
+ function getJsonValue() {
+
+ $values = [];
+ foreach ($this->getParts() as $k => $v) {
+ if (strcmp($k, 'UNTIL') === 0) {
+ $date = new DateTime($this->root, null, $v);
+ $values[strtolower($k)] = $date->getJsonValue()[0];
+ } elseif (strcmp($k, 'COUNT') === 0) {
+ $values[strtolower($k)] = intval($v);
+ } else {
+ $values[strtolower($k)] = $v;
+ }
+ }
+ return [$values];
+
+ }
+
+ /**
+ * This method serializes only the value of a property. This is used to
+ * create xCard or xCal documents.
+ *
+ * @param Xml\Writer $writer XML writer.
+ *
+ * @return void
+ */
+ protected function xmlSerializeValue(Xml\Writer $writer) {
+
+ $valueType = strtolower($this->getValueType());
+
+ foreach ($this->getJsonValue() as $value) {
+ $writer->writeElement($valueType, $value);
+ }
+
+ }
+
+ /**
+ * Parses an RRULE value string, and turns it into a struct-ish array.
+ *
+ * @param string $value
+ *
+ * @return array
+ */
+ static function stringToArray($value) {
+
+ $value = strtoupper($value);
+ $newValue = [];
+ foreach (explode(';', $value) as $part) {
+
+ // Skipping empty parts.
+ if (empty($part)) {
+ continue;
+ }
+ list($partName, $partValue) = explode('=', $part);
+
+ // The value itself had multiple values..
+ if (strpos($partValue, ',') !== false) {
+ $partValue = explode(',', $partValue);
+ }
+ $newValue[$partName] = $partValue;
+
+ }
+
+ return $newValue;
+ }
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ *
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on)
+ * 2 - An inconsequential issue
+ * 3 - A severe issue.
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $repair = ($options & self::REPAIR);
+
+ $warnings = parent::validate($options);
+ $values = $this->getParts();
+
+ foreach ($values as $key => $value) {
+
+ if (empty($value)) {
+ $warnings[] = [
+ 'level' => $repair ? 3 : 1,
+ 'message' => 'Invalid value for ' . $key . ' in ' . $this->name,
+ 'node' => $this
+ ];
+ if ($repair) {
+ unset($values[$key]);
+ }
+ }
+
+ }
+ if (!isset($values['FREQ'])) {
+ $warnings[] = [
+ 'level' => $repair ? 3 : 1,
+ 'message' => 'FREQ is required in ' . $this->name,
+ 'node' => $this
+ ];
+ if ($repair) {
+ $this->parent->remove($this);
+ }
+ }
+ if ($repair) {
+ $this->setValue($values);
+ }
+
+ return $warnings;
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Property/IntegerValue.php b/vendor/sabre/vobject/lib/Property/IntegerValue.php
new file mode 100644
index 000000000..5bd1887fa
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/IntegerValue.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use
+ Sabre\VObject\Property;
+
+/**
+ * Integer property.
+ *
+ * This object represents INTEGER values. These are always a single integer.
+ * They may be preceeded by either + or -.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class IntegerValue extends Property {
+
+ /**
+ * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+ *
+ * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+ * not yet done, but parameters are not included.
+ *
+ * @param string $val
+ *
+ * @return void
+ */
+ function setRawMimeDirValue($val) {
+
+ $this->setValue((int)$val);
+
+ }
+
+ /**
+ * Returns a raw mime-dir representation of the value.
+ *
+ * @return string
+ */
+ function getRawMimeDirValue() {
+
+ return $this->value;
+
+ }
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'INTEGER';
+
+ }
+
+ /**
+ * Returns the value, in the format it should be encoded for json.
+ *
+ * This method must always return an array.
+ *
+ * @return array
+ */
+ function getJsonValue() {
+
+ return [(int)$this->getValue()];
+
+ }
+
+ /**
+ * Hydrate data from a XML subtree, as it would appear in a xCard or xCal
+ * object.
+ *
+ * @param array $value
+ *
+ * @return void
+ */
+ function setXmlValue(array $value) {
+
+ $value = array_map('intval', $value);
+ parent::setXmlValue($value);
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Property/Text.php b/vendor/sabre/vobject/lib/Property/Text.php
new file mode 100644
index 000000000..2e16ac534
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/Text.php
@@ -0,0 +1,413 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use Sabre\VObject\Property;
+use Sabre\VObject\Component;
+use Sabre\VObject\Parser\MimeDir;
+use Sabre\VObject\Document;
+use Sabre\Xml;
+
+/**
+ * Text property.
+ *
+ * This object represents TEXT values.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Text extends Property {
+
+ /**
+ * In case this is a multi-value property. This string will be used as a
+ * delimiter.
+ *
+ * @var string
+ */
+ public $delimiter = ',';
+
+ /**
+ * List of properties that are considered 'structured'.
+ *
+ * @var array
+ */
+ protected $structuredValues = [
+ // vCard
+ 'N',
+ 'ADR',
+ 'ORG',
+ 'GENDER',
+ 'CLIENTPIDMAP',
+
+ // iCalendar
+ 'REQUEST-STATUS',
+ ];
+
+ /**
+ * Some text components have a minimum number of components.
+ *
+ * N must for instance be represented as 5 components, separated by ;, even
+ * if the last few components are unused.
+ *
+ * @var array
+ */
+ protected $minimumPropertyValues = [
+ 'N' => 5,
+ 'ADR' => 7,
+ ];
+
+ /**
+ * Creates the property.
+ *
+ * You can specify the parameters either in key=>value syntax, in which case
+ * parameters will automatically be created, or you can just pass a list of
+ * Parameter objects.
+ *
+ * @param Component $root The root document
+ * @param string $name
+ * @param string|array|null $value
+ * @param array $parameters List of parameters
+ * @param string $group The vcard property group
+ *
+ * @return void
+ */
+ function __construct(Component $root, $name, $value = null, array $parameters = [], $group = null) {
+
+ // There's two types of multi-valued text properties:
+ // 1. multivalue properties.
+ // 2. structured value properties
+ //
+ // The former is always separated by a comma, the latter by semi-colon.
+ if (in_array($name, $this->structuredValues)) {
+ $this->delimiter = ';';
+ }
+
+ parent::__construct($root, $name, $value, $parameters, $group);
+
+ }
+
+ /**
+ * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+ *
+ * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+ * not yet done, but parameters are not included.
+ *
+ * @param string $val
+ *
+ * @return void
+ */
+ function setRawMimeDirValue($val) {
+
+ $this->setValue(MimeDir::unescapeValue($val, $this->delimiter));
+
+ }
+
+ /**
+ * Sets the value as a quoted-printable encoded string.
+ *
+ * @param string $val
+ *
+ * @return void
+ */
+ function setQuotedPrintableValue($val) {
+
+ $val = quoted_printable_decode($val);
+
+ // Quoted printable only appears in vCard 2.1, and the only character
+ // that may be escaped there is ;. So we are simply splitting on just
+ // that.
+ //
+ // We also don't have to unescape \\, so all we need to look for is a ;
+ // that's not preceeded with a \.
+ $regex = '# (?<!\\\\) ; #x';
+ $matches = preg_split($regex, $val);
+ $this->setValue($matches);
+
+ }
+
+ /**
+ * Returns a raw mime-dir representation of the value.
+ *
+ * @return string
+ */
+ function getRawMimeDirValue() {
+
+ $val = $this->getParts();
+
+ if (isset($this->minimumPropertyValues[$this->name])) {
+ $val = array_pad($val, $this->minimumPropertyValues[$this->name], '');
+ }
+
+ foreach ($val as &$item) {
+
+ if (!is_array($item)) {
+ $item = [$item];
+ }
+
+ foreach ($item as &$subItem) {
+ $subItem = strtr(
+ $subItem,
+ [
+ '\\' => '\\\\',
+ ';' => '\;',
+ ',' => '\,',
+ "\n" => '\n',
+ "\r" => "",
+ ]
+ );
+ }
+ $item = implode(',', $item);
+
+ }
+
+ return implode($this->delimiter, $val);
+
+ }
+
+ /**
+ * Returns the value, in the format it should be encoded for json.
+ *
+ * This method must always return an array.
+ *
+ * @return array
+ */
+ function getJsonValue() {
+
+ // Structured text values should always be returned as a single
+ // array-item. Multi-value text should be returned as multiple items in
+ // the top-array.
+ if (in_array($this->name, $this->structuredValues)) {
+ return [$this->getParts()];
+ }
+ return $this->getParts();
+
+ }
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'TEXT';
+
+ }
+
+ /**
+ * Turns the object back into a serialized blob.
+ *
+ * @return string
+ */
+ function serialize() {
+
+ // We need to kick in a special type of encoding, if it's a 2.1 vcard.
+ if ($this->root->getDocumentType() !== Document::VCARD21) {
+ return parent::serialize();
+ }
+
+ $val = $this->getParts();
+
+ if (isset($this->minimumPropertyValues[$this->name])) {
+ $val = array_pad($val, $this->minimumPropertyValues[$this->name], '');
+ }
+
+ // Imploding multiple parts into a single value, and splitting the
+ // values with ;.
+ if (count($val) > 1) {
+ foreach ($val as $k => $v) {
+ $val[$k] = str_replace(';', '\;', $v);
+ }
+ $val = implode(';', $val);
+ } else {
+ $val = $val[0];
+ }
+
+ $str = $this->name;
+ if ($this->group) $str = $this->group . '.' . $this->name;
+ foreach ($this->parameters as $param) {
+
+ if ($param->getValue() === 'QUOTED-PRINTABLE') {
+ continue;
+ }
+ $str .= ';' . $param->serialize();
+
+ }
+
+
+
+ // If the resulting value contains a \n, we must encode it as
+ // quoted-printable.
+ if (strpos($val, "\n") !== false) {
+
+ $str .= ';ENCODING=QUOTED-PRINTABLE:';
+ $lastLine = $str;
+ $out = null;
+
+ // The PHP built-in quoted-printable-encode does not correctly
+ // encode newlines for us. Specifically, the \r\n sequence must in
+ // vcards be encoded as =0D=OA and we must insert soft-newlines
+ // every 75 bytes.
+ for ($ii = 0;$ii < strlen($val);$ii++) {
+ $ord = ord($val[$ii]);
+ // These characters are encoded as themselves.
+ if ($ord >= 32 && $ord <= 126) {
+ $lastLine .= $val[$ii];
+ } else {
+ $lastLine .= '=' . strtoupper(bin2hex($val[$ii]));
+ }
+ if (strlen($lastLine) >= 75) {
+ // Soft line break
+ $out .= $lastLine . "=\r\n ";
+ $lastLine = null;
+ }
+
+ }
+ if (!is_null($lastLine)) $out .= $lastLine . "\r\n";
+ return $out;
+
+ } else {
+ $str .= ':' . $val;
+ $out = '';
+ while (strlen($str) > 0) {
+ if (strlen($str) > 75) {
+ $out .= mb_strcut($str, 0, 75, 'utf-8') . "\r\n";
+ $str = ' ' . mb_strcut($str, 75, strlen($str), 'utf-8');
+ } else {
+ $out .= $str . "\r\n";
+ $str = '';
+ break;
+ }
+ }
+
+ return $out;
+
+ }
+
+ }
+
+ /**
+ * This method serializes only the value of a property. This is used to
+ * create xCard or xCal documents.
+ *
+ * @param Xml\Writer $writer XML writer.
+ *
+ * @return void
+ */
+ protected function xmlSerializeValue(Xml\Writer $writer) {
+
+ $values = $this->getParts();
+
+ $map = function($items) use ($values, $writer) {
+ foreach ($items as $i => $item) {
+ $writer->writeElement(
+ $item,
+ !empty($values[$i]) ? $values[$i] : null
+ );
+ }
+ };
+
+ switch ($this->name) {
+
+ // Special-casing the REQUEST-STATUS property.
+ //
+ // See:
+ // http://tools.ietf.org/html/rfc6321#section-3.4.1.3
+ case 'REQUEST-STATUS':
+ $writer->writeElement('code', $values[0]);
+ $writer->writeElement('description', $values[1]);
+
+ if (isset($values[2])) {
+ $writer->writeElement('data', $values[2]);
+ }
+ break;
+
+ case 'N':
+ $map([
+ 'surname',
+ 'given',
+ 'additional',
+ 'prefix',
+ 'suffix'
+ ]);
+ break;
+
+ case 'GENDER':
+ $map([
+ 'sex',
+ 'text'
+ ]);
+ break;
+
+ case 'ADR':
+ $map([
+ 'pobox',
+ 'ext',
+ 'street',
+ 'locality',
+ 'region',
+ 'code',
+ 'country'
+ ]);
+ break;
+
+ case 'CLIENTPIDMAP':
+ $map([
+ 'sourceid',
+ 'uri'
+ ]);
+ break;
+
+ default:
+ parent::xmlSerializeValue($writer);
+ }
+
+ }
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * - Node::REPAIR - If something is broken, and automatic repair may
+ * be attempted.
+ *
+ * An array is returned with warnings.
+ *
+ * Every item in the array has the following properties:
+ * * level - (number between 1 and 3 with severity information)
+ * * message - (human readable message)
+ * * node - (reference to the offending node)
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $warnings = parent::validate($options);
+
+ if (isset($this->minimumPropertyValues[$this->name])) {
+
+ $minimum = $this->minimumPropertyValues[$this->name];
+ $parts = $this->getParts();
+ if (count($parts) < $minimum) {
+ $warnings[] = [
+ 'level' => $options & self::REPAIR ? 1 : 3,
+ 'message' => 'The ' . $this->name . ' property must have at least ' . $minimum . ' values. It only has ' . count($parts),
+ 'node' => $this,
+ ];
+ if ($options & self::REPAIR) {
+ $parts = array_pad($parts, $minimum, '');
+ $this->setParts($parts);
+ }
+ }
+
+ }
+ return $warnings;
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Property/Time.php b/vendor/sabre/vobject/lib/Property/Time.php
new file mode 100644
index 000000000..dbafc3b85
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/Time.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use Sabre\VObject\DateTimeParser;
+
+/**
+ * Time property.
+ *
+ * This object encodes TIME values.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Time extends Text {
+
+ /**
+ * In case this is a multi-value property. This string will be used as a
+ * delimiter.
+ *
+ * @var string|null
+ */
+ public $delimiter = null;
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'TIME';
+
+ }
+
+ /**
+ * Sets the JSON value, as it would appear in a jCard or jCal object.
+ *
+ * The value must always be an array.
+ *
+ * @param array $value
+ *
+ * @return void
+ */
+ function setJsonValue(array $value) {
+
+ // Removing colons from value.
+ $value = str_replace(
+ ':',
+ '',
+ $value
+ );
+
+ if (count($value) === 1) {
+ $this->setValue(reset($value));
+ } else {
+ $this->setValue($value);
+ }
+
+ }
+
+ /**
+ * Returns the value, in the format it should be encoded for json.
+ *
+ * This method must always return an array.
+ *
+ * @return array
+ */
+ function getJsonValue() {
+
+ $parts = DateTimeParser::parseVCardTime($this->getValue());
+ $timeStr = '';
+
+ // Hour
+ if (!is_null($parts['hour'])) {
+ $timeStr .= $parts['hour'];
+
+ if (!is_null($parts['minute'])) {
+ $timeStr .= ':';
+ }
+ } else {
+ // We know either minute or second _must_ be set, so we insert a
+ // dash for an empty value.
+ $timeStr .= '-';
+ }
+
+ // Minute
+ if (!is_null($parts['minute'])) {
+ $timeStr .= $parts['minute'];
+
+ if (!is_null($parts['second'])) {
+ $timeStr .= ':';
+ }
+ } else {
+ if (isset($parts['second'])) {
+ // Dash for empty minute
+ $timeStr .= '-';
+ }
+ }
+
+ // Second
+ if (!is_null($parts['second'])) {
+ $timeStr .= $parts['second'];
+ }
+
+ // Timezone
+ if (!is_null($parts['timezone'])) {
+ if ($parts['timezone'] === 'Z') {
+ $timeStr .= 'Z';
+ } else {
+ $timeStr .=
+ preg_replace('/([0-9]{2})([0-9]{2})$/', '$1:$2', $parts['timezone']);
+ }
+ }
+
+ return [$timeStr];
+
+ }
+
+ /**
+ * Hydrate data from a XML subtree, as it would appear in a xCard or xCal
+ * object.
+ *
+ * @param array $value
+ *
+ * @return void
+ */
+ function setXmlValue(array $value) {
+
+ $value = array_map(
+ function($value) {
+ return str_replace(':', '', $value);
+ },
+ $value
+ );
+ parent::setXmlValue($value);
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Property/Unknown.php b/vendor/sabre/vobject/lib/Property/Unknown.php
new file mode 100644
index 000000000..7a3373868
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/Unknown.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+/**
+ * Unknown property.
+ *
+ * This object represents any properties not recognized by the parser.
+ * This type of value has been introduced by the jCal, jCard specs.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Unknown extends Text {
+
+ /**
+ * Returns the value, in the format it should be encoded for json.
+ *
+ * This method must always return an array.
+ *
+ * @return array
+ */
+ function getJsonValue() {
+
+ return [$this->getRawMimeDirValue()];
+
+ }
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'UNKNOWN';
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Property/Uri.php b/vendor/sabre/vobject/lib/Property/Uri.php
new file mode 100644
index 000000000..58e676e60
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/Uri.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use Sabre\VObject\Property;
+use Sabre\VObject\Parameter;
+
+/**
+ * URI property.
+ *
+ * This object encodes URI values. vCard 2.1 calls these URL.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Uri extends Text {
+
+ /**
+ * In case this is a multi-value property. This string will be used as a
+ * delimiter.
+ *
+ * @var string|null
+ */
+ public $delimiter = null;
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'URI';
+
+ }
+
+ /**
+ * Returns an iterable list of children.
+ *
+ * @return array
+ */
+ function parameters() {
+
+ $parameters = parent::parameters();
+ if (!isset($parameters['VALUE']) && in_array($this->name, ['URL', 'PHOTO'])) {
+ // If we are encoding a URI value, and this URI value has no
+ // VALUE=URI parameter, we add it anyway.
+ //
+ // This is not required by any spec, but both Apple iCal and Apple
+ // AddressBook (at least in version 10.8) will trip over this if
+ // this is not set, and so it improves compatibility.
+ //
+ // See Issue #227 and #235
+ $parameters['VALUE'] = new Parameter($this->root, 'VALUE', 'URI');
+ }
+ return $parameters;
+
+ }
+
+ /**
+ * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+ *
+ * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+ * not yet done, but parameters are not included.
+ *
+ * @param string $val
+ *
+ * @return void
+ */
+ function setRawMimeDirValue($val) {
+
+ // Normally we don't need to do any type of unescaping for these
+ // properties, however.. we've noticed that Google Contacts
+ // specifically escapes the colon (:) with a blackslash. While I have
+ // no clue why they thought that was a good idea, I'm unescaping it
+ // anyway.
+ //
+ // Good thing backslashes are not allowed in urls. Makes it easy to
+ // assume that a backslash is always intended as an escape character.
+ if ($this->name === 'URL') {
+ $regex = '# (?: (\\\\ (?: \\\\ | : ) ) ) #x';
+ $matches = preg_split($regex, $val, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+ $newVal = '';
+ foreach ($matches as $match) {
+ switch ($match) {
+ case '\:' :
+ $newVal .= ':';
+ break;
+ default :
+ $newVal .= $match;
+ break;
+ }
+ }
+ $this->value = $newVal;
+ } else {
+ $this->value = strtr($val, ['\,' => ',']);
+ }
+
+ }
+
+ /**
+ * Returns a raw mime-dir representation of the value.
+ *
+ * @return string
+ */
+ function getRawMimeDirValue() {
+
+ if (is_array($this->value)) {
+ $value = $this->value[0];
+ } else {
+ $value = $this->value;
+ }
+
+ return strtr($value, [',' => '\,']);
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Property/UtcOffset.php b/vendor/sabre/vobject/lib/Property/UtcOffset.php
new file mode 100644
index 000000000..61895c48e
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/UtcOffset.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+/**
+ * UtcOffset property.
+ *
+ * This object encodes UTC-OFFSET values.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class UtcOffset extends Text {
+
+ /**
+ * In case this is a multi-value property. This string will be used as a
+ * delimiter.
+ *
+ * @var string|null
+ */
+ public $delimiter = null;
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'UTC-OFFSET';
+
+ }
+
+ /**
+ * Sets the JSON value, as it would appear in a jCard or jCal object.
+ *
+ * The value must always be an array.
+ *
+ * @param array $value
+ *
+ * @return void
+ */
+ function setJsonValue(array $value) {
+
+ $value = array_map(
+ function($value) {
+ return str_replace(':', '', $value);
+ },
+ $value
+ );
+ parent::setJsonValue($value);
+
+ }
+
+ /**
+ * Returns the value, in the format it should be encoded for JSON.
+ *
+ * This method must always return an array.
+ *
+ * @return array
+ */
+ function getJsonValue() {
+
+ return array_map(
+ function($value) {
+ return substr($value, 0, -2) . ':' .
+ substr($value, -2);
+ },
+ parent::getJsonValue()
+ );
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Property/VCard/Date.php b/vendor/sabre/vobject/lib/Property/VCard/Date.php
new file mode 100644
index 000000000..1ef6dff34
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/VCard/Date.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Sabre\VObject\Property\VCard;
+
+/**
+ * Date property.
+ *
+ * This object encodes vCard DATE values.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Date extends DateAndOrTime {
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'DATE';
+
+ }
+
+ /**
+ * Sets the property as a DateTime object.
+ *
+ * @param \DateTimeInterface $dt
+ *
+ * @return void
+ */
+ function setDateTime(\DateTimeInterface $dt) {
+
+ $this->value = $dt->format('Ymd');
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Property/VCard/DateAndOrTime.php b/vendor/sabre/vobject/lib/Property/VCard/DateAndOrTime.php
new file mode 100644
index 000000000..650e19cbf
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/VCard/DateAndOrTime.php
@@ -0,0 +1,405 @@
+<?php
+
+namespace Sabre\VObject\Property\VCard;
+
+use DateTimeInterface;
+use DateTimeImmutable;
+use DateTime;
+use Sabre\VObject\DateTimeParser;
+use Sabre\VObject\InvalidDataException;
+use Sabre\VObject\Property;
+use Sabre\Xml;
+
+/**
+ * DateAndOrTime property.
+ *
+ * This object encodes DATE-AND-OR-TIME values.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class DateAndOrTime extends Property {
+
+ /**
+ * Field separator.
+ *
+ * @var null|string
+ */
+ public $delimiter = null;
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'DATE-AND-OR-TIME';
+
+ }
+
+ /**
+ * Sets a multi-valued property.
+ *
+ * You may also specify DateTime objects here.
+ *
+ * @param array $parts
+ *
+ * @return void
+ */
+ function setParts(array $parts) {
+
+ if (count($parts) > 1) {
+ throw new \InvalidArgumentException('Only one value allowed');
+ }
+ if (isset($parts[0]) && $parts[0] instanceof \DateTime) {
+ $this->setDateTime($parts[0]);
+ } else {
+ parent::setParts($parts);
+ }
+
+ }
+
+ /**
+ * Updates the current value.
+ *
+ * This may be either a single, or multiple strings in an array.
+ *
+ * Instead of strings, you may also use DateTime here.
+ *
+ * @param string|array|\DateTime $value
+ *
+ * @return void
+ */
+ function setValue($value) {
+
+ if ($value instanceof \DateTime) {
+ $this->setDateTime($value);
+ } else {
+ parent::setValue($value);
+ }
+
+ }
+
+ /**
+ * Sets the property as a DateTime object.
+ *
+ * @param DateTimeInterface $dt
+ *
+ * @return void
+ */
+ function setDateTime(DateTimeInterface $dt) {
+
+ $tz = $dt->getTimeZone();
+ $isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z']);
+
+ if ($isUtc) {
+ $value = $dt->format('Ymd\\THis\\Z');
+ } else {
+ // Calculating the offset.
+ $value = $dt->format('Ymd\\THisO');
+ }
+
+ $this->value = $value;
+
+ }
+
+ /**
+ * Returns a date-time value.
+ *
+ * Note that if this property contained more than 1 date-time, only the
+ * first will be returned. To get an array with multiple values, call
+ * getDateTimes.
+ *
+ * If no time was specified, we will always use midnight (in the default
+ * timezone) as the time.
+ *
+ * If parts of the date were omitted, such as the year, we will grab the
+ * current values for those. So at the time of writing, if the year was
+ * omitted, we would have filled in 2014.
+ *
+ * @return DateTimeImmutable
+ */
+ function getDateTime() {
+
+ $now = new DateTime();
+
+ $tzFormat = $now->getTimezone()->getOffset($now) === 0 ? '\\Z' : 'O';
+ $nowParts = DateTimeParser::parseVCardDateTime($now->format('Ymd\\This' . $tzFormat));
+
+ $dateParts = DateTimeParser::parseVCardDateTime($this->getValue());
+
+ // This sets all the missing parts to the current date/time.
+ // So if the year was missing for a birthday, we're making it 'this
+ // year'.
+ foreach ($dateParts as $k => $v) {
+ if (is_null($v)) {
+ $dateParts[$k] = $nowParts[$k];
+ }
+ }
+ return new DateTimeImmutable("$dateParts[year]-$dateParts[month]-$dateParts[date] $dateParts[hour]:$dateParts[minute]:$dateParts[second] $dateParts[timezone]");
+
+ }
+
+ /**
+ * Returns the value, in the format it should be encoded for json.
+ *
+ * This method must always return an array.
+ *
+ * @return array
+ */
+ function getJsonValue() {
+
+ $parts = DateTimeParser::parseVCardDateTime($this->getValue());
+
+ $dateStr = '';
+
+ // Year
+ if (!is_null($parts['year'])) {
+
+ $dateStr .= $parts['year'];
+
+ if (!is_null($parts['month'])) {
+ // If a year and a month is set, we need to insert a separator
+ // dash.
+ $dateStr .= '-';
+ }
+
+ } else {
+
+ if (!is_null($parts['month']) || !is_null($parts['date'])) {
+ // Inserting two dashes
+ $dateStr .= '--';
+ }
+
+ }
+
+ // Month
+ if (!is_null($parts['month'])) {
+
+ $dateStr .= $parts['month'];
+
+ if (isset($parts['date'])) {
+ // If month and date are set, we need the separator dash.
+ $dateStr .= '-';
+ }
+
+ } elseif (isset($parts['date'])) {
+ // If the month is empty, and a date is set, we need a 'empty
+ // dash'
+ $dateStr .= '-';
+ }
+
+ // Date
+ if (!is_null($parts['date'])) {
+ $dateStr .= $parts['date'];
+ }
+
+
+ // Early exit if we don't have a time string.
+ if (is_null($parts['hour']) && is_null($parts['minute']) && is_null($parts['second'])) {
+ return [$dateStr];
+ }
+
+ $dateStr .= 'T';
+
+ // Hour
+ if (!is_null($parts['hour'])) {
+
+ $dateStr .= $parts['hour'];
+
+ if (!is_null($parts['minute'])) {
+ $dateStr .= ':';
+ }
+
+ } else {
+ // We know either minute or second _must_ be set, so we insert a
+ // dash for an empty value.
+ $dateStr .= '-';
+ }
+
+ // Minute
+ if (!is_null($parts['minute'])) {
+
+ $dateStr .= $parts['minute'];
+
+ if (!is_null($parts['second'])) {
+ $dateStr .= ':';
+ }
+
+ } elseif (isset($parts['second'])) {
+ // Dash for empty minute
+ $dateStr .= '-';
+ }
+
+ // Second
+ if (!is_null($parts['second'])) {
+ $dateStr .= $parts['second'];
+ }
+
+ // Timezone
+ if (!is_null($parts['timezone'])) {
+ $dateStr .= $parts['timezone'];
+ }
+
+ return [$dateStr];
+
+ }
+
+ /**
+ * This method serializes only the value of a property. This is used to
+ * create xCard or xCal documents.
+ *
+ * @param Xml\Writer $writer XML writer.
+ *
+ * @return void
+ */
+ protected function xmlSerializeValue(Xml\Writer $writer) {
+
+ $valueType = strtolower($this->getValueType());
+ $parts = DateTimeParser::parseVCardDateAndOrTime($this->getValue());
+ $value = '';
+
+ // $d = defined
+ $d = function($part) use ($parts) {
+ return !is_null($parts[$part]);
+ };
+
+ // $r = read
+ $r = function($part) use ($parts) {
+ return $parts[$part];
+ };
+
+ // From the Relax NG Schema.
+ //
+ // # 4.3.1
+ // value-date = element date {
+ // xsd:string { pattern = "\d{8}|\d{4}-\d\d|--\d\d(\d\d)?|---\d\d" }
+ // }
+ if (($d('year') || $d('month') || $d('date'))
+ && (!$d('hour') && !$d('minute') && !$d('second') && !$d('timezone'))) {
+
+ if ($d('year') && $d('month') && $d('date')) {
+ $value .= $r('year') . $r('month') . $r('date');
+ } elseif ($d('year') && $d('month') && !$d('date')) {
+ $value .= $r('year') . '-' . $r('month');
+ } elseif (!$d('year') && $d('month')) {
+ $value .= '--' . $r('month') . $r('date');
+ } elseif (!$d('year') && !$d('month') && $d('date')) {
+ $value .= '---' . $r('date');
+ }
+
+ // # 4.3.2
+ // value-time = element time {
+ // xsd:string { pattern = "(\d\d(\d\d(\d\d)?)?|-\d\d(\d\d?)|--\d\d)"
+ // ~ "(Z|[+\-]\d\d(\d\d)?)?" }
+ // }
+ } elseif ((!$d('year') && !$d('month') && !$d('date'))
+ && ($d('hour') || $d('minute') || $d('second'))) {
+
+ if ($d('hour')) {
+ $value .= $r('hour') . $r('minute') . $r('second');
+ } elseif ($d('minute')) {
+ $value .= '-' . $r('minute') . $r('second');
+ } elseif ($d('second')) {
+ $value .= '--' . $r('second');
+ }
+
+ $value .= $r('timezone');
+
+ // # 4.3.3
+ // value-date-time = element date-time {
+ // xsd:string { pattern = "(\d{8}|--\d{4}|---\d\d)T\d\d(\d\d(\d\d)?)?"
+ // ~ "(Z|[+\-]\d\d(\d\d)?)?" }
+ // }
+ } elseif ($d('date') && $d('hour')) {
+
+ if ($d('year') && $d('month') && $d('date')) {
+ $value .= $r('year') . $r('month') . $r('date');
+ } elseif (!$d('year') && $d('month') && $d('date')) {
+ $value .= '--' . $r('month') . $r('date');
+ } elseif (!$d('year') && !$d('month') && $d('date')) {
+ $value .= '---' . $r('date');
+ }
+
+ $value .= 'T' . $r('hour') . $r('minute') . $r('second') .
+ $r('timezone');
+
+ }
+
+ $writer->writeElement($valueType, $value);
+
+ }
+
+ /**
+ * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+ *
+ * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+ * not yet done, but parameters are not included.
+ *
+ * @param string $val
+ *
+ * @return void
+ */
+ function setRawMimeDirValue($val) {
+
+ $this->setValue($val);
+
+ }
+
+ /**
+ * Returns a raw mime-dir representation of the value.
+ *
+ * @return string
+ */
+ function getRawMimeDirValue() {
+
+ return implode($this->delimiter, $this->getParts());
+
+ }
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ *
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on)
+ * 2 - An inconsequential issue
+ * 3 - A severe issue.
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $messages = parent::validate($options);
+ $value = $this->getValue();
+
+ try {
+ DateTimeParser::parseVCardDateTime($value);
+ } catch (InvalidDataException $e) {
+ $messages[] = [
+ 'level' => 3,
+ 'message' => 'The supplied value (' . $value . ') is not a correct DATE-AND-OR-TIME property',
+ 'node' => $this,
+ ];
+ }
+
+ return $messages;
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Property/VCard/DateTime.php b/vendor/sabre/vobject/lib/Property/VCard/DateTime.php
new file mode 100644
index 000000000..e7c804ca7
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/VCard/DateTime.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Sabre\VObject\Property\VCard;
+
+/**
+ * DateTime property.
+ *
+ * This object encodes DATE-TIME values for vCards.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class DateTime extends DateAndOrTime {
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'DATE-TIME';
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Property/VCard/LanguageTag.php b/vendor/sabre/vobject/lib/Property/VCard/LanguageTag.php
new file mode 100644
index 000000000..aa7e9178d
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/VCard/LanguageTag.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Sabre\VObject\Property\VCard;
+
+use
+ Sabre\VObject\Property;
+
+/**
+ * LanguageTag property.
+ *
+ * This object represents LANGUAGE-TAG values as used in vCards.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class LanguageTag extends Property {
+
+ /**
+ * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+ *
+ * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+ * not yet done, but parameters are not included.
+ *
+ * @param string $val
+ *
+ * @return void
+ */
+ function setRawMimeDirValue($val) {
+
+ $this->setValue($val);
+
+ }
+
+ /**
+ * Returns a raw mime-dir representation of the value.
+ *
+ * @return string
+ */
+ function getRawMimeDirValue() {
+
+ return $this->getValue();
+
+ }
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'LANGUAGE-TAG';
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Property/VCard/TimeStamp.php b/vendor/sabre/vobject/lib/Property/VCard/TimeStamp.php
new file mode 100644
index 000000000..9d311f99d
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Property/VCard/TimeStamp.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Sabre\VObject\Property\VCard;
+
+use Sabre\VObject\DateTimeParser;
+use Sabre\VObject\Property\Text;
+use Sabre\Xml;
+
+/**
+ * TimeStamp property.
+ *
+ * This object encodes TIMESTAMP values.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class TimeStamp extends Text {
+
+ /**
+ * In case this is a multi-value property. This string will be used as a
+ * delimiter.
+ *
+ * @var string|null
+ */
+ public $delimiter = null;
+
+ /**
+ * Returns the type of value.
+ *
+ * This corresponds to the VALUE= parameter. Every property also has a
+ * 'default' valueType.
+ *
+ * @return string
+ */
+ function getValueType() {
+
+ return 'TIMESTAMP';
+
+ }
+
+ /**
+ * Returns the value, in the format it should be encoded for json.
+ *
+ * This method must always return an array.
+ *
+ * @return array
+ */
+ function getJsonValue() {
+
+ $parts = DateTimeParser::parseVCardDateTime($this->getValue());
+
+ $dateStr =
+ $parts['year'] . '-' .
+ $parts['month'] . '-' .
+ $parts['date'] . 'T' .
+ $parts['hour'] . ':' .
+ $parts['minute'] . ':' .
+ $parts['second'];
+
+ // Timezone
+ if (!is_null($parts['timezone'])) {
+ $dateStr .= $parts['timezone'];
+ }
+
+ return [$dateStr];
+
+ }
+
+ /**
+ * This method serializes only the value of a property. This is used to
+ * create xCard or xCal documents.
+ *
+ * @param Xml\Writer $writer XML writer.
+ *
+ * @return void
+ */
+ protected function xmlSerializeValue(Xml\Writer $writer) {
+
+ // xCard is the only XML and JSON format that has the same date and time
+ // format than vCard.
+ $valueType = strtolower($this->getValueType());
+ $writer->writeElement($valueType, $this->getValue());
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Reader.php b/vendor/sabre/vobject/lib/Reader.php
new file mode 100644
index 000000000..709929337
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Reader.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * iCalendar/vCard/jCal/jCard/xCal/xCard reader object.
+ *
+ * This object provides a few (static) convenience methods to quickly access
+ * the parsers.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Reader {
+
+ /**
+ * If this option is passed to the reader, it will be less strict about the
+ * validity of the lines.
+ */
+ const OPTION_FORGIVING = 1;
+
+ /**
+ * If this option is turned on, any lines we cannot parse will be ignored
+ * by the reader.
+ */
+ const OPTION_IGNORE_INVALID_LINES = 2;
+
+ /**
+ * Parses a vCard or iCalendar object, and returns the top component.
+ *
+ * The options argument is a bitfield. Pass any of the OPTIONS constant to
+ * alter the parsers' behaviour.
+ *
+ * You can either supply a string, or a readable stream for input.
+ *
+ * @param string|resource $data
+ * @param int $options
+ * @param string $charset
+ * @return Document
+ */
+ static function read($data, $options = 0, $charset = 'UTF-8') {
+
+ $parser = new Parser\MimeDir();
+ $parser->setCharset($charset);
+ $result = $parser->parse($data, $options);
+
+ return $result;
+
+ }
+
+ /**
+ * Parses a jCard or jCal object, and returns the top component.
+ *
+ * The options argument is a bitfield. Pass any of the OPTIONS constant to
+ * alter the parsers' behaviour.
+ *
+ * You can either a string, a readable stream, or an array for it's input.
+ * Specifying the array is useful if json_decode was already called on the
+ * input.
+ *
+ * @param string|resource|array $data
+ * @param int $options
+ *
+ * @return Document
+ */
+ static function readJson($data, $options = 0) {
+
+ $parser = new Parser\Json();
+ $result = $parser->parse($data, $options);
+
+ return $result;
+
+ }
+
+ /**
+ * Parses a xCard or xCal object, and returns the top component.
+ *
+ * The options argument is a bitfield. Pass any of the OPTIONS constant to
+ * alter the parsers' behaviour.
+ *
+ * You can either supply a string, or a readable stream for input.
+ *
+ * @param string|resource $data
+ * @param int $options
+ *
+ * @return Document
+ */
+ static function readXML($data, $options = 0) {
+
+ $parser = new Parser\XML();
+ $result = $parser->parse($data, $options);
+
+ return $result;
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Recur/EventIterator.php b/vendor/sabre/vobject/lib/Recur/EventIterator.php
new file mode 100644
index 000000000..86c996aec
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Recur/EventIterator.php
@@ -0,0 +1,507 @@
+<?php
+
+namespace Sabre\VObject\Recur;
+
+use DateTimeZone;
+use DateTimeImmutable;
+use DateTimeInterface;
+use InvalidArgumentException;
+use Sabre\VObject\Component;
+use Sabre\VObject\Component\VEvent;
+use Sabre\VObject\Settings;
+
+/**
+ * This class is used to determine new for a recurring event, when the next
+ * events occur.
+ *
+ * This iterator may loop infinitely in the future, therefore it is important
+ * that if you use this class, you set hard limits for the amount of iterations
+ * you want to handle.
+ *
+ * Note that currently there is not full support for the entire iCalendar
+ * specification, as it's very complex and contains a lot of permutations
+ * that's not yet used very often in software.
+ *
+ * For the focus has been on features as they actually appear in Calendaring
+ * software, but this may well get expanded as needed / on demand
+ *
+ * The following RRULE properties are supported
+ * * UNTIL
+ * * INTERVAL
+ * * COUNT
+ * * FREQ=DAILY
+ * * BYDAY
+ * * BYHOUR
+ * * BYMONTH
+ * * FREQ=WEEKLY
+ * * BYDAY
+ * * BYHOUR
+ * * WKST
+ * * FREQ=MONTHLY
+ * * BYMONTHDAY
+ * * BYDAY
+ * * BYSETPOS
+ * * FREQ=YEARLY
+ * * BYMONTH
+ * * BYMONTHDAY (only if BYMONTH is also set)
+ * * BYDAY (only if BYMONTH is also set)
+ *
+ * Anything beyond this is 'undefined', which means that it may get ignored, or
+ * you may get unexpected results. The effect is that in some applications the
+ * specified recurrence may look incorrect, or is missing.
+ *
+ * The recurrence iterator also does not yet support THISANDFUTURE.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class EventIterator implements \Iterator {
+
+ /**
+ * Reference timeZone for floating dates and times.
+ *
+ * @var DateTimeZone
+ */
+ protected $timeZone;
+
+ /**
+ * True if we're iterating an all-day event.
+ *
+ * @var bool
+ */
+ protected $allDay = false;
+
+ /**
+ * Creates the iterator.
+ *
+ * There's three ways to set up the iterator.
+ *
+ * 1. You can pass a VCALENDAR component and a UID.
+ * 2. You can pass an array of VEVENTs (all UIDS should match).
+ * 3. You can pass a single VEVENT component.
+ *
+ * Only the second method is recomended. The other 1 and 3 will be removed
+ * at some point in the future.
+ *
+ * The $uid parameter is only required for the first method.
+ *
+ * @param Component|array $input
+ * @param string|null $uid
+ * @param DateTimeZone $timeZone Reference timezone for floating dates and
+ * times.
+ */
+ function __construct($input, $uid = null, DateTimeZone $timeZone = null) {
+
+ if (is_null($timeZone)) {
+ $timeZone = new DateTimeZone('UTC');
+ }
+ $this->timeZone = $timeZone;
+
+ if (is_array($input)) {
+ $events = $input;
+ } elseif ($input instanceof VEvent) {
+ // Single instance mode.
+ $events = [$input];
+ } else {
+ // Calendar + UID mode.
+ $uid = (string)$uid;
+ if (!$uid) {
+ throw new InvalidArgumentException('The UID argument is required when a VCALENDAR is passed to this constructor');
+ }
+ if (!isset($input->VEVENT)) {
+ throw new InvalidArgumentException('No events found in this calendar');
+ }
+ $events = $input->getByUID($uid);
+
+ }
+
+ foreach ($events as $vevent) {
+
+ if (!isset($vevent->{'RECURRENCE-ID'})) {
+
+ $this->masterEvent = $vevent;
+
+ } else {
+
+ $this->exceptions[
+ $vevent->{'RECURRENCE-ID'}->getDateTime($this->timeZone)->getTimeStamp()
+ ] = true;
+ $this->overriddenEvents[] = $vevent;
+
+ }
+
+ }
+
+ if (!$this->masterEvent) {
+ // No base event was found. CalDAV does allow cases where only
+ // overridden instances are stored.
+ //
+ // In this particular case, we're just going to grab the first
+ // event and use that instead. This may not always give the
+ // desired result.
+ if (!count($this->overriddenEvents)) {
+ throw new InvalidArgumentException('This VCALENDAR did not have an event with UID: ' . $uid);
+ }
+ $this->masterEvent = array_shift($this->overriddenEvents);
+ }
+
+ $this->startDate = $this->masterEvent->DTSTART->getDateTime($this->timeZone);
+ $this->allDay = !$this->masterEvent->DTSTART->hasTime();
+
+ if (isset($this->masterEvent->EXDATE)) {
+
+ foreach ($this->masterEvent->EXDATE as $exDate) {
+
+ foreach ($exDate->getDateTimes($this->timeZone) as $dt) {
+ $this->exceptions[$dt->getTimeStamp()] = true;
+ }
+
+ }
+
+ }
+
+ if (isset($this->masterEvent->DTEND)) {
+ $this->eventDuration =
+ $this->masterEvent->DTEND->getDateTime($this->timeZone)->getTimeStamp() -
+ $this->startDate->getTimeStamp();
+ } elseif (isset($this->masterEvent->DURATION)) {
+ $duration = $this->masterEvent->DURATION->getDateInterval();
+ $end = clone $this->startDate;
+ $end = $end->add($duration);
+ $this->eventDuration = $end->getTimeStamp() - $this->startDate->getTimeStamp();
+ } elseif ($this->allDay) {
+ $this->eventDuration = 3600 * 24;
+ } else {
+ $this->eventDuration = 0;
+ }
+
+ if (isset($this->masterEvent->RDATE)) {
+ $this->recurIterator = new RDateIterator(
+ $this->masterEvent->RDATE->getParts(),
+ $this->startDate
+ );
+ } elseif (isset($this->masterEvent->RRULE)) {
+ $this->recurIterator = new RRuleIterator(
+ $this->masterEvent->RRULE->getParts(),
+ $this->startDate
+ );
+ } else {
+ $this->recurIterator = new RRuleIterator(
+ [
+ 'FREQ' => 'DAILY',
+ 'COUNT' => 1,
+ ],
+ $this->startDate
+ );
+ }
+
+ $this->rewind();
+ if (!$this->valid()) {
+ throw new NoInstancesException('This recurrence rule does not generate any valid instances');
+ }
+
+ }
+
+ /**
+ * Returns the date for the current position of the iterator.
+ *
+ * @return DateTimeImmutable
+ */
+ function current() {
+
+ if ($this->currentDate) {
+ return clone $this->currentDate;
+ }
+
+ }
+
+ /**
+ * This method returns the start date for the current iteration of the
+ * event.
+ *
+ * @return DateTimeImmutable
+ */
+ function getDtStart() {
+
+ if ($this->currentDate) {
+ return clone $this->currentDate;
+ }
+
+ }
+
+ /**
+ * This method returns the end date for the current iteration of the
+ * event.
+ *
+ * @return DateTimeImmutable
+ */
+ function getDtEnd() {
+
+ if (!$this->valid()) {
+ return;
+ }
+ $end = clone $this->currentDate;
+ return $end->modify('+' . $this->eventDuration . ' seconds');
+
+ }
+
+ /**
+ * Returns a VEVENT for the current iterations of the event.
+ *
+ * This VEVENT will have a recurrence id, and it's DTSTART and DTEND
+ * altered.
+ *
+ * @return VEvent
+ */
+ function getEventObject() {
+
+ if ($this->currentOverriddenEvent) {
+ return $this->currentOverriddenEvent;
+ }
+
+ $event = clone $this->masterEvent;
+
+ // Ignoring the following block, because PHPUnit's code coverage
+ // ignores most of these lines, and this messes with our stats.
+ //
+ // @codeCoverageIgnoreStart
+ unset(
+ $event->RRULE,
+ $event->EXDATE,
+ $event->RDATE,
+ $event->EXRULE,
+ $event->{'RECURRENCE-ID'}
+ );
+ // @codeCoverageIgnoreEnd
+
+ $event->DTSTART->setDateTime($this->getDtStart(), $event->DTSTART->isFloating());
+ if (isset($event->DTEND)) {
+ $event->DTEND->setDateTime($this->getDtEnd(), $event->DTEND->isFloating());
+ }
+ $recurid = clone $event->DTSTART;
+ $recurid->name = 'RECURRENCE-ID';
+ $event->add($recurid);
+ return $event;
+
+ }
+
+ /**
+ * Returns the current position of the iterator.
+ *
+ * This is for us simply a 0-based index.
+ *
+ * @return int
+ */
+ function key() {
+
+ // The counter is always 1 ahead.
+ return $this->counter - 1;
+
+ }
+
+ /**
+ * This is called after next, to see if the iterator is still at a valid
+ * position, or if it's at the end.
+ *
+ * @return bool
+ */
+ function valid() {
+
+ if ($this->counter > Settings::$maxRecurrences && Settings::$maxRecurrences !== -1) {
+ throw new MaxInstancesExceededException('Recurring events are only allowed to generate ' . Settings::$maxRecurrences);
+ }
+ return !!$this->currentDate;
+
+ }
+
+ /**
+ * Sets the iterator back to the starting point.
+ */
+ function rewind() {
+
+ $this->recurIterator->rewind();
+ // re-creating overridden event index.
+ $index = [];
+ foreach ($this->overriddenEvents as $key => $event) {
+ $stamp = $event->DTSTART->getDateTime($this->timeZone)->getTimeStamp();
+ $index[$stamp] = $key;
+ }
+ krsort($index);
+ $this->counter = 0;
+ $this->overriddenEventsIndex = $index;
+ $this->currentOverriddenEvent = null;
+
+ $this->nextDate = null;
+ $this->currentDate = clone $this->startDate;
+
+ $this->next();
+
+ }
+
+ /**
+ * Advances the iterator with one step.
+ *
+ * @return void
+ */
+ function next() {
+
+ $this->currentOverriddenEvent = null;
+ $this->counter++;
+ if ($this->nextDate) {
+ // We had a stored value.
+ $nextDate = $this->nextDate;
+ $this->nextDate = null;
+ } else {
+ // We need to ask rruleparser for the next date.
+ // We need to do this until we find a date that's not in the
+ // exception list.
+ do {
+ if (!$this->recurIterator->valid()) {
+ $nextDate = null;
+ break;
+ }
+ $nextDate = $this->recurIterator->current();
+ $this->recurIterator->next();
+ } while (isset($this->exceptions[$nextDate->getTimeStamp()]));
+
+ }
+
+
+ // $nextDate now contains what rrule thinks is the next one, but an
+ // overridden event may cut ahead.
+ if ($this->overriddenEventsIndex) {
+
+ $offset = end($this->overriddenEventsIndex);
+ $timestamp = key($this->overriddenEventsIndex);
+ if (!$nextDate || $timestamp < $nextDate->getTimeStamp()) {
+ // Overridden event comes first.
+ $this->currentOverriddenEvent = $this->overriddenEvents[$offset];
+
+ // Putting the rrule next date aside.
+ $this->nextDate = $nextDate;
+ $this->currentDate = $this->currentOverriddenEvent->DTSTART->getDateTime($this->timeZone);
+
+ // Ensuring that this item will only be used once.
+ array_pop($this->overriddenEventsIndex);
+
+ // Exit point!
+ return;
+
+ }
+
+ }
+
+ $this->currentDate = $nextDate;
+
+ }
+
+ /**
+ * Quickly jump to a date in the future.
+ *
+ * @param DateTimeInterface $dateTime
+ */
+ function fastForward(DateTimeInterface $dateTime) {
+
+ while ($this->valid() && $this->getDtEnd() < $dateTime) {
+ $this->next();
+ }
+
+ }
+
+ /**
+ * Returns true if this recurring event never ends.
+ *
+ * @return bool
+ */
+ function isInfinite() {
+
+ return $this->recurIterator->isInfinite();
+
+ }
+
+ /**
+ * RRULE parser.
+ *
+ * @var RRuleIterator
+ */
+ protected $recurIterator;
+
+ /**
+ * The duration, in seconds, of the master event.
+ *
+ * We use this to calculate the DTEND for subsequent events.
+ */
+ protected $eventDuration;
+
+ /**
+ * A reference to the main (master) event.
+ *
+ * @var VEVENT
+ */
+ protected $masterEvent;
+
+ /**
+ * List of overridden events.
+ *
+ * @var array
+ */
+ protected $overriddenEvents = [];
+
+ /**
+ * Overridden event index.
+ *
+ * Key is timestamp, value is the index of the item in the $overriddenEvent
+ * property.
+ *
+ * @var array
+ */
+ protected $overriddenEventsIndex;
+
+ /**
+ * A list of recurrence-id's that are either part of EXDATE, or are
+ * overridden.
+ *
+ * @var array
+ */
+ protected $exceptions = [];
+
+ /**
+ * Internal event counter.
+ *
+ * @var int
+ */
+ protected $counter;
+
+ /**
+ * The very start of the iteration process.
+ *
+ * @var DateTimeImmutable
+ */
+ protected $startDate;
+
+ /**
+ * Where we are currently in the iteration process.
+ *
+ * @var DateTimeImmutable
+ */
+ protected $currentDate;
+
+ /**
+ * The next date from the rrule parser.
+ *
+ * Sometimes we need to temporary store the next date, because an
+ * overridden event came before.
+ *
+ * @var DateTimeImmutable
+ */
+ protected $nextDate;
+
+ /**
+ * The event that overwrites the current iteration
+ *
+ * @var VEVENT
+ */
+ protected $currentOverriddenEvent;
+
+}
diff --git a/vendor/sabre/vobject/lib/Recur/MaxInstancesExceededException.php b/vendor/sabre/vobject/lib/Recur/MaxInstancesExceededException.php
new file mode 100644
index 000000000..264df7d2b
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Recur/MaxInstancesExceededException.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Sabre\VObject\Recur;
+
+use Exception;
+
+/**
+ * This exception will get thrown when a recurrence rule generated more than
+ * the maximum number of instances.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class MaxInstancesExceededException extends Exception {
+}
diff --git a/vendor/sabre/vobject/lib/Recur/NoInstancesException.php b/vendor/sabre/vobject/lib/Recur/NoInstancesException.php
new file mode 100644
index 000000000..8f8bb472b
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Recur/NoInstancesException.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Sabre\VObject\Recur;
+
+use Exception;
+
+/**
+ * This exception gets thrown when a recurrence iterator produces 0 instances.
+ *
+ * This may happen when every occurence in a rrule is also in EXDATE.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class NoInstancesException extends Exception {
+
+}
diff --git a/vendor/sabre/vobject/lib/Recur/RDateIterator.php b/vendor/sabre/vobject/lib/Recur/RDateIterator.php
new file mode 100644
index 000000000..f44960e12
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Recur/RDateIterator.php
@@ -0,0 +1,182 @@
+<?php
+
+namespace Sabre\VObject\Recur;
+
+use DateTimeInterface;
+use Iterator;
+use Sabre\VObject\DateTimeParser;
+
+/**
+ * RRuleParser.
+ *
+ * This class receives an RRULE string, and allows you to iterate to get a list
+ * of dates in that recurrence.
+ *
+ * For instance, passing: FREQ=DAILY;LIMIT=5 will cause the iterator to contain
+ * 5 items, one for each day.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class RDateIterator implements Iterator {
+
+ /**
+ * Creates the Iterator.
+ *
+ * @param string|array $rrule
+ * @param DateTimeInterface $start
+ */
+ function __construct($rrule, DateTimeInterface $start) {
+
+ $this->startDate = $start;
+ $this->parseRDate($rrule);
+ $this->currentDate = clone $this->startDate;
+
+ }
+
+ /* Implementation of the Iterator interface {{{ */
+
+ function current() {
+
+ if (!$this->valid()) return;
+ return clone $this->currentDate;
+
+ }
+
+ /**
+ * Returns the current item number.
+ *
+ * @return int
+ */
+ function key() {
+
+ return $this->counter;
+
+ }
+
+ /**
+ * Returns whether the current item is a valid item for the recurrence
+ * iterator.
+ *
+ * @return bool
+ */
+ function valid() {
+
+ return ($this->counter <= count($this->dates));
+
+ }
+
+ /**
+ * Resets the iterator.
+ *
+ * @return void
+ */
+ function rewind() {
+
+ $this->currentDate = clone $this->startDate;
+ $this->counter = 0;
+
+ }
+
+ /**
+ * Goes on to the next iteration.
+ *
+ * @return void
+ */
+ function next() {
+
+ $this->counter++;
+ if (!$this->valid()) return;
+
+ $this->currentDate =
+ DateTimeParser::parse(
+ $this->dates[$this->counter - 1],
+ $this->startDate->getTimezone()
+ );
+
+ }
+
+ /* End of Iterator implementation }}} */
+
+ /**
+ * Returns true if this recurring event never ends.
+ *
+ * @return bool
+ */
+ function isInfinite() {
+
+ return false;
+
+ }
+
+ /**
+ * This method allows you to quickly go to the next occurrence after the
+ * specified date.
+ *
+ * @param DateTimeInterface $dt
+ *
+ * @return void
+ */
+ function fastForward(DateTimeInterface $dt) {
+
+ while ($this->valid() && $this->currentDate < $dt) {
+ $this->next();
+ }
+
+ }
+
+ /**
+ * The reference start date/time for the rrule.
+ *
+ * All calculations are based on this initial date.
+ *
+ * @var DateTimeInterface
+ */
+ protected $startDate;
+
+ /**
+ * The date of the current iteration. You can get this by calling
+ * ->current().
+ *
+ * @var DateTimeInterface
+ */
+ protected $currentDate;
+
+ /**
+ * The current item in the list.
+ *
+ * You can get this number with the key() method.
+ *
+ * @var int
+ */
+ protected $counter = 0;
+
+ /* }}} */
+
+ /**
+ * This method receives a string from an RRULE property, and populates this
+ * class with all the values.
+ *
+ * @param string|array $rrule
+ *
+ * @return void
+ */
+ protected function parseRDate($rdate) {
+
+ if (is_string($rdate)) {
+ $rdate = explode(',', $rdate);
+ }
+
+ $this->dates = $rdate;
+
+ }
+
+ /**
+ * Array with the RRULE dates
+ *
+ * @var array
+ */
+ protected $dates = [];
+
+}
diff --git a/vendor/sabre/vobject/lib/Recur/RRuleIterator.php b/vendor/sabre/vobject/lib/Recur/RRuleIterator.php
new file mode 100644
index 000000000..402e2de83
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Recur/RRuleIterator.php
@@ -0,0 +1,921 @@
+<?php
+
+namespace Sabre\VObject\Recur;
+
+use DateTimeInterface;
+use DateTimeImmutable;
+use Iterator;
+use Sabre\VObject\DateTimeParser;
+use Sabre\VObject\InvalidDataException;
+use Sabre\VObject\Property;
+
+/**
+ * RRuleParser.
+ *
+ * This class receives an RRULE string, and allows you to iterate to get a list
+ * of dates in that recurrence.
+ *
+ * For instance, passing: FREQ=DAILY;LIMIT=5 will cause the iterator to contain
+ * 5 items, one for each day.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class RRuleIterator implements Iterator {
+
+ /**
+ * Creates the Iterator.
+ *
+ * @param string|array $rrule
+ * @param DateTimeInterface $start
+ */
+ function __construct($rrule, DateTimeInterface $start) {
+
+ $this->startDate = $start;
+ $this->parseRRule($rrule);
+ $this->currentDate = clone $this->startDate;
+
+ }
+
+ /* Implementation of the Iterator interface {{{ */
+
+ function current() {
+
+ if (!$this->valid()) return;
+ return clone $this->currentDate;
+
+ }
+
+ /**
+ * Returns the current item number.
+ *
+ * @return int
+ */
+ function key() {
+
+ return $this->counter;
+
+ }
+
+ /**
+ * Returns whether the current item is a valid item for the recurrence
+ * iterator. This will return false if we've gone beyond the UNTIL or COUNT
+ * statements.
+ *
+ * @return bool
+ */
+ function valid() {
+
+ if (!is_null($this->count)) {
+ return $this->counter < $this->count;
+ }
+ return is_null($this->until) || $this->currentDate <= $this->until;
+
+ }
+
+ /**
+ * Resets the iterator.
+ *
+ * @return void
+ */
+ function rewind() {
+
+ $this->currentDate = clone $this->startDate;
+ $this->counter = 0;
+
+ }
+
+ /**
+ * Goes on to the next iteration.
+ *
+ * @return void
+ */
+ function next() {
+
+ // Otherwise, we find the next event in the normal RRULE
+ // sequence.
+ switch ($this->frequency) {
+
+ case 'hourly' :
+ $this->nextHourly();
+ break;
+
+ case 'daily' :
+ $this->nextDaily();
+ break;
+
+ case 'weekly' :
+ $this->nextWeekly();
+ break;
+
+ case 'monthly' :
+ $this->nextMonthly();
+ break;
+
+ case 'yearly' :
+ $this->nextYearly();
+ break;
+
+ }
+ $this->counter++;
+
+ }
+
+ /* End of Iterator implementation }}} */
+
+ /**
+ * Returns true if this recurring event never ends.
+ *
+ * @return bool
+ */
+ function isInfinite() {
+
+ return !$this->count && !$this->until;
+
+ }
+
+ /**
+ * This method allows you to quickly go to the next occurrence after the
+ * specified date.
+ *
+ * @param DateTimeInterface $dt
+ *
+ * @return void
+ */
+ function fastForward(DateTimeInterface $dt) {
+
+ while ($this->valid() && $this->currentDate < $dt) {
+ $this->next();
+ }
+
+ }
+
+ /**
+ * The reference start date/time for the rrule.
+ *
+ * All calculations are based on this initial date.
+ *
+ * @var DateTimeInterface
+ */
+ protected $startDate;
+
+ /**
+ * The date of the current iteration. You can get this by calling
+ * ->current().
+ *
+ * @var DateTimeInterface
+ */
+ protected $currentDate;
+
+ /**
+ * Frequency is one of: secondly, minutely, hourly, daily, weekly, monthly,
+ * yearly.
+ *
+ * @var string
+ */
+ protected $frequency;
+
+ /**
+ * The number of recurrences, or 'null' if infinitely recurring.
+ *
+ * @var int
+ */
+ protected $count;
+
+ /**
+ * The interval.
+ *
+ * If for example frequency is set to daily, interval = 2 would mean every
+ * 2 days.
+ *
+ * @var int
+ */
+ protected $interval = 1;
+
+ /**
+ * The last instance of this recurrence, inclusively.
+ *
+ * @var DateTimeInterface|null
+ */
+ protected $until;
+
+ /**
+ * Which seconds to recur.
+ *
+ * This is an array of integers (between 0 and 60)
+ *
+ * @var array
+ */
+ protected $bySecond;
+
+ /**
+ * Which minutes to recur.
+ *
+ * This is an array of integers (between 0 and 59)
+ *
+ * @var array
+ */
+ protected $byMinute;
+
+ /**
+ * Which hours to recur.
+ *
+ * This is an array of integers (between 0 and 23)
+ *
+ * @var array
+ */
+ protected $byHour;
+
+ /**
+ * The current item in the list.
+ *
+ * You can get this number with the key() method.
+ *
+ * @var int
+ */
+ protected $counter = 0;
+
+ /**
+ * Which weekdays to recur.
+ *
+ * This is an array of weekdays
+ *
+ * This may also be preceeded by a positive or negative integer. If present,
+ * this indicates the nth occurrence of a specific day within the monthly or
+ * yearly rrule. For instance, -2TU indicates the second-last tuesday of
+ * the month, or year.
+ *
+ * @var array
+ */
+ protected $byDay;
+
+ /**
+ * Which days of the month to recur.
+ *
+ * This is an array of days of the months (1-31). The value can also be
+ * negative. -5 for instance means the 5th last day of the month.
+ *
+ * @var array
+ */
+ protected $byMonthDay;
+
+ /**
+ * Which days of the year to recur.
+ *
+ * This is an array with days of the year (1 to 366). The values can also
+ * be negative. For instance, -1 will always represent the last day of the
+ * year. (December 31st).
+ *
+ * @var array
+ */
+ protected $byYearDay;
+
+ /**
+ * Which week numbers to recur.
+ *
+ * This is an array of integers from 1 to 53. The values can also be
+ * negative. -1 will always refer to the last week of the year.
+ *
+ * @var array
+ */
+ protected $byWeekNo;
+
+ /**
+ * Which months to recur.
+ *
+ * This is an array of integers from 1 to 12.
+ *
+ * @var array
+ */
+ protected $byMonth;
+
+ /**
+ * Which items in an existing st to recur.
+ *
+ * These numbers work together with an existing by* rule. It specifies
+ * exactly which items of the existing by-rule to filter.
+ *
+ * Valid values are 1 to 366 and -1 to -366. As an example, this can be
+ * used to recur the last workday of the month.
+ *
+ * This would be done by setting frequency to 'monthly', byDay to
+ * 'MO,TU,WE,TH,FR' and bySetPos to -1.
+ *
+ * @var array
+ */
+ protected $bySetPos;
+
+ /**
+ * When the week starts.
+ *
+ * @var string
+ */
+ protected $weekStart = 'MO';
+
+ /* Functions that advance the iterator {{{ */
+
+ /**
+ * Does the processing for advancing the iterator for hourly frequency.
+ *
+ * @return void
+ */
+ protected function nextHourly() {
+
+ $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' hours');
+
+ }
+
+ /**
+ * Does the processing for advancing the iterator for daily frequency.
+ *
+ * @return void
+ */
+ protected function nextDaily() {
+
+ if (!$this->byHour && !$this->byDay) {
+ $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' days');
+ return;
+ }
+
+ if (!empty($this->byHour)) {
+ $recurrenceHours = $this->getHours();
+ }
+
+ if (!empty($this->byDay)) {
+ $recurrenceDays = $this->getDays();
+ }
+
+ if (!empty($this->byMonth)) {
+ $recurrenceMonths = $this->getMonths();
+ }
+
+ do {
+ if ($this->byHour) {
+ if ($this->currentDate->format('G') == '23') {
+ // to obey the interval rule
+ $this->currentDate = $this->currentDate->modify('+' . $this->interval - 1 . ' days');
+ }
+
+ $this->currentDate = $this->currentDate->modify('+1 hours');
+
+ } else {
+ $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' days');
+
+ }
+
+ // Current month of the year
+ $currentMonth = $this->currentDate->format('n');
+
+ // Current day of the week
+ $currentDay = $this->currentDate->format('w');
+
+ // Current hour of the day
+ $currentHour = $this->currentDate->format('G');
+
+ } while (
+ ($this->byDay && !in_array($currentDay, $recurrenceDays)) ||
+ ($this->byHour && !in_array($currentHour, $recurrenceHours)) ||
+ ($this->byMonth && !in_array($currentMonth, $recurrenceMonths))
+ );
+
+ }
+
+ /**
+ * Does the processing for advancing the iterator for weekly frequency.
+ *
+ * @return void
+ */
+ protected function nextWeekly() {
+
+ if (!$this->byHour && !$this->byDay) {
+ $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' weeks');
+ return;
+ }
+
+ if ($this->byHour) {
+ $recurrenceHours = $this->getHours();
+ }
+
+ if ($this->byDay) {
+ $recurrenceDays = $this->getDays();
+ }
+
+ // First day of the week:
+ $firstDay = $this->dayMap[$this->weekStart];
+
+ do {
+
+ if ($this->byHour) {
+ $this->currentDate = $this->currentDate->modify('+1 hours');
+ } else {
+ $this->currentDate = $this->currentDate->modify('+1 days');
+ }
+
+ // Current day of the week
+ $currentDay = (int)$this->currentDate->format('w');
+
+ // Current hour of the day
+ $currentHour = (int)$this->currentDate->format('G');
+
+ // We need to roll over to the next week
+ if ($currentDay === $firstDay && (!$this->byHour || $currentHour == '0')) {
+ $this->currentDate = $this->currentDate->modify('+' . $this->interval - 1 . ' weeks');
+
+ // We need to go to the first day of this week, but only if we
+ // are not already on this first day of this week.
+ if ($this->currentDate->format('w') != $firstDay) {
+ $this->currentDate = $this->currentDate->modify('last ' . $this->dayNames[$this->dayMap[$this->weekStart]]);
+ }
+ }
+
+ // We have a match
+ } while (($this->byDay && !in_array($currentDay, $recurrenceDays)) || ($this->byHour && !in_array($currentHour, $recurrenceHours)));
+ }
+
+ /**
+ * Does the processing for advancing the iterator for monthly frequency.
+ *
+ * @return void
+ */
+ protected function nextMonthly() {
+
+ $currentDayOfMonth = $this->currentDate->format('j');
+ if (!$this->byMonthDay && !$this->byDay) {
+
+ // If the current day is higher than the 28th, rollover can
+ // occur to the next month. We Must skip these invalid
+ // entries.
+ if ($currentDayOfMonth < 29) {
+ $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' months');
+ } else {
+ $increase = 0;
+ do {
+ $increase++;
+ $tempDate = clone $this->currentDate;
+ $tempDate = $tempDate->modify('+ ' . ($this->interval * $increase) . ' months');
+ } while ($tempDate->format('j') != $currentDayOfMonth);
+ $this->currentDate = $tempDate;
+ }
+ return;
+ }
+
+ while (true) {
+
+ $occurrences = $this->getMonthlyOccurrences();
+
+ foreach ($occurrences as $occurrence) {
+
+ // The first occurrence thats higher than the current
+ // day of the month wins.
+ if ($occurrence > $currentDayOfMonth) {
+ break 2;
+ }
+
+ }
+
+ // If we made it all the way here, it means there were no
+ // valid occurrences, and we need to advance to the next
+ // month.
+ //
+ // This line does not currently work in hhvm. Temporary workaround
+ // follows:
+ // $this->currentDate->modify('first day of this month');
+ $this->currentDate = new DateTimeImmutable($this->currentDate->format('Y-m-1 H:i:s'), $this->currentDate->getTimezone());
+ // end of workaround
+ $this->currentDate = $this->currentDate->modify('+ ' . $this->interval . ' months');
+
+ // This goes to 0 because we need to start counting at the
+ // beginning.
+ $currentDayOfMonth = 0;
+
+ }
+
+ $this->currentDate = $this->currentDate->setDate(
+ (int)$this->currentDate->format('Y'),
+ (int)$this->currentDate->format('n'),
+ (int)$occurrence
+ );
+
+ }
+
+ /**
+ * Does the processing for advancing the iterator for yearly frequency.
+ *
+ * @return void
+ */
+ protected function nextYearly() {
+
+ $currentMonth = $this->currentDate->format('n');
+ $currentYear = $this->currentDate->format('Y');
+ $currentDayOfMonth = $this->currentDate->format('j');
+
+ // No sub-rules, so we just advance by year
+ if (empty($this->byMonth)) {
+
+ // Unless it was a leap day!
+ if ($currentMonth == 2 && $currentDayOfMonth == 29) {
+
+ $counter = 0;
+ do {
+ $counter++;
+ // Here we increase the year count by the interval, until
+ // we hit a date that's also in a leap year.
+ //
+ // We could just find the next interval that's dividable by
+ // 4, but that would ignore the rule that there's no leap
+ // year every year that's dividable by a 100, but not by
+ // 400. (1800, 1900, 2100). So we just rely on the datetime
+ // functions instead.
+ $nextDate = clone $this->currentDate;
+ $nextDate = $nextDate->modify('+ ' . ($this->interval * $counter) . ' years');
+ } while ($nextDate->format('n') != 2);
+
+ $this->currentDate = $nextDate;
+
+ return;
+
+ }
+
+ // The easiest form
+ $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' years');
+ return;
+
+ }
+
+ $currentMonth = $this->currentDate->format('n');
+ $currentYear = $this->currentDate->format('Y');
+ $currentDayOfMonth = $this->currentDate->format('j');
+
+ $advancedToNewMonth = false;
+
+ // If we got a byDay or getMonthDay filter, we must first expand
+ // further.
+ if ($this->byDay || $this->byMonthDay) {
+
+ while (true) {
+
+ $occurrences = $this->getMonthlyOccurrences();
+
+ foreach ($occurrences as $occurrence) {
+
+ // The first occurrence that's higher than the current
+ // day of the month wins.
+ // If we advanced to the next month or year, the first
+ // occurrence is always correct.
+ if ($occurrence > $currentDayOfMonth || $advancedToNewMonth) {
+ break 2;
+ }
+
+ }
+
+ // If we made it here, it means we need to advance to
+ // the next month or year.
+ $currentDayOfMonth = 1;
+ $advancedToNewMonth = true;
+ do {
+
+ $currentMonth++;
+ if ($currentMonth > 12) {
+ $currentYear += $this->interval;
+ $currentMonth = 1;
+ }
+ } while (!in_array($currentMonth, $this->byMonth));
+
+ $this->currentDate = $this->currentDate->setDate(
+ (int)$currentYear,
+ (int)$currentMonth,
+ (int)$currentDayOfMonth
+ );
+
+ }
+
+ // If we made it here, it means we got a valid occurrence
+ $this->currentDate = $this->currentDate->setDate(
+ (int)$currentYear,
+ (int)$currentMonth,
+ (int)$occurrence
+ );
+ return;
+
+ } else {
+
+ // These are the 'byMonth' rules, if there are no byDay or
+ // byMonthDay sub-rules.
+ do {
+
+ $currentMonth++;
+ if ($currentMonth > 12) {
+ $currentYear += $this->interval;
+ $currentMonth = 1;
+ }
+ } while (!in_array($currentMonth, $this->byMonth));
+ $this->currentDate = $this->currentDate->setDate(
+ (int)$currentYear,
+ (int)$currentMonth,
+ (int)$currentDayOfMonth
+ );
+
+ return;
+
+ }
+
+ }
+
+ /* }}} */
+
+ /**
+ * This method receives a string from an RRULE property, and populates this
+ * class with all the values.
+ *
+ * @param string|array $rrule
+ *
+ * @return void
+ */
+ protected function parseRRule($rrule) {
+
+ if (is_string($rrule)) {
+ $rrule = Property\ICalendar\Recur::stringToArray($rrule);
+ }
+
+ foreach ($rrule as $key => $value) {
+
+ $key = strtoupper($key);
+ switch ($key) {
+
+ case 'FREQ' :
+ $value = strtolower($value);
+ if (!in_array(
+ $value,
+ ['secondly', 'minutely', 'hourly', 'daily', 'weekly', 'monthly', 'yearly']
+ )) {
+ throw new InvalidDataException('Unknown value for FREQ=' . strtoupper($value));
+ }
+ $this->frequency = $value;
+ break;
+
+ case 'UNTIL' :
+ $this->until = DateTimeParser::parse($value, $this->startDate->getTimezone());
+
+ // In some cases events are generated with an UNTIL=
+ // parameter before the actual start of the event.
+ //
+ // Not sure why this is happening. We assume that the
+ // intention was that the event only recurs once.
+ //
+ // So we are modifying the parameter so our code doesn't
+ // break.
+ if ($this->until < $this->startDate) {
+ $this->until = $this->startDate;
+ }
+ break;
+
+ case 'INTERVAL' :
+ // No break
+
+ case 'COUNT' :
+ $val = (int)$value;
+ if ($val < 1) {
+ throw new InvalidDataException(strtoupper($key) . ' in RRULE must be a positive integer!');
+ }
+ $key = strtolower($key);
+ $this->$key = $val;
+ break;
+
+ case 'BYSECOND' :
+ $this->bySecond = (array)$value;
+ break;
+
+ case 'BYMINUTE' :
+ $this->byMinute = (array)$value;
+ break;
+
+ case 'BYHOUR' :
+ $this->byHour = (array)$value;
+ break;
+
+ case 'BYDAY' :
+ $value = (array)$value;
+ foreach ($value as $part) {
+ if (!preg_match('#^ (-|\+)? ([1-5])? (MO|TU|WE|TH|FR|SA|SU) $# xi', $part)) {
+ throw new InvalidDataException('Invalid part in BYDAY clause: ' . $part);
+ }
+ }
+ $this->byDay = $value;
+ break;
+
+ case 'BYMONTHDAY' :
+ $this->byMonthDay = (array)$value;
+ break;
+
+ case 'BYYEARDAY' :
+ $this->byYearDay = (array)$value;
+ break;
+
+ case 'BYWEEKNO' :
+ $this->byWeekNo = (array)$value;
+ break;
+
+ case 'BYMONTH' :
+ $this->byMonth = (array)$value;
+ break;
+
+ case 'BYSETPOS' :
+ $this->bySetPos = (array)$value;
+ break;
+
+ case 'WKST' :
+ $this->weekStart = strtoupper($value);
+ break;
+
+ default:
+ throw new InvalidDataException('Not supported: ' . strtoupper($key));
+
+ }
+
+ }
+
+ }
+
+ /**
+ * Mappings between the day number and english day name.
+ *
+ * @var array
+ */
+ protected $dayNames = [
+ 0 => 'Sunday',
+ 1 => 'Monday',
+ 2 => 'Tuesday',
+ 3 => 'Wednesday',
+ 4 => 'Thursday',
+ 5 => 'Friday',
+ 6 => 'Saturday',
+ ];
+
+ /**
+ * Returns all the occurrences for a monthly frequency with a 'byDay' or
+ * 'byMonthDay' expansion for the current month.
+ *
+ * The returned list is an array of integers with the day of month (1-31).
+ *
+ * @return array
+ */
+ protected function getMonthlyOccurrences() {
+
+ $startDate = clone $this->currentDate;
+
+ $byDayResults = [];
+
+ // Our strategy is to simply go through the byDays, advance the date to
+ // that point and add it to the results.
+ if ($this->byDay) foreach ($this->byDay as $day) {
+
+ $dayName = $this->dayNames[$this->dayMap[substr($day, -2)]];
+
+
+ // Dayname will be something like 'wednesday'. Now we need to find
+ // all wednesdays in this month.
+ $dayHits = [];
+
+ // workaround for missing 'first day of the month' support in hhvm
+ $checkDate = new \DateTime($startDate->format('Y-m-1'));
+ // workaround modify always advancing the date even if the current day is a $dayName in hhvm
+ if ($checkDate->format('l') !== $dayName) {
+ $checkDate = $checkDate->modify($dayName);
+ }
+
+ do {
+ $dayHits[] = $checkDate->format('j');
+ $checkDate = $checkDate->modify('next ' . $dayName);
+ } while ($checkDate->format('n') === $startDate->format('n'));
+
+ // So now we have 'all wednesdays' for month. It is however
+ // possible that the user only really wanted the 1st, 2nd or last
+ // wednesday.
+ if (strlen($day) > 2) {
+ $offset = (int)substr($day, 0, -2);
+
+ if ($offset > 0) {
+ // It is possible that the day does not exist, such as a
+ // 5th or 6th wednesday of the month.
+ if (isset($dayHits[$offset - 1])) {
+ $byDayResults[] = $dayHits[$offset - 1];
+ }
+ } else {
+
+ // if it was negative we count from the end of the array
+ // might not exist, fx. -5th tuesday
+ if (isset($dayHits[count($dayHits) + $offset])) {
+ $byDayResults[] = $dayHits[count($dayHits) + $offset];
+ }
+ }
+ } else {
+ // There was no counter (first, second, last wednesdays), so we
+ // just need to add the all to the list).
+ $byDayResults = array_merge($byDayResults, $dayHits);
+
+ }
+
+ }
+
+ $byMonthDayResults = [];
+ if ($this->byMonthDay) foreach ($this->byMonthDay as $monthDay) {
+
+ // Removing values that are out of range for this month
+ if ($monthDay > $startDate->format('t') ||
+ $monthDay < 0 - $startDate->format('t')) {
+ continue;
+ }
+ if ($monthDay > 0) {
+ $byMonthDayResults[] = $monthDay;
+ } else {
+ // Negative values
+ $byMonthDayResults[] = $startDate->format('t') + 1 + $monthDay;
+ }
+ }
+
+ // If there was just byDay or just byMonthDay, they just specify our
+ // (almost) final list. If both were provided, then byDay limits the
+ // list.
+ if ($this->byMonthDay && $this->byDay) {
+ $result = array_intersect($byMonthDayResults, $byDayResults);
+ } elseif ($this->byMonthDay) {
+ $result = $byMonthDayResults;
+ } else {
+ $result = $byDayResults;
+ }
+ $result = array_unique($result);
+ sort($result, SORT_NUMERIC);
+
+ // The last thing that needs checking is the BYSETPOS. If it's set, it
+ // means only certain items in the set survive the filter.
+ if (!$this->bySetPos) {
+ return $result;
+ }
+
+ $filteredResult = [];
+ foreach ($this->bySetPos as $setPos) {
+
+ if ($setPos < 0) {
+ $setPos = count($result) + ($setPos + 1);
+ }
+ if (isset($result[$setPos - 1])) {
+ $filteredResult[] = $result[$setPos - 1];
+ }
+ }
+
+ sort($filteredResult, SORT_NUMERIC);
+ return $filteredResult;
+
+ }
+
+ /**
+ * Simple mapping from iCalendar day names to day numbers.
+ *
+ * @var array
+ */
+ protected $dayMap = [
+ 'SU' => 0,
+ 'MO' => 1,
+ 'TU' => 2,
+ 'WE' => 3,
+ 'TH' => 4,
+ 'FR' => 5,
+ 'SA' => 6,
+ ];
+
+ protected function getHours() {
+
+ $recurrenceHours = [];
+ foreach ($this->byHour as $byHour) {
+ $recurrenceHours[] = $byHour;
+ }
+
+ return $recurrenceHours;
+ }
+
+ protected function getDays() {
+
+ $recurrenceDays = [];
+ foreach ($this->byDay as $byDay) {
+
+ // The day may be preceeded with a positive (+n) or
+ // negative (-n) integer. However, this does not make
+ // sense in 'weekly' so we ignore it here.
+ $recurrenceDays[] = $this->dayMap[substr($byDay, -2)];
+
+ }
+
+ return $recurrenceDays;
+ }
+
+ protected function getMonths() {
+
+ $recurrenceMonths = [];
+ foreach ($this->byMonth as $byMonth) {
+ $recurrenceMonths[] = $byMonth;
+ }
+
+ return $recurrenceMonths;
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Component.php b/vendor/sabre/vobject/lib/Sabre/VObject/Component.php
deleted file mode 100644
index 1c1d92444..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Component.php
+++ /dev/null
@@ -1,405 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-/**
- * VObject Component
- *
- * This class represents a VCALENDAR/VCARD component. A component is for example
- * VEVENT, VTODO and also VCALENDAR. It starts with BEGIN:COMPONENTNAME and
- * ends with END:COMPONENTNAME
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class Component extends Node {
-
- /**
- * Name, for example VEVENT
- *
- * @var string
- */
- public $name;
-
- /**
- * Children properties and components
- *
- * @var array
- */
- public $children = array();
-
- /**
- * If components are added to this map, they will be automatically mapped
- * to their respective classes, if parsed by the reader or constructed with
- * the 'create' method.
- *
- * @var array
- */
- static public $classMap = array(
- 'VALARM' => 'Sabre\\VObject\\Component\\VAlarm',
- 'VCALENDAR' => 'Sabre\\VObject\\Component\\VCalendar',
- 'VCARD' => 'Sabre\\VObject\\Component\\VCard',
- 'VEVENT' => 'Sabre\\VObject\\Component\\VEvent',
- 'VJOURNAL' => 'Sabre\\VObject\\Component\\VJournal',
- 'VTODO' => 'Sabre\\VObject\\Component\\VTodo',
- 'VFREEBUSY' => 'Sabre\\VObject\\Component\\VFreeBusy',
- );
-
- /**
- * Creates the new component by name, but in addition will also see if
- * there's a class mapped to the property name.
- *
- * @param string $name
- * @param string $value
- * @return Component
- */
- static public function create($name, $value = null) {
-
- $name = strtoupper($name);
-
- if (isset(self::$classMap[$name])) {
- return new self::$classMap[$name]($name, $value);
- } else {
- return new self($name, $value);
- }
-
- }
-
- /**
- * Creates a new component.
- *
- * By default this object will iterate over its own children, but this can
- * be overridden with the iterator argument
- *
- * @param string $name
- * @param ElementList $iterator
- */
- public function __construct($name, ElementList $iterator = null) {
-
- $this->name = strtoupper($name);
- if (!is_null($iterator)) $this->iterator = $iterator;
-
- }
-
- /**
- * Turns the object back into a serialized blob.
- *
- * @return string
- */
- public function serialize() {
-
- $str = "BEGIN:" . $this->name . "\r\n";
-
- /**
- * Gives a component a 'score' for sorting purposes.
- *
- * This is solely used by the childrenSort method.
- *
- * A higher score means the item will be lower in the list.
- * To avoid score collisions, each "score category" has a reasonable
- * space to accomodate elements. The $key is added to the $score to
- * preserve the original relative order of elements.
- *
- * @param int $key
- * @param array $array
- * @return int
- */
- $sortScore = function($key, $array) {
-
- if ($array[$key] instanceof Component) {
-
- // We want to encode VTIMEZONE first, this is a personal
- // preference.
- if ($array[$key]->name === 'VTIMEZONE') {
- $score=300000000;
- return $score+$key;
- } else {
- $score=400000000;
- return $score+$key;
- }
- } else {
- // Properties get encoded first
- // VCARD version 4.0 wants the VERSION property to appear first
- if ($array[$key] instanceof Property) {
- if ($array[$key]->name === 'VERSION') {
- $score=100000000;
- return $score+$key;
- } else {
- // All other properties
- $score=200000000;
- return $score+$key;
- }
- }
- }
-
- };
-
- $tmp = $this->children;
- uksort($this->children, function($a, $b) use ($sortScore, $tmp) {
-
- $sA = $sortScore($a, $tmp);
- $sB = $sortScore($b, $tmp);
-
- if ($sA === $sB) return 0;
-
- return ($sA < $sB) ? -1 : 1;
-
- });
-
- foreach($this->children as $child) $str.=$child->serialize();
- $str.= "END:" . $this->name . "\r\n";
-
- return $str;
-
- }
-
- /**
- * Adds a new component or element
- *
- * You can call this method with the following syntaxes:
- *
- * add(Node $node)
- * add(string $name, $value, array $parameters = array())
- *
- * The first version adds an Element
- * The second adds a property as a string.
- *
- * @param mixed $item
- * @param mixed $itemValue
- * @return void
- */
- public function add($item, $itemValue = null, array $parameters = array()) {
-
- if ($item instanceof Node) {
- if (!is_null($itemValue)) {
- throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject Node');
- }
- $item->parent = $this;
- $this->children[] = $item;
- } elseif(is_string($item)) {
-
- $item = Property::create($item,$itemValue, $parameters);
- $item->parent = $this;
- $this->children[] = $item;
-
- } else {
-
- throw new \InvalidArgumentException('The first argument must either be a \\Sabre\\VObject\\Node or a string');
-
- }
-
- }
-
- /**
- * Returns an iterable list of children
- *
- * @return ElementList
- */
- public function children() {
-
- return new ElementList($this->children);
-
- }
-
- /**
- * Returns an array with elements that match the specified name.
- *
- * This function is also aware of MIME-Directory groups (as they appear in
- * vcards). This means that if a property is grouped as "HOME.EMAIL", it
- * will also be returned when searching for just "EMAIL". If you want to
- * search for a property in a specific group, you can select on the entire
- * string ("HOME.EMAIL"). If you want to search on a specific property that
- * has not been assigned a group, specify ".EMAIL".
- *
- * Keys are retained from the 'children' array, which may be confusing in
- * certain cases.
- *
- * @param string $name
- * @return array
- */
- public function select($name) {
-
- $group = null;
- $name = strtoupper($name);
- if (strpos($name,'.')!==false) {
- list($group,$name) = explode('.', $name, 2);
- }
-
- $result = array();
- foreach($this->children as $key=>$child) {
-
- if (
- strtoupper($child->name) === $name &&
- (is_null($group) || ( $child instanceof Property && strtoupper($child->group) === $group))
- ) {
-
- $result[$key] = $child;
-
- }
- }
-
- reset($result);
- return $result;
-
- }
-
- /**
- * This method only returns a list of sub-components. Properties are
- * ignored.
- *
- * @return array
- */
- public function getComponents() {
-
- $result = array();
- foreach($this->children as $child) {
- if ($child instanceof Component) {
- $result[] = $child;
- }
- }
-
- return $result;
-
- }
-
- /**
- * Validates the node for correctness.
- *
- * The following options are supported:
- * - Node::REPAIR - If something is broken, and automatic repair may
- * be attempted.
- *
- * An array is returned with warnings.
- *
- * Every item in the array has the following properties:
- * * level - (number between 1 and 3 with severity information)
- * * message - (human readable message)
- * * node - (reference to the offending node)
- *
- * @param int $options
- * @return array
- */
- public function validate($options = 0) {
-
- $result = array();
- foreach($this->children as $child) {
- $result = array_merge($result, $child->validate($options));
- }
- return $result;
-
- }
-
- /* Magic property accessors {{{ */
-
- /**
- * Using 'get' you will either get a property or component,
- *
- * If there were no child-elements found with the specified name,
- * null is returned.
- *
- * @param string $name
- * @return Property
- */
- public function __get($name) {
-
- $matches = $this->select($name);
- if (count($matches)===0) {
- return null;
- } else {
- $firstMatch = current($matches);
- /** @var $firstMatch Property */
- $firstMatch->setIterator(new ElementList(array_values($matches)));
- return $firstMatch;
- }
-
- }
-
- /**
- * This method checks if a sub-element with the specified name exists.
- *
- * @param string $name
- * @return bool
- */
- public function __isset($name) {
-
- $matches = $this->select($name);
- return count($matches)>0;
-
- }
-
- /**
- * Using the setter method you can add properties or subcomponents
- *
- * You can either pass a Component, Property
- * object, or a string to automatically create a Property.
- *
- * If the item already exists, it will be removed. If you want to add
- * a new item with the same name, always use the add() method.
- *
- * @param string $name
- * @param mixed $value
- * @return void
- */
- public function __set($name, $value) {
-
- $matches = $this->select($name);
- $overWrite = count($matches)?key($matches):null;
-
- if ($value instanceof Component || $value instanceof Property) {
- $value->parent = $this;
- if (!is_null($overWrite)) {
- $this->children[$overWrite] = $value;
- } else {
- $this->children[] = $value;
- }
- } elseif (is_scalar($value)) {
- $property = Property::create($name,$value);
- $property->parent = $this;
- if (!is_null($overWrite)) {
- $this->children[$overWrite] = $property;
- } else {
- $this->children[] = $property;
- }
- } else {
- throw new \InvalidArgumentException('You must pass a \\Sabre\\VObject\\Component, \\Sabre\\VObject\\Property or scalar type');
- }
-
- }
-
- /**
- * Removes all properties and components within this component.
- *
- * @param string $name
- * @return void
- */
- public function __unset($name) {
-
- $matches = $this->select($name);
- foreach($matches as $k=>$child) {
-
- unset($this->children[$k]);
- $child->parent = null;
-
- }
-
- }
-
- /* }}} */
-
- /**
- * This method is automatically called when the object is cloned.
- * Specifically, this will ensure all child elements are also cloned.
- *
- * @return void
- */
- public function __clone() {
-
- foreach($this->children as $key=>$child) {
- $this->children[$key] = clone $child;
- $this->children[$key]->parent = $this;
- }
-
- }
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Component/VCalendar.php b/vendor/sabre/vobject/lib/Sabre/VObject/Component/VCalendar.php
deleted file mode 100644
index 9de67982e..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Component/VCalendar.php
+++ /dev/null
@@ -1,244 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Component;
-
-use Sabre\VObject;
-
-/**
- * The VCalendar component
- *
- * This component adds functionality to a component, specific for a VCALENDAR.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class VCalendar extends VObject\Document {
-
- static $defaultName = 'VCALENDAR';
-
- /**
- * Returns a list of all 'base components'. For instance, if an Event has
- * a recurrence rule, and one instance is overridden, the overridden event
- * will have the same UID, but will be excluded from this list.
- *
- * VTIMEZONE components will always be excluded.
- *
- * @param string $componentName filter by component name
- * @return array
- */
- public function getBaseComponents($componentName = null) {
-
- $components = array();
- foreach($this->children as $component) {
-
- if (!$component instanceof VObject\Component)
- continue;
-
- if (isset($component->{'RECURRENCE-ID'}))
- continue;
-
- if ($componentName && $component->name !== strtoupper($componentName))
- continue;
-
- if ($component->name === 'VTIMEZONE')
- continue;
-
- $components[] = $component;
-
- }
-
- return $components;
-
- }
-
- /**
- * If this calendar object, has events with recurrence rules, this method
- * can be used to expand the event into multiple sub-events.
- *
- * Each event will be stripped from it's recurrence information, and only
- * the instances of the event in the specified timerange will be left
- * alone.
- *
- * In addition, this method will cause timezone information to be stripped,
- * and normalized to UTC.
- *
- * This method will alter the VCalendar. This cannot be reversed.
- *
- * This functionality is specifically used by the CalDAV standard. It is
- * possible for clients to request expand events, if they are rather simple
- * clients and do not have the possibility to calculate recurrences.
- *
- * @param DateTime $start
- * @param DateTime $end
- * @return void
- */
- public function expand(\DateTime $start, \DateTime $end) {
-
- $newEvents = array();
-
- foreach($this->select('VEVENT') as $key=>$vevent) {
-
- if (isset($vevent->{'RECURRENCE-ID'})) {
- unset($this->children[$key]);
- continue;
- }
-
-
- if (!$vevent->rrule) {
- unset($this->children[$key]);
- if ($vevent->isInTimeRange($start, $end)) {
- $newEvents[] = $vevent;
- }
- continue;
- }
-
- $uid = (string)$vevent->uid;
- if (!$uid) {
- throw new \LogicException('Event did not have a UID!');
- }
-
- $it = new VObject\RecurrenceIterator($this, $vevent->uid);
- $it->fastForward($start);
-
- while($it->valid() && $it->getDTStart() < $end) {
-
- if ($it->getDTEnd() > $start) {
-
- $newEvents[] = $it->getEventObject();
-
- }
- $it->next();
-
- }
- unset($this->children[$key]);
-
- }
-
- foreach($newEvents as $newEvent) {
-
- foreach($newEvent->children as $child) {
- if ($child instanceof VObject\Property\DateTime &&
- $child->getDateType() == VObject\Property\DateTime::LOCALTZ) {
- $child->setDateTime($child->getDateTime(),VObject\Property\DateTime::UTC);
- }
- }
-
- $this->add($newEvent);
-
- }
-
- // Removing all VTIMEZONE components
- unset($this->VTIMEZONE);
-
- }
-
- /**
- * Validates the node for correctness.
- * An array is returned with warnings.
- *
- * Every item in the array has the following properties:
- * * level - (number between 1 and 3 with severity information)
- * * message - (human readable message)
- * * node - (reference to the offending node)
- *
- * @return array
- */
- /*
- public function validate() {
-
- $warnings = array();
-
- $version = $this->select('VERSION');
- if (count($version)!==1) {
- $warnings[] = array(
- 'level' => 1,
- 'message' => 'The VERSION property must appear in the VCALENDAR component exactly 1 time',
- 'node' => $this,
- );
- } else {
- if ((string)$this->VERSION !== '2.0') {
- $warnings[] = array(
- 'level' => 1,
- 'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.',
- 'node' => $this,
- );
- }
- }
- $version = $this->select('PRODID');
- if (count($version)!==1) {
- $warnings[] = array(
- 'level' => 2,
- 'message' => 'The PRODID property must appear in the VCALENDAR component exactly 1 time',
- 'node' => $this,
- );
- }
- if (count($this->CALSCALE) > 1) {
- $warnings[] = array(
- 'level' => 2,
- 'message' => 'The CALSCALE property must not be specified more than once.',
- 'node' => $this,
- );
- }
- if (count($this->METHOD) > 1) {
- $warnings[] = array(
- 'level' => 2,
- 'message' => 'The METHOD property must not be specified more than once.',
- 'node' => $this,
- );
- }
-
- $allowedComponents = array(
- 'VEVENT',
- 'VTODO',
- 'VJOURNAL',
- 'VFREEBUSY',
- 'VTIMEZONE',
- );
- $allowedProperties = array(
- 'PRODID',
- 'VERSION',
- 'CALSCALE',
- 'METHOD',
- );
- $componentsFound = 0;
- foreach($this->children as $child) {
- if($child instanceof Component) {
- $componentsFound++;
- if (!in_array($child->name, $allowedComponents)) {
- $warnings[] = array(
- 'level' => 1,
- 'message' => 'The ' . $child->name . " component is not allowed in the VCALENDAR component",
- 'node' => $this,
- );
- }
- }
- if ($child instanceof Property) {
- if (!in_array($child->name, $allowedProperties)) {
- $warnings[] = array(
- 'level' => 2,
- 'message' => 'The ' . $child->name . " property is not allowed in the VCALENDAR component",
- 'node' => $this,
- );
- }
- }
- }
-
- if ($componentsFound===0) {
- $warnings[] = array(
- 'level' => 1,
- 'message' => 'An iCalendar object must have at least 1 component.',
- 'node' => $this,
- );
- }
-
- return array_merge(
- $warnings,
- parent::validate()
- );
-
- }
- */
-
-}
-
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Component/VCard.php b/vendor/sabre/vobject/lib/Sabre/VObject/Component/VCard.php
deleted file mode 100644
index 0fc8b7020..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Component/VCard.php
+++ /dev/null
@@ -1,107 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Component;
-
-use Sabre\VObject;
-
-/**
- * The VCard component
- *
- * This component represents the BEGIN:VCARD and END:VCARD found in every
- * vcard.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class VCard extends VObject\Component {
-
- static $defaultName = 'VCARD';
-
- /**
- * VCards with version 2.1, 3.0 and 4.0 are found.
- *
- * If the VCARD doesn't know its version, 4.0 is assumed.
- */
- const DEFAULT_VERSION = '4.0';
-
- /**
- * Validates the node for correctness.
- *
- * The following options are supported:
- * - Node::REPAIR - If something is broken, and automatic repair may
- * be attempted.
- *
- * An array is returned with warnings.
- *
- * Every item in the array has the following properties:
- * * level - (number between 1 and 3 with severity information)
- * * message - (human readable message)
- * * node - (reference to the offending node)
- *
- * @param int $options
- * @return array
- */
- public function validate($options = 0) {
-
- $warnings = array();
-
- $version = $this->select('VERSION');
- if (count($version)!==1) {
- $warnings[] = array(
- 'level' => 1,
- 'message' => 'The VERSION property must appear in the VCARD component exactly 1 time',
- 'node' => $this,
- );
- if ($options & self::REPAIR) {
- $this->VERSION = self::DEFAULT_VERSION;
- }
- } else {
- $version = (string)$this->VERSION;
- if ($version!=='2.1' && $version!=='3.0' && $version!=='4.0') {
- $warnings[] = array(
- 'level' => 1,
- 'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.',
- 'node' => $this,
- );
- if ($options & self::REPAIR) {
- $this->VERSION = '4.0';
- }
- }
-
- }
- $fn = $this->select('FN');
- if (count($fn)!==1) {
- $warnings[] = array(
- 'level' => 1,
- 'message' => 'The FN property must appear in the VCARD component exactly 1 time',
- 'node' => $this,
- );
- if (($options & self::REPAIR) && count($fn) === 0) {
- // We're going to try to see if we can use the contents of the
- // N property.
- if (isset($this->N)) {
- $value = explode(';', (string)$this->N);
- if (isset($value[1]) && $value[1]) {
- $this->FN = $value[1] . ' ' . $value[0];
- } else {
- $this->FN = $value[0];
- }
-
- // Otherwise, the ORG property may work
- } elseif (isset($this->ORG)) {
- $this->FN = (string)$this->ORG;
- }
-
- }
- }
-
- return array_merge(
- parent::validate($options),
- $warnings
- );
-
- }
-
-}
-
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Component/VEvent.php b/vendor/sabre/vobject/lib/Sabre/VObject/Component/VEvent.php
deleted file mode 100644
index 2375c5317..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Component/VEvent.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Component;
-use Sabre\VObject;
-
-/**
- * VEvent component
- *
- * This component contains some additional functionality specific for VEVENT's.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class VEvent extends VObject\Component {
-
- /**
- * Returns true or false depending on if the event falls in the specified
- * time-range. This is used for filtering purposes.
- *
- * The rules used to determine if an event falls within the specified
- * time-range is based on the CalDAV specification.
- *
- * @param \DateTime $start
- * @param \DateTime $end
- * @return bool
- */
- public function isInTimeRange(\DateTime $start, \DateTime $end) {
-
- if ($this->RRULE) {
- $it = new VObject\RecurrenceIterator($this);
- $it->fastForward($start);
-
- // We fast-forwarded to a spot where the end-time of the
- // recurrence instance exceeded the start of the requested
- // time-range.
- //
- // If the starttime of the recurrence did not exceed the
- // end of the time range as well, we have a match.
- return ($it->getDTStart() < $end && $it->getDTEnd() > $start);
-
- }
-
- $effectiveStart = $this->DTSTART->getDateTime();
- if (isset($this->DTEND)) {
-
- // The DTEND property is considered non inclusive. So for a 3 day
- // event in july, dtstart and dtend would have to be July 1st and
- // July 4th respectively.
- //
- // See:
- // http://tools.ietf.org/html/rfc5545#page-54
- $effectiveEnd = $this->DTEND->getDateTime();
-
- } elseif (isset($this->DURATION)) {
- $effectiveEnd = clone $effectiveStart;
- $effectiveEnd->add( VObject\DateTimeParser::parseDuration($this->DURATION) );
- } elseif ($this->DTSTART->getDateType() == VObject\Property\DateTime::DATE) {
- $effectiveEnd = clone $effectiveStart;
- $effectiveEnd->modify('+1 day');
- } else {
- $effectiveEnd = clone $effectiveStart;
- }
- return (
- ($start <= $effectiveEnd) && ($end > $effectiveStart)
- );
-
- }
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Component/VFreeBusy.php b/vendor/sabre/vobject/lib/Sabre/VObject/Component/VFreeBusy.php
deleted file mode 100644
index 7afe9fdb3..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Component/VFreeBusy.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Component;
-
-use Sabre\VObject;
-
-/**
- * The VFreeBusy component
- *
- * This component adds functionality to a component, specific for VFREEBUSY
- * components.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class VFreeBusy extends VObject\Component {
-
- /**
- * Checks based on the contained FREEBUSY information, if a timeslot is
- * available.
- *
- * @param DateTime $start
- * @param Datetime $end
- * @return bool
- */
- public function isFree(\DateTime $start, \Datetime $end) {
-
- foreach($this->select('FREEBUSY') as $freebusy) {
-
- // We are only interested in FBTYPE=BUSY (the default),
- // FBTYPE=BUSY-TENTATIVE or FBTYPE=BUSY-UNAVAILABLE.
- if (isset($freebusy['FBTYPE']) && strtoupper(substr((string)$freebusy['FBTYPE'],0,4))!=='BUSY') {
- continue;
- }
-
- // The freebusy component can hold more than 1 value, separated by
- // commas.
- $periods = explode(',', (string)$freebusy);
-
- foreach($periods as $period) {
- // Every period is formatted as [start]/[end]. The start is an
- // absolute UTC time, the end may be an absolute UTC time, or
- // duration (relative) value.
- list($busyStart, $busyEnd) = explode('/', $period);
-
- $busyStart = VObject\DateTimeParser::parse($busyStart);
- $busyEnd = VObject\DateTimeParser::parse($busyEnd);
- if ($busyEnd instanceof \DateInterval) {
- $tmp = clone $busyStart;
- $tmp->add($busyEnd);
- $busyEnd = $tmp;
- }
-
- if($start < $busyEnd && $end > $busyStart) {
- return false;
- }
-
- }
-
- }
-
- return true;
-
- }
-
-}
-
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Component/VJournal.php b/vendor/sabre/vobject/lib/Sabre/VObject/Component/VJournal.php
deleted file mode 100644
index 232887879..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Component/VJournal.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Component;
-
-use Sabre\VObject;
-
-/**
- * VJournal component
- *
- * This component contains some additional functionality specific for VJOURNALs.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class VJournal extends VObject\Component {
-
- /**
- * Returns true or false depending on if the event falls in the specified
- * time-range. This is used for filtering purposes.
- *
- * The rules used to determine if an event falls within the specified
- * time-range is based on the CalDAV specification.
- *
- * @param DateTime $start
- * @param DateTime $end
- * @return bool
- */
- public function isInTimeRange(\DateTime $start, \DateTime $end) {
-
- $dtstart = isset($this->DTSTART)?$this->DTSTART->getDateTime():null;
- if ($dtstart) {
- $effectiveEnd = clone $dtstart;
- if ($this->DTSTART->getDateType() == VObject\Property\DateTime::DATE) {
- $effectiveEnd->modify('+1 day');
- }
-
- return ($start <= $effectiveEnd && $end > $dtstart);
-
- }
- return false;
-
-
- }
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Component/VTodo.php b/vendor/sabre/vobject/lib/Sabre/VObject/Component/VTodo.php
deleted file mode 100644
index b1579cf74..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Component/VTodo.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Component;
-
-use Sabre\VObject;
-
-/**
- * VTodo component
- *
- * This component contains some additional functionality specific for VTODOs.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class VTodo extends VObject\Component {
-
- /**
- * Returns true or false depending on if the event falls in the specified
- * time-range. This is used for filtering purposes.
- *
- * The rules used to determine if an event falls within the specified
- * time-range is based on the CalDAV specification.
- *
- * @param DateTime $start
- * @param DateTime $end
- * @return bool
- */
- public function isInTimeRange(\DateTime $start, \DateTime $end) {
-
- $dtstart = isset($this->DTSTART)?$this->DTSTART->getDateTime():null;
- $duration = isset($this->DURATION)?VObject\DateTimeParser::parseDuration($this->DURATION):null;
- $due = isset($this->DUE)?$this->DUE->getDateTime():null;
- $completed = isset($this->COMPLETED)?$this->COMPLETED->getDateTime():null;
- $created = isset($this->CREATED)?$this->CREATED->getDateTime():null;
-
- if ($dtstart) {
- if ($duration) {
- $effectiveEnd = clone $dtstart;
- $effectiveEnd->add($duration);
- return $start <= $effectiveEnd && $end > $dtstart;
- } elseif ($due) {
- return
- ($start < $due || $start <= $dtstart) &&
- ($end > $dtstart || $end >= $due);
- } else {
- return $start <= $dtstart && $end > $dtstart;
- }
- }
- if ($due) {
- return ($start < $due && $end >= $due);
- }
- if ($completed && $created) {
- return
- ($start <= $created || $start <= $completed) &&
- ($end >= $created || $end >= $completed);
- }
- if ($completed) {
- return ($start <= $completed && $end >= $completed);
- }
- if ($created) {
- return ($end > $created);
- }
- return true;
-
- }
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/DateTimeParser.php b/vendor/sabre/vobject/lib/Sabre/VObject/DateTimeParser.php
deleted file mode 100644
index 03600506d..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/DateTimeParser.php
+++ /dev/null
@@ -1,181 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-/**
- * DateTimeParser
- *
- * This class is responsible for parsing the several different date and time
- * formats iCalendar and vCards have.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class DateTimeParser {
-
- /**
- * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object
- *
- * Specifying a reference timezone is optional. It will only be used
- * if the non-UTC format is used. The argument is used as a reference, the
- * returned DateTime object will still be in the UTC timezone.
- *
- * @param string $dt
- * @param DateTimeZone $tz
- * @return DateTime
- */
- static public function parseDateTime($dt,\DateTimeZone $tz = null) {
-
- // Format is YYYYMMDD + "T" + hhmmss
- $result = preg_match('/^([1-4][0-9]{3})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches);
-
- if (!$result) {
- throw new \LogicException('The supplied iCalendar datetime value is incorrect: ' . $dt);
- }
-
- if ($matches[7]==='Z' || is_null($tz)) {
- $tz = new \DateTimeZone('UTC');
- }
- $date = new \DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz);
-
- // Still resetting the timezone, to normalize everything to UTC
- $date->setTimeZone(new \DateTimeZone('UTC'));
- return $date;
-
- }
-
- /**
- * Parses an iCalendar (rfc5545) formatted date and returns a DateTime object
- *
- * @param string $date
- * @return DateTime
- */
- static public function parseDate($date) {
-
- // Format is YYYYMMDD
- $result = preg_match('/^([1-4][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches);
-
- if (!$result) {
- throw new \LogicException('The supplied iCalendar date value is incorrect: ' . $date);
- }
-
- $date = new \DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new \DateTimeZone('UTC'));
- return $date;
-
- }
-
- /**
- * Parses an iCalendar (RFC5545) formatted duration value.
- *
- * This method will either return a DateTimeInterval object, or a string
- * suitable for strtotime or DateTime::modify.
- *
- * @param string $duration
- * @param bool $asString
- * @return DateInterval|string
- */
- static public function parseDuration($duration, $asString = false) {
-
- $result = preg_match('/^(?P<plusminus>\+|-)?P((?P<week>\d+)W)?((?P<day>\d+)D)?(T((?P<hour>\d+)H)?((?P<minute>\d+)M)?((?P<second>\d+)S)?)?$/', $duration, $matches);
- if (!$result) {
- throw new \LogicException('The supplied iCalendar duration value is incorrect: ' . $duration);
- }
-
- if (!$asString) {
- $invert = false;
- if ($matches['plusminus']==='-') {
- $invert = true;
- }
-
-
- $parts = array(
- 'week',
- 'day',
- 'hour',
- 'minute',
- 'second',
- );
- foreach($parts as $part) {
- $matches[$part] = isset($matches[$part])&&$matches[$part]?(int)$matches[$part]:0;
- }
-
-
- // We need to re-construct the $duration string, because weeks and
- // days are not supported by DateInterval in the same string.
- $duration = 'P';
- $days = $matches['day'];
- if ($matches['week']) {
- $days+=$matches['week']*7;
- }
- if ($days)
- $duration.=$days . 'D';
-
- if ($matches['minute'] || $matches['second'] || $matches['hour']) {
- $duration.='T';
-
- if ($matches['hour'])
- $duration.=$matches['hour'].'H';
-
- if ($matches['minute'])
- $duration.=$matches['minute'].'M';
-
- if ($matches['second'])
- $duration.=$matches['second'].'S';
-
- }
-
- if ($duration==='P') {
- $duration = 'PT0S';
- }
- $iv = new \DateInterval($duration);
- if ($invert) $iv->invert = true;
-
- return $iv;
-
- }
-
-
-
- $parts = array(
- 'week',
- 'day',
- 'hour',
- 'minute',
- 'second',
- );
-
- $newDur = '';
- foreach($parts as $part) {
- if (isset($matches[$part]) && $matches[$part]) {
- $newDur.=' '.$matches[$part] . ' ' . $part . 's';
- }
- }
-
- $newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur);
- if ($newDur === '+') { $newDur = '+0 seconds'; };
- return $newDur;
-
- }
-
- /**
- * Parses either a Date or DateTime, or Duration value.
- *
- * @param string $date
- * @param DateTimeZone|string $referenceTZ
- * @return DateTime|DateInterval
- */
- static public function parse($date, $referenceTZ = null) {
-
- if ($date[0]==='P' || ($date[0]==='-' && $date[1]==='P')) {
- return self::parseDuration($date);
- } elseif (strlen($date)===8) {
- return self::parseDate($date);
- } else {
- return self::parseDateTime($date, $referenceTZ);
- }
-
- }
-
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Document.php b/vendor/sabre/vobject/lib/Sabre/VObject/Document.php
deleted file mode 100644
index 50a662ee9..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Document.php
+++ /dev/null
@@ -1,109 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-/**
- * Document
- *
- * A document is just like a component, except that it's also the top level
- * element.
- *
- * Both a VCALENDAR and a VCARD are considered documents.
- *
- * This class also provides a registry for document types.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH. All rights reserved.
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-abstract class Document extends Component {
-
- /**
- * The default name for this component.
- *
- * This should be 'VCALENDAR' or 'VCARD'.
- *
- * @var string
- */
- static $defaultName;
-
- /**
- * Creates a new document.
- *
- * We're changing the default behavior slightly here. First, we don't want
- * to have to specify a name (we already know it), and we want to allow
- * children to be specified in the first argument.
- *
- * But, the default behavior also works.
- *
- * So the two sigs:
- *
- * new Document(array $children = array());
- * new Document(string $name, array $children = array())
- *
- * @return void
- */
- public function __construct() {
-
- $args = func_get_args();
- if (count($args)===0 || is_array($args[0])) {
- array_unshift($args, static::$defaultName);
- call_user_func_array(array('parent', '__construct'), $args);
- } else {
- call_user_func_array(array('parent', '__construct'), $args);
- }
-
- }
-
- /**
- * Creates a new component
- *
- * This method automatically searches for the correct component class, based
- * on its name.
- *
- * You can specify the children either in key=>value syntax, in which case
- * properties will automatically be created, or you can just pass a list of
- * Component and Property object.
- *
- * @param string $name
- * @param array $children
- * @return Component
- */
- public function createComponent($name, array $children = array()) {
-
- $component = Component::create($name);
- foreach($children as $k=>$v) {
-
- if ($v instanceof Node) {
- $component->add($v);
- } else {
- $component->add($k, $v);
- }
-
- }
- return $component;
-
- }
-
- /**
- * Factory method for creating new properties
- *
- * This method automatically searches for the correct property class, based
- * on its name.
- *
- * You can specify the parameters either in key=>value syntax, in which case
- * parameters will automatically be created, or you can just pass a list of
- * Parameter objects.
- *
- * @param string $name
- * @param mixed $value
- * @param array $parameters
- * @return Property
- */
- public function createProperty($name, $value = null, array $parameters = array()) {
-
- return Property::create($name, $value, $parameters);
-
- }
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/ElementList.php b/vendor/sabre/vobject/lib/Sabre/VObject/ElementList.php
deleted file mode 100644
index 1c203708e..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/ElementList.php
+++ /dev/null
@@ -1,172 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-/**
- * VObject ElementList
- *
- * This class represents a list of elements. Lists are the result of queries,
- * such as doing $vcalendar->vevent where there's multiple VEVENT objects.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class ElementList implements \Iterator, \Countable, \ArrayAccess {
-
- /**
- * Inner elements
- *
- * @var array
- */
- protected $elements = array();
-
- /**
- * Creates the element list.
- *
- * @param array $elements
- */
- public function __construct(array $elements) {
-
- $this->elements = $elements;
-
- }
-
- /* {{{ Iterator interface */
-
- /**
- * Current position
- *
- * @var int
- */
- private $key = 0;
-
- /**
- * Returns current item in iteration
- *
- * @return Element
- */
- public function current() {
-
- return $this->elements[$this->key];
-
- }
-
- /**
- * To the next item in the iterator
- *
- * @return void
- */
- public function next() {
-
- $this->key++;
-
- }
-
- /**
- * Returns the current iterator key
- *
- * @return int
- */
- public function key() {
-
- return $this->key;
-
- }
-
- /**
- * Returns true if the current position in the iterator is a valid one
- *
- * @return bool
- */
- public function valid() {
-
- return isset($this->elements[$this->key]);
-
- }
-
- /**
- * Rewinds the iterator
- *
- * @return void
- */
- public function rewind() {
-
- $this->key = 0;
-
- }
-
- /* }}} */
-
- /* {{{ Countable interface */
-
- /**
- * Returns the number of elements
- *
- * @return int
- */
- public function count() {
-
- return count($this->elements);
-
- }
-
- /* }}} */
-
- /* {{{ ArrayAccess Interface */
-
-
- /**
- * Checks if an item exists through ArrayAccess.
- *
- * @param int $offset
- * @return bool
- */
- public function offsetExists($offset) {
-
- return isset($this->elements[$offset]);
-
- }
-
- /**
- * Gets an item through ArrayAccess.
- *
- * @param int $offset
- * @return mixed
- */
- public function offsetGet($offset) {
-
- return $this->elements[$offset];
-
- }
-
- /**
- * Sets an item through ArrayAccess.
- *
- * @param int $offset
- * @param mixed $value
- * @return void
- */
- public function offsetSet($offset,$value) {
-
- throw new \LogicException('You can not add new objects to an ElementList');
-
- }
-
- /**
- * Sets an item through ArrayAccess.
- *
- * This method just forwards the request to the inner iterator
- *
- * @param int $offset
- * @return void
- */
- public function offsetUnset($offset) {
-
- throw new \LogicException('You can not remove objects from an ElementList');
-
- }
-
- /* }}} */
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/FreeBusyGenerator.php b/vendor/sabre/vobject/lib/Sabre/VObject/FreeBusyGenerator.php
deleted file mode 100644
index 96d0be5ac..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/FreeBusyGenerator.php
+++ /dev/null
@@ -1,322 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-/**
- * This class helps with generating FREEBUSY reports based on existing sets of
- * objects.
- *
- * It only looks at VEVENT and VFREEBUSY objects from the sourcedata, and
- * generates a single VFREEBUSY object.
- *
- * VFREEBUSY components are described in RFC5545, The rules for what should
- * go in a single freebusy report is taken from RFC4791, section 7.10.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class FreeBusyGenerator {
-
- /**
- * Input objects
- *
- * @var array
- */
- protected $objects;
-
- /**
- * Start of range
- *
- * @var DateTime|null
- */
- protected $start;
-
- /**
- * End of range
- *
- * @var DateTime|null
- */
- protected $end;
-
- /**
- * VCALENDAR object
- *
- * @var Component
- */
- protected $baseObject;
-
- /**
- * Creates the generator.
- *
- * Check the setTimeRange and setObjects methods for details about the
- * arguments.
- *
- * @param DateTime $start
- * @param DateTime $end
- * @param mixed $objects
- * @return void
- */
- public function __construct(\DateTime $start = null, \DateTime $end = null, $objects = null) {
-
- if ($start && $end) {
- $this->setTimeRange($start, $end);
- }
-
- if ($objects) {
- $this->setObjects($objects);
- }
-
- }
-
- /**
- * Sets the VCALENDAR object.
- *
- * If this is set, it will not be generated for you. You are responsible
- * for setting things like the METHOD, CALSCALE, VERSION, etc..
- *
- * The VFREEBUSY object will be automatically added though.
- *
- * @param Component $vcalendar
- * @return void
- */
- public function setBaseObject(Component $vcalendar) {
-
- $this->baseObject = $vcalendar;
-
- }
-
- /**
- * Sets the input objects
- *
- * You must either specify a valendar object as a strong, or as the parse
- * Component.
- * It's also possible to specify multiple objects as an array.
- *
- * @param mixed $objects
- * @return void
- */
- public function setObjects($objects) {
-
- if (!is_array($objects)) {
- $objects = array($objects);
- }
-
- $this->objects = array();
- foreach($objects as $object) {
-
- if (is_string($object)) {
- $this->objects[] = Reader::read($object);
- } elseif ($object instanceof Component) {
- $this->objects[] = $object;
- } else {
- throw new \InvalidArgumentException('You can only pass strings or \\Sabre\\VObject\\Component arguments to setObjects');
- }
-
- }
-
- }
-
- /**
- * Sets the time range
- *
- * Any freebusy object falling outside of this time range will be ignored.
- *
- * @param DateTime $start
- * @param DateTime $end
- * @return void
- */
- public function setTimeRange(\DateTime $start = null, \DateTime $end = null) {
-
- $this->start = $start;
- $this->end = $end;
-
- }
-
- /**
- * Parses the input data and returns a correct VFREEBUSY object, wrapped in
- * a VCALENDAR.
- *
- * @return Component
- */
- public function getResult() {
-
- $busyTimes = array();
-
- foreach($this->objects as $object) {
-
- foreach($object->getBaseComponents() as $component) {
-
- switch($component->name) {
-
- case 'VEVENT' :
-
- $FBTYPE = 'BUSY';
- if (isset($component->TRANSP) && (strtoupper($component->TRANSP) === 'TRANSPARENT')) {
- break;
- }
- if (isset($component->STATUS)) {
- $status = strtoupper($component->STATUS);
- if ($status==='CANCELLED') {
- break;
- }
- if ($status==='TENTATIVE') {
- $FBTYPE = 'BUSY-TENTATIVE';
- }
- }
-
- $times = array();
-
- if ($component->RRULE) {
-
- $iterator = new RecurrenceIterator($object, (string)$component->uid);
- if ($this->start) {
- $iterator->fastForward($this->start);
- }
-
- $maxRecurrences = 200;
-
- while($iterator->valid() && --$maxRecurrences) {
-
- $startTime = $iterator->getDTStart();
- if ($this->end && $startTime > $this->end) {
- break;
- }
- $times[] = array(
- $iterator->getDTStart(),
- $iterator->getDTEnd(),
- );
-
- $iterator->next();
-
- }
-
- } else {
-
- $startTime = $component->DTSTART->getDateTime();
- if ($this->end && $startTime > $this->end) {
- break;
- }
- $endTime = null;
- if (isset($component->DTEND)) {
- $endTime = $component->DTEND->getDateTime();
- } elseif (isset($component->DURATION)) {
- $duration = DateTimeParser::parseDuration((string)$component->DURATION);
- $endTime = clone $startTime;
- $endTime->add($duration);
- } elseif ($component->DTSTART->getDateType() === Property\DateTime::DATE) {
- $endTime = clone $startTime;
- $endTime->modify('+1 day');
- } else {
- // The event had no duration (0 seconds)
- break;
- }
-
- $times[] = array($startTime, $endTime);
-
- }
-
- foreach($times as $time) {
-
- if ($this->end && $time[0] > $this->end) break;
- if ($this->start && $time[1] < $this->start) break;
-
- $busyTimes[] = array(
- $time[0],
- $time[1],
- $FBTYPE,
- );
- }
- break;
-
- case 'VFREEBUSY' :
- foreach($component->FREEBUSY as $freebusy) {
-
- $fbType = isset($freebusy['FBTYPE'])?strtoupper($freebusy['FBTYPE']):'BUSY';
-
- // Skipping intervals marked as 'free'
- if ($fbType==='FREE')
- continue;
-
- $values = explode(',', $freebusy);
- foreach($values as $value) {
- list($startTime, $endTime) = explode('/', $value);
- $startTime = DateTimeParser::parseDateTime($startTime);
-
- if (substr($endTime,0,1)==='P' || substr($endTime,0,2)==='-P') {
- $duration = DateTimeParser::parseDuration($endTime);
- $endTime = clone $startTime;
- $endTime->add($duration);
- } else {
- $endTime = DateTimeParser::parseDateTime($endTime);
- }
-
- if($this->start && $this->start > $endTime) continue;
- if($this->end && $this->end < $startTime) continue;
- $busyTimes[] = array(
- $startTime,
- $endTime,
- $fbType
- );
-
- }
-
-
- }
- break;
-
-
-
- }
-
-
- }
-
- }
-
- if ($this->baseObject) {
- $calendar = $this->baseObject;
- } else {
- $calendar = Component::create('VCALENDAR');
- $calendar->version = '2.0';
- $calendar->prodid = '-//Sabre//Sabre VObject ' . Version::VERSION . '//EN';
- $calendar->calscale = 'GREGORIAN';
- }
-
- $vfreebusy = Component::create('VFREEBUSY');
- $calendar->add($vfreebusy);
-
- if ($this->start) {
- $dtstart = Property::create('DTSTART');
- $dtstart->setDateTime($this->start,Property\DateTime::UTC);
- $vfreebusy->add($dtstart);
- }
- if ($this->end) {
- $dtend = Property::create('DTEND');
- $dtend->setDateTime($this->end,Property\DateTime::UTC);
- $vfreebusy->add($dtend);
- }
- $dtstamp = Property::create('DTSTAMP');
- $dtstamp->setDateTime(new \DateTime('now'), Property\DateTime::UTC);
- $vfreebusy->add($dtstamp);
-
- foreach($busyTimes as $busyTime) {
-
- $busyTime[0]->setTimeZone(new \DateTimeZone('UTC'));
- $busyTime[1]->setTimeZone(new \DateTimeZone('UTC'));
-
- $prop = Property::create(
- 'FREEBUSY',
- $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z')
- );
- $prop['FBTYPE'] = $busyTime[2];
- $vfreebusy->add($prop);
-
- }
-
- return $calendar;
-
- }
-
-}
-
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Node.php b/vendor/sabre/vobject/lib/Sabre/VObject/Node.php
deleted file mode 100644
index bee68ec2a..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Node.php
+++ /dev/null
@@ -1,187 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-/**
- * Base class for all nodes
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-abstract class Node implements \IteratorAggregate, \ArrayAccess, \Countable {
-
- /**
- * The following constants are used by the validate() method.
- */
- const REPAIR = 1;
-
- /**
- * Turns the object back into a serialized blob.
- *
- * @return string
- */
- abstract function serialize();
-
- /**
- * Iterator override
- *
- * @var ElementList
- */
- protected $iterator = null;
-
- /**
- * A link to the parent node
- *
- * @var Node
- */
- public $parent = null;
-
- /**
- * Validates the node for correctness.
- *
- * The following options are supported:
- * - Node::REPAIR - If something is broken, and automatic repair may
- * be attempted.
- *
- * An array is returned with warnings.
- *
- * Every item in the array has the following properties:
- * * level - (number between 1 and 3 with severity information)
- * * message - (human readable message)
- * * node - (reference to the offending node)
- *
- * @param int $options
- * @return array
- */
- public function validate($options = 0) {
-
- return array();
-
- }
-
- /* {{{ IteratorAggregator interface */
-
- /**
- * Returns the iterator for this object
- *
- * @return ElementList
- */
- public function getIterator() {
-
- if (!is_null($this->iterator))
- return $this->iterator;
-
- return new ElementList(array($this));
-
- }
-
- /**
- * Sets the overridden iterator
- *
- * Note that this is not actually part of the iterator interface
- *
- * @param ElementList $iterator
- * @return void
- */
- public function setIterator(ElementList $iterator) {
-
- $this->iterator = $iterator;
-
- }
-
- /* }}} */
-
- /* {{{ Countable interface */
-
- /**
- * Returns the number of elements
- *
- * @return int
- */
- public function count() {
-
- $it = $this->getIterator();
- return $it->count();
-
- }
-
- /* }}} */
-
- /* {{{ ArrayAccess Interface */
-
-
- /**
- * Checks if an item exists through ArrayAccess.
- *
- * This method just forwards the request to the inner iterator
- *
- * @param int $offset
- * @return bool
- */
- public function offsetExists($offset) {
-
- $iterator = $this->getIterator();
- return $iterator->offsetExists($offset);
-
- }
-
- /**
- * Gets an item through ArrayAccess.
- *
- * This method just forwards the request to the inner iterator
- *
- * @param int $offset
- * @return mixed
- */
- public function offsetGet($offset) {
-
- $iterator = $this->getIterator();
- return $iterator->offsetGet($offset);
-
- }
-
- /**
- * Sets an item through ArrayAccess.
- *
- * This method just forwards the request to the inner iterator
- *
- * @param int $offset
- * @param mixed $value
- * @return void
- */
- public function offsetSet($offset,$value) {
-
- $iterator = $this->getIterator();
- $iterator->offsetSet($offset,$value);
-
- // @codeCoverageIgnoreStart
- //
- // This method always throws an exception, so we ignore the closing
- // brace
- }
- // @codeCoverageIgnoreEnd
-
- /**
- * Sets an item through ArrayAccess.
- *
- * This method just forwards the request to the inner iterator
- *
- * @param int $offset
- * @return void
- */
- public function offsetUnset($offset) {
-
- $iterator = $this->getIterator();
- $iterator->offsetUnset($offset);
-
- // @codeCoverageIgnoreStart
- //
- // This method always throws an exception, so we ignore the closing
- // brace
- }
- // @codeCoverageIgnoreEnd
-
- /* }}} */
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Parameter.php b/vendor/sabre/vobject/lib/Sabre/VObject/Parameter.php
deleted file mode 100644
index 72bf03b47..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Parameter.php
+++ /dev/null
@@ -1,104 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-/**
- * VObject Parameter
- *
- * This class represents a parameter. A parameter is always tied to a property.
- * In the case of:
- * DTSTART;VALUE=DATE:20101108
- * VALUE=DATE would be the parameter name and value.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class Parameter extends Node {
-
- /**
- * Parameter name
- *
- * @var string
- */
- public $name;
-
- /**
- * Parameter value
- *
- * @var string
- */
- public $value;
-
- /**
- * Sets up the object
- *
- * @param string $name
- * @param string $value
- */
- public function __construct($name, $value = null) {
-
- if (!is_scalar($value) && !is_null($value)) {
- throw new \InvalidArgumentException('The value argument must be a scalar value or null');
- }
-
- $this->name = strtoupper($name);
- $this->value = $value;
-
- }
-
- /**
- * Returns the parameter's internal value.
- *
- * @return string
- */
- public function getValue() {
-
- return $this->value;
-
- }
-
-
- /**
- * Turns the object back into a serialized blob.
- *
- * @return string
- */
- public function serialize() {
-
- if (is_null($this->value)) {
- return $this->name;
- }
- $src = array(
- '\\',
- "\n",
- ';',
- ',',
- );
- $out = array(
- '\\\\',
- '\n',
- '\;',
- '\,',
- );
-
- $value = str_replace($src, $out, $this->value);
- if (strpos($value,":")!==false) {
- $value = '"' . $value . '"';
- }
- return $this->name . '=' . $value;
-
- }
-
- /**
- * Called when this object is being cast to a string
- *
- * @return string
- */
- public function __toString() {
-
- return $this->value;
-
- }
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/ParseException.php b/vendor/sabre/vobject/lib/Sabre/VObject/ParseException.php
deleted file mode 100644
index 66b49c606..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/ParseException.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-/**
- * Exception thrown by Reader if an invalid object was attempted to be parsed.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class ParseException extends \Exception { }
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Property.php b/vendor/sabre/vobject/lib/Sabre/VObject/Property.php
deleted file mode 100644
index 63ae64574..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Property.php
+++ /dev/null
@@ -1,444 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-/**
- * VObject Property
- *
- * A property in VObject is usually in the form PARAMNAME:paramValue.
- * An example is : SUMMARY:Weekly meeting
- *
- * Properties can also have parameters:
- * SUMMARY;LANG=en:Weekly meeting.
- *
- * Parameters can be accessed using the ArrayAccess interface.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class Property extends Node {
-
- /**
- * Propertyname
- *
- * @var string
- */
- public $name;
-
- /**
- * Group name
- *
- * This may be something like 'HOME' for vcards.
- *
- * @var string
- */
- public $group;
-
- /**
- * Property parameters
- *
- * @var array
- */
- public $parameters = array();
-
- /**
- * Property value
- *
- * @var string
- */
- public $value;
-
- /**
- * If properties are added to this map, they will be automatically mapped
- * to their respective classes, if parsed by the reader or constructed with
- * the 'create' method.
- *
- * @var array
- */
- static public $classMap = array(
- 'COMPLETED' => 'Sabre\\VObject\\Property\\DateTime',
- 'CREATED' => 'Sabre\\VObject\\Property\\DateTime',
- 'DTEND' => 'Sabre\\VObject\\Property\\DateTime',
- 'DTSTAMP' => 'Sabre\\VObject\\Property\\DateTime',
- 'DTSTART' => 'Sabre\\VObject\\Property\\DateTime',
- 'DUE' => 'Sabre\\VObject\\Property\\DateTime',
- 'EXDATE' => 'Sabre\\VObject\\Property\\MultiDateTime',
- 'LAST-MODIFIED' => 'Sabre\\VObject\\Property\\DateTime',
- 'RECURRENCE-ID' => 'Sabre\\VObject\\Property\\DateTime',
- 'TRIGGER' => 'Sabre\\VObject\\Property\\DateTime',
- 'N' => 'Sabre\\VObject\\Property\\Compound',
- 'ORG' => 'Sabre\\VObject\\Property\\Compound',
- 'ADR' => 'Sabre\\VObject\\Property\\Compound',
- 'CATEGORIES' => 'Sabre\\VObject\\Property\\Compound',
- );
-
- /**
- * Creates the new property by name, but in addition will also see if
- * there's a class mapped to the property name.
- *
- * Parameters can be specified with the optional third argument. Parameters
- * must be a key->value map of the parameter name, and value. If the value
- * is specified as an array, it is assumed that multiple parameters with
- * the same name should be added.
- *
- * @param string $name
- * @param string $value
- * @param array $parameters
- * @return Property
- */
- static public function create($name, $value = null, array $parameters = array()) {
-
- $name = strtoupper($name);
- $shortName = $name;
- $group = null;
- if (strpos($shortName,'.')!==false) {
- list($group, $shortName) = explode('.', $shortName);
- }
-
- if (isset(self::$classMap[$shortName])) {
- return new self::$classMap[$shortName]($name, $value, $parameters);
- } else {
- return new self($name, $value, $parameters);
- }
-
- }
-
- /**
- * Creates a new property object
- *
- * Parameters can be specified with the optional third argument. Parameters
- * must be a key->value map of the parameter name, and value. If the value
- * is specified as an array, it is assumed that multiple parameters with
- * the same name should be added.
- *
- * @param string $name
- * @param string $value
- * @param array $parameters
- */
- public function __construct($name, $value = null, array $parameters = array()) {
-
- if (!is_scalar($value) && !is_null($value)) {
- throw new \InvalidArgumentException('The value argument must be scalar or null');
- }
-
- $name = strtoupper($name);
- $group = null;
- if (strpos($name,'.')!==false) {
- list($group, $name) = explode('.', $name);
- }
- $this->name = $name;
- $this->group = $group;
- $this->setValue($value);
-
- foreach($parameters as $paramName => $paramValues) {
-
- if (!is_array($paramValues)) {
- $paramValues = array($paramValues);
- }
-
- foreach($paramValues as $paramValue) {
- $this->add($paramName, $paramValue);
- }
-
- }
-
- }
-
- /**
- * Updates the internal value
- *
- * @param string $value
- * @return void
- */
- public function setValue($value) {
-
- $this->value = $value;
-
- }
-
- /**
- * Returns the internal value
- *
- * @param string $value
- * @return string
- */
- public function getValue() {
-
- return $this->value;
-
- }
-
- /**
- * Turns the object back into a serialized blob.
- *
- * @return string
- */
- public function serialize() {
-
- $str = $this->name;
- if ($this->group) $str = $this->group . '.' . $this->name;
-
- foreach($this->parameters as $param) {
-
- $str.=';' . $param->serialize();
-
- }
-
- $src = array(
- '\\',
- "\n",
- "\r",
- );
- $out = array(
- '\\\\',
- '\n',
- '',
- );
- $str.=':' . str_replace($src, $out, $this->value);
-
- $out = '';
- while(strlen($str)>0) {
- if (strlen($str)>75) {
- $out.= mb_strcut($str,0,75,'utf-8') . "\r\n";
- $str = ' ' . mb_strcut($str,75,strlen($str),'utf-8');
- } else {
- $out.=$str . "\r\n";
- $str='';
- break;
- }
- }
-
- return $out;
-
- }
-
- /**
- * Adds a new componenten or element
- *
- * You can call this method with the following syntaxes:
- *
- * add(Parameter $element)
- * add(string $name, $value)
- *
- * The first version adds an Parameter
- * The second adds a property as a string.
- *
- * @param mixed $item
- * @param mixed $itemValue
- * @return void
- */
- public function add($item, $itemValue = null) {
-
- if ($item instanceof Parameter) {
- if (!is_null($itemValue)) {
- throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject');
- }
- $item->parent = $this;
- $this->parameters[] = $item;
- } elseif(is_string($item)) {
-
- $parameter = new Parameter($item,$itemValue);
- $parameter->parent = $this;
- $this->parameters[] = $parameter;
-
- } else {
-
- throw new \InvalidArgumentException('The first argument must either be a Node a string');
-
- }
-
- }
-
- /* ArrayAccess interface {{{ */
-
- /**
- * Checks if an array element exists
- *
- * @param mixed $name
- * @return bool
- */
- public function offsetExists($name) {
-
- if (is_int($name)) return parent::offsetExists($name);
-
- $name = strtoupper($name);
-
- foreach($this->parameters as $parameter) {
- if ($parameter->name == $name) return true;
- }
- return false;
-
- }
-
- /**
- * Returns a parameter, or parameter list.
- *
- * @param string $name
- * @return Node
- */
- public function offsetGet($name) {
-
- if (is_int($name)) return parent::offsetGet($name);
- $name = strtoupper($name);
-
- $result = array();
- foreach($this->parameters as $parameter) {
- if ($parameter->name == $name)
- $result[] = $parameter;
- }
-
- if (count($result)===0) {
- return null;
- } elseif (count($result)===1) {
- return $result[0];
- } else {
- $result[0]->setIterator(new ElementList($result));
- return $result[0];
- }
-
- }
-
- /**
- * Creates a new parameter
- *
- * @param string $name
- * @param mixed $value
- * @return void
- */
- public function offsetSet($name, $value) {
-
- if (is_int($name)) parent::offsetSet($name, $value);
-
- if (is_scalar($value)) {
- if (!is_string($name))
- throw new \InvalidArgumentException('A parameter name must be specified. This means you cannot use the $array[]="string" to add parameters.');
-
- $this->offsetUnset($name);
- $parameter = new Parameter($name, $value);
- $parameter->parent = $this;
- $this->parameters[] = $parameter;
-
- } elseif ($value instanceof Parameter) {
- if (!is_null($name))
- throw new \InvalidArgumentException('Don\'t specify a parameter name if you\'re passing a \\Sabre\\VObject\\Parameter. Add using $array[]=$parameterObject.');
-
- $value->parent = $this;
- $this->parameters[] = $value;
- } else {
- throw new \InvalidArgumentException('You can only add parameters to the property object');
- }
-
- }
-
- /**
- * Removes one or more parameters with the specified name
- *
- * @param string $name
- * @return void
- */
- public function offsetUnset($name) {
-
- if (is_int($name)) parent::offsetUnset($name);
- $name = strtoupper($name);
-
- foreach($this->parameters as $key=>$parameter) {
- if ($parameter->name == $name) {
- $parameter->parent = null;
- unset($this->parameters[$key]);
- }
-
- }
-
- }
-
- /* }}} */
-
- /**
- * Called when this object is being cast to a string
- *
- * @return string
- */
- public function __toString() {
-
- return (string)$this->value;
-
- }
-
- /**
- * This method is automatically called when the object is cloned.
- * Specifically, this will ensure all child elements are also cloned.
- *
- * @return void
- */
- public function __clone() {
-
- foreach($this->parameters as $key=>$child) {
- $this->parameters[$key] = clone $child;
- $this->parameters[$key]->parent = $this;
- }
-
- }
-
- /**
- * Validates the node for correctness.
- *
- * The following options are supported:
- * - Node::REPAIR - If something is broken, and automatic repair may
- * be attempted.
- *
- * An array is returned with warnings.
- *
- * Every item in the array has the following properties:
- * * level - (number between 1 and 3 with severity information)
- * * message - (human readable message)
- * * node - (reference to the offending node)
- *
- * @param int $options
- * @return array
- */
- public function validate($options = 0) {
-
- $warnings = array();
-
- // Checking if our value is UTF-8
- if (!StringUtil::isUTF8($this->value)) {
- $warnings[] = array(
- 'level' => 1,
- 'message' => 'Property is not valid UTF-8!',
- 'node' => $this,
- );
- if ($options & self::REPAIR) {
- $this->value = StringUtil::convertToUTF8($this->value);
- }
- }
-
- // Checking if the propertyname does not contain any invalid bytes.
- if (!preg_match('/^([A-Z0-9-]+)$/', $this->name)) {
- $warnings[] = array(
- 'level' => 1,
- 'message' => 'The propertyname: ' . $this->name . ' contains invalid characters. Only A-Z, 0-9 and - are allowed',
- 'node' => $this,
- );
- if ($options & self::REPAIR) {
- // Uppercasing and converting underscores to dashes.
- $this->name = strtoupper(
- str_replace('_', '-', $this->name)
- );
- // Removing every other invalid character
- $this->name = preg_replace('/([^A-Z0-9-])/u', '', $this->name);
-
- }
-
- }
-
- // Validating inner parameters
- foreach($this->parameters as $param) {
- $warnings = array_merge($warnings, $param->validate($options));
- }
-
- return $warnings;
-
- }
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Property/Compound.php b/vendor/sabre/vobject/lib/Sabre/VObject/Property/Compound.php
deleted file mode 100644
index 26f090069..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Property/Compound.php
+++ /dev/null
@@ -1,125 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Property;
-
-use Sabre\VObject;
-
-/**
- * Compound property.
- *
- * This class adds (de)serialization of compound properties to/from arrays.
- *
- * Currently the following properties from RFC 6350 are mapped to use this
- * class:
- *
- * N: Section 6.2.2
- * ADR: Section 6.3.1
- * ORG: Section 6.6.4
- * CATEGORIES: Section 6.7.1
- *
- * In order to use this correctly, you must call setParts and getParts to
- * retrieve and modify dates respectively.
- *
- * @author Thomas Tanghus (http://tanghus.net/)
- * @author Lars Kneschke
- * @author Evert Pot (http://evertpot.com/)
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class Compound extends VObject\Property {
-
- /**
- * If property names are added to this map, they will be (de)serialised as arrays
- * using the getParts() and setParts() methods.
- * The keys are the property names, values are delimiter chars.
- *
- * @var array
- */
- static public $delimiterMap = array(
- 'N' => ';',
- 'ADR' => ';',
- 'ORG' => ';',
- 'CATEGORIES' => ',',
- );
-
- /**
- * The currently used delimiter.
- *
- * @var string
- */
- protected $delimiter = null;
-
- /**
- * Get a compound value as an array.
- *
- * @param $name string
- * @return array
- */
- public function getParts() {
-
- if (is_null($this->value)) {
- return array();
- }
-
- $delimiter = $this->getDelimiter();
-
- // split by any $delimiter which is NOT prefixed by a slash.
- // Note that this is not a a perfect solution. If a value is prefixed
- // by two slashes, it should actually be split anyway.
- //
- // Hopefully we can fix this better in a future version, where we can
- // break compatibility a bit.
- $compoundValues = preg_split("/(?<!\\\)$delimiter/", $this->value);
-
- // remove slashes from any semicolon and comma left escaped in the single values
- $compoundValues = array_map(
- function($val) {
- return strtr($val, array('\,' => ',', '\;' => ';'));
- }, $compoundValues);
-
- return $compoundValues;
-
- }
-
- /**
- * Returns the delimiter for this property.
- *
- * @return string
- */
- public function getDelimiter() {
-
- if (!$this->delimiter) {
- if (isset(self::$delimiterMap[$this->name])) {
- $this->delimiter = self::$delimiterMap[$this->name];
- } else {
- // To be a bit future proof, we are going to default the
- // delimiter to ;
- $this->delimiter = ';';
- }
- }
- return $this->delimiter;
-
- }
-
- /**
- * Set a compound value as an array.
- *
- *
- * @param $name string
- * @return array
- */
- public function setParts(array $values) {
-
- // add slashes to all semicolons and commas in the single values
- $values = array_map(
- function($val) {
- return strtr($val, array(',' => '\,', ';' => '\;'));
- }, $values);
-
- $this->setValue(
- implode($this->getDelimiter(), $values)
- );
-
- }
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Property/DateTime.php b/vendor/sabre/vobject/lib/Sabre/VObject/Property/DateTime.php
deleted file mode 100644
index 95e9b0209..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Property/DateTime.php
+++ /dev/null
@@ -1,245 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Property;
-
-use Sabre\VObject;
-
-/**
- * DateTime property
- *
- * This element is used for iCalendar properties such as the DTSTART property.
- * It basically provides a few helper functions that make it easier to deal
- * with these. It supports both DATE-TIME and DATE values.
- *
- * In order to use this correctly, you must call setDateTime and getDateTime to
- * retrieve and modify dates respectively.
- *
- * If you use the 'value' or properties directly, this object does not keep
- * reference and results might appear incorrectly.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class DateTime extends VObject\Property {
-
- /**
- * Local 'floating' time
- */
- const LOCAL = 1;
-
- /**
- * UTC-based time
- */
- const UTC = 2;
-
- /**
- * Local time plus timezone
- */
- const LOCALTZ = 3;
-
- /**
- * Only a date, time is ignored
- */
- const DATE = 4;
-
- /**
- * DateTime representation
- *
- * @var \DateTime
- */
- protected $dateTime;
-
- /**
- * dateType
- *
- * @var int
- */
- protected $dateType;
-
- /**
- * Updates the Date and Time.
- *
- * @param \DateTime $dt
- * @param int $dateType
- * @return void
- */
- public function setDateTime(\DateTime $dt, $dateType = self::LOCALTZ) {
-
- switch($dateType) {
-
- case self::LOCAL :
- $this->setValue($dt->format('Ymd\\THis'));
- $this->offsetUnset('VALUE');
- $this->offsetUnset('TZID');
- $this->offsetSet('VALUE','DATE-TIME');
- break;
- case self::UTC :
- $dt->setTimeZone(new \DateTimeZone('UTC'));
- $this->setValue($dt->format('Ymd\\THis\\Z'));
- $this->offsetUnset('VALUE');
- $this->offsetUnset('TZID');
- $this->offsetSet('VALUE','DATE-TIME');
- break;
- case self::LOCALTZ :
- $this->setValue($dt->format('Ymd\\THis'));
- $this->offsetUnset('VALUE');
- $this->offsetUnset('TZID');
- $this->offsetSet('VALUE','DATE-TIME');
- $this->offsetSet('TZID', $dt->getTimeZone()->getName());
- break;
- case self::DATE :
- $this->setValue($dt->format('Ymd'));
- $this->offsetUnset('VALUE');
- $this->offsetUnset('TZID');
- $this->offsetSet('VALUE','DATE');
- break;
- default :
- throw new \InvalidArgumentException('You must pass a valid dateType constant');
-
- }
- $this->dateTime = $dt;
- $this->dateType = $dateType;
-
- }
-
- /**
- * Returns the current DateTime value.
- *
- * If no value was set, this method returns null.
- *
- * @return \DateTime|null
- */
- public function getDateTime() {
-
- if ($this->dateTime)
- return $this->dateTime;
-
- list(
- $this->dateType,
- $this->dateTime
- ) = self::parseData($this->value, $this);
- return $this->dateTime;
-
- }
-
- /**
- * Returns the type of Date format.
- *
- * This method returns one of the format constants. If no date was set,
- * this method will return null.
- *
- * @return int|null
- */
- public function getDateType() {
-
- if ($this->dateType)
- return $this->dateType;
-
- list(
- $this->dateType,
- $this->dateTime,
- ) = self::parseData($this->value, $this);
- return $this->dateType;
-
- }
-
- /**
- * This method will return true, if the property had a date and a time, as
- * opposed to only a date.
- *
- * @return bool
- */
- public function hasTime() {
-
- return $this->getDateType()!==self::DATE;
-
- }
-
- /**
- * Parses the internal data structure to figure out what the current date
- * and time is.
- *
- * The returned array contains two elements:
- * 1. A 'DateType' constant (as defined on this class), or null.
- * 2. A DateTime object (or null)
- *
- * @param string|null $propertyValue The string to parse (yymmdd or
- * ymmddThhmmss, etc..)
- * @param \Sabre\VObject\Property|null $property The instance of the
- * property we're parsing.
- * @return array
- */
- static public function parseData($propertyValue, VObject\Property $property = null) {
-
- if (is_null($propertyValue)) {
- return array(null, null);
- }
-
- $date = '(?P<year>[1-2][0-9]{3})(?P<month>[0-1][0-9])(?P<date>[0-3][0-9])';
- $time = '(?P<hour>[0-2][0-9])(?P<minute>[0-5][0-9])(?P<second>[0-5][0-9])';
- $regex = "/^$date(T$time(?P<isutc>Z)?)?$/";
-
- if (!preg_match($regex, $propertyValue, $matches)) {
- throw new \InvalidArgumentException($propertyValue . ' is not a valid \DateTime or Date string');
- }
-
- if (!isset($matches['hour'])) {
- // Date-only
- return array(
- self::DATE,
- new \DateTime($matches['year'] . '-' . $matches['month'] . '-' . $matches['date'] . ' 00:00:00', new \DateTimeZone('UTC')),
- );
- }
-
- $dateStr =
- $matches['year'] .'-' .
- $matches['month'] . '-' .
- $matches['date'] . ' ' .
- $matches['hour'] . ':' .
- $matches['minute'] . ':' .
- $matches['second'];
-
- if (isset($matches['isutc'])) {
- $dt = new \DateTime($dateStr,new \DateTimeZone('UTC'));
- $dt->setTimeZone(new \DateTimeZone('UTC'));
- return array(
- self::UTC,
- $dt
- );
- }
-
- // Finding the timezone.
- $tzid = $property['TZID'];
- if (!$tzid) {
- // This was a floating time string. This implies we use the
- // timezone from date_default_timezone_set / date.timezone ini
- // setting.
- return array(
- self::LOCAL,
- new \DateTime($dateStr)
- );
- }
-
- // To look up the timezone, we must first find the VCALENDAR component.
- $root = $property;
- while($root->parent) {
- $root = $root->parent;
- }
- if ($root->name === 'VCALENDAR') {
- $tz = VObject\TimeZoneUtil::getTimeZone((string)$tzid, $root);
- } else {
- $tz = VObject\TimeZoneUtil::getTimeZone((string)$tzid);
- }
-
- $dt = new \DateTime($dateStr, $tz);
- $dt->setTimeZone($tz);
-
- return array(
- self::LOCALTZ,
- $dt
- );
-
- }
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Property/MultiDateTime.php b/vendor/sabre/vobject/lib/Sabre/VObject/Property/MultiDateTime.php
deleted file mode 100644
index f01491b66..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Property/MultiDateTime.php
+++ /dev/null
@@ -1,180 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Property;
-
-use Sabre\VObject;
-
-/**
- * Multi-DateTime property
- *
- * This element is used for iCalendar properties such as the EXDATE property.
- * It basically provides a few helper functions that make it easier to deal
- * with these. It supports both DATE-TIME and DATE values.
- *
- * In order to use this correctly, you must call setDateTimes and getDateTimes
- * to retrieve and modify dates respectively.
- *
- * If you use the 'value' or properties directly, this object does not keep
- * reference and results might appear incorrectly.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class MultiDateTime extends VObject\Property {
-
- /**
- * DateTime representation
- *
- * @var DateTime[]
- */
- protected $dateTimes;
-
- /**
- * dateType
- *
- * This is one of the Sabre\VObject\Property\DateTime constants.
- *
- * @var int
- */
- protected $dateType;
-
- /**
- * Updates the value
- *
- * @param array $dt Must be an array of DateTime objects.
- * @param int $dateType
- * @return void
- */
- public function setDateTimes(array $dt, $dateType = VObject\Property\DateTime::LOCALTZ) {
-
- foreach($dt as $i)
- if (!$i instanceof \DateTime)
- throw new \InvalidArgumentException('You must pass an array of DateTime objects');
-
- $this->offsetUnset('VALUE');
- $this->offsetUnset('TZID');
- switch($dateType) {
-
- case DateTime::LOCAL :
- $val = array();
- foreach($dt as $i) {
- $val[] = $i->format('Ymd\\THis');
- }
- $this->setValue(implode(',',$val));
- $this->offsetSet('VALUE','DATE-TIME');
- break;
- case DateTime::UTC :
- $val = array();
- foreach($dt as $i) {
- $i->setTimeZone(new \DateTimeZone('UTC'));
- $val[] = $i->format('Ymd\\THis\\Z');
- }
- $this->setValue(implode(',',$val));
- $this->offsetSet('VALUE','DATE-TIME');
- break;
- case DateTime::LOCALTZ :
- $val = array();
- foreach($dt as $i) {
- $val[] = $i->format('Ymd\\THis');
- }
- $this->setValue(implode(',',$val));
- $this->offsetSet('VALUE','DATE-TIME');
- $this->offsetSet('TZID', $dt[0]->getTimeZone()->getName());
- break;
- case DateTime::DATE :
- $val = array();
- foreach($dt as $i) {
- $val[] = $i->format('Ymd');
- }
- $this->setValue(implode(',',$val));
- $this->offsetSet('VALUE','DATE');
- break;
- default :
- throw new \InvalidArgumentException('You must pass a valid dateType constant');
-
- }
- $this->dateTimes = $dt;
- $this->dateType = $dateType;
-
- }
-
- /**
- * Returns the current DateTime value.
- *
- * If no value was set, this method returns null.
- *
- * @return array|null
- */
- public function getDateTimes() {
-
- if ($this->dateTimes)
- return $this->dateTimes;
-
- $dts = array();
-
- if (!$this->value) {
- $this->dateTimes = null;
- $this->dateType = null;
- return null;
- }
-
- foreach(explode(',',$this->value) as $val) {
- list(
- $type,
- $dt
- ) = DateTime::parseData($val, $this);
- $dts[] = $dt;
- $this->dateType = $type;
- }
- $this->dateTimes = $dts;
- return $this->dateTimes;
-
- }
-
- /**
- * Returns the type of Date format.
- *
- * This method returns one of the format constants. If no date was set,
- * this method will return null.
- *
- * @return int|null
- */
- public function getDateType() {
-
- if ($this->dateType)
- return $this->dateType;
-
- if (!$this->value) {
- $this->dateTimes = null;
- $this->dateType = null;
- return null;
- }
-
- $dts = array();
- foreach(explode(',',$this->value) as $val) {
- list(
- $type,
- $dt
- ) = DateTime::parseData($val, $this);
- $dts[] = $dt;
- $this->dateType = $type;
- }
- $this->dateTimes = $dts;
- return $this->dateType;
-
- }
-
- /**
- * This method will return true, if the property had a date and a time, as
- * opposed to only a date.
- *
- * @return bool
- */
- public function hasTime() {
-
- return $this->getDateType()!==DateTime::DATE;
-
- }
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Reader.php b/vendor/sabre/vobject/lib/Sabre/VObject/Reader.php
deleted file mode 100644
index a001b2bf1..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Reader.php
+++ /dev/null
@@ -1,223 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-/**
- * VCALENDAR/VCARD reader
- *
- * This class reads the vobject file, and returns a full element tree.
- *
- * TODO: this class currently completely works 'statically'. This is pointless,
- * and defeats OOP principals. Needs refactoring in a future version.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class Reader {
-
- /**
- * If this option is passed to the reader, it will be less strict about the
- * validity of the lines.
- *
- * Currently using this option just means, that it will accept underscores
- * in property names.
- */
- const OPTION_FORGIVING = 1;
-
- /**
- * If this option is turned on, any lines we cannot parse will be ignored
- * by the reader.
- */
- const OPTION_IGNORE_INVALID_LINES = 2;
-
- /**
- * Parses the file and returns the top component
- *
- * The options argument is a bitfield. Pass any of the OPTIONS constant to
- * alter the parsers' behaviour.
- *
- * @param string $data
- * @param int $options
- * @return Node
- */
- static function read($data, $options = 0) {
-
- // Normalizing newlines
- $data = str_replace(array("\r","\n\n"), array("\n","\n"), $data);
-
- $lines = explode("\n", $data);
-
- // Unfolding lines
- $lines2 = array();
- foreach($lines as $line) {
-
- // Skipping empty lines
- if (!$line) continue;
-
- if ($line[0]===" " || $line[0]==="\t") {
- $lines2[count($lines2)-1].=substr($line,1);
- } else {
- $lines2[] = $line;
- }
-
- }
-
- unset($lines);
-
- reset($lines2);
-
- return self::readLine($lines2, $options);
-
- }
-
- /**
- * Reads and parses a single line.
- *
- * This method receives the full array of lines. The array pointer is used
- * to traverse.
- *
- * This method returns null if an invalid line was encountered, and the
- * IGNORE_INVALID_LINES option was turned on.
- *
- * @param array $lines
- * @param int $options See the OPTIONS constants.
- * @return Node
- */
- static private function readLine(&$lines, $options = 0) {
-
- $line = current($lines);
- $lineNr = key($lines);
- next($lines);
-
- // Components
- if (strtoupper(substr($line,0,6)) === "BEGIN:") {
-
- $componentName = strtoupper(substr($line,6));
- $obj = Component::create($componentName);
-
- $nextLine = current($lines);
-
- while(strtoupper(substr($nextLine,0,4))!=="END:") {
-
- $parsedLine = self::readLine($lines, $options);
- $nextLine = current($lines);
-
- if (is_null($parsedLine)) {
- continue;
- }
- $obj->add($parsedLine);
-
- if ($nextLine===false)
- throw new ParseException('Invalid VObject. Document ended prematurely.');
-
- }
-
- // Checking component name of the 'END:' line.
- if (substr($nextLine,4)!==$obj->name) {
- throw new ParseException('Invalid VObject, expected: "END:' . $obj->name . '" got: "' . $nextLine . '"');
- }
- next($lines);
-
- return $obj;
-
- }
-
- // Properties
- //$result = preg_match('/(?P<name>[A-Z0-9-]+)(?:;(?P<parameters>^(?<!:):))(.*)$/',$line,$matches);
-
- if ($options & self::OPTION_FORGIVING) {
- $token = '[A-Z0-9-\._]+';
- } else {
- $token = '[A-Z0-9-\.]+';
- }
- $parameters = "(?:;(?P<parameters>([^:^\"]|\"([^\"]*)\")*))?";
- $regex = "/^(?P<name>$token)$parameters:(?P<value>.*)$/i";
-
- $result = preg_match($regex,$line,$matches);
-
- if (!$result) {
- if ($options & self::OPTION_IGNORE_INVALID_LINES) {
- return null;
- } else {
- throw new ParseException('Invalid VObject, line ' . ($lineNr+1) . ' did not follow the icalendar/vcard format');
- }
- }
-
- $propertyName = strtoupper($matches['name']);
- $propertyValue = preg_replace_callback('#(\\\\(\\\\|N|n))#',function($matches) {
- if ($matches[2]==='n' || $matches[2]==='N') {
- return "\n";
- } else {
- return $matches[2];
- }
- }, $matches['value']);
-
- $obj = Property::create($propertyName, $propertyValue);
-
- if ($matches['parameters']) {
-
- foreach(self::readParameters($matches['parameters']) as $param) {
- $obj->add($param);
- }
-
- }
-
- return $obj;
-
-
- }
-
- /**
- * Reads a parameter list from a property
- *
- * This method returns an array of Parameter
- *
- * @param string $parameters
- * @return array
- */
- static private function readParameters($parameters) {
-
- $token = '[A-Z0-9-]+';
-
- $paramValue = '(?P<paramValue>[^\"^;]*|"[^"]*")';
-
- $regex = "/(?<=^|;)(?P<paramName>$token)(=$paramValue(?=$|;))?/i";
- preg_match_all($regex, $parameters, $matches, PREG_SET_ORDER);
-
- $params = array();
- foreach($matches as $match) {
-
- if (!isset($match['paramValue'])) {
-
- $value = null;
-
- } else {
-
- $value = $match['paramValue'];
-
- if (isset($value[0]) && $value[0]==='"') {
- // Stripping quotes, if needed
- $value = substr($value,1,strlen($value)-2);
- }
-
- $value = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) {
- if ($matches[2]==='n' || $matches[2]==='N') {
- return "\n";
- } else {
- return $matches[2];
- }
- }, $value);
-
- }
-
- $params[] = new Parameter($match['paramName'], $value);
-
- }
-
- return $params;
-
- }
-
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/RecurrenceIterator.php b/vendor/sabre/vobject/lib/Sabre/VObject/RecurrenceIterator.php
deleted file mode 100644
index 8bd4ed224..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/RecurrenceIterator.php
+++ /dev/null
@@ -1,1144 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-/**
- * This class is used to determine new for a recurring event, when the next
- * events occur.
- *
- * This iterator may loop infinitely in the future, therefore it is important
- * that if you use this class, you set hard limits for the amount of iterations
- * you want to handle.
- *
- * Note that currently there is not full support for the entire iCalendar
- * specification, as it's very complex and contains a lot of permutations
- * that's not yet used very often in software.
- *
- * For the focus has been on features as they actually appear in Calendaring
- * software, but this may well get expanded as needed / on demand
- *
- * The following RRULE properties are supported
- * * UNTIL
- * * INTERVAL
- * * COUNT
- * * FREQ=DAILY
- * * BYDAY
- * * BYHOUR
- * * FREQ=WEEKLY
- * * BYDAY
- * * BYHOUR
- * * WKST
- * * FREQ=MONTHLY
- * * BYMONTHDAY
- * * BYDAY
- * * BYSETPOS
- * * FREQ=YEARLY
- * * BYMONTH
- * * BYMONTHDAY (only if BYMONTH is also set)
- * * BYDAY (only if BYMONTH is also set)
- *
- * Anything beyond this is 'undefined', which means that it may get ignored, or
- * you may get unexpected results. The effect is that in some applications the
- * specified recurrence may look incorrect, or is missing.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class RecurrenceIterator implements \Iterator {
-
- /**
- * The initial event date
- *
- * @var DateTime
- */
- public $startDate;
-
- /**
- * The end-date of the initial event
- *
- * @var DateTime
- */
- public $endDate;
-
- /**
- * The 'current' recurrence.
- *
- * This will be increased for every iteration.
- *
- * @var DateTime
- */
- public $currentDate;
-
-
- /**
- * List of dates that are excluded from the rules.
- *
- * This list contains the items that have been overriden by the EXDATE
- * property.
- *
- * @var array
- */
- public $exceptionDates = array();
-
- /**
- * Base event
- *
- * @var Component\VEvent
- */
- public $baseEvent;
-
- /**
- * List of dates that are overridden by other events.
- * Similar to $overriddenEvents, but this just contains the original dates.
- *
- * @var array
- */
- public $overriddenDates = array();
-
- /**
- * list of events that are 'overridden'.
- *
- * This is an array of Component\VEvent objects.
- *
- * @var array
- */
- public $overriddenEvents = array();
-
- /**
- * Frequency is one of: secondly, minutely, hourly, daily, weekly, monthly,
- * yearly.
- *
- * @var string
- */
- public $frequency;
-
- /**
- * The last instance of this recurrence, inclusively
- *
- * @var DateTime|null
- */
- public $until;
-
- /**
- * The number of recurrences, or 'null' if infinitely recurring.
- *
- * @var int
- */
- public $count;
-
- /**
- * The interval.
- *
- * If for example frequency is set to daily, interval = 2 would mean every
- * 2 days.
- *
- * @var int
- */
- public $interval = 1;
-
- /**
- * Which seconds to recur.
- *
- * This is an array of integers (between 0 and 60)
- *
- * @var array
- */
- public $bySecond;
-
- /**
- * Which minutes to recur
- *
- * This is an array of integers (between 0 and 59)
- *
- * @var array
- */
- public $byMinute;
-
- /**
- * Which hours to recur
- *
- * This is an array of integers (between 0 and 23)
- *
- * @var array
- */
- public $byHour;
-
- /**
- * Which weekdays to recur.
- *
- * This is an array of weekdays
- *
- * This may also be preceeded by a positive or negative integer. If present,
- * this indicates the nth occurrence of a specific day within the monthly or
- * yearly rrule. For instance, -2TU indicates the second-last tuesday of
- * the month, or year.
- *
- * @var array
- */
- public $byDay;
-
- /**
- * Which days of the month to recur
- *
- * This is an array of days of the months (1-31). The value can also be
- * negative. -5 for instance means the 5th last day of the month.
- *
- * @var array
- */
- public $byMonthDay;
-
- /**
- * Which days of the year to recur.
- *
- * This is an array with days of the year (1 to 366). The values can also
- * be negative. For instance, -1 will always represent the last day of the
- * year. (December 31st).
- *
- * @var array
- */
- public $byYearDay;
-
- /**
- * Which week numbers to recur.
- *
- * This is an array of integers from 1 to 53. The values can also be
- * negative. -1 will always refer to the last week of the year.
- *
- * @var array
- */
- public $byWeekNo;
-
- /**
- * Which months to recur
- *
- * This is an array of integers from 1 to 12.
- *
- * @var array
- */
- public $byMonth;
-
- /**
- * Which items in an existing st to recur.
- *
- * These numbers work together with an existing by* rule. It specifies
- * exactly which items of the existing by-rule to filter.
- *
- * Valid values are 1 to 366 and -1 to -366. As an example, this can be
- * used to recur the last workday of the month.
- *
- * This would be done by setting frequency to 'monthly', byDay to
- * 'MO,TU,WE,TH,FR' and bySetPos to -1.
- *
- * @var array
- */
- public $bySetPos;
-
- /**
- * When a week starts
- *
- * @var string
- */
- public $weekStart = 'MO';
-
- /**
- * The current item in the list
- *
- * @var int
- */
- public $counter = 0;
-
- /**
- * Simple mapping from iCalendar day names to day numbers
- *
- * @var array
- */
- private $dayMap = array(
- 'SU' => 0,
- 'MO' => 1,
- 'TU' => 2,
- 'WE' => 3,
- 'TH' => 4,
- 'FR' => 5,
- 'SA' => 6,
- );
-
- /**
- * Mappings between the day number and english day name.
- *
- * @var array
- */
- private $dayNames = array(
- 0 => 'Sunday',
- 1 => 'Monday',
- 2 => 'Tuesday',
- 3 => 'Wednesday',
- 4 => 'Thursday',
- 5 => 'Friday',
- 6 => 'Saturday',
- );
-
- /**
- * If the current iteration of the event is an overriden event, this
- * property will hold the VObject
- *
- * @var Component
- */
- private $currentOverriddenEvent;
-
- /**
- * This property may contain the date of the next not-overridden event.
- * This date is calculated sometimes a bit early, before overridden events
- * are evaluated.
- *
- * @var DateTime
- */
- private $nextDate;
-
- /**
- * This counts the number of overridden events we've handled so far
- *
- * @var int
- */
- private $handledOverridden = 0;
-
- /**
- * Creates the iterator
- *
- * You should pass a VCALENDAR component, as well as the UID of the event
- * we're going to traverse.
- *
- * @param Component $vcal
- * @param string|null $uid
- */
- public function __construct(Component $vcal, $uid=null) {
-
- if (is_null($uid)) {
- if ($vcal->name === 'VCALENDAR') {
- throw new \InvalidArgumentException('If you pass a VCALENDAR object, you must pass a uid argument as well');
- }
- $components = array($vcal);
- $uid = (string)$vcal->uid;
- } else {
- $components = $vcal->select('VEVENT');
- }
- foreach($components as $component) {
- if ((string)$component->uid == $uid) {
- if (isset($component->{'RECURRENCE-ID'})) {
- $this->overriddenEvents[$component->DTSTART->getDateTime()->getTimeStamp()] = $component;
- $this->overriddenDates[] = $component->{'RECURRENCE-ID'}->getDateTime();
- } else {
- $this->baseEvent = $component;
- }
- }
- }
-
- ksort($this->overriddenEvents);
-
- if (!$this->baseEvent) {
- throw new \InvalidArgumentException('Could not find a base event with uid: ' . $uid);
- }
-
- $this->startDate = clone $this->baseEvent->DTSTART->getDateTime();
-
- $this->endDate = null;
- if (isset($this->baseEvent->DTEND)) {
- $this->endDate = clone $this->baseEvent->DTEND->getDateTime();
- } else {
- $this->endDate = clone $this->startDate;
- if (isset($this->baseEvent->DURATION)) {
- $this->endDate->add(DateTimeParser::parse($this->baseEvent->DURATION->value));
- } elseif ($this->baseEvent->DTSTART->getDateType()===Property\DateTime::DATE) {
- $this->endDate->modify('+1 day');
- }
- }
- $this->currentDate = clone $this->startDate;
-
- $rrule = (string)$this->baseEvent->RRULE;
-
- $parts = explode(';', $rrule);
-
- // If no rrule was specified, we create a default setting
- if (!$rrule) {
- $this->frequency = 'daily';
- $this->count = 1;
- } else foreach($parts as $part) {
-
- list($key, $value) = explode('=', $part, 2);
-
- switch(strtoupper($key)) {
-
- case 'FREQ' :
- if (!in_array(
- strtolower($value),
- array('secondly','minutely','hourly','daily','weekly','monthly','yearly')
- )) {
- throw new \InvalidArgumentException('Unknown value for FREQ=' . strtoupper($value));
-
- }
- $this->frequency = strtolower($value);
- break;
-
- case 'UNTIL' :
- $this->until = DateTimeParser::parse($value);
-
- // In some cases events are generated with an UNTIL=
- // parameter before the actual start of the event.
- //
- // Not sure why this is happening. We assume that the
- // intention was that the event only recurs once.
- //
- // So we are modifying the parameter so our code doesn't
- // break.
- if($this->until < $this->baseEvent->DTSTART->getDateTime()) {
- $this->until = $this->baseEvent->DTSTART->getDateTime();
- }
- break;
-
- case 'COUNT' :
- $this->count = (int)$value;
- break;
-
- case 'INTERVAL' :
- $this->interval = (int)$value;
- if ($this->interval < 1) {
- throw new \InvalidArgumentException('INTERVAL in RRULE must be a positive integer!');
- }
- break;
-
- case 'BYSECOND' :
- $this->bySecond = explode(',', $value);
- break;
-
- case 'BYMINUTE' :
- $this->byMinute = explode(',', $value);
- break;
-
- case 'BYHOUR' :
- $this->byHour = explode(',', $value);
- break;
-
- case 'BYDAY' :
- $this->byDay = explode(',', strtoupper($value));
- break;
-
- case 'BYMONTHDAY' :
- $this->byMonthDay = explode(',', $value);
- break;
-
- case 'BYYEARDAY' :
- $this->byYearDay = explode(',', $value);
- break;
-
- case 'BYWEEKNO' :
- $this->byWeekNo = explode(',', $value);
- break;
-
- case 'BYMONTH' :
- $this->byMonth = explode(',', $value);
- break;
-
- case 'BYSETPOS' :
- $this->bySetPos = explode(',', $value);
- break;
-
- case 'WKST' :
- $this->weekStart = strtoupper($value);
- break;
-
- }
-
- }
-
- // Parsing exception dates
- if (isset($this->baseEvent->EXDATE)) {
- foreach($this->baseEvent->EXDATE as $exDate) {
-
- foreach(explode(',', (string)$exDate) as $exceptionDate) {
-
- $this->exceptionDates[] =
- DateTimeParser::parse($exceptionDate, $this->startDate->getTimeZone());
-
- }
-
- }
-
- }
-
- }
-
- /**
- * Returns the current item in the list
- *
- * @return DateTime
- */
- public function current() {
-
- if (!$this->valid()) return null;
- return clone $this->currentDate;
-
- }
-
- /**
- * This method returns the startdate for the current iteration of the
- * event.
- *
- * @return DateTime
- */
- public function getDtStart() {
-
- if (!$this->valid()) return null;
- return clone $this->currentDate;
-
- }
-
- /**
- * This method returns the enddate for the current iteration of the
- * event.
- *
- * @return DateTime
- */
- public function getDtEnd() {
-
- if (!$this->valid()) return null;
- $dtEnd = clone $this->currentDate;
- $dtEnd->add( $this->startDate->diff( $this->endDate ) );
- return clone $dtEnd;
-
- }
-
- /**
- * Returns a VEVENT object with the updated start and end date.
- *
- * Any recurrence information is removed, and this function may return an
- * 'overridden' event instead.
- *
- * This method always returns a cloned instance.
- *
- * @return Component\VEvent
- */
- public function getEventObject() {
-
- if ($this->currentOverriddenEvent) {
- return clone $this->currentOverriddenEvent;
- }
- $event = clone $this->baseEvent;
- unset($event->RRULE);
- unset($event->EXDATE);
- unset($event->RDATE);
- unset($event->EXRULE);
-
- $event->DTSTART->setDateTime($this->getDTStart(), $event->DTSTART->getDateType());
- if (isset($event->DTEND)) {
- $event->DTEND->setDateTime($this->getDtEnd(), $event->DTSTART->getDateType());
- }
- if ($this->counter > 0) {
- $event->{'RECURRENCE-ID'} = (string)$event->DTSTART;
- }
-
- return $event;
-
- }
-
- /**
- * Returns the current item number
- *
- * @return int
- */
- public function key() {
-
- return $this->counter;
-
- }
-
- /**
- * Whether or not there is a 'next item'
- *
- * @return bool
- */
- public function valid() {
-
- if (!is_null($this->count)) {
- return $this->counter < $this->count;
- }
- if (!is_null($this->until) && $this->currentDate > $this->until) {
-
- // Need to make sure there's no overridden events past the
- // until date.
- foreach($this->overriddenEvents as $overriddenEvent) {
-
- if ($overriddenEvent->DTSTART->getDateTime() >= $this->currentDate) {
-
- return true;
- }
- }
- return false;
- }
- return true;
-
- }
-
- /**
- * Resets the iterator
- *
- * @return void
- */
- public function rewind() {
-
- $this->currentDate = clone $this->startDate;
- $this->counter = 0;
-
- }
-
- /**
- * This method allows you to quickly go to the next occurrence after the
- * specified date.
- *
- * Note that this checks the current 'endDate', not the 'stardDate'. This
- * means that if you forward to January 1st, the iterator will stop at the
- * first event that ends *after* January 1st.
- *
- * @param DateTime $dt
- * @return void
- */
- public function fastForward(\DateTime $dt) {
-
- while($this->valid() && $this->getDTEnd() <= $dt) {
- $this->next();
- }
-
- }
-
- /**
- * Returns true if this recurring event never ends.
- *
- * @return bool
- */
- public function isInfinite() {
-
- return !$this->count && !$this->until;
-
- }
-
- /**
- * Goes on to the next iteration
- *
- * @return void
- */
- public function next() {
-
- $previousStamp = $this->currentDate->getTimeStamp();
-
- // Finding the next overridden event in line, and storing that for
- // later use.
- $overriddenEvent = null;
- $overriddenDate = null;
- $this->currentOverriddenEvent = null;
-
- foreach($this->overriddenEvents as $index=>$event) {
- if ($index > $previousStamp) {
- $overriddenEvent = $event;
- $overriddenDate = clone $event->DTSTART->getDateTime();
- break;
- }
- }
-
- // If we have a stored 'next date', we will use that.
- if ($this->nextDate) {
- if (!$overriddenDate || $this->nextDate < $overriddenDate) {
- $this->currentDate = $this->nextDate;
- $currentStamp = $this->currentDate->getTimeStamp();
- $this->nextDate = null;
- } else {
- $this->currentDate = clone $overriddenDate;
- $this->currentOverriddenEvent = $overriddenEvent;
- }
- $this->counter++;
- return;
- }
-
- while(true) {
-
- // Otherwise, we find the next event in the normal RRULE
- // sequence.
- switch($this->frequency) {
-
- case 'hourly' :
- $this->nextHourly();
- break;
-
- case 'daily' :
- $this->nextDaily();
- break;
-
- case 'weekly' :
- $this->nextWeekly();
- break;
-
- case 'monthly' :
- $this->nextMonthly();
- break;
-
- case 'yearly' :
- $this->nextYearly();
- break;
-
- }
- $currentStamp = $this->currentDate->getTimeStamp();
-
-
- // Checking exception dates
- foreach($this->exceptionDates as $exceptionDate) {
- if ($this->currentDate == $exceptionDate) {
- $this->counter++;
- continue 2;
- }
- }
- foreach($this->overriddenDates as $check) {
- if ($this->currentDate == $check) {
- continue 2;
- }
- }
- break;
-
- }
-
-
-
- // Is the date we have actually higher than the next overiddenEvent?
- if ($overriddenDate && $this->currentDate > $overriddenDate) {
- $this->nextDate = clone $this->currentDate;
- $this->currentDate = clone $overriddenDate;
- $this->currentOverriddenEvent = $overriddenEvent;
- $this->handledOverridden++;
- }
- $this->counter++;
-
-
- /*
- * If we have overridden events left in the queue, but our counter is
- * running out, we should grab one of those.
- */
- if (!is_null($overriddenEvent) && !is_null($this->count) && count($this->overriddenEvents) - $this->handledOverridden >= ($this->count - $this->counter)) {
-
- $this->currentOverriddenEvent = $overriddenEvent;
- $this->currentDate = clone $overriddenDate;
- $this->handledOverridden++;
-
- }
-
- }
-
- /**
- * Does the processing for advancing the iterator for hourly frequency.
- *
- * @return void
- */
- protected function nextHourly() {
-
- if (!$this->byHour) {
- $this->currentDate->modify('+' . $this->interval . ' hours');
- return;
- }
- }
-
- /**
- * Does the processing for advancing the iterator for daily frequency.
- *
- * @return void
- */
- protected function nextDaily() {
-
- if (!$this->byHour && !$this->byDay) {
- $this->currentDate->modify('+' . $this->interval . ' days');
- return;
- }
-
- if (isset($this->byHour)) {
- $recurrenceHours = $this->getHours();
- }
-
- if (isset($this->byDay)) {
- $recurrenceDays = $this->getDays();
- }
-
- do {
-
- if ($this->byHour) {
- if ($this->currentDate->format('G') == '23') {
- // to obey the interval rule
- $this->currentDate->modify('+' . $this->interval-1 . ' days');
- }
-
- $this->currentDate->modify('+1 hours');
-
- } else {
- $this->currentDate->modify('+' . $this->interval . ' days');
-
- }
-
- // Current day of the week
- $currentDay = $this->currentDate->format('w');
-
- // Current hour of the day
- $currentHour = $this->currentDate->format('G');
-
- } while (($this->byDay && !in_array($currentDay, $recurrenceDays)) || ($this->byHour && !in_array($currentHour, $recurrenceHours)));
-
- }
-
- /**
- * Does the processing for advancing the iterator for weekly frequency.
- *
- * @return void
- */
- protected function nextWeekly() {
-
- if (!$this->byHour && !$this->byDay) {
- $this->currentDate->modify('+' . $this->interval . ' weeks');
- return;
- }
-
- if ($this->byHour) {
- $recurrenceHours = $this->getHours();
- }
-
- if ($this->byDay) {
- $recurrenceDays = $this->getDays();
- }
-
- // First day of the week:
- $firstDay = $this->dayMap[$this->weekStart];
-
- do {
-
- if ($this->byHour) {
- $this->currentDate->modify('+1 hours');
- } else {
- $this->currentDate->modify('+1 days');
- }
-
- // Current day of the week
- $currentDay = (int) $this->currentDate->format('w');
-
- // Current hour of the day
- $currentHour = (int) $this->currentDate->format('G');
-
- // We need to roll over to the next week
- if ($currentDay === $firstDay && (!$this->byHour || $currentHour == '0')) {
- $this->currentDate->modify('+' . $this->interval-1 . ' weeks');
-
- // We need to go to the first day of this week, but only if we
- // are not already on this first day of this week.
- if($this->currentDate->format('w') != $firstDay) {
- $this->currentDate->modify('last ' . $this->dayNames[$this->dayMap[$this->weekStart]]);
- }
- }
-
- // We have a match
- } while (($this->byDay && !in_array($currentDay, $recurrenceDays)) || ($this->byHour && !in_array($currentHour, $recurrenceHours)));
- }
-
- /**
- * Does the processing for advancing the iterator for monthly frequency.
- *
- * @return void
- */
- protected function nextMonthly() {
-
- $currentDayOfMonth = $this->currentDate->format('j');
- if (!$this->byMonthDay && !$this->byDay) {
-
- // If the current day is higher than the 28th, rollover can
- // occur to the next month. We Must skip these invalid
- // entries.
- if ($currentDayOfMonth < 29) {
- $this->currentDate->modify('+' . $this->interval . ' months');
- } else {
- $increase = 0;
- do {
- $increase++;
- $tempDate = clone $this->currentDate;
- $tempDate->modify('+ ' . ($this->interval*$increase) . ' months');
- } while ($tempDate->format('j') != $currentDayOfMonth);
- $this->currentDate = $tempDate;
- }
- return;
- }
-
- while(true) {
-
- $occurrences = $this->getMonthlyOccurrences();
-
- foreach($occurrences as $occurrence) {
-
- // The first occurrence thats higher than the current
- // day of the month wins.
- if ($occurrence > $currentDayOfMonth) {
- break 2;
- }
-
- }
-
- // If we made it all the way here, it means there were no
- // valid occurrences, and we need to advance to the next
- // month.
- $this->currentDate->modify('first day of this month');
- $this->currentDate->modify('+ ' . $this->interval . ' months');
-
- // This goes to 0 because we need to start counting at hte
- // beginning.
- $currentDayOfMonth = 0;
-
- }
-
- $this->currentDate->setDate($this->currentDate->format('Y'), $this->currentDate->format('n'), $occurrence);
-
- }
-
- /**
- * Does the processing for advancing the iterator for yearly frequency.
- *
- * @return void
- */
- protected function nextYearly() {
-
- $currentMonth = $this->currentDate->format('n');
- $currentYear = $this->currentDate->format('Y');
- $currentDayOfMonth = $this->currentDate->format('j');
-
- // No sub-rules, so we just advance by year
- if (!$this->byMonth) {
-
- // Unless it was a leap day!
- if ($currentMonth==2 && $currentDayOfMonth==29) {
-
- $counter = 0;
- do {
- $counter++;
- // Here we increase the year count by the interval, until
- // we hit a date that's also in a leap year.
- //
- // We could just find the next interval that's dividable by
- // 4, but that would ignore the rule that there's no leap
- // year every year that's dividable by a 100, but not by
- // 400. (1800, 1900, 2100). So we just rely on the datetime
- // functions instead.
- $nextDate = clone $this->currentDate;
- $nextDate->modify('+ ' . ($this->interval*$counter) . ' years');
- } while ($nextDate->format('n')!=2);
- $this->currentDate = $nextDate;
-
- return;
-
- }
-
- // The easiest form
- $this->currentDate->modify('+' . $this->interval . ' years');
- return;
-
- }
-
- $currentMonth = $this->currentDate->format('n');
- $currentYear = $this->currentDate->format('Y');
- $currentDayOfMonth = $this->currentDate->format('j');
-
- $advancedToNewMonth = false;
-
- // If we got a byDay or getMonthDay filter, we must first expand
- // further.
- if ($this->byDay || $this->byMonthDay) {
-
- while(true) {
-
- $occurrences = $this->getMonthlyOccurrences();
-
- foreach($occurrences as $occurrence) {
-
- // The first occurrence that's higher than the current
- // day of the month wins.
- // If we advanced to the next month or year, the first
- // occurrence is always correct.
- if ($occurrence > $currentDayOfMonth || $advancedToNewMonth) {
- break 2;
- }
-
- }
-
- // If we made it here, it means we need to advance to
- // the next month or year.
- $currentDayOfMonth = 1;
- $advancedToNewMonth = true;
- do {
-
- $currentMonth++;
- if ($currentMonth>12) {
- $currentYear+=$this->interval;
- $currentMonth = 1;
- }
- } while (!in_array($currentMonth, $this->byMonth));
-
- $this->currentDate->setDate($currentYear, $currentMonth, $currentDayOfMonth);
-
- }
-
- // If we made it here, it means we got a valid occurrence
- $this->currentDate->setDate($currentYear, $currentMonth, $occurrence);
- return;
-
- } else {
-
- // These are the 'byMonth' rules, if there are no byDay or
- // byMonthDay sub-rules.
- do {
-
- $currentMonth++;
- if ($currentMonth>12) {
- $currentYear+=$this->interval;
- $currentMonth = 1;
- }
- } while (!in_array($currentMonth, $this->byMonth));
- $this->currentDate->setDate($currentYear, $currentMonth, $currentDayOfMonth);
-
- return;
-
- }
-
- }
-
- /**
- * Returns all the occurrences for a monthly frequency with a 'byDay' or
- * 'byMonthDay' expansion for the current month.
- *
- * The returned list is an array of integers with the day of month (1-31).
- *
- * @return array
- */
- protected function getMonthlyOccurrences() {
-
- $startDate = clone $this->currentDate;
-
- $byDayResults = array();
-
- // Our strategy is to simply go through the byDays, advance the date to
- // that point and add it to the results.
- if ($this->byDay) foreach($this->byDay as $day) {
-
- $dayName = $this->dayNames[$this->dayMap[substr($day,-2)]];
-
- // Dayname will be something like 'wednesday'. Now we need to find
- // all wednesdays in this month.
- $dayHits = array();
-
- $checkDate = clone $startDate;
- $checkDate->modify('first day of this month');
- $checkDate->modify($dayName);
-
- do {
- $dayHits[] = $checkDate->format('j');
- $checkDate->modify('next ' . $dayName);
- } while ($checkDate->format('n') === $startDate->format('n'));
-
- // So now we have 'all wednesdays' for month. It is however
- // possible that the user only really wanted the 1st, 2nd or last
- // wednesday.
- if (strlen($day)>2) {
- $offset = (int)substr($day,0,-2);
-
- if ($offset>0) {
- // It is possible that the day does not exist, such as a
- // 5th or 6th wednesday of the month.
- if (isset($dayHits[$offset-1])) {
- $byDayResults[] = $dayHits[$offset-1];
- }
- } else {
-
- // if it was negative we count from the end of the array
- $byDayResults[] = $dayHits[count($dayHits) + $offset];
- }
- } else {
- // There was no counter (first, second, last wednesdays), so we
- // just need to add the all to the list).
- $byDayResults = array_merge($byDayResults, $dayHits);
-
- }
-
- }
-
- $byMonthDayResults = array();
- if ($this->byMonthDay) foreach($this->byMonthDay as $monthDay) {
-
- // Removing values that are out of range for this month
- if ($monthDay > $startDate->format('t') ||
- $monthDay < 0-$startDate->format('t')) {
- continue;
- }
- if ($monthDay>0) {
- $byMonthDayResults[] = $monthDay;
- } else {
- // Negative values
- $byMonthDayResults[] = $startDate->format('t') + 1 + $monthDay;
- }
- }
-
- // If there was just byDay or just byMonthDay, they just specify our
- // (almost) final list. If both were provided, then byDay limits the
- // list.
- if ($this->byMonthDay && $this->byDay) {
- $result = array_intersect($byMonthDayResults, $byDayResults);
- } elseif ($this->byMonthDay) {
- $result = $byMonthDayResults;
- } else {
- $result = $byDayResults;
- }
- $result = array_unique($result);
- sort($result, SORT_NUMERIC);
-
- // The last thing that needs checking is the BYSETPOS. If it's set, it
- // means only certain items in the set survive the filter.
- if (!$this->bySetPos) {
- return $result;
- }
-
- $filteredResult = array();
- foreach($this->bySetPos as $setPos) {
-
- if ($setPos<0) {
- $setPos = count($result)-($setPos+1);
- }
- if (isset($result[$setPos-1])) {
- $filteredResult[] = $result[$setPos-1];
- }
- }
-
- sort($filteredResult, SORT_NUMERIC);
- return $filteredResult;
-
- }
-
- protected function getHours()
- {
- $recurrenceHours = array();
- foreach($this->byHour as $byHour) {
- $recurrenceHours[] = $byHour;
- }
-
- return $recurrenceHours;
- }
-
- protected function getDays()
- {
- $recurrenceDays = array();
- foreach($this->byDay as $byDay) {
-
- // The day may be preceeded with a positive (+n) or
- // negative (-n) integer. However, this does not make
- // sense in 'weekly' so we ignore it here.
- $recurrenceDays[] = $this->dayMap[substr($byDay,-2)];
-
- }
-
- return $recurrenceDays;
- }
-}
-
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/StringUtil.php b/vendor/sabre/vobject/lib/Sabre/VObject/StringUtil.php
deleted file mode 100644
index ea88e1e86..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/StringUtil.php
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-/**
- * Useful utilities for working with various strings.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class StringUtil {
-
- /**
- * Returns true or false depending on if a string is valid UTF-8
- *
- * @param string $str
- * @return bool
- */
- static function isUTF8($str) {
-
- // First check.. mb_check_encoding
- if (!mb_check_encoding($str, 'UTF-8')) {
- return false;
- }
-
- // Control characters
- if (preg_match('%(?:[\x00-\x08\x0B-\x0C\x0E\x0F])%', $str)) {
- return false;
- }
-
- return true;
-
- }
-
- /**
- * This method tries its best to convert the input string to UTF-8.
- *
- * Currently only ISO-5991-1 input and UTF-8 input is supported, but this
- * may be expanded upon if we receive other examples.
- *
- * @param string $str
- * @return string
- */
- static function convertToUTF8($str) {
-
- $encoding = mb_detect_encoding($str , array('UTF-8','ISO-8859-1'), true);
-
- if ($encoding === 'ISO-8859-1') {
- $newStr = utf8_encode($str);
- } else {
- $newStr = $str;
- }
-
- // Removing any control characters
- return (preg_replace('%(?:[\x00-\x08\x0B-\x0C\x0E\x0F])%', '', $newStr));
-
- }
-
-}
-
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/TimeZoneUtil.php b/vendor/sabre/vobject/lib/Sabre/VObject/TimeZoneUtil.php
deleted file mode 100644
index c009a6af0..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/TimeZoneUtil.php
+++ /dev/null
@@ -1,527 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-/**
- * Time zone name translation
- *
- * This file translates well-known time zone names into "Olson database" time zone names.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Frank Edelhaeuser (fedel@users.sourceforge.net)
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class TimeZoneUtil {
-
- public static $map = array(
-
- // from http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html
- // snapshot taken on 2012/01/16
-
- // windows
- 'AUS Central Standard Time'=>'Australia/Darwin',
- 'AUS Eastern Standard Time'=>'Australia/Sydney',
- 'Afghanistan Standard Time'=>'Asia/Kabul',
- 'Alaskan Standard Time'=>'America/Anchorage',
- 'Arab Standard Time'=>'Asia/Riyadh',
- 'Arabian Standard Time'=>'Asia/Dubai',
- 'Arabic Standard Time'=>'Asia/Baghdad',
- 'Argentina Standard Time'=>'America/Buenos_Aires',
- 'Armenian Standard Time'=>'Asia/Yerevan',
- 'Atlantic Standard Time'=>'America/Halifax',
- 'Azerbaijan Standard Time'=>'Asia/Baku',
- 'Azores Standard Time'=>'Atlantic/Azores',
- 'Bangladesh Standard Time'=>'Asia/Dhaka',
- 'Canada Central Standard Time'=>'America/Regina',
- 'Cape Verde Standard Time'=>'Atlantic/Cape_Verde',
- 'Caucasus Standard Time'=>'Asia/Yerevan',
- 'Cen. Australia Standard Time'=>'Australia/Adelaide',
- 'Central America Standard Time'=>'America/Guatemala',
- 'Central Asia Standard Time'=>'Asia/Almaty',
- 'Central Brazilian Standard Time'=>'America/Cuiaba',
- 'Central Europe Standard Time'=>'Europe/Budapest',
- 'Central European Standard Time'=>'Europe/Warsaw',
- 'Central Pacific Standard Time'=>'Pacific/Guadalcanal',
- 'Central Standard Time'=>'America/Chicago',
- 'Central Standard Time (Mexico)'=>'America/Mexico_City',
- 'China Standard Time'=>'Asia/Shanghai',
- 'Dateline Standard Time'=>'Etc/GMT+12',
- 'E. Africa Standard Time'=>'Africa/Nairobi',
- 'E. Australia Standard Time'=>'Australia/Brisbane',
- 'E. Europe Standard Time'=>'Europe/Minsk',
- 'E. South America Standard Time'=>'America/Sao_Paulo',
- 'Eastern Standard Time'=>'America/New_York',
- 'Egypt Standard Time'=>'Africa/Cairo',
- 'Ekaterinburg Standard Time'=>'Asia/Yekaterinburg',
- 'FLE Standard Time'=>'Europe/Kiev',
- 'Fiji Standard Time'=>'Pacific/Fiji',
- 'GMT Standard Time'=>'Europe/London',
- 'GTB Standard Time'=>'Europe/Istanbul',
- 'Georgian Standard Time'=>'Asia/Tbilisi',
- 'Greenland Standard Time'=>'America/Godthab',
- 'Greenwich Standard Time'=>'Atlantic/Reykjavik',
- 'Hawaiian Standard Time'=>'Pacific/Honolulu',
- 'India Standard Time'=>'Asia/Calcutta',
- 'Iran Standard Time'=>'Asia/Tehran',
- 'Israel Standard Time'=>'Asia/Jerusalem',
- 'Jordan Standard Time'=>'Asia/Amman',
- 'Kamchatka Standard Time'=>'Asia/Kamchatka',
- 'Korea Standard Time'=>'Asia/Seoul',
- 'Magadan Standard Time'=>'Asia/Magadan',
- 'Mauritius Standard Time'=>'Indian/Mauritius',
- 'Mexico Standard Time'=>'America/Mexico_City',
- 'Mexico Standard Time 2'=>'America/Chihuahua',
- 'Mid-Atlantic Standard Time'=>'Etc/GMT-2',
- 'Middle East Standard Time'=>'Asia/Beirut',
- 'Montevideo Standard Time'=>'America/Montevideo',
- 'Morocco Standard Time'=>'Africa/Casablanca',
- 'Mountain Standard Time'=>'America/Denver',
- 'Mountain Standard Time (Mexico)'=>'America/Chihuahua',
- 'Myanmar Standard Time'=>'Asia/Rangoon',
- 'N. Central Asia Standard Time'=>'Asia/Novosibirsk',
- 'Namibia Standard Time'=>'Africa/Windhoek',
- 'Nepal Standard Time'=>'Asia/Katmandu',
- 'New Zealand Standard Time'=>'Pacific/Auckland',
- 'Newfoundland Standard Time'=>'America/St_Johns',
- 'North Asia East Standard Time'=>'Asia/Irkutsk',
- 'North Asia Standard Time'=>'Asia/Krasnoyarsk',
- 'Pacific SA Standard Time'=>'America/Santiago',
- 'Pacific Standard Time'=>'America/Los_Angeles',
- 'Pacific Standard Time (Mexico)'=>'America/Santa_Isabel',
- 'Pakistan Standard Time'=>'Asia/Karachi',
- 'Paraguay Standard Time'=>'America/Asuncion',
- 'Romance Standard Time'=>'Europe/Paris',
- 'Russian Standard Time'=>'Europe/Moscow',
- 'SA Eastern Standard Time'=>'America/Cayenne',
- 'SA Pacific Standard Time'=>'America/Bogota',
- 'SA Western Standard Time'=>'America/La_Paz',
- 'SE Asia Standard Time'=>'Asia/Bangkok',
- 'Samoa Standard Time'=>'Pacific/Apia',
- 'Singapore Standard Time'=>'Asia/Singapore',
- 'South Africa Standard Time'=>'Africa/Johannesburg',
- 'Sri Lanka Standard Time'=>'Asia/Colombo',
- 'Syria Standard Time'=>'Asia/Damascus',
- 'Taipei Standard Time'=>'Asia/Taipei',
- 'Tasmania Standard Time'=>'Australia/Hobart',
- 'Tokyo Standard Time'=>'Asia/Tokyo',
- 'Tonga Standard Time'=>'Pacific/Tongatapu',
- 'US Eastern Standard Time'=>'America/Indianapolis',
- 'US Mountain Standard Time'=>'America/Phoenix',
- 'UTC+12'=>'Etc/GMT-12',
- 'UTC-02'=>'Etc/GMT+2',
- 'UTC-11'=>'Etc/GMT+11',
- 'Ulaanbaatar Standard Time'=>'Asia/Ulaanbaatar',
- 'Venezuela Standard Time'=>'America/Caracas',
- 'Vladivostok Standard Time'=>'Asia/Vladivostok',
- 'W. Australia Standard Time'=>'Australia/Perth',
- 'W. Central Africa Standard Time'=>'Africa/Lagos',
- 'W. Europe Standard Time'=>'Europe/Berlin',
- 'West Asia Standard Time'=>'Asia/Tashkent',
- 'West Pacific Standard Time'=>'Pacific/Port_Moresby',
- 'Yakutsk Standard Time'=>'Asia/Yakutsk',
-
- // Microsoft exchange timezones
- // Source:
- // http://msdn.microsoft.com/en-us/library/ms988620%28v=exchg.65%29.aspx
- //
- // Correct timezones deduced with help from:
- // http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
- 'Universal Coordinated Time' => 'UTC',
- 'Casablanca, Monrovia' => 'Africa/Casablanca',
- 'Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London' => 'Europe/Lisbon',
- 'Greenwich Mean Time; Dublin, Edinburgh, London' => 'Europe/London',
- 'Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' => 'Europe/Berlin',
- 'Belgrade, Pozsony, Budapest, Ljubljana, Prague' => 'Europe/Prague',
- 'Brussels, Copenhagen, Madrid, Paris' => 'Europe/Paris',
- 'Paris, Madrid, Brussels, Copenhagen' => 'Europe/Paris',
- 'Prague, Central Europe' => 'Europe/Prague',
- 'Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb' => 'Europe/Sarajevo',
- 'West Central Africa' => 'Africa/Luanda', // This was a best guess
- 'Athens, Istanbul, Minsk' => 'Europe/Athens',
- 'Bucharest' => 'Europe/Bucharest',
- 'Cairo' => 'Africa/Cairo',
- 'Harare, Pretoria' => 'Africa/Harare',
- 'Helsinki, Riga, Tallinn' => 'Europe/Helsinki',
- 'Israel, Jerusalem Standard Time' => 'Asia/Jerusalem',
- 'Baghdad' => 'Asia/Baghdad',
- 'Arab, Kuwait, Riyadh' => 'Asia/Kuwait',
- 'Moscow, St. Petersburg, Volgograd' => 'Europe/Moscow',
- 'East Africa, Nairobi' => 'Africa/Nairobi',
- 'Tehran' => 'Asia/Tehran',
- 'Abu Dhabi, Muscat' => 'Asia/Muscat', // Best guess
- 'Baku, Tbilisi, Yerevan' => 'Asia/Baku',
- 'Kabul' => 'Asia/Kabul',
- 'Ekaterinburg' => 'Asia/Yekaterinburg',
- 'Islamabad, Karachi, Tashkent' => 'Asia/Karachi',
- 'Kolkata, Chennai, Mumbai, New Delhi, India Standard Time' => 'Asia/Calcutta',
- 'Kathmandu, Nepal' => 'Asia/Kathmandu',
- 'Almaty, Novosibirsk, North Central Asia' => 'Asia/Almaty',
- 'Astana, Dhaka' => 'Asia/Dhaka',
- 'Sri Jayawardenepura, Sri Lanka' => 'Asia/Colombo',
- 'Rangoon' => 'Asia/Rangoon',
- 'Bangkok, Hanoi, Jakarta' => 'Asia/Bangkok',
- 'Krasnoyarsk' => 'Asia/Krasnoyarsk',
- 'Beijing, Chongqing, Hong Kong SAR, Urumqi' => 'Asia/Shanghai',
- 'Irkutsk, Ulaan Bataar' => 'Asia/Irkutsk',
- 'Kuala Lumpur, Singapore' => 'Asia/Singapore',
- 'Perth, Western Australia' => 'Australia/Perth',
- 'Taipei' => 'Asia/Taipei',
- 'Osaka, Sapporo, Tokyo' => 'Asia/Tokyo',
- 'Seoul, Korea Standard time' => 'Asia/Seoul',
- 'Yakutsk' => 'Asia/Yakutsk',
- 'Adelaide, Central Australia' => 'Australia/Adelaide',
- 'Darwin' => 'Australia/Darwin',
- 'Brisbane, East Australia' => 'Australia/Brisbane',
- 'Canberra, Melbourne, Sydney, Hobart (year 2000 only)' => 'Australia/Sydney',
- 'Guam, Port Moresby' => 'Pacific/Guam',
- 'Hobart, Tasmania' => 'Australia/Hobart',
- 'Vladivostok' => 'Asia/Vladivostok',
- 'Magadan, Solomon Is., New Caledonia' => 'Asia/Magadan',
- 'Auckland, Wellington' => 'Pacific/Auckland',
- 'Fiji Islands, Kamchatka, Marshall Is.' => 'Pacific/Fiji',
- 'Nuku\'alofa, Tonga' => 'Pacific/Tongatapu',
- 'Azores' => 'Atlantic/Azores',
- 'Cape Verde Is.' => 'Atlantic/Cape_Verde',
- 'Mid-Atlantic' => 'America/Noronha',
- 'Brasilia' => 'America/Sao_Paulo', // Best guess
- 'Buenos Aires' => 'America/Argentina/Buenos_Aires',
- 'Greenland' => 'America/Godthab',
- 'Newfoundland' => 'America/St_Johns',
- 'Atlantic Time (Canada)' => 'America/Halifax',
- 'Caracas, La Paz' => 'America/Caracas',
- 'Santiago' => 'America/Santiago',
- 'Bogota, Lima, Quito' => 'America/Bogota',
- 'Eastern Time (US & Canada)' => 'America/New_York',
- 'Indiana (East)' => 'America/Indiana/Indianapolis',
- 'Central America' => 'America/Guatemala',
- 'Central Time (US & Canada)' => 'America/Chicago',
- 'Mexico City, Tegucigalpa' => 'America/Mexico_City',
- 'Saskatchewan' => 'America/Edmonton',
- 'Arizona' => 'America/Phoenix',
- 'Mountain Time (US & Canada)' => 'America/Denver', // Best guess
- 'Pacific Time (US & Canada); Tijuana' => 'America/Los_Angeles', // Best guess
- 'Alaska' => 'America/Anchorage',
- 'Hawaii' => 'Pacific/Honolulu',
- 'Midway Island, Samoa' => 'Pacific/Midway',
- 'Eniwetok, Kwajalein, Dateline Time' => 'Pacific/Kwajalein',
-
- // The following list are timezone names that could be generated by
- // Lotus / Domino
- 'Dateline' => 'Etc/GMT-12',
- 'Samoa' => 'Pacific/Apia',
- 'Hawaiian' => 'Pacific/Honolulu',
- 'Alaskan' => 'America/Anchorage',
- 'Pacific' => 'America/Los_Angeles',
- 'Pacific Standard Time' => 'America/Los_Angeles',
- 'Mexico Standard Time 2' => 'America/Chihuahua',
- 'Mountain' => 'America/Denver',
- 'Mountain Standard Time' => 'America/Chihuahua',
- 'US Mountain' => 'America/Phoenix',
- 'Canada Central' => 'America/Edmonton',
- 'Central America' => 'America/Guatemala',
- 'Central' => 'America/Chicago',
- 'Central Standard Time' => 'America/Mexico_City',
- 'Mexico' => 'America/Mexico_City',
- 'Eastern' => 'America/New_York',
- 'SA Pacific' => 'America/Bogota',
- 'US Eastern' => 'America/Indiana/Indianapolis',
- 'Venezuela' => 'America/Caracas',
- 'Atlantic' => 'America/Halifax',
- 'Central Brazilian' => 'America/Manaus',
- 'Pacific SA' => 'America/Santiago',
- 'SA Western' => 'America/La_Paz',
- 'Newfoundland' => 'America/St_Johns',
- 'Argentina' => 'America/Argentina/Buenos_Aires',
- 'E. South America' => 'America/Belem',
- 'Greenland' => 'America/Godthab',
- 'Montevideo' => 'America/Montevideo',
- 'SA Eastern' => 'America/Belem',
- 'Mid-Atlantic' => 'Etc/GMT-2',
- 'Azores' => 'Atlantic/Azores',
- 'Cape Verde' => 'Atlantic/Cape_Verde',
- 'Greenwich' => 'Atlantic/Reykjavik', // No I'm serious.. Greenwich is not GMT.
- 'Morocco' => 'Africa/Casablanca',
- 'Central Europe' => 'Europe/Prague',
- 'Central European' => 'Europe/Sarajevo',
- 'Romance' => 'Europe/Paris',
- 'W. Central Africa' => 'Africa/Lagos', // Best guess
- 'W. Europe' => 'Europe/Amsterdam',
- 'E. Europe' => 'Europe/Minsk',
- 'Egypt' => 'Africa/Cairo',
- 'FLE' => 'Europe/Helsinki',
- 'GTB' => 'Europe/Athens',
- 'Israel' => 'Asia/Jerusalem',
- 'Jordan' => 'Asia/Amman',
- 'Middle East' => 'Asia/Beirut',
- 'Namibia' => 'Africa/Windhoek',
- 'South Africa' => 'Africa/Harare',
- 'Arab' => 'Asia/Kuwait',
- 'Arabic' => 'Asia/Baghdad',
- 'E. Africa' => 'Africa/Nairobi',
- 'Georgian' => 'Asia/Tbilisi',
- 'Russian' => 'Europe/Moscow',
- 'Iran' => 'Asia/Tehran',
- 'Arabian' => 'Asia/Muscat',
- 'Armenian' => 'Asia/Yerevan',
- 'Azerbijan' => 'Asia/Baku',
- 'Caucasus' => 'Asia/Yerevan',
- 'Mauritius' => 'Indian/Mauritius',
- 'Afghanistan' => 'Asia/Kabul',
- 'Ekaterinburg' => 'Asia/Yekaterinburg',
- 'Pakistan' => 'Asia/Karachi',
- 'West Asia' => 'Asia/Tashkent',
- 'India' => 'Asia/Calcutta',
- 'Sri Lanka' => 'Asia/Colombo',
- 'Nepal' => 'Asia/Kathmandu',
- 'Central Asia' => 'Asia/Dhaka',
- 'N. Central Asia' => 'Asia/Almaty',
- 'Myanmar' => 'Asia/Rangoon',
- 'North Asia' => 'Asia/Krasnoyarsk',
- 'SE Asia' => 'Asia/Bangkok',
- 'China' => 'Asia/Shanghai',
- 'North Asia East' => 'Asia/Irkutsk',
- 'Singapore' => 'Asia/Singapore',
- 'Taipei' => 'Asia/Taipei',
- 'W. Australia' => 'Australia/Perth',
- 'Korea' => 'Asia/Seoul',
- 'Tokyo' => 'Asia/Tokyo',
- 'Yakutsk' => 'Asia/Yakutsk',
- 'AUS Central' => 'Australia/Darwin',
- 'Cen. Australia' => 'Australia/Adelaide',
- 'AUS Eastern' => 'Australia/Sydney',
- 'E. Australia' => 'Australia/Brisbane',
- 'Tasmania' => 'Australia/Hobart',
- 'Vladivostok' => 'Asia/Vladivostok',
- 'West Pacific' => 'Pacific/Guam',
- 'Central Pacific' => 'Asia/Magadan',
- 'Fiji' => 'Pacific/Fiji',
- 'New Zealand' => 'Pacific/Auckland',
- 'Tonga' => 'Pacific/Tongatapu',
-
- // PHP 5.5.10 failed on a few timezones that were valid before. We're
- // normalizing them here.
- 'CST6CDT' => 'America/Chicago',
- 'Cuba' => 'America/Havana',
- 'Egypt' => 'Africa/Cairo',
- 'Eire' => 'Europe/Dublin',
- 'EST5EDT' => 'America/New_York',
- 'Factory' => 'UTC',
- 'GB-Eire' => 'Europe/London',
- 'GMT0' => 'UTC',
- 'Greenwich' => 'UTC',
- 'Hongkong' => 'Asia/Hong_Kong',
- 'Iceland' => 'Atlantic/Reykjavik',
- 'Iran' => 'Asia/Tehran',
- 'Israel' => 'Asia/Jerusalem',
- 'Jamaica' => 'America/Jamaica',
- 'Japan' => 'Asia/Tokyo',
- 'Kwajalein' => 'Pacific/Kwajalein',
- 'Libya' => 'Africa/Tripoli',
- 'MST7MDT' => 'America/Denver',
- 'Navajo' => 'America/Denver',
- 'NZ-CHAT' => 'Pacific/Chatham',
- 'Poland' => 'Europe/Warsaw',
- 'Portugal' => 'Europe/Lisbon',
- 'PST8PDT' => 'America/Los_Angeles',
- 'Singapore' => 'Asia/Singapore',
- 'Turkey' => 'Europe/Istanbul',
- 'Universal' => 'UTC',
- 'W-SU' => 'Europe/Moscow',
- );
-
- /**
- * List of microsoft exchange timezone ids.
- *
- * Source: http://msdn.microsoft.com/en-us/library/aa563018(loband).aspx
- */
- public static $microsoftExchangeMap = array(
- 0 => 'UTC',
- 31 => 'Africa/Casablanca',
-
- // Insanely, id #2 is used for both Europe/Lisbon, and Europe/Sarajevo.
- // I'm not even kidding.. We handle this special case in the
- // getTimeZone method.
- 2 => 'Europe/Lisbon',
- 1 => 'Europe/London',
- 4 => 'Europe/Berlin',
- 6 => 'Europe/Prague',
- 3 => 'Europe/Paris',
- 69 => 'Africa/Luanda', // This was a best guess
- 7 => 'Europe/Athens',
- 5 => 'Europe/Bucharest',
- 49 => 'Africa/Cairo',
- 50 => 'Africa/Harare',
- 59 => 'Europe/Helsinki',
- 27 => 'Asia/Jerusalem',
- 26 => 'Asia/Baghdad',
- 74 => 'Asia/Kuwait',
- 51 => 'Europe/Moscow',
- 56 => 'Africa/Nairobi',
- 25 => 'Asia/Tehran',
- 24 => 'Asia/Muscat', // Best guess
- 54 => 'Asia/Baku',
- 48 => 'Asia/Kabul',
- 58 => 'Asia/Yekaterinburg',
- 47 => 'Asia/Karachi',
- 23 => 'Asia/Calcutta',
- 62 => 'Asia/Kathmandu',
- 46 => 'Asia/Almaty',
- 71 => 'Asia/Dhaka',
- 66 => 'Asia/Colombo',
- 61 => 'Asia/Rangoon',
- 22 => 'Asia/Bangkok',
- 64 => 'Asia/Krasnoyarsk',
- 45 => 'Asia/Shanghai',
- 63 => 'Asia/Irkutsk',
- 21 => 'Asia/Singapore',
- 73 => 'Australia/Perth',
- 75 => 'Asia/Taipei',
- 20 => 'Asia/Tokyo',
- 72 => 'Asia/Seoul',
- 70 => 'Asia/Yakutsk',
- 19 => 'Australia/Adelaide',
- 44 => 'Australia/Darwin',
- 18 => 'Australia/Brisbane',
- 76 => 'Australia/Sydney',
- 43 => 'Pacific/Guam',
- 42 => 'Australia/Hobart',
- 68 => 'Asia/Vladivostok',
- 41 => 'Asia/Magadan',
- 17 => 'Pacific/Auckland',
- 40 => 'Pacific/Fiji',
- 67 => 'Pacific/Tongatapu',
- 29 => 'Atlantic/Azores',
- 53 => 'Atlantic/Cape_Verde',
- 30 => 'America/Noronha',
- 8 => 'America/Sao_Paulo', // Best guess
- 32 => 'America/Argentina/Buenos_Aires',
- 60 => 'America/Godthab',
- 28 => 'America/St_Johns',
- 9 => 'America/Halifax',
- 33 => 'America/Caracas',
- 65 => 'America/Santiago',
- 35 => 'America/Bogota',
- 10 => 'America/New_York',
- 34 => 'America/Indiana/Indianapolis',
- 55 => 'America/Guatemala',
- 11 => 'America/Chicago',
- 37 => 'America/Mexico_City',
- 36 => 'America/Edmonton',
- 38 => 'America/Phoenix',
- 12 => 'America/Denver', // Best guess
- 13 => 'America/Los_Angeles', // Best guess
- 14 => 'America/Anchorage',
- 15 => 'Pacific/Honolulu',
- 16 => 'Pacific/Midway',
- 39 => 'Pacific/Kwajalein',
- );
-
- /**
- * This method will try to find out the correct timezone for an iCalendar
- * date-time value.
- *
- * You must pass the contents of the TZID parameter, as well as the full
- * calendar.
- *
- * If the lookup fails, this method will return the default PHP timezone
- * (as configured using date_default_timezone_set, or the date.timezone ini
- * setting).
- *
- * Alternatively, if $failIfUncertain is set to true, it will throw an
- * exception if we cannot accurately determine the timezone.
- *
- * @param string $tzid
- * @param Sabre\VObject\Component $vcalendar
- * @return DateTimeZone
- */
- static public function getTimeZone($tzid, Component $vcalendar = null, $failIfUncertain = false) {
-
- // First we will just see if the tzid is a support timezone identifier.
- //
- // The only exception is if the timezone starts with (. This is to
- // handle cases where certain microsoft products generate timezone
- // identifiers that for instance look like:
- //
- // (GMT+01.00) Sarajevo/Warsaw/Zagreb
- //
- // Since PHP 5.5.10, the first bit will be used as the timezone and
- // this method will return just GMT+01:00. This is wrong, because it
- // doesn't take DST into account.
- if ($tzid[0]!=='(') {
- try {
- return new \DateTimeZone($tzid);
- } catch (\Exception $e) {
- }
- }
-
- // Next, we check if the tzid is somewhere in our tzid map.
- if (isset(self::$map[$tzid])) {
- return new \DateTimeZone(self::$map[$tzid]);
- }
-
- // Maybe the author was hyper-lazy and just included an offset. We
- // support it, but we aren't happy about it.
- //
- // Note that the path in the source will never be taken from PHP 5.5.10
- // onwards. PHP 5.5.10 supports the "GMT+0100" style of format, so it
- // already gets returned early in this function. Once we drop support
- // for versions under PHP 5.5.10, this bit can be taken out of the
- // source.
- if (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) {
- return new \DateTimeZone('Etc/GMT' . $matches[1] . ltrim(substr($matches[2],0,2),'0'));
- }
-
- if ($vcalendar) {
-
- // If that didn't work, we will scan VTIMEZONE objects
- foreach($vcalendar->select('VTIMEZONE') as $vtimezone) {
-
- if ((string)$vtimezone->TZID === $tzid) {
-
- // Some clients add 'X-LIC-LOCATION' with the olson name.
- if (isset($vtimezone->{'X-LIC-LOCATION'})) {
-
- $lic = (string)$vtimezone->{'X-LIC-LOCATION'};
-
- // Libical generators may specify strings like
- // "SystemV/EST5EDT". For those we must remove the
- // SystemV part.
- if (substr($lic,0,8)==='SystemV/') {
- $lic = substr($lic,8);
- }
-
- return self::getTimeZone($lic, null, $failIfUncertain);
-
- }
- // Microsoft may add a magic number, which we also have an
- // answer for.
- if (isset($vtimezone->{'X-MICROSOFT-CDO-TZID'})) {
- $cdoId = (int)$vtimezone->{'X-MICROSOFT-CDO-TZID'}->value;
-
- // 2 can mean both Europe/Lisbon and Europe/Sarajevo.
- if ($cdoId===2 && strpos((string)$vtimezone->TZID, 'Sarajevo')!==false) {
- return new \DateTimeZone('Europe/Sarajevo');
- }
-
- if (isset(self::$microsoftExchangeMap[$cdoId])) {
- return new \DateTimeZone(self::$microsoftExchangeMap[$cdoId]);
- }
- }
-
- }
-
- }
-
- }
-
- if ($failIfUncertain) {
- throw new \InvalidArgumentException('We were unable to determine the correct PHP timezone for tzid: ' . $tzid);
- }
-
- // If we got all the way here, we default to UTC.
- return new \DateTimeZone(date_default_timezone_get());
-
- }
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Version.php b/vendor/sabre/vobject/lib/Sabre/VObject/Version.php
deleted file mode 100644
index 5b04d0b7a..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Version.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-/**
- * This class contains the version number for the VObject package
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-class Version {
-
- /**
- * Full version number
- */
- const VERSION = '2.1.4';
-
- /**
- * Stability : alpha, beta, stable
- */
- const STABILITY = 'stable';
-
-}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/includes.php b/vendor/sabre/vobject/lib/Sabre/VObject/includes.php
deleted file mode 100644
index d15329a6a..000000000
--- a/vendor/sabre/vobject/lib/Sabre/VObject/includes.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-/**
- * Includes file
- *
- * This file includes the entire VObject library in one go.
- * The benefit is that an autoloader is not needed, which is often faster.
- *
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Evert Pot (http://evertpot.com/)
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
- */
-
-// Begin includes
-include __DIR__ . '/DateTimeParser.php';
-include __DIR__ . '/ElementList.php';
-include __DIR__ . '/FreeBusyGenerator.php';
-include __DIR__ . '/Node.php';
-include __DIR__ . '/Parameter.php';
-include __DIR__ . '/ParseException.php';
-include __DIR__ . '/Property.php';
-include __DIR__ . '/Reader.php';
-include __DIR__ . '/RecurrenceIterator.php';
-include __DIR__ . '/Splitter/SplitterInterface.php';
-include __DIR__ . '/StringUtil.php';
-include __DIR__ . '/TimeZoneUtil.php';
-include __DIR__ . '/Version.php';
-include __DIR__ . '/Splitter/VCard.php';
-include __DIR__ . '/Component.php';
-include __DIR__ . '/Document.php';
-include __DIR__ . '/Property/Compound.php';
-include __DIR__ . '/Property/DateTime.php';
-include __DIR__ . '/Property/MultiDateTime.php';
-include __DIR__ . '/Splitter/ICalendar.php';
-include __DIR__ . '/Component/VAlarm.php';
-include __DIR__ . '/Component/VCalendar.php';
-include __DIR__ . '/Component/VEvent.php';
-include __DIR__ . '/Component/VFreeBusy.php';
-include __DIR__ . '/Component/VJournal.php';
-include __DIR__ . '/Component/VTodo.php';
-// End includes
diff --git a/vendor/sabre/vobject/lib/Settings.php b/vendor/sabre/vobject/lib/Settings.php
new file mode 100644
index 000000000..3f274ba8e
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Settings.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * This class provides a list of global defaults for vobject.
+ *
+ * Some of these started to appear in various classes, so it made a bit more
+ * sense to centralize them, so it's easier for user to find and change these.
+ *
+ * The global nature of them does mean that changing the settings for one
+ * instance has a global influence.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Settings {
+
+ /**
+ * The minimum date we accept for various calculations with dates, such as
+ * recurrences.
+ *
+ * The choice of 1900 is pretty arbitrary, but it covers most common
+ * use-cases. In particular, it covers birthdates for virtually everyone
+ * alive on earth, which is less than 5 people at the time of writing.
+ */
+ static $minDate = '1900-01-01';
+
+ /**
+ * The maximum date we accept for various calculations with dates, such as
+ * recurrences.
+ *
+ * The choice of 2100 is pretty arbitrary, but should cover most
+ * appointments made for many years to come.
+ */
+ static $maxDate = '2100-01-01';
+
+ /**
+ * The maximum number of recurrences that will be generated.
+ *
+ * This setting limits the maximum of recurring events that this library
+ * generates in its recurrence iterators.
+ *
+ * This is a security measure. Without this, it would be possible to craft
+ * specific events that recur many, many times, potentially DDOSing the
+ * server.
+ *
+ * The default (3500) allows creation of a dialy event that goes on for 10
+ * years, which is hopefully long enough for most.
+ *
+ * Set this value to -1 to disable this control altogether.
+ */
+ static $maxRecurrences = 3500;
+
+}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Splitter/ICalendar.php b/vendor/sabre/vobject/lib/Splitter/ICalendar.php
index 657cfb810..c0007ba01 100644
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Splitter/ICalendar.php
+++ b/vendor/sabre/vobject/lib/Splitter/ICalendar.php
@@ -3,9 +3,10 @@
namespace Sabre\VObject\Splitter;
use Sabre\VObject;
+use Sabre\VObject\Component\VCalendar;
/**
- * Splitter
+ * Splitter.
*
* This class is responsible for splitting up iCalendar objects.
*
@@ -13,41 +14,44 @@ use Sabre\VObject;
* calendar-objects inside. Objects with identical UID's will be combined into
* a single object.
*
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Dominik Tobschall
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Dominik Tobschall (http://tobschall.de/)
* @author Armin Hackmann
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ * @license http://sabre.io/license/ Modified BSD License
*/
class ICalendar implements SplitterInterface {
/**
- * Timezones
+ * Timezones.
*
* @var array
*/
- protected $vtimezones = array();
+ protected $vtimezones = [];
/**
- * iCalendar objects
+ * iCalendar objects.
*
* @var array
*/
- protected $objects = array();
+ protected $objects = [];
/**
- * Constructor
+ * Constructor.
*
* The splitter should receive an readable file stream as it's input.
*
* @param resource $input
+ * @param int $options Parser options, see the OPTIONS constants.
*/
- public function __construct($input) {
+ function __construct($input, $options = 0) {
- $data = VObject\Reader::read(stream_get_contents($input));
- $vtimezones = array();
- $components = array();
+ $data = VObject\Reader::read($input, $options);
- foreach($data->children as $component) {
+ if (!$data instanceof VObject\Component\VCalendar) {
+ throw new VObject\ParseException('Supplied input could not be parsed as VCALENDAR.');
+ }
+
+ foreach ($data->children() as $component) {
if (!$component instanceof VObject\Component) {
continue;
}
@@ -59,16 +63,14 @@ class ICalendar implements SplitterInterface {
}
// Get component UID for recurring Events search
- if($component->UID) {
- $uid = (string)$component->UID;
- } else {
- // Generating a random UID
- $uid = sha1(microtime()) . '-vobjectimport';
+ if (!$component->UID) {
+ $component->UID = sha1(microtime()) . '-vobjectimport';
}
+ $uid = (string)$component->UID;
// Take care of recurring events
if (!array_key_exists($uid, $this->objects)) {
- $this->objects[$uid] = VObject\Component::create('VCALENDAR');
+ $this->objects[$uid] = new VCalendar();
}
$this->objects[$uid]->add(clone $component);
@@ -84,9 +86,9 @@ class ICalendar implements SplitterInterface {
*
* @return Sabre\VObject\Component|null
*/
- public function getNext() {
+ function getNext() {
- if($object=array_shift($this->objects)) {
+ if ($object = array_shift($this->objects)) {
// create our baseobject
$object->version = '2.0';
@@ -102,10 +104,10 @@ class ICalendar implements SplitterInterface {
} else {
- return null;
+ return;
}
- }
+ }
}
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Splitter/SplitterInterface.php b/vendor/sabre/vobject/lib/Splitter/SplitterInterface.php
index c0126883a..8f827cc4b 100644
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Splitter/SplitterInterface.php
+++ b/vendor/sabre/vobject/lib/Splitter/SplitterInterface.php
@@ -3,7 +3,7 @@
namespace Sabre\VObject\Splitter;
/**
- * VObject splitter
+ * VObject splitter.
*
* The splitter is responsible for reading a large vCard or iCalendar object,
* and splitting it into multiple objects.
@@ -11,14 +11,14 @@ namespace Sabre\VObject\Splitter;
* This is for example for Card and CalDAV, which require every event and vcard
* to exist in their own objects, instead of one large one.
*
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Dominik Tobschall
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Dominik Tobschall (http://tobschall.de/)
+ * @license http://sabre.io/license/ Modified BSD License
*/
interface SplitterInterface {
/**
- * Constructor
+ * Constructor.
*
* The splitter should receive an readable file stream as it's input.
*
diff --git a/vendor/sabre/vobject/lib/Sabre/VObject/Splitter/VCard.php b/vendor/sabre/vobject/lib/Splitter/VCard.php
index 7a8718c00..0bb82abe9 100644
--- a/vendor/sabre/vobject/lib/Sabre/VObject/Splitter/VCard.php
+++ b/vendor/sabre/vobject/lib/Splitter/VCard.php
@@ -3,9 +3,10 @@
namespace Sabre\VObject\Splitter;
use Sabre\VObject;
+use Sabre\VObject\Parser\MimeDir;
/**
- * Splitter
+ * Splitter.
*
* This class is responsible for splitting up VCard objects.
*
@@ -13,30 +14,39 @@ use Sabre\VObject;
* class checks for BEGIN:VCARD and END:VCARD and parses each encountered
* component individually.
*
- * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
- * @author Dominik Tobschall
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Dominik Tobschall (http://tobschall.de/)
* @author Armin Hackmann
- * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ * @license http://sabre.io/license/ Modified BSD License
*/
class VCard implements SplitterInterface {
/**
- * File handle
+ * File handle.
*
* @var resource
*/
protected $input;
/**
- * Constructor
+ * Persistent parser.
+ *
+ * @var MimeDir
+ */
+ protected $parser;
+
+ /**
+ * Constructor.
*
* The splitter should receive an readable file stream as it's input.
*
* @param resource $input
+ * @param int $options Parser options, see the OPTIONS constants.
*/
- public function __construct($input) {
+ function __construct($input, $options = 0) {
$this->input = $input;
+ $this->parser = new MimeDir($input, $options);
}
@@ -48,25 +58,17 @@ class VCard implements SplitterInterface {
*
* @return Sabre\VObject\Component|null
*/
- public function getNext() {
+ function getNext() {
- $vcard = '';
+ try {
+ $object = $this->parser->parse();
- do {
-
- if (feof($this->input)) {
- return false;
+ if (!$object instanceof VObject\Component\VCard) {
+ throw new VObject\ParseException('The supplied input contained non-VCARD data.');
}
- $line = fgets($this->input);
- $vcard .= $line;
-
- } while(strtoupper(substr($line,0,4))!=="END:");
-
- $object = VObject\Reader::read($vcard);
-
- if($object->name !== 'VCARD') {
- throw new \InvalidArgumentException("Thats no vCard!", 1);
+ } catch (VObject\EofException $e) {
+ return;
}
return $object;
diff --git a/vendor/sabre/vobject/lib/StringUtil.php b/vendor/sabre/vobject/lib/StringUtil.php
new file mode 100644
index 000000000..b8615f2ba
--- /dev/null
+++ b/vendor/sabre/vobject/lib/StringUtil.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * Useful utilities for working with various strings.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class StringUtil {
+
+ /**
+ * Returns true or false depending on if a string is valid UTF-8.
+ *
+ * @param string $str
+ *
+ * @return bool
+ */
+ static function isUTF8($str) {
+
+ // Control characters
+ if (preg_match('%[\x00-\x08\x0B-\x0C\x0E\x0F]%', $str)) {
+ return false;
+ }
+
+ return (bool)preg_match('%%u', $str);
+
+ }
+
+ /**
+ * This method tries its best to convert the input string to UTF-8.
+ *
+ * Currently only ISO-5991-1 input and UTF-8 input is supported, but this
+ * may be expanded upon if we receive other examples.
+ *
+ * @param string $str
+ *
+ * @return string
+ */
+ static function convertToUTF8($str) {
+
+ $encoding = mb_detect_encoding($str, ['UTF-8', 'ISO-8859-1', 'WINDOWS-1252'], true);
+
+ switch ($encoding) {
+ case 'ISO-8859-1' :
+ $newStr = utf8_encode($str);
+ break;
+ /* Unreachable code. Not sure yet how we can improve this
+ * situation.
+ case 'WINDOWS-1252' :
+ $newStr = iconv('cp1252', 'UTF-8', $str);
+ break;
+ */
+ default :
+ $newStr = $str;
+
+ }
+
+ // Removing any control characters
+ return (preg_replace('%(?:[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F])%', '', $newStr));
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/TimeZoneUtil.php b/vendor/sabre/vobject/lib/TimeZoneUtil.php
new file mode 100644
index 000000000..4873daf92
--- /dev/null
+++ b/vendor/sabre/vobject/lib/TimeZoneUtil.php
@@ -0,0 +1,266 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * Time zone name translation.
+ *
+ * This file translates well-known time zone names into "Olson database" time zone names.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Frank Edelhaeuser (fedel@users.sourceforge.net)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class TimeZoneUtil {
+
+ static $map = null;
+
+ /**
+ * List of microsoft exchange timezone ids.
+ *
+ * Source: http://msdn.microsoft.com/en-us/library/aa563018(loband).aspx
+ */
+ static $microsoftExchangeMap = [
+ 0 => 'UTC',
+ 31 => 'Africa/Casablanca',
+
+ // Insanely, id #2 is used for both Europe/Lisbon, and Europe/Sarajevo.
+ // I'm not even kidding.. We handle this special case in the
+ // getTimeZone method.
+ 2 => 'Europe/Lisbon',
+ 1 => 'Europe/London',
+ 4 => 'Europe/Berlin',
+ 6 => 'Europe/Prague',
+ 3 => 'Europe/Paris',
+ 69 => 'Africa/Luanda', // This was a best guess
+ 7 => 'Europe/Athens',
+ 5 => 'Europe/Bucharest',
+ 49 => 'Africa/Cairo',
+ 50 => 'Africa/Harare',
+ 59 => 'Europe/Helsinki',
+ 27 => 'Asia/Jerusalem',
+ 26 => 'Asia/Baghdad',
+ 74 => 'Asia/Kuwait',
+ 51 => 'Europe/Moscow',
+ 56 => 'Africa/Nairobi',
+ 25 => 'Asia/Tehran',
+ 24 => 'Asia/Muscat', // Best guess
+ 54 => 'Asia/Baku',
+ 48 => 'Asia/Kabul',
+ 58 => 'Asia/Yekaterinburg',
+ 47 => 'Asia/Karachi',
+ 23 => 'Asia/Calcutta',
+ 62 => 'Asia/Kathmandu',
+ 46 => 'Asia/Almaty',
+ 71 => 'Asia/Dhaka',
+ 66 => 'Asia/Colombo',
+ 61 => 'Asia/Rangoon',
+ 22 => 'Asia/Bangkok',
+ 64 => 'Asia/Krasnoyarsk',
+ 45 => 'Asia/Shanghai',
+ 63 => 'Asia/Irkutsk',
+ 21 => 'Asia/Singapore',
+ 73 => 'Australia/Perth',
+ 75 => 'Asia/Taipei',
+ 20 => 'Asia/Tokyo',
+ 72 => 'Asia/Seoul',
+ 70 => 'Asia/Yakutsk',
+ 19 => 'Australia/Adelaide',
+ 44 => 'Australia/Darwin',
+ 18 => 'Australia/Brisbane',
+ 76 => 'Australia/Sydney',
+ 43 => 'Pacific/Guam',
+ 42 => 'Australia/Hobart',
+ 68 => 'Asia/Vladivostok',
+ 41 => 'Asia/Magadan',
+ 17 => 'Pacific/Auckland',
+ 40 => 'Pacific/Fiji',
+ 67 => 'Pacific/Tongatapu',
+ 29 => 'Atlantic/Azores',
+ 53 => 'Atlantic/Cape_Verde',
+ 30 => 'America/Noronha',
+ 8 => 'America/Sao_Paulo', // Best guess
+ 32 => 'America/Argentina/Buenos_Aires',
+ 60 => 'America/Godthab',
+ 28 => 'America/St_Johns',
+ 9 => 'America/Halifax',
+ 33 => 'America/Caracas',
+ 65 => 'America/Santiago',
+ 35 => 'America/Bogota',
+ 10 => 'America/New_York',
+ 34 => 'America/Indiana/Indianapolis',
+ 55 => 'America/Guatemala',
+ 11 => 'America/Chicago',
+ 37 => 'America/Mexico_City',
+ 36 => 'America/Edmonton',
+ 38 => 'America/Phoenix',
+ 12 => 'America/Denver', // Best guess
+ 13 => 'America/Los_Angeles', // Best guess
+ 14 => 'America/Anchorage',
+ 15 => 'Pacific/Honolulu',
+ 16 => 'Pacific/Midway',
+ 39 => 'Pacific/Kwajalein',
+ ];
+
+ /**
+ * This method will try to find out the correct timezone for an iCalendar
+ * date-time value.
+ *
+ * You must pass the contents of the TZID parameter, as well as the full
+ * calendar.
+ *
+ * If the lookup fails, this method will return the default PHP timezone
+ * (as configured using date_default_timezone_set, or the date.timezone ini
+ * setting).
+ *
+ * Alternatively, if $failIfUncertain is set to true, it will throw an
+ * exception if we cannot accurately determine the timezone.
+ *
+ * @param string $tzid
+ * @param Sabre\VObject\Component $vcalendar
+ *
+ * @return DateTimeZone
+ */
+ static function getTimeZone($tzid, Component $vcalendar = null, $failIfUncertain = false) {
+
+ // First we will just see if the tzid is a support timezone identifier.
+ //
+ // The only exception is if the timezone starts with (. This is to
+ // handle cases where certain microsoft products generate timezone
+ // identifiers that for instance look like:
+ //
+ // (GMT+01.00) Sarajevo/Warsaw/Zagreb
+ //
+ // Since PHP 5.5.10, the first bit will be used as the timezone and
+ // this method will return just GMT+01:00. This is wrong, because it
+ // doesn't take DST into account.
+ if ($tzid[0] !== '(') {
+
+ // PHP has a bug that logs PHP warnings even it shouldn't:
+ // https://bugs.php.net/bug.php?id=67881
+ //
+ // That's why we're checking if we'll be able to successfull instantiate
+ // \DateTimeZone() before doing so. Otherwise we could simply instantiate
+ // and catch the exception.
+ $tzIdentifiers = \DateTimeZone::listIdentifiers();
+
+ try {
+ if (
+ (in_array($tzid, $tzIdentifiers)) ||
+ (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) ||
+ (in_array($tzid, self::getIdentifiersBC()))
+ ) {
+ return new \DateTimeZone($tzid);
+ }
+ } catch (\Exception $e) {
+ }
+
+ }
+
+ self::loadTzMaps();
+
+ // Next, we check if the tzid is somewhere in our tzid map.
+ if (isset(self::$map[$tzid])) {
+ return new \DateTimeZone(self::$map[$tzid]);
+ }
+
+ // Maybe the author was hyper-lazy and just included an offset. We
+ // support it, but we aren't happy about it.
+ if (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) {
+
+ // Note that the path in the source will never be taken from PHP 5.5.10
+ // onwards. PHP 5.5.10 supports the "GMT+0100" style of format, so it
+ // already gets returned early in this function. Once we drop support
+ // for versions under PHP 5.5.10, this bit can be taken out of the
+ // source.
+ // @codeCoverageIgnoreStart
+ return new \DateTimeZone('Etc/GMT' . $matches[1] . ltrim(substr($matches[2], 0, 2), '0'));
+ // @codeCoverageIgnoreEnd
+ }
+
+ if ($vcalendar) {
+
+ // If that didn't work, we will scan VTIMEZONE objects
+ foreach ($vcalendar->select('VTIMEZONE') as $vtimezone) {
+
+ if ((string)$vtimezone->TZID === $tzid) {
+
+ // Some clients add 'X-LIC-LOCATION' with the olson name.
+ if (isset($vtimezone->{'X-LIC-LOCATION'})) {
+
+ $lic = (string)$vtimezone->{'X-LIC-LOCATION'};
+
+ // Libical generators may specify strings like
+ // "SystemV/EST5EDT". For those we must remove the
+ // SystemV part.
+ if (substr($lic, 0, 8) === 'SystemV/') {
+ $lic = substr($lic, 8);
+ }
+
+ return self::getTimeZone($lic, null, $failIfUncertain);
+
+ }
+ // Microsoft may add a magic number, which we also have an
+ // answer for.
+ if (isset($vtimezone->{'X-MICROSOFT-CDO-TZID'})) {
+ $cdoId = (int)$vtimezone->{'X-MICROSOFT-CDO-TZID'}->getValue();
+
+ // 2 can mean both Europe/Lisbon and Europe/Sarajevo.
+ if ($cdoId === 2 && strpos((string)$vtimezone->TZID, 'Sarajevo') !== false) {
+ return new \DateTimeZone('Europe/Sarajevo');
+ }
+
+ if (isset(self::$microsoftExchangeMap[$cdoId])) {
+ return new \DateTimeZone(self::$microsoftExchangeMap[$cdoId]);
+ }
+ }
+
+ }
+
+ }
+
+ }
+
+ if ($failIfUncertain) {
+ throw new \InvalidArgumentException('We were unable to determine the correct PHP timezone for tzid: ' . $tzid);
+ }
+
+ // If we got all the way here, we default to UTC.
+ return new \DateTimeZone(date_default_timezone_get());
+
+ }
+
+ /**
+ * This method will load in all the tz mapping information, if it's not yet
+ * done.
+ */
+ static function loadTzMaps() {
+
+ if (!is_null(self::$map)) return;
+
+ self::$map = array_merge(
+ include __DIR__ . '/timezonedata/windowszones.php',
+ include __DIR__ . '/timezonedata/lotuszones.php',
+ include __DIR__ . '/timezonedata/exchangezones.php',
+ include __DIR__ . '/timezonedata/php-workaround.php'
+ );
+
+ }
+
+ /**
+ * This method returns an array of timezone identifiers, that are supported
+ * by DateTimeZone(), but not returned by DateTimeZone::listIdentifiers().
+ *
+ * We're not using DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) because:
+ * - It's not supported by some PHP versions as well as HHVM.
+ * - It also returns identifiers, that are invalid values for new DateTimeZone() on some PHP versions.
+ * (See timezonedata/php-bc.php and timezonedata php-workaround.php)
+ *
+ * @return array
+ */
+ static function getIdentifiersBC() {
+ return include __DIR__ . '/timezonedata/php-bc.php';
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/UUIDUtil.php b/vendor/sabre/vobject/lib/UUIDUtil.php
new file mode 100644
index 000000000..24ebe3cf8
--- /dev/null
+++ b/vendor/sabre/vobject/lib/UUIDUtil.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * UUID Utility.
+ *
+ * This class has static methods to generate and validate UUID's.
+ * UUIDs are used a decent amount within various *DAV standards, so it made
+ * sense to include it.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class UUIDUtil {
+
+ /**
+ * Returns a pseudo-random v4 UUID.
+ *
+ * This function is based on a comment by Andrew Moore on php.net
+ *
+ * @see http://www.php.net/manual/en/function.uniqid.php#94959
+ *
+ * @return string
+ */
+ static function getUUID() {
+
+ return sprintf(
+
+ '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
+
+ // 32 bits for "time_low"
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff),
+
+ // 16 bits for "time_mid"
+ mt_rand(0, 0xffff),
+
+ // 16 bits for "time_hi_and_version",
+ // four most significant bits holds version number 4
+ mt_rand(0, 0x0fff) | 0x4000,
+
+ // 16 bits, 8 bits for "clk_seq_hi_res",
+ // 8 bits for "clk_seq_low",
+ // two most significant bits holds zero and one for variant DCE1.1
+ mt_rand(0, 0x3fff) | 0x8000,
+
+ // 48 bits for "node"
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
+ );
+ }
+
+ /**
+ * Checks if a string is a valid UUID.
+ *
+ * @param string $uuid
+ *
+ * @return bool
+ */
+ static function validateUUID($uuid) {
+
+ return preg_match(
+ '/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i',
+ $uuid
+ ) !== 0;
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/VCardConverter.php b/vendor/sabre/vobject/lib/VCardConverter.php
new file mode 100644
index 000000000..1f6d016f1
--- /dev/null
+++ b/vendor/sabre/vobject/lib/VCardConverter.php
@@ -0,0 +1,467 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * This utility converts vcards from one version to another.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VCardConverter {
+
+ /**
+ * Converts a vCard object to a new version.
+ *
+ * targetVersion must be one of:
+ * Document::VCARD21
+ * Document::VCARD30
+ * Document::VCARD40
+ *
+ * Currently only 3.0 and 4.0 as input and output versions.
+ *
+ * 2.1 has some minor support for the input version, it's incomplete at the
+ * moment though.
+ *
+ * If input and output version are identical, a clone is returned.
+ *
+ * @param Component\VCard $input
+ * @param int $targetVersion
+ */
+ function convert(Component\VCard $input, $targetVersion) {
+
+ $inputVersion = $input->getDocumentType();
+ if ($inputVersion === $targetVersion) {
+ return clone $input;
+ }
+
+ if (!in_array($inputVersion, [Document::VCARD21, Document::VCARD30, Document::VCARD40])) {
+ throw new \InvalidArgumentException('Only vCard 2.1, 3.0 and 4.0 are supported for the input data');
+ }
+ if (!in_array($targetVersion, [Document::VCARD30, Document::VCARD40])) {
+ throw new \InvalidArgumentException('You can only use vCard 3.0 or 4.0 for the target version');
+ }
+
+ $newVersion = $targetVersion === Document::VCARD40 ? '4.0' : '3.0';
+
+ $output = new Component\VCard([
+ 'VERSION' => $newVersion,
+ ]);
+
+ // We might have generated a default UID. Remove it!
+ unset($output->UID);
+
+ foreach ($input->children() as $property) {
+
+ $this->convertProperty($input, $output, $property, $targetVersion);
+
+ }
+
+ return $output;
+
+ }
+
+ /**
+ * Handles conversion of a single property.
+ *
+ * @param Component\VCard $input
+ * @param Component\VCard $output
+ * @param Property $property
+ * @param int $targetVersion
+ *
+ * @return void
+ */
+ protected function convertProperty(Component\VCard $input, Component\VCard $output, Property $property, $targetVersion) {
+
+ // Skipping these, those are automatically added.
+ if (in_array($property->name, ['VERSION', 'PRODID'])) {
+ return;
+ }
+
+ $parameters = $property->parameters();
+ $valueType = null;
+ if (isset($parameters['VALUE'])) {
+ $valueType = $parameters['VALUE']->getValue();
+ unset($parameters['VALUE']);
+ }
+ if (!$valueType) {
+ $valueType = $property->getValueType();
+ }
+ $newProperty = $output->createProperty(
+ $property->name,
+ $property->getParts(),
+ [], // parameters will get added a bit later.
+ $valueType
+ );
+
+
+ if ($targetVersion === Document::VCARD30) {
+
+ if ($property instanceof Property\Uri && in_array($property->name, ['PHOTO', 'LOGO', 'SOUND'])) {
+
+ $newProperty = $this->convertUriToBinary($output, $newProperty);
+
+ } elseif ($property instanceof Property\VCard\DateAndOrTime) {
+
+ // In vCard 4, the birth year may be optional. This is not the
+ // case for vCard 3. Apple has a workaround for this that
+ // allows applications that support Apple's extension still
+ // omit birthyears in vCard 3, but applications that do not
+ // support this, will just use a random birthyear. We're
+ // choosing 1604 for the birthyear, because that's what apple
+ // uses.
+ $parts = DateTimeParser::parseVCardDateTime($property->getValue());
+ if (is_null($parts['year'])) {
+ $newValue = '1604-' . $parts['month'] . '-' . $parts['date'];
+ $newProperty->setValue($newValue);
+ $newProperty['X-APPLE-OMIT-YEAR'] = '1604';
+ }
+
+ if ($newProperty->name == 'ANNIVERSARY') {
+ // Microsoft non-standard anniversary
+ $newProperty->name = 'X-ANNIVERSARY';
+
+ // We also need to add a new apple property for the same
+ // purpose. This apple property needs a 'label' in the same
+ // group, so we first need to find a groupname that doesn't
+ // exist yet.
+ $x = 1;
+ while ($output->select('ITEM' . $x . '.')) {
+ $x++;
+ }
+ $output->add('ITEM' . $x . '.X-ABDATE', $newProperty->getValue(), ['VALUE' => 'DATE-AND-OR-TIME']);
+ $output->add('ITEM' . $x . '.X-ABLABEL', '_$!<Anniversary>!$_');
+ }
+
+ } elseif ($property->name === 'KIND') {
+
+ switch (strtolower($property->getValue())) {
+ case 'org' :
+ // vCard 3.0 does not have an equivalent to KIND:ORG,
+ // but apple has an extension that means the same
+ // thing.
+ $newProperty = $output->createProperty('X-ABSHOWAS', 'COMPANY');
+ break;
+
+ case 'individual' :
+ // Individual is implicit, so we skip it.
+ return;
+
+ case 'group' :
+ // OS X addressbook property
+ $newProperty = $output->createProperty('X-ADDRESSBOOKSERVER-KIND', 'GROUP');
+ break;
+ }
+
+
+ }
+
+ } elseif ($targetVersion === Document::VCARD40) {
+
+ // These properties were removed in vCard 4.0
+ if (in_array($property->name, ['NAME', 'MAILER', 'LABEL', 'CLASS'])) {
+ return;
+ }
+
+ if ($property instanceof Property\Binary) {
+
+ $newProperty = $this->convertBinaryToUri($output, $newProperty, $parameters);
+
+ } elseif ($property instanceof Property\VCard\DateAndOrTime && isset($parameters['X-APPLE-OMIT-YEAR'])) {
+
+ // If a property such as BDAY contained 'X-APPLE-OMIT-YEAR',
+ // then we're stripping the year from the vcard 4 value.
+ $parts = DateTimeParser::parseVCardDateTime($property->getValue());
+ if ($parts['year'] === $property['X-APPLE-OMIT-YEAR']->getValue()) {
+ $newValue = '--' . $parts['month'] . '-' . $parts['date'];
+ $newProperty->setValue($newValue);
+ }
+
+ // Regardless if the year matched or not, we do need to strip
+ // X-APPLE-OMIT-YEAR.
+ unset($parameters['X-APPLE-OMIT-YEAR']);
+
+ }
+ switch ($property->name) {
+ case 'X-ABSHOWAS' :
+ if (strtoupper($property->getValue()) === 'COMPANY') {
+ $newProperty = $output->createProperty('KIND', 'ORG');
+ }
+ break;
+ case 'X-ADDRESSBOOKSERVER-KIND' :
+ if (strtoupper($property->getValue()) === 'GROUP') {
+ $newProperty = $output->createProperty('KIND', 'GROUP');
+ }
+ break;
+ case 'X-ANNIVERSARY' :
+ $newProperty->name = 'ANNIVERSARY';
+ // If we already have an anniversary property with the same
+ // value, ignore.
+ foreach ($output->select('ANNIVERSARY') as $anniversary) {
+ if ($anniversary->getValue() === $newProperty->getValue()) {
+ return;
+ }
+ }
+ break;
+ case 'X-ABDATE' :
+ // Find out what the label was, if it exists.
+ if (!$property->group) {
+ break;
+ }
+ $label = $input->{$property->group . '.X-ABLABEL'};
+
+ // We only support converting anniversaries.
+ if (!$label || $label->getValue() !== '_$!<Anniversary>!$_') {
+ break;
+ }
+
+ // If we already have an anniversary property with the same
+ // value, ignore.
+ foreach ($output->select('ANNIVERSARY') as $anniversary) {
+ if ($anniversary->getValue() === $newProperty->getValue()) {
+ return;
+ }
+ }
+ $newProperty->name = 'ANNIVERSARY';
+ break;
+ // Apple's per-property label system.
+ case 'X-ABLABEL' :
+ if ($newProperty->getValue() === '_$!<Anniversary>!$_') {
+ // We can safely remove these, as they are converted to
+ // ANNIVERSARY properties.
+ return;
+ }
+ break;
+
+ }
+
+ }
+
+ // set property group
+ $newProperty->group = $property->group;
+
+ if ($targetVersion === Document::VCARD40) {
+ $this->convertParameters40($newProperty, $parameters);
+ } else {
+ $this->convertParameters30($newProperty, $parameters);
+ }
+
+ // Lastly, we need to see if there's a need for a VALUE parameter.
+ //
+ // We can do that by instantating a empty property with that name, and
+ // seeing if the default valueType is identical to the current one.
+ $tempProperty = $output->createProperty($newProperty->name);
+ if ($tempProperty->getValueType() !== $newProperty->getValueType()) {
+ $newProperty['VALUE'] = $newProperty->getValueType();
+ }
+
+ $output->add($newProperty);
+
+
+ }
+
+ /**
+ * Converts a BINARY property to a URI property.
+ *
+ * vCard 4.0 no longer supports BINARY properties.
+ *
+ * @param Component\VCard $output
+ * @param Property\Uri $property The input property.
+ * @param $parameters List of parameters that will eventually be added to
+ * the new property.
+ *
+ * @return Property\Uri
+ */
+ protected function convertBinaryToUri(Component\VCard $output, Property\Binary $newProperty, array &$parameters) {
+
+ $value = $newProperty->getValue();
+ $newProperty = $output->createProperty(
+ $newProperty->name,
+ null, // no value
+ [], // no parameters yet
+ 'URI' // Forcing the BINARY type
+ );
+
+ $mimeType = 'application/octet-stream';
+
+ // See if we can find a better mimetype.
+ if (isset($parameters['TYPE'])) {
+
+ $newTypes = [];
+ foreach ($parameters['TYPE']->getParts() as $typePart) {
+ if (in_array(
+ strtoupper($typePart),
+ ['JPEG', 'PNG', 'GIF']
+ )) {
+ $mimeType = 'image/' . strtolower($typePart);
+ } else {
+ $newTypes[] = $typePart;
+ }
+ }
+
+ // If there were any parameters we're not converting to a
+ // mime-type, we need to keep them.
+ if ($newTypes) {
+ $parameters['TYPE']->setParts($newTypes);
+ } else {
+ unset($parameters['TYPE']);
+ }
+
+ }
+
+ $newProperty->setValue('data:' . $mimeType . ';base64,' . base64_encode($value));
+ return $newProperty;
+
+ }
+
+ /**
+ * Converts a URI property to a BINARY property.
+ *
+ * In vCard 4.0 attachments are encoded as data: uri. Even though these may
+ * be valid in vCard 3.0 as well, we should convert those to BINARY if
+ * possible, to improve compatibility.
+ *
+ * @param Component\VCard $output
+ * @param Property\Uri $property The input property.
+ *
+ * @return Property\Binary|null
+ */
+ protected function convertUriToBinary(Component\VCard $output, Property\Uri $newProperty) {
+
+ $value = $newProperty->getValue();
+
+ // Only converting data: uris
+ if (substr($value, 0, 5) !== 'data:') {
+ return $newProperty;
+ }
+
+ $newProperty = $output->createProperty(
+ $newProperty->name,
+ null, // no value
+ [], // no parameters yet
+ 'BINARY'
+ );
+
+ $mimeType = substr($value, 5, strpos($value, ',') - 5);
+ if (strpos($mimeType, ';')) {
+ $mimeType = substr($mimeType, 0, strpos($mimeType, ';'));
+ $newProperty->setValue(base64_decode(substr($value, strpos($value, ',') + 1)));
+ } else {
+ $newProperty->setValue(substr($value, strpos($value, ',') + 1));
+ }
+ unset($value);
+
+ $newProperty['ENCODING'] = 'b';
+ switch ($mimeType) {
+
+ case 'image/jpeg' :
+ $newProperty['TYPE'] = 'JPEG';
+ break;
+ case 'image/png' :
+ $newProperty['TYPE'] = 'PNG';
+ break;
+ case 'image/gif' :
+ $newProperty['TYPE'] = 'GIF';
+ break;
+
+ }
+
+
+ return $newProperty;
+
+ }
+
+ /**
+ * Adds parameters to a new property for vCard 4.0.
+ *
+ * @param Property $newProperty
+ * @param array $parameters
+ *
+ * @return void
+ */
+ protected function convertParameters40(Property $newProperty, array $parameters) {
+
+ // Adding all parameters.
+ foreach ($parameters as $param) {
+
+ // vCard 2.1 allowed parameters with no name
+ if ($param->noName) $param->noName = false;
+
+ switch ($param->name) {
+
+ // We need to see if there's any TYPE=PREF, because in vCard 4
+ // that's now PREF=1.
+ case 'TYPE' :
+ foreach ($param->getParts() as $paramPart) {
+
+ if (strtoupper($paramPart) === 'PREF') {
+ $newProperty->add('PREF', '1');
+ } else {
+ $newProperty->add($param->name, $paramPart);
+ }
+
+ }
+ break;
+ // These no longer exist in vCard 4
+ case 'ENCODING' :
+ case 'CHARSET' :
+ break;
+
+ default :
+ $newProperty->add($param->name, $param->getParts());
+ break;
+
+ }
+
+ }
+
+ }
+
+ /**
+ * Adds parameters to a new property for vCard 3.0.
+ *
+ * @param Property $newProperty
+ * @param array $parameters
+ *
+ * @return void
+ */
+ protected function convertParameters30(Property $newProperty, array $parameters) {
+
+ // Adding all parameters.
+ foreach ($parameters as $param) {
+
+ // vCard 2.1 allowed parameters with no name
+ if ($param->noName) $param->noName = false;
+
+ switch ($param->name) {
+
+ case 'ENCODING' :
+ // This value only existed in vCard 2.1, and should be
+ // removed for anything else.
+ if (strtoupper($param->getValue()) !== 'QUOTED-PRINTABLE') {
+ $newProperty->add($param->name, $param->getParts());
+ }
+ break;
+
+ /*
+ * Converting PREF=1 to TYPE=PREF.
+ *
+ * Any other PREF numbers we'll drop.
+ */
+ case 'PREF' :
+ if ($param->getValue() == '1') {
+ $newProperty->add('TYPE', 'PREF');
+ }
+ break;
+
+ default :
+ $newProperty->add($param->name, $param->getParts());
+ break;
+
+ }
+
+ }
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Version.php b/vendor/sabre/vobject/lib/Version.php
new file mode 100644
index 000000000..0b0e16c03
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Version.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * This class contains the version number for the VObject package.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Version {
+
+ /**
+ * Full version number.
+ */
+ const VERSION = '4.1.0';
+
+}
diff --git a/vendor/sabre/vobject/lib/Writer.php b/vendor/sabre/vobject/lib/Writer.php
new file mode 100644
index 000000000..f8a58758d
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Writer.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Sabre\VObject;
+
+use Sabre\Xml;
+
+/**
+ * iCalendar/vCard/jCal/jCard/xCal/xCard writer object.
+ *
+ * This object provides a few (static) convenience methods to quickly access
+ * the serializers.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Ivan Enderlin
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Writer {
+
+ /**
+ * Serializes a vCard or iCalendar object.
+ *
+ * @param Component $component
+ *
+ * @return string
+ */
+ static function write(Component $component) {
+
+ return $component->serialize();
+
+ }
+
+ /**
+ * Serializes a jCal or jCard object.
+ *
+ * @param Component $component
+ * @param int $options
+ *
+ * @return string
+ */
+ static function writeJson(Component $component, $options = 0) {
+
+ return json_encode($component, $options);
+
+ }
+
+ /**
+ * Serializes a xCal or xCard object.
+ *
+ * @param Component $component
+ *
+ * @return string
+ */
+ static function writeXml(Component $component) {
+
+ $writer = new Xml\Writer();
+ $writer->openMemory();
+ $writer->setIndent(true);
+
+ $writer->startDocument('1.0', 'utf-8');
+
+ if ($component instanceof Component\VCalendar) {
+
+ $writer->startElement('icalendar');
+ $writer->writeAttribute('xmlns', Parser\Xml::XCAL_NAMESPACE);
+
+ } else {
+
+ $writer->startElement('vcards');
+ $writer->writeAttribute('xmlns', Parser\Xml::XCARD_NAMESPACE);
+
+ }
+
+ $component->xmlSerialize($writer);
+
+ $writer->endElement();
+
+ return $writer->outputMemory();
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/timezonedata/exchangezones.php b/vendor/sabre/vobject/lib/timezonedata/exchangezones.php
new file mode 100644
index 000000000..38138354a
--- /dev/null
+++ b/vendor/sabre/vobject/lib/timezonedata/exchangezones.php
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * Microsoft exchange timezones
+ * Source:
+ * http://msdn.microsoft.com/en-us/library/ms988620%28v=exchg.65%29.aspx.
+ *
+ * Correct timezones deduced with help from:
+ * http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+return [
+ 'Universal Coordinated Time' => 'UTC',
+ 'Casablanca, Monrovia' => 'Africa/Casablanca',
+ 'Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London' => 'Europe/Lisbon',
+ 'Greenwich Mean Time; Dublin, Edinburgh, London' => 'Europe/London',
+ 'Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' => 'Europe/Berlin',
+ 'Belgrade, Pozsony, Budapest, Ljubljana, Prague' => 'Europe/Prague',
+ 'Brussels, Copenhagen, Madrid, Paris' => 'Europe/Paris',
+ 'Paris, Madrid, Brussels, Copenhagen' => 'Europe/Paris',
+ 'Prague, Central Europe' => 'Europe/Prague',
+ 'Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb' => 'Europe/Sarajevo',
+ 'West Central Africa' => 'Africa/Luanda', // This was a best guess
+ 'Athens, Istanbul, Minsk' => 'Europe/Athens',
+ 'Bucharest' => 'Europe/Bucharest',
+ 'Cairo' => 'Africa/Cairo',
+ 'Harare, Pretoria' => 'Africa/Harare',
+ 'Helsinki, Riga, Tallinn' => 'Europe/Helsinki',
+ 'Israel, Jerusalem Standard Time' => 'Asia/Jerusalem',
+ 'Baghdad' => 'Asia/Baghdad',
+ 'Arab, Kuwait, Riyadh' => 'Asia/Kuwait',
+ 'Moscow, St. Petersburg, Volgograd' => 'Europe/Moscow',
+ 'East Africa, Nairobi' => 'Africa/Nairobi',
+ 'Tehran' => 'Asia/Tehran',
+ 'Abu Dhabi, Muscat' => 'Asia/Muscat', // Best guess
+ 'Baku, Tbilisi, Yerevan' => 'Asia/Baku',
+ 'Kabul' => 'Asia/Kabul',
+ 'Ekaterinburg' => 'Asia/Yekaterinburg',
+ 'Islamabad, Karachi, Tashkent' => 'Asia/Karachi',
+ 'Kolkata, Chennai, Mumbai, New Delhi, India Standard Time' => 'Asia/Calcutta',
+ 'Kathmandu, Nepal' => 'Asia/Kathmandu',
+ 'Almaty, Novosibirsk, North Central Asia' => 'Asia/Almaty',
+ 'Astana, Dhaka' => 'Asia/Dhaka',
+ 'Sri Jayawardenepura, Sri Lanka' => 'Asia/Colombo',
+ 'Rangoon' => 'Asia/Rangoon',
+ 'Bangkok, Hanoi, Jakarta' => 'Asia/Bangkok',
+ 'Krasnoyarsk' => 'Asia/Krasnoyarsk',
+ 'Beijing, Chongqing, Hong Kong SAR, Urumqi' => 'Asia/Shanghai',
+ 'Irkutsk, Ulaan Bataar' => 'Asia/Irkutsk',
+ 'Kuala Lumpur, Singapore' => 'Asia/Singapore',
+ 'Perth, Western Australia' => 'Australia/Perth',
+ 'Taipei' => 'Asia/Taipei',
+ 'Osaka, Sapporo, Tokyo' => 'Asia/Tokyo',
+ 'Seoul, Korea Standard time' => 'Asia/Seoul',
+ 'Yakutsk' => 'Asia/Yakutsk',
+ 'Adelaide, Central Australia' => 'Australia/Adelaide',
+ 'Darwin' => 'Australia/Darwin',
+ 'Brisbane, East Australia' => 'Australia/Brisbane',
+ 'Canberra, Melbourne, Sydney, Hobart (year 2000 only)' => 'Australia/Sydney',
+ 'Guam, Port Moresby' => 'Pacific/Guam',
+ 'Hobart, Tasmania' => 'Australia/Hobart',
+ 'Vladivostok' => 'Asia/Vladivostok',
+ 'Magadan, Solomon Is., New Caledonia' => 'Asia/Magadan',
+ 'Auckland, Wellington' => 'Pacific/Auckland',
+ 'Fiji Islands, Kamchatka, Marshall Is.' => 'Pacific/Fiji',
+ 'Nuku\'alofa, Tonga' => 'Pacific/Tongatapu',
+ 'Azores' => 'Atlantic/Azores',
+ 'Cape Verde Is.' => 'Atlantic/Cape_Verde',
+ 'Mid-Atlantic' => 'America/Noronha',
+ 'Brasilia' => 'America/Sao_Paulo', // Best guess
+ 'Buenos Aires' => 'America/Argentina/Buenos_Aires',
+ 'Greenland' => 'America/Godthab',
+ 'Newfoundland' => 'America/St_Johns',
+ 'Atlantic Time (Canada)' => 'America/Halifax',
+ 'Caracas, La Paz' => 'America/Caracas',
+ 'Santiago' => 'America/Santiago',
+ 'Bogota, Lima, Quito' => 'America/Bogota',
+ 'Eastern Time (US & Canada)' => 'America/New_York',
+ 'Indiana (East)' => 'America/Indiana/Indianapolis',
+ 'Central America' => 'America/Guatemala',
+ 'Central Time (US & Canada)' => 'America/Chicago',
+ 'Mexico City, Tegucigalpa' => 'America/Mexico_City',
+ 'Saskatchewan' => 'America/Edmonton',
+ 'Arizona' => 'America/Phoenix',
+ 'Mountain Time (US & Canada)' => 'America/Denver', // Best guess
+ 'Pacific Time (US & Canada); Tijuana' => 'America/Los_Angeles', // Best guess
+ 'Alaska' => 'America/Anchorage',
+ 'Hawaii' => 'Pacific/Honolulu',
+ 'Midway Island, Samoa' => 'Pacific/Midway',
+ 'Eniwetok, Kwajalein, Dateline Time' => 'Pacific/Kwajalein',
+];
diff --git a/vendor/sabre/vobject/lib/timezonedata/lotuszones.php b/vendor/sabre/vobject/lib/timezonedata/lotuszones.php
new file mode 100644
index 000000000..7d0785f04
--- /dev/null
+++ b/vendor/sabre/vobject/lib/timezonedata/lotuszones.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * The following list are timezone names that could be generated by
+ * Lotus / Domino.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+return [
+ 'Dateline' => 'Etc/GMT-12',
+ 'Samoa' => 'Pacific/Apia',
+ 'Hawaiian' => 'Pacific/Honolulu',
+ 'Alaskan' => 'America/Anchorage',
+ 'Pacific' => 'America/Los_Angeles',
+ 'Pacific Standard Time' => 'America/Los_Angeles',
+ 'Mexico Standard Time 2' => 'America/Chihuahua',
+ 'Mountain' => 'America/Denver',
+ // 'Mountain Standard Time' => 'America/Chihuahua', // conflict with windows timezones.
+ 'US Mountain' => 'America/Phoenix',
+ 'Canada Central' => 'America/Edmonton',
+ 'Central America' => 'America/Guatemala',
+ 'Central' => 'America/Chicago',
+ // 'Central Standard Time' => 'America/Mexico_City', // conflict with windows timezones.
+ 'Mexico' => 'America/Mexico_City',
+ 'Eastern' => 'America/New_York',
+ 'SA Pacific' => 'America/Bogota',
+ 'US Eastern' => 'America/Indiana/Indianapolis',
+ 'Venezuela' => 'America/Caracas',
+ 'Atlantic' => 'America/Halifax',
+ 'Central Brazilian' => 'America/Manaus',
+ 'Pacific SA' => 'America/Santiago',
+ 'SA Western' => 'America/La_Paz',
+ 'Newfoundland' => 'America/St_Johns',
+ 'Argentina' => 'America/Argentina/Buenos_Aires',
+ 'E. South America' => 'America/Belem',
+ 'Greenland' => 'America/Godthab',
+ 'Montevideo' => 'America/Montevideo',
+ 'SA Eastern' => 'America/Belem',
+ // 'Mid-Atlantic' => 'Etc/GMT-2', // conflict with windows timezones.
+ 'Azores' => 'Atlantic/Azores',
+ 'Cape Verde' => 'Atlantic/Cape_Verde',
+ 'Greenwich' => 'Atlantic/Reykjavik', // No I'm serious.. Greenwich is not GMT.
+ 'Morocco' => 'Africa/Casablanca',
+ 'Central Europe' => 'Europe/Prague',
+ 'Central European' => 'Europe/Sarajevo',
+ 'Romance' => 'Europe/Paris',
+ 'W. Central Africa' => 'Africa/Lagos', // Best guess
+ 'W. Europe' => 'Europe/Amsterdam',
+ 'E. Europe' => 'Europe/Minsk',
+ 'Egypt' => 'Africa/Cairo',
+ 'FLE' => 'Europe/Helsinki',
+ 'GTB' => 'Europe/Athens',
+ 'Israel' => 'Asia/Jerusalem',
+ 'Jordan' => 'Asia/Amman',
+ 'Middle East' => 'Asia/Beirut',
+ 'Namibia' => 'Africa/Windhoek',
+ 'South Africa' => 'Africa/Harare',
+ 'Arab' => 'Asia/Kuwait',
+ 'Arabic' => 'Asia/Baghdad',
+ 'E. Africa' => 'Africa/Nairobi',
+ 'Georgian' => 'Asia/Tbilisi',
+ 'Russian' => 'Europe/Moscow',
+ 'Iran' => 'Asia/Tehran',
+ 'Arabian' => 'Asia/Muscat',
+ 'Armenian' => 'Asia/Yerevan',
+ 'Azerbijan' => 'Asia/Baku',
+ 'Caucasus' => 'Asia/Yerevan',
+ 'Mauritius' => 'Indian/Mauritius',
+ 'Afghanistan' => 'Asia/Kabul',
+ 'Ekaterinburg' => 'Asia/Yekaterinburg',
+ 'Pakistan' => 'Asia/Karachi',
+ 'West Asia' => 'Asia/Tashkent',
+ 'India' => 'Asia/Calcutta',
+ 'Sri Lanka' => 'Asia/Colombo',
+ 'Nepal' => 'Asia/Kathmandu',
+ 'Central Asia' => 'Asia/Dhaka',
+ 'N. Central Asia' => 'Asia/Almaty',
+ 'Myanmar' => 'Asia/Rangoon',
+ 'North Asia' => 'Asia/Krasnoyarsk',
+ 'SE Asia' => 'Asia/Bangkok',
+ 'China' => 'Asia/Shanghai',
+ 'North Asia East' => 'Asia/Irkutsk',
+ 'Singapore' => 'Asia/Singapore',
+ 'Taipei' => 'Asia/Taipei',
+ 'W. Australia' => 'Australia/Perth',
+ 'Korea' => 'Asia/Seoul',
+ 'Tokyo' => 'Asia/Tokyo',
+ 'Yakutsk' => 'Asia/Yakutsk',
+ 'AUS Central' => 'Australia/Darwin',
+ 'Cen. Australia' => 'Australia/Adelaide',
+ 'AUS Eastern' => 'Australia/Sydney',
+ 'E. Australia' => 'Australia/Brisbane',
+ 'Tasmania' => 'Australia/Hobart',
+ 'Vladivostok' => 'Asia/Vladivostok',
+ 'West Pacific' => 'Pacific/Guam',
+ 'Central Pacific' => 'Asia/Magadan',
+ 'Fiji' => 'Pacific/Fiji',
+ 'New Zealand' => 'Pacific/Auckland',
+ 'Tonga' => 'Pacific/Tongatapu',
+];
diff --git a/vendor/sabre/vobject/lib/timezonedata/php-bc.php b/vendor/sabre/vobject/lib/timezonedata/php-bc.php
new file mode 100644
index 000000000..906ccb0e4
--- /dev/null
+++ b/vendor/sabre/vobject/lib/timezonedata/php-bc.php
@@ -0,0 +1,154 @@
+<?php
+
+/**
+ * A list of additional PHP timezones that are returned by
+ * DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC)
+ * valid for new DateTimeZone().
+ *
+ * This list does not include those timezone identifiers that we have to map to
+ * a different identifier for some PHP versions (see php-workaround.php).
+ *
+ * Instead of using DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC)
+ * directly, we use this file because DateTimeZone::ALL_WITH_BC is not properly
+ * supported by all PHP version and HHVM.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+return [
+ 'Africa/Asmera',
+ 'Africa/Timbuktu',
+ 'America/Argentina/ComodRivadavia',
+ 'America/Atka',
+ 'America/Buenos_Aires',
+ 'America/Catamarca',
+ 'America/Coral_Harbour',
+ 'America/Cordoba',
+ 'America/Ensenada',
+ 'America/Fort_Wayne',
+ 'America/Indianapolis',
+ 'America/Jujuy',
+ 'America/Knox_IN',
+ 'America/Louisville',
+ 'America/Mendoza',
+ 'America/Montreal',
+ 'America/Porto_Acre',
+ 'America/Rosario',
+ 'America/Shiprock',
+ 'America/Virgin',
+ 'Antarctica/South_Pole',
+ 'Asia/Ashkhabad',
+ 'Asia/Calcutta',
+ 'Asia/Chungking',
+ 'Asia/Dacca',
+ 'Asia/Istanbul',
+ 'Asia/Katmandu',
+ 'Asia/Macao',
+ 'Asia/Saigon',
+ 'Asia/Tel_Aviv',
+ 'Asia/Thimbu',
+ 'Asia/Ujung_Pandang',
+ 'Asia/Ulan_Bator',
+ 'Atlantic/Faeroe',
+ 'Atlantic/Jan_Mayen',
+ 'Australia/ACT',
+ 'Australia/Canberra',
+ 'Australia/LHI',
+ 'Australia/North',
+ 'Australia/NSW',
+ 'Australia/Queensland',
+ 'Australia/South',
+ 'Australia/Tasmania',
+ 'Australia/Victoria',
+ 'Australia/West',
+ 'Australia/Yancowinna',
+ 'Brazil/Acre',
+ 'Brazil/DeNoronha',
+ 'Brazil/East',
+ 'Brazil/West',
+ 'Canada/Atlantic',
+ 'Canada/Central',
+ 'Canada/East-Saskatchewan',
+ 'Canada/Eastern',
+ 'Canada/Mountain',
+ 'Canada/Newfoundland',
+ 'Canada/Pacific',
+ 'Canada/Saskatchewan',
+ 'Canada/Yukon',
+ 'CET',
+ 'Chile/Continental',
+ 'Chile/EasterIsland',
+ 'EET',
+ 'EST',
+ 'Etc/GMT',
+ 'Etc/GMT+0',
+ 'Etc/GMT+1',
+ 'Etc/GMT+10',
+ 'Etc/GMT+11',
+ 'Etc/GMT+12',
+ 'Etc/GMT+2',
+ 'Etc/GMT+3',
+ 'Etc/GMT+4',
+ 'Etc/GMT+5',
+ 'Etc/GMT+6',
+ 'Etc/GMT+7',
+ 'Etc/GMT+8',
+ 'Etc/GMT+9',
+ 'Etc/GMT-0',
+ 'Etc/GMT-1',
+ 'Etc/GMT-10',
+ 'Etc/GMT-11',
+ 'Etc/GMT-12',
+ 'Etc/GMT-13',
+ 'Etc/GMT-14',
+ 'Etc/GMT-2',
+ 'Etc/GMT-3',
+ 'Etc/GMT-4',
+ 'Etc/GMT-5',
+ 'Etc/GMT-6',
+ 'Etc/GMT-7',
+ 'Etc/GMT-8',
+ 'Etc/GMT-9',
+ 'Etc/GMT0',
+ 'Etc/Greenwich',
+ 'Etc/UCT',
+ 'Etc/Universal',
+ 'Etc/UTC',
+ 'Etc/Zulu',
+ 'Europe/Belfast',
+ 'Europe/Nicosia',
+ 'Europe/Tiraspol',
+ 'GB',
+ 'GMT',
+ 'GMT+0',
+ 'GMT-0',
+ 'HST',
+ 'MET',
+ 'Mexico/BajaNorte',
+ 'Mexico/BajaSur',
+ 'Mexico/General',
+ 'MST',
+ 'NZ',
+ 'Pacific/Ponape',
+ 'Pacific/Samoa',
+ 'Pacific/Truk',
+ 'Pacific/Yap',
+ 'PRC',
+ 'ROC',
+ 'ROK',
+ 'UCT',
+ 'US/Alaska',
+ 'US/Aleutian',
+ 'US/Arizona',
+ 'US/Central',
+ 'US/East-Indiana',
+ 'US/Eastern',
+ 'US/Hawaii',
+ 'US/Indiana-Starke',
+ 'US/Michigan',
+ 'US/Mountain',
+ 'US/Pacific',
+ 'US/Pacific-New',
+ 'US/Samoa',
+ 'WET',
+];
diff --git a/vendor/sabre/vobject/lib/timezonedata/php-workaround.php b/vendor/sabre/vobject/lib/timezonedata/php-workaround.php
new file mode 100644
index 000000000..6b9cb6ef7
--- /dev/null
+++ b/vendor/sabre/vobject/lib/timezonedata/php-workaround.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * A list of PHP timezones that were supported until 5.5.9, removed in
+ * PHP 5.5.10 and re-introduced in PHP 5.5.17.
+ *
+ * DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) returns them,
+ * but they are invalid for new DateTimeZone(). Fixed in PHP 5.5.17.
+ * https://bugs.php.net/bug.php?id=66985
+ *
+ * Some more info here:
+ * http://evertpot.com/php-5-5-10-timezone-changes/
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+return [
+ 'CST6CDT' => 'America/Chicago',
+ 'Cuba' => 'America/Havana',
+ 'Egypt' => 'Africa/Cairo',
+ 'Eire' => 'Europe/Dublin',
+ 'EST5EDT' => 'America/New_York',
+ 'Factory' => 'UTC',
+ 'GB-Eire' => 'Europe/London',
+ 'GMT0' => 'UTC',
+ 'Greenwich' => 'UTC',
+ 'Hongkong' => 'Asia/Hong_Kong',
+ 'Iceland' => 'Atlantic/Reykjavik',
+ 'Iran' => 'Asia/Tehran',
+ 'Israel' => 'Asia/Jerusalem',
+ 'Jamaica' => 'America/Jamaica',
+ 'Japan' => 'Asia/Tokyo',
+ 'Kwajalein' => 'Pacific/Kwajalein',
+ 'Libya' => 'Africa/Tripoli',
+ 'MST7MDT' => 'America/Denver',
+ 'Navajo' => 'America/Denver',
+ 'NZ-CHAT' => 'Pacific/Chatham',
+ 'Poland' => 'Europe/Warsaw',
+ 'Portugal' => 'Europe/Lisbon',
+ 'PST8PDT' => 'America/Los_Angeles',
+ 'Singapore' => 'Asia/Singapore',
+ 'Turkey' => 'Europe/Istanbul',
+ 'Universal' => 'UTC',
+ 'W-SU' => 'Europe/Moscow',
+ 'Zulu' => 'UTC',
+];
diff --git a/vendor/sabre/vobject/lib/timezonedata/windowszones.php b/vendor/sabre/vobject/lib/timezonedata/windowszones.php
new file mode 100644
index 000000000..ac69847cc
--- /dev/null
+++ b/vendor/sabre/vobject/lib/timezonedata/windowszones.php
@@ -0,0 +1,119 @@
+<?php
+
+/**
+ * Automatically generated timezone file
+ *
+ * Last update: 2015-07-27T16:56:36-04:00
+ * Source: http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+
+return [
+ 'AUS Central Standard Time' => 'Australia/Darwin',
+ 'AUS Eastern Standard Time' => 'Australia/Sydney',
+ 'Afghanistan Standard Time' => 'Asia/Kabul',
+ 'Alaskan Standard Time' => 'America/Anchorage',
+ 'Arab Standard Time' => 'Asia/Riyadh',
+ 'Arabian Standard Time' => 'Asia/Dubai',
+ 'Arabic Standard Time' => 'Asia/Baghdad',
+ 'Argentina Standard Time' => 'America/Buenos_Aires',
+ 'Atlantic Standard Time' => 'America/Halifax',
+ 'Azerbaijan Standard Time' => 'Asia/Baku',
+ 'Azores Standard Time' => 'Atlantic/Azores',
+ 'Bahia Standard Time' => 'America/Bahia',
+ 'Bangladesh Standard Time' => 'Asia/Dhaka',
+ 'Belarus Standard Time' => 'Europe/Minsk',
+ 'Canada Central Standard Time' => 'America/Regina',
+ 'Cape Verde Standard Time' => 'Atlantic/Cape_Verde',
+ 'Caucasus Standard Time' => 'Asia/Yerevan',
+ 'Cen. Australia Standard Time' => 'Australia/Adelaide',
+ 'Central America Standard Time' => 'America/Guatemala',
+ 'Central Asia Standard Time' => 'Asia/Almaty',
+ 'Central Brazilian Standard Time' => 'America/Cuiaba',
+ 'Central Europe Standard Time' => 'Europe/Budapest',
+ 'Central European Standard Time' => 'Europe/Warsaw',
+ 'Central Pacific Standard Time' => 'Pacific/Guadalcanal',
+ 'Central Standard Time' => 'America/Chicago',
+ 'Central Standard Time (Mexico)' => 'America/Mexico_City',
+ 'China Standard Time' => 'Asia/Shanghai',
+ 'Dateline Standard Time' => 'Etc/GMT+12',
+ 'E. Africa Standard Time' => 'Africa/Nairobi',
+ 'E. Australia Standard Time' => 'Australia/Brisbane',
+ 'E. South America Standard Time' => 'America/Sao_Paulo',
+ 'Eastern Standard Time' => 'America/New_York',
+ 'Eastern Standard Time (Mexico)' => 'America/Cancun',
+ 'Egypt Standard Time' => 'Africa/Cairo',
+ 'Ekaterinburg Standard Time' => 'Asia/Yekaterinburg',
+ 'FLE Standard Time' => 'Europe/Kiev',
+ 'Fiji Standard Time' => 'Pacific/Fiji',
+ 'GMT Standard Time' => 'Europe/London',
+ 'GTB Standard Time' => 'Europe/Bucharest',
+ 'Georgian Standard Time' => 'Asia/Tbilisi',
+ 'Greenland Standard Time' => 'America/Godthab',
+ 'Greenwich Standard Time' => 'Atlantic/Reykjavik',
+ 'Hawaiian Standard Time' => 'Pacific/Honolulu',
+ 'India Standard Time' => 'Asia/Calcutta',
+ 'Iran Standard Time' => 'Asia/Tehran',
+ 'Israel Standard Time' => 'Asia/Jerusalem',
+ 'Jordan Standard Time' => 'Asia/Amman',
+ 'Kaliningrad Standard Time' => 'Europe/Kaliningrad',
+ 'Korea Standard Time' => 'Asia/Seoul',
+ 'Libya Standard Time' => 'Africa/Tripoli',
+ 'Line Islands Standard Time' => 'Pacific/Kiritimati',
+ 'Magadan Standard Time' => 'Asia/Magadan',
+ 'Mauritius Standard Time' => 'Indian/Mauritius',
+ 'Middle East Standard Time' => 'Asia/Beirut',
+ 'Montevideo Standard Time' => 'America/Montevideo',
+ 'Morocco Standard Time' => 'Africa/Casablanca',
+ 'Mountain Standard Time' => 'America/Denver',
+ 'Mountain Standard Time (Mexico)' => 'America/Chihuahua',
+ 'Myanmar Standard Time' => 'Asia/Rangoon',
+ 'N. Central Asia Standard Time' => 'Asia/Novosibirsk',
+ 'Namibia Standard Time' => 'Africa/Windhoek',
+ 'Nepal Standard Time' => 'Asia/Katmandu',
+ 'New Zealand Standard Time' => 'Pacific/Auckland',
+ 'Newfoundland Standard Time' => 'America/St_Johns',
+ 'North Asia East Standard Time' => 'Asia/Irkutsk',
+ 'North Asia Standard Time' => 'Asia/Krasnoyarsk',
+ 'Pacific SA Standard Time' => 'America/Santiago',
+ 'Pacific Standard Time' => 'America/Los_Angeles',
+ 'Pacific Standard Time (Mexico)' => 'America/Santa_Isabel',
+ 'Pakistan Standard Time' => 'Asia/Karachi',
+ 'Paraguay Standard Time' => 'America/Asuncion',
+ 'Romance Standard Time' => 'Europe/Paris',
+ 'Russia Time Zone 10' => 'Asia/Srednekolymsk',
+ 'Russia Time Zone 11' => 'Asia/Kamchatka',
+ 'Russia Time Zone 3' => 'Europe/Samara',
+ 'Russian Standard Time' => 'Europe/Moscow',
+ 'SA Eastern Standard Time' => 'America/Cayenne',
+ 'SA Pacific Standard Time' => 'America/Bogota',
+ 'SA Western Standard Time' => 'America/La_Paz',
+ 'SE Asia Standard Time' => 'Asia/Bangkok',
+ 'Samoa Standard Time' => 'Pacific/Apia',
+ 'Singapore Standard Time' => 'Asia/Singapore',
+ 'South Africa Standard Time' => 'Africa/Johannesburg',
+ 'Sri Lanka Standard Time' => 'Asia/Colombo',
+ 'Syria Standard Time' => 'Asia/Damascus',
+ 'Taipei Standard Time' => 'Asia/Taipei',
+ 'Tasmania Standard Time' => 'Australia/Hobart',
+ 'Tokyo Standard Time' => 'Asia/Tokyo',
+ 'Tonga Standard Time' => 'Pacific/Tongatapu',
+ 'Turkey Standard Time' => 'Europe/Istanbul',
+ 'US Eastern Standard Time' => 'America/Indianapolis',
+ 'US Mountain Standard Time' => 'America/Phoenix',
+ 'UTC' => 'Etc/GMT',
+ 'UTC+12' => 'Etc/GMT-12',
+ 'UTC-02' => 'Etc/GMT+2',
+ 'UTC-11' => 'Etc/GMT+11',
+ 'Ulaanbaatar Standard Time' => 'Asia/Ulaanbaatar',
+ 'Venezuela Standard Time' => 'America/Caracas',
+ 'Vladivostok Standard Time' => 'Asia/Vladivostok',
+ 'W. Australia Standard Time' => 'Australia/Perth',
+ 'W. Central Africa Standard Time' => 'Africa/Lagos',
+ 'W. Europe Standard Time' => 'Europe/Berlin',
+ 'West Asia Standard Time' => 'Asia/Tashkent',
+ 'West Pacific Standard Time' => 'Pacific/Port_Moresby',
+ 'Yakutsk Standard Time' => 'Asia/Yakutsk',
+];
diff --git a/vendor/sabre/vobject/resources/schema/xcal.rng b/vendor/sabre/vobject/resources/schema/xcal.rng
new file mode 100644
index 000000000..4a51460e7
--- /dev/null
+++ b/vendor/sabre/vobject/resources/schema/xcal.rng
@@ -0,0 +1,1192 @@
+# RELAX NG Schema for iCalendar in XML
+# Extract from RFC6321.
+# Erratum 3042 applied.
+# Erratum 3050 applied.
+# Erratum 3314 applied.
+
+default namespace = "urn:ietf:params:xml:ns:icalendar-2.0"
+
+# 3.2 Property Parameters
+
+# 3.2.1 Alternate Text Representation
+
+altrepparam = element altrep {
+ value-uri
+}
+
+# 3.2.2 Common Name
+
+cnparam = element cn {
+ value-text
+}
+
+# 3.2.3 Calendar User Type
+
+cutypeparam = element cutype {
+ element text {
+ "INDIVIDUAL" |
+ "GROUP" |
+ "RESOURCE" |
+ "ROOM" |
+ "UNKNOWN"
+ }
+}
+
+# 3.2.4 Delegators
+
+delfromparam = element delegated-from {
+ value-cal-address+
+}
+
+# 3.2.5 Delegatees
+
+deltoparam = element delegated-to {
+ value-cal-address+
+}
+
+# 3.2.6 Directory Entry Reference
+
+dirparam = element dir {
+ value-uri
+}
+
+# 3.2.7 Inline Encoding
+
+encodingparam = element encoding {
+ element text {
+ "8BIT" |
+ "BASE64"
+ }
+}
+
+# 3.2.8 Format Type
+
+fmttypeparam = element fmttype {
+ value-text
+}
+
+# 3.2.9 Free/Busy Time Type
+
+fbtypeparam = element fbtype {
+ element text {
+ "FREE" |
+ "BUSY" |
+ "BUSY-UNAVAILABLE" |
+ "BUSY-TENTATIVE"
+ }
+}
+
+# 3.2.10 Language
+
+languageparam = element language {
+ value-text
+}
+
+# 3.2.11 Group or List Membership
+
+memberparam = element member {
+ value-cal-address+
+}
+
+# 3.2.12 Participation Status
+
+partstatparam = element partstat {
+ type-partstat-event |
+ type-partstat-todo |
+ type-partstat-jour
+}
+
+type-partstat-event = (
+ element text {
+ "NEEDS-ACTION" |
+ "ACCEPTED" |
+ "DECLINED" |
+ "TENTATIVE" |
+ "DELEGATED"
+ }
+)
+
+type-partstat-todo = (
+ element text {
+ "NEEDS-ACTION" |
+ "ACCEPTED" |
+ "DECLINED" |
+ "TENTATIVE" |
+ "DELEGATED" |
+ "COMPLETED" |
+ "IN-PROCESS"
+ }
+)
+
+type-partstat-jour = (
+ element text {
+ "NEEDS-ACTION" |
+ "ACCEPTED" |
+ "DECLINED"
+ }
+)
+
+# 3.2.13 Recurrence Identifier Range
+
+rangeparam = element range {
+ element text {
+ "THISANDFUTURE"
+ }
+}
+
+# 3.2.14 Alarm Trigger Relationship
+
+trigrelparam = element related {
+ element text {
+ "START" |
+ "END"
+ }
+}
+
+# 3.2.15 Relationship Type
+
+reltypeparam = element reltype {
+ element text {
+ "PARENT" |
+ "CHILD" |
+ "SIBLING"
+ }
+}
+
+# 3.2.16 Participation Role
+
+roleparam = element role {
+ element text {
+ "CHAIR" |
+ "REQ-PARTICIPANT" |
+ "OPT-PARTICIPANT" |
+ "NON-PARTICIPANT"
+ }
+}
+
+# 3.2.17 RSVP Expectation
+
+rsvpparam = element rsvp {
+ value-boolean
+}
+
+# 3.2.18 Sent By
+
+sentbyparam = element sent-by {
+ value-cal-address
+}
+
+# 3.2.19 Time Zone Identifier
+
+tzidparam = element tzid {
+ value-text
+}
+
+# 3.3 Property Value Data Types
+
+# 3.3.1 BINARY
+
+value-binary = element binary {
+ xsd:string
+}
+
+# 3.3.2 BOOLEAN
+
+value-boolean = element boolean {
+ xsd:boolean
+}
+
+# 3.3.3 CAL-ADDRESS
+
+value-cal-address = element cal-address {
+ xsd:anyURI
+}
+
+# 3.3.4 DATE
+
+pattern-date = xsd:string {
+ pattern = "\d\d\d\d-\d\d-\d\d"
+}
+
+value-date = element date {
+ pattern-date
+}
+
+# 3.3.5 DATE-TIME
+
+pattern-date-time = xsd:string {
+ pattern = "\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ?"
+}
+
+value-date-time = element date-time {
+ pattern-date-time
+}
+
+# 3.3.6 DURATION
+
+pattern-duration = xsd:string {
+ pattern = "(+|-)?P(\d+W)|(\d+D)?"
+ ~ "(T(\d+H(\d+M)?(\d+S)?)|"
+ ~ "(\d+M(\d+S)?)|"
+ ~ "(\d+S))?"
+}
+
+value-duration = element duration {
+ pattern-duration
+}
+
+# 3.3.7 FLOAT
+
+value-float = element float {
+ xsd:float
+}
+
+# 3.3.8 INTEGER
+
+value-integer = element integer {
+ xsd:integer
+}
+
+# 3.3.9 PERIOD
+
+value-period = element period {
+ element start {
+ pattern-date-time
+ },
+ (
+ element end {
+ pattern-date-time
+ } |
+ element duration {
+ pattern-duration
+ }
+ )
+}
+
+# 3.3.10 RECUR
+
+value-recur = element recur {
+ type-freq,
+ (type-until | type-count)?,
+ element interval {
+ xsd:positiveInteger
+ }?,
+ type-bysecond*,
+ type-byminute*,
+ type-byhour*,
+ type-byday*,
+ type-bymonthday*,
+ type-byyearday*,
+ type-byweekno*,
+ type-bymonth*,
+ type-bysetpos*,
+ element wkst { type-weekday }?
+}
+
+type-freq = element freq {
+ "SECONDLY" |
+ "MINUTELY" |
+ "HOURLY" |
+ "DAILY" |
+ "WEEKLY" |
+ "MONTHLY" |
+ "YEARLY"
+}
+
+type-until = element until {
+ type-date |
+ type-date-time
+}
+
+type-count = element count {
+ xsd:positiveInteger
+}
+
+type-bysecond = element bysecond {
+ xsd:nonNegativeInteger
+}
+
+type-byminute = element byminute {
+ xsd:nonNegativeInteger
+}
+
+type-byhour = element byhour {
+ xsd:nonNegativeInteger
+}
+
+type-weekday = (
+ "SU" |
+ "MO" |
+ "TU" |
+ "WE" |
+ "TH" |
+ "FR" |
+ "SA"
+)
+
+type-byday = element byday {
+ xsd:integer?,
+ type-weekday
+}
+
+type-bymonthday = element bymonthday {
+ xsd:integer
+}
+
+type-byyearday = element byyearday {
+ xsd:integer
+}
+
+type-byweekno = element byweekno {
+ xsd:integer
+}
+
+type-bymonth = element bymonth {
+ xsd:positiveInteger
+}
+
+type-bysetpos = element bysetpos {
+ xsd:integer
+}
+
+# 3.3.11 TEXT
+
+value-text = element text {
+ xsd:string
+}
+
+# 3.3.12 TIME
+
+pattern-time = xsd:string {
+ pattern = "\d\d:\d\d:\d\dZ?"
+}
+
+value-time = element time {
+ pattern-time
+}
+
+# 3.3.13 URI
+
+value-uri = element uri {
+ xsd:anyURI
+}
+
+# 3.3.14 UTC-OFFSET
+
+value-utc-offset = element utc-offset {
+ xsd:string { pattern = "(+|-)\d\d:\d\d(:\d\d)?" }
+}
+
+# UNKNOWN
+
+value-unknown = element unknown {
+ xsd:string
+}
+
+# 3.4 iCalendar Stream
+
+start = element icalendar {
+ vcalendar+
+}
+
+# 3.6 Calendar Components
+
+vcalendar = element vcalendar {
+ type-calprops,
+ type-component
+}
+
+type-calprops = element properties {
+ property-prodid &
+ property-version &
+ property-calscale? &
+ property-method?
+}
+
+type-component = element components {
+ (
+ component-vevent |
+ component-vtodo |
+ component-vjournal |
+ component-vfreebusy |
+ component-vtimezone
+ )*
+}
+
+# 3.6.1 Event Component
+
+component-vevent = element vevent {
+ type-eventprop,
+ element components {
+ component-valarm+
+ }?
+}
+
+type-eventprop = element properties {
+ property-dtstamp &
+ property-dtstart &
+ property-uid &
+
+ property-class? &
+ property-created? &
+ property-description? &
+ property-geo? &
+ property-last-mod? &
+ property-location? &
+ property-organizer? &
+ property-priority? &
+ property-seq? &
+ property-status-event? &
+ property-summary? &
+ property-transp? &
+ property-url? &
+ property-recurid? &
+
+ property-rrule? &
+
+ (property-dtend | property-duration)? &
+
+ property-attach* &
+ property-attendee* &
+ property-categories* &
+ property-comment* &
+ property-contact* &
+ property-exdate* &
+ property-rstatus* &
+ property-related* &
+ property-resources* &
+ property-rdate*
+}
+
+# 3.6.2 To-do Component
+
+component-vtodo = element vtodo {
+ type-todoprop,
+ element components {
+ component-valarm+
+ }?
+}
+
+type-todoprop = element properties {
+ property-dtstamp &
+ property-uid &
+
+ property-class? &
+ property-completed? &
+ property-created? &
+ property-description? &
+ property-geo? &
+ property-last-mod? &
+ property-location? &
+ property-organizer? &
+ property-percent? &
+ property-priority? &
+ property-recurid? &
+ property-seq? &
+ property-status-todo? &
+ property-summary? &
+ property-url? &
+
+ property-rrule? &
+
+ (
+ (property-dtstart?, property-dtend? ) |
+ (property-dtstart, property-duration)?
+ ) &
+
+ property-attach* &
+ property-attendee* &
+ property-categories* &
+ property-comment* &
+ property-contact* &
+ property-exdate* &
+ property-rstatus* &
+ property-related* &
+ property-resources* &
+ property-rdate*
+}
+
+# 3.6.3 Journal Component
+
+component-vjournal = element vjournal {
+ type-jourprop
+}
+
+type-jourprop = element properties {
+ property-dtstamp &
+ property-uid &
+
+ property-class? &
+ property-created? &
+ property-dtstart? &
+ property-last-mod? &
+ property-organizer? &
+ property-recurid? &
+ property-seq? &
+ property-status-jour? &
+ property-summary? &
+ property-url? &
+
+ property-rrule? &
+
+ property-attach* &
+ property-attendee* &
+ property-categories* &
+ property-comment* &
+ property-contact* &
+ property-description? &
+ property-exdate* &
+ property-related* &
+ property-rdate* &
+ property-rstatus*
+}
+
+# 3.6.4 Free/Busy Component
+
+component-vfreebusy = element vfreebusy {
+ type-fbprop
+}
+
+type-fbprop = element properties {
+ property-dtstamp &
+ property-uid &
+
+ property-contact? &
+ property-dtstart? &
+ property-dtend? &
+ property-duration? &
+ property-organizer? &
+ property-url? &
+
+ property-attendee* &
+ property-comment* &
+ property-freebusy* &
+ property-rstatus*
+}
+
+# 3.6.5 Time Zone Component
+
+component-vtimezone = element vtimezone {
+ element properties {
+ property-tzid &
+
+ property-last-mod? &
+ property-tzurl?
+ },
+ element components {
+ (component-standard | component-daylight) &
+ component-standard* &
+ component-daylight*
+ }
+}
+
+component-standard = element standard {
+ type-tzprop
+}
+
+component-daylight = element daylight {
+ type-tzprop
+}
+
+type-tzprop = element properties {
+ property-dtstart &
+ property-tzoffsetto &
+ property-tzoffsetfrom &
+
+ property-rrule? &
+
+ property-comment* &
+ property-rdate* &
+ property-tzname*
+}
+
+# 3.6.6 Alarm Component
+
+component-valarm = element valarm {
+ type-audioprop | type-dispprop | type-emailprop
+}
+
+type-audioprop = element properties {
+ property-action &
+
+ property-trigger &
+
+ (property-duration, property-repeat)? &
+
+ property-attach?
+}
+
+type-emailprop = element properties {
+ property-action &
+ property-description &
+ property-trigger &
+ property-summary &
+
+ property-attendee+ &
+
+ (property-duration, property-repeat)? &
+
+ property-attach*
+}
+
+type-dispprop = element properties {
+ property-action &
+ property-description &
+ property-trigger &
+
+ (property-duration, property-repeat)?
+}
+
+# 3.7 Calendar Properties
+
+# 3.7.1 Calendar Scale
+
+property-calscale = element calscale {
+
+ element parameters { empty }?,
+
+ element text { "GREGORIAN" }
+}
+
+# 3.7.2 Method
+
+property-method = element method {
+
+ element parameters { empty }?,
+
+ value-text
+}
+
+# 3.7.3 Product Identifier
+
+property-prodid = element prodid {
+
+ element parameters { empty }?,
+
+ value-text
+}
+
+# 3.7.4 Version
+
+property-version = element version {
+
+ element parameters { empty }?,
+
+ element text { "2.0" }
+}
+
+# 3.8 Component Properties
+
+# 3.8.1 Descriptive Component Properties
+
+# 3.8.1.1 Attachment
+
+property-attach = element attach {
+
+ element parameters {
+ fmttypeparam? &
+ encodingparam?
+ }?,
+
+ value-uri | value-binary
+}
+
+# 3.8.1.2 Categories
+
+property-categories = element categories {
+
+ element parameters {
+ languageparam? &
+ }?,
+
+ value-text+
+}
+
+# 3.8.1.3 Classification
+
+property-class = element class {
+
+ element parameters { empty }?,
+
+ element text {
+ "PUBLIC" |
+ "PRIVATE" |
+ "CONFIDENTIAL"
+ }
+}
+
+# 3.8.1.4 Comment
+
+property-comment = element comment {
+
+ element parameters {
+ altrepparam? &
+ languageparam?
+ }?,
+
+ value-text
+}
+
+# 3.8.1.5 Description
+
+property-description = element description {
+
+ element parameters {
+ altrepparam? &
+ languageparam?
+ }?,
+
+ value-text
+}
+
+# 3.8.1.6 Geographic Position
+
+property-geo = element geo {
+
+ element parameters { empty }?,
+
+ element latitude { xsd:float },
+ element longitude { xsd:float }
+}
+
+# 3.8.1.7 Location
+
+property-location = element location {
+
+ element parameters {
+
+ altrepparam? &
+ languageparam?
+ }?,
+
+ value-text
+}
+
+# 3.8.1.8 Percent Complete
+
+property-percent = element percent-complete {
+
+ element parameters { empty }?,
+
+ value-integer
+}
+
+# 3.8.1.9 Priority
+
+property-priority = element priority {
+
+ element parameters { empty }?,
+
+ value-integer
+}
+
+# 3.8.1.10 Resources
+
+property-resources = element resources {
+
+ element parameters {
+ altrepparam? &
+ languageparam?
+ }?,
+
+ value-text+
+}
+
+# 3.8.1.11 Status
+
+property-status-event = element status {
+
+ element parameters { empty }?,
+
+ element text {
+ "TENTATIVE" |
+ "CONFIRMED" |
+ "CANCELLED"
+ }
+}
+
+property-status-todo = element status {
+
+ element parameters { empty }?,
+
+ element text {
+ "NEEDS-ACTION" |
+ "COMPLETED" |
+ "IN-PROCESS" |
+ "CANCELLED"
+ }
+}
+
+property-status-jour = element status {
+
+ element parameters { empty }?,
+
+ element text {
+ "DRAFT" |
+ "FINAL" |
+ "CANCELLED"
+ }
+}
+
+# 3.8.1.12 Summary
+
+property-summary = element summary {
+
+ element parameters {
+ altrepparam? &
+ languageparam?
+ }?,
+
+ value-text
+}
+
+# 3.8.2 Date and Time Component Properties
+
+# 3.8.2.1 Date/Time Completed
+
+property-completed = element completed {
+
+ element parameters { empty }?,
+
+ value-date-time
+}
+
+# 3.8.2.2 Date/Time End
+
+property-dtend = element dtend {
+
+ element parameters {
+ tzidparam?
+ }?,
+
+ value-date-time |
+ value-date
+}
+
+# 3.8.2.3 Date/Time Due
+
+property-due = element due {
+
+ element parameters {
+ tzidparam?
+ }?,
+
+ value-date-time |
+ value-date
+}
+
+# 3.8.2.4 Date/Time Start
+
+property-dtstart = element dtstart {
+
+ element parameters {
+ tzidparam?
+ }?,
+
+ value-date-time |
+ value-date
+}
+
+# 3.8.2.5 Duration
+
+property-duration = element duration {
+
+ element parameters { empty }?,
+
+ value-duration
+}
+
+# 3.8.2.6 Free/Busy Time
+
+property-freebusy = element freebusy {
+
+ element parameters {
+ fbtypeparam?
+ }?,
+
+
+ value-period+
+}
+
+# 3.8.2.7 Time Transparency
+
+property-transp = element transp {
+
+ element parameters { empty }?,
+
+ element text {
+ "OPAQUE" |
+ "TRANSPARENT"
+ }
+}
+
+# 3.8.3 Time Zone Component Properties
+
+# 3.8.3.1 Time Zone Identifier
+
+property-tzid = element tzid {
+
+ element parameters { empty }?,
+
+ value-text
+}
+
+# 3.8.3.2 Time Zone Name
+
+property-tzname = element tzname {
+
+ element parameters {
+ languageparam?
+ }?,
+
+ value-text
+}
+
+# 3.8.3.3 Time Zone Offset From
+
+property-tzoffsetfrom = element tzoffsetfrom {
+
+ element parameters { empty }?,
+
+ value-utc-offset
+}
+
+# 3.8.3.4 Time Zone Offset To
+
+property-tzoffsetto = element tzoffsetto {
+
+ element parameters { empty }?,
+
+ value-utc-offset
+}
+
+# 3.8.3.5 Time Zone URL
+
+property-tzurl = element tzurl {
+
+ element parameters { empty }?,
+
+ value-uri
+}
+
+# 3.8.4 Relationship Component Properties
+
+# 3.8.4.1 Attendee
+
+property-attendee = element attendee {
+
+ element parameters {
+ cutypeparam? &
+ memberparam? &
+ roleparam? &
+ partstatparam? &
+ rsvpparam? &
+ deltoparam? &
+ delfromparam? &
+ sentbyparam? &
+ cnparam? &
+ dirparam? &
+ languageparam?
+ }?,
+
+ value-cal-address
+}
+
+# 3.8.4.2 Contact
+
+property-contact = element contact {
+
+ element parameters {
+ altrepparam? &
+ languageparam?
+ }?,
+
+ value-text
+}
+
+# 3.8.4.3 Organizer
+
+property-organizer = element organizer {
+
+ element parameters {
+ cnparam? &
+ dirparam? &
+ sentbyparam? &
+ languageparam?
+ }?,
+
+ value-cal-address
+}
+
+# 3.8.4.4 Recurrence ID
+
+property-recurid = element recurrence-id {
+
+ element parameters {
+ tzidparam? &
+ rangeparam?
+ }?,
+
+ value-date-time |
+ value-date
+}
+
+# 3.8.4.5 Related-To
+
+property-related = element related-to {
+
+ element parameters {
+ reltypeparam?
+ }?,
+
+ value-text
+}
+
+# 3.8.4.6 Uniform Resource Locator
+
+property-url = element url {
+
+ element parameters { empty }?,
+
+ value-uri
+}
+
+# 3.8.4.7 Unique Identifier
+
+property-uid = element uid {
+
+ element parameters { empty }?,
+
+ value-text
+}
+
+# 3.8.5 Recurrence Component Properties
+
+# 3.8.5.1 Exception Date/Times
+
+property-exdate = element exdate {
+
+ element parameters {
+ tzidparam?
+ }?,
+
+ value-date-time+ |
+ value-date+
+}
+
+# 3.8.5.2 Recurrence Date/Times
+
+property-rdate = element rdate {
+
+ element parameters {
+ tzidparam?
+ }?,
+
+ value-date-time+ |
+ value-date+ |
+ value-period+
+}
+
+# 3.8.5.3 Recurrence Rule
+
+property-rrule = element rrule {
+
+ element parameters { empty }?,
+
+ value-recur
+}
+
+# 3.8.6 Alarm Component Properties
+
+# 3.8.6.1 Action
+
+property-action = element action {
+
+ element parameters { empty }?,
+
+ element text {
+ "AUDIO" |
+ "DISPLAY" |
+ "EMAIL"
+ }
+}
+
+# 3.8.6.2 Repeat Count
+
+property-repeat = element repeat {
+
+ element parameters { empty }?,
+
+ value-integer
+}
+
+# 3.8.6.3 Trigger
+
+property-trigger = element trigger {
+
+ (
+ element parameters {
+ trigrelparam?
+ }?,
+
+ value-duration
+ ) |
+ (
+ element parameters { empty }?,
+
+ value-date-time
+ )
+}
+
+# 3.8.7 Change Management Component Properties
+
+# 3.8.7.1 Date/Time Created
+
+property-created = element created {
+
+ element parameters { empty }?,
+
+ value-date-time
+}
+
+# 3.8.7.2 Date/Time Stamp
+
+property-dtstamp = element dtstamp {
+
+ element parameters { empty }?,
+
+ value-date-time
+}
+
+# 3.8.7.3 Last Modified
+
+property-last-mod = element last-modified {
+
+ element parameters { empty }?,
+
+ value-date-time
+}
+
+# 3.8.7.4 Sequence Number
+
+property-seq = element sequence {
+
+ element parameters { empty }?,
+
+ value-integer
+}
+
+# 3.8.8 Miscellaneous Component Properties
+
+# 3.8.8.3 Request Status
+
+property-rstatus = element request-status {
+
+ element parameters {
+ languageparam?
+ }?,
+
+ element code { xsd:string },
+ element description { xsd:string },
+ element data { xsd:string }?
+}
diff --git a/vendor/sabre/vobject/resources/schema/xcard.rng b/vendor/sabre/vobject/resources/schema/xcard.rng
new file mode 100644
index 000000000..c0b7cfb35
--- /dev/null
+++ b/vendor/sabre/vobject/resources/schema/xcard.rng
@@ -0,0 +1,388 @@
+# RELAX NG Schema for vCard in XML
+# Extract from RFC6351.
+# Erratum 2994 applied.
+# Erratum 3047 applied.
+# Erratum 3008 applied.
+# Erratum 4247 applied.
+
+default namespace = "urn:ietf:params:xml:ns:vcard-4.0"
+
+### Section 3.3: vCard Format Specification
+#
+# 3.3
+iana-token = xsd:string { pattern = "[a-zA-Z0-9\-]+" }
+x-name = xsd:string { pattern = "x-[a-zA-Z0-9\-]+" }
+
+### Section 4: Value types
+#
+# 4.1
+value-text = element text { text }
+value-text-list = value-text+
+
+# 4.2
+value-uri = element uri { xsd:anyURI }
+
+# 4.3.1
+value-date = element date {
+ xsd:string { pattern = "\d{8}|\d{4}-\d\d|--\d\d(\d\d)?|---\d\d" }
+ }
+
+# 4.3.2
+value-time = element time {
+ xsd:string { pattern = "(\d\d(\d\d(\d\d)?)?|-\d\d(\d\d)?|--\d\d)"
+ ~ "(Z|[+\-]\d\d(\d\d)?)?" }
+ }
+
+# 4.3.3
+value-date-time = element date-time {
+ xsd:string { pattern = "(\d{8}|--\d{4}|---\d\d)T\d\d(\d\d(\d\d)?)?"
+ ~ "(Z|[+\-]\d\d(\d\d)?)?" }
+ }
+
+# 4.3.4
+value-date-and-or-time = value-date | value-date-time | value-time
+
+# 4.3.5
+value-timestamp = element timestamp {
+ xsd:string { pattern = "\d{8}T\d{6}(Z|[+\-]\d\d(\d\d)?)?" }
+ }
+
+# 4.4
+value-boolean = element boolean { xsd:boolean }
+
+# 4.5
+value-integer = element integer { xsd:integer }
+
+# 4.6
+value-float = element float { xsd:float }
+
+# 4.7
+value-utc-offset = element utc-offset {
+ xsd:string { pattern = "[+\-]\d\d(\d\d)?" }
+ }
+
+# 4.8
+value-language-tag = element language-tag {
+ xsd:string { pattern = "([a-z]{2,3}((-[a-z]{3}){0,3})?|[a-z]{4,8})"
+ ~ "(-[a-z]{4})?(-([a-z]{2}|\d{3}))?"
+ ~ "(-([0-9a-z]{5,8}|\d[0-9a-z]{3}))*"
+ ~ "(-[0-9a-wyz](-[0-9a-z]{2,8})+)*"
+ ~ "(-x(-[0-9a-z]{1,8})+)?|x(-[0-9a-z]{1,8})+|"
+ ~ "[a-z]{1,3}(-[0-9a-z]{2,8}){1,2}" }
+ }
+
+### Section 5: Parameters
+#
+# 5.1
+param-language = element language { value-language-tag }?
+
+# 5.2
+param-pref = element pref {
+ element integer {
+ xsd:integer { minInclusive = "1" maxInclusive = "100" }
+ }
+ }?
+
+# 5.4
+param-altid = element altid { value-text }?
+
+# 5.5
+param-pid = element pid {
+ element text { xsd:string { pattern = "\d+(\.\d+)?" } }+
+ }?
+
+# 5.6
+param-type = element type { element text { "work" | "home" }+ }?
+
+# 5.7
+param-mediatype = element mediatype { value-text }?
+
+# 5.8
+param-calscale = element calscale { element text { "gregorian" } }?
+
+# 5.9
+param-sort-as = element sort-as { value-text+ }?
+
+# 5.10
+param-geo = element geo { value-uri }?
+
+# 5.11
+param-tz = element tz { value-text | value-uri }?
+
+### Section 6: Properties
+#
+# 6.1.3
+property-source = element source {
+ element parameters { param-altid, param-pid, param-pref,
+ param-mediatype }?,
+ value-uri
+ }
+
+# 6.1.4
+property-kind = element kind {
+ element text { "individual" | "group" | "org" | "location" |
+ x-name | iana-token }*
+ }
+
+# 6.2.1
+property-fn = element fn {
+ element parameters { param-language, param-altid, param-pid,
+ param-pref, param-type }?,
+ value-text
+ }
+
+# 6.2.2
+property-n = element n {
+ element parameters { param-language, param-sort-as, param-altid }?,
+ element surname { text }+,
+ element given { text }+,
+ element additional { text }+,
+ element prefix { text }+,
+ element suffix { text }+
+ }
+
+# 6.2.3
+property-nickname = element nickname {
+ element parameters { param-language, param-altid, param-pid,
+ param-pref, param-type }?,
+ value-text-list
+ }
+
+# 6.2.4
+property-photo = element photo {
+ element parameters { param-altid, param-pid, param-pref, param-type,
+ param-mediatype }?,
+ value-uri
+ }
+
+# 6.2.5
+property-bday = element bday {
+ element parameters { param-altid, param-calscale }?,
+ (value-date-and-or-time | value-text)
+ }
+
+# 6.2.6
+property-anniversary = element anniversary {
+ element parameters { param-altid, param-calscale }?,
+ (value-date-and-or-time | value-text)
+ }
+
+# 6.2.7
+property-gender = element gender {
+ element sex { "" | "M" | "F" | "O" | "N" | "U" },
+ element identity { text }?
+ }
+
+# 6.3.1
+param-label = element label { value-text }?
+property-adr = element adr {
+ element parameters { param-language, param-altid, param-pid,
+ param-pref, param-type, param-geo, param-tz,
+ param-label }?,
+ element pobox { text }+,
+ element ext { text }+,
+ element street { text }+,
+ element locality { text }+,
+ element region { text }+,
+ element code { text }+,
+ element country { text }+
+ }
+
+# 6.4.1
+property-tel = element tel {
+ element parameters {
+ param-altid,
+ param-pid,
+ param-pref,
+ element type {
+ element text { "work" | "home" | "text" | "voice"
+ | "fax" | "cell" | "video" | "pager"
+ | "textphone" | x-name | iana-token }+
+ }?,
+ param-mediatype
+ }?,
+ (value-text | value-uri)
+ }
+
+# 6.4.2
+property-email = element email {
+ element parameters { param-altid, param-pid, param-pref,
+ param-type }?,
+ value-text
+ }
+
+# 6.4.3
+property-impp = element impp {
+ element parameters { param-altid, param-pid, param-pref,
+ param-type, param-mediatype }?,
+ value-uri
+ }
+
+# 6.4.4
+property-lang = element lang {
+ element parameters { param-altid, param-pid, param-pref,
+ param-type }?,
+ value-language-tag
+ }
+
+# 6.5.1
+property-tz = element tz {
+ element parameters { param-altid, param-pid, param-pref,
+ param-type, param-mediatype }?,
+ (value-text | value-uri | value-utc-offset)
+ }
+
+# 6.5.2
+property-geo = element geo {
+ element parameters { param-altid, param-pid, param-pref,
+ param-type, param-mediatype }?,
+ value-uri
+ }
+
+# 6.6.1
+property-title = element title {
+ element parameters { param-language, param-altid, param-pid,
+ param-pref, param-type }?,
+ value-text
+ }
+
+# 6.6.2
+property-role = element role {
+ element parameters { param-language, param-altid, param-pid,
+ param-pref, param-type }?,
+ value-text
+ }
+
+# 6.6.3
+property-logo = element logo {
+ element parameters { param-language, param-altid, param-pid,
+ param-pref, param-type, param-mediatype }?,
+ value-uri
+ }
+
+# 6.6.4
+property-org = element org {
+ element parameters { param-language, param-altid, param-pid,
+ param-pref, param-type, param-sort-as }?,
+ value-text-list
+ }
+
+# 6.6.5
+property-member = element member {
+ element parameters { param-altid, param-pid, param-pref,
+ param-mediatype }?,
+ value-uri
+ }
+
+# 6.6.6
+property-related = element related {
+ element parameters {
+ param-altid,
+ param-pid,
+ param-pref,
+ element type {
+ element text {
+ "work" | "home" | "contact" | "acquaintance" |
+ "friend" | "met" | "co-worker" | "colleague" | "co-resident" |
+ "neighbor" | "child" | "parent" | "sibling" | "spouse" |
+ "kin" | "muse" | "crush" | "date" | "sweetheart" | "me" |
+ "agent" | "emergency"
+ }+
+ }?,
+ param-mediatype
+ }?,
+ (value-uri | value-text)
+ }
+
+# 6.7.1
+property-categories = element categories {
+ element parameters { param-altid, param-pid, param-pref,
+ param-type }?,
+ value-text-list
+ }
+
+# 6.7.2
+property-note = element note {
+ element parameters { param-language, param-altid, param-pid,
+ param-pref, param-type }?,
+ value-text
+ }
+
+# 6.7.3
+property-prodid = element prodid { value-text }
+
+# 6.7.4
+property-rev = element rev { value-timestamp }
+
+# 6.7.5
+property-sound = element sound {
+ element parameters { param-language, param-altid, param-pid,
+ param-pref, param-type, param-mediatype }?,
+ value-uri
+ }
+
+# 6.7.6
+property-uid = element uid { value-uri }
+
+# 6.7.7
+property-clientpidmap = element clientpidmap {
+ element sourceid { xsd:positiveInteger },
+ value-uri
+ }
+
+# 6.7.8
+property-url = element url {
+ element parameters { param-altid, param-pid, param-pref,
+ param-type, param-mediatype }?,
+ value-uri
+ }
+
+# 6.8.1
+property-key = element key {
+ element parameters { param-altid, param-pid, param-pref,
+ param-type, param-mediatype }?,
+ (value-uri | value-text)
+ }
+
+# 6.9.1
+property-fburl = element fburl {
+ element parameters { param-altid, param-pid, param-pref,
+ param-type, param-mediatype }?,
+ value-uri
+ }
+
+# 6.9.2
+property-caladruri = element caladruri {
+ element parameters { param-altid, param-pid, param-pref,
+ param-type, param-mediatype }?,
+ value-uri
+ }
+
+# 6.9.3
+property-caluri = element caluri {
+ element parameters { param-altid, param-pid, param-pref,
+ param-type, param-mediatype }?,
+ value-uri
+ }
+
+# Top-level grammar
+property = property-adr | property-anniversary | property-bday
+ | property-caladruri | property-caluri | property-categories
+ | property-clientpidmap | property-email | property-fburl
+ | property-fn | property-geo | property-impp | property-key
+ | property-kind | property-lang | property-logo
+ | property-member | property-n | property-nickname
+ | property-note | property-org | property-photo
+ | property-prodid | property-related | property-rev
+ | property-role | property-gender | property-sound
+ | property-source | property-tel | property-title
+ | property-tz | property-uid | property-url
+start = element vcards {
+ element vcard {
+ (property
+ | element group {
+ attribute name { text },
+ property*
+ })+
+ }+
+ }
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Component/VAlarmTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/Component/VAlarmTest.php
deleted file mode 100644
index d57be7aa5..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Component/VAlarmTest.php
+++ /dev/null
@@ -1,175 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Component;
-
-use Sabre\VObject\Component;
-use DateTime;
-use Sabre\VObject\Reader;
-
-class VAlarmTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @dataProvider timeRangeTestData
- */
- public function testInTimeRange(VAlarm $valarm,$start,$end,$outcome) {
-
- $this->assertEquals($outcome, $valarm->isInTimeRange($start, $end));
-
- }
-
- public function timeRangeTestData() {
-
- $tests = array();
-
- // Hard date and time
- $valarm1 = Component::create('VALARM');
- $valarm1->TRIGGER = '20120312T130000Z';
- $valarm1->TRIGGER['VALUE'] = 'DATE-TIME';
-
- $tests[] = array($valarm1, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-04-01 01:00:00'), true);
- $tests[] = array($valarm1, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-03-10 01:00:00'), false);
-
- // Relation to start time of event
- $valarm2 = Component::create('VALARM');
- $valarm2->TRIGGER = '-P1D';
- $valarm2->TRIGGER['VALUE'] = 'DURATION';
-
- $vevent2 = Component::create('VEVENT');
- $vevent2->DTSTART = '20120313T130000Z';
- $vevent2->add($valarm2);
-
- $tests[] = array($valarm2, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-04-01 01:00:00'), true);
- $tests[] = array($valarm2, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-03-10 01:00:00'), false);
-
- // Relation to end time of event
- $valarm3 = Component::create('VALARM');
- $valarm3->TRIGGER = '-P1D';
- $valarm3->TRIGGER['VALUE'] = 'DURATION';
- $valarm3->TRIGGER['RELATED']= 'END';
-
- $vevent3 = Component::create('VEVENT');
- $vevent3->DTSTART = '20120301T130000Z';
- $vevent3->DTEND = '20120401T130000Z';
- $vevent3->add($valarm3);
-
- $tests[] = array($valarm3, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), false);
- $tests[] = array($valarm3, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), true);
-
- // Relation to end time of todo
- $valarm4 = Component::create('VALARM');
- $valarm4->TRIGGER = '-P1D';
- $valarm4->TRIGGER['VALUE'] = 'DURATION';
- $valarm4->TRIGGER['RELATED']= 'END';
-
- $vtodo4 = Component::create('VTODO');
- $vtodo4->DTSTART = '20120301T130000Z';
- $vtodo4->DUE = '20120401T130000Z';
- $vtodo4->add($valarm4);
-
- $tests[] = array($valarm4, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), false);
- $tests[] = array($valarm4, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), true);
-
- // Relation to start time of event + repeat
- $valarm5 = Component::create('VALARM');
- $valarm5->TRIGGER = '-P1D';
- $valarm5->TRIGGER['VALUE'] = 'DURATION';
- $valarm5->REPEAT = 10;
- $valarm5->DURATION = 'P1D';
-
- $vevent5 = Component::create('VEVENT');
- $vevent5->DTSTART = '20120301T130000Z';
- $vevent5->add($valarm5);
-
- $tests[] = array($valarm5, new DateTime('2012-03-09 01:00:00'), new DateTime('2012-03-10 01:00:00'), true);
-
- // Relation to start time of event + duration, but no repeat
- $valarm6 = Component::create('VALARM');
- $valarm6->TRIGGER = '-P1D';
- $valarm6->TRIGGER['VALUE'] = 'DURATION';
- $valarm6->DURATION = 'P1D';
-
- $vevent6 = Component::create('VEVENT');
- $vevent6->DTSTART = '20120313T130000Z';
- $vevent6->add($valarm6);
-
- $tests[] = array($valarm6, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-04-01 01:00:00'), true);
- $tests[] = array($valarm6, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-03-10 01:00:00'), false);
-
-
- // Relation to end time of event (DURATION instead of DTEND)
- $valarm7 = Component::create('VALARM');
- $valarm7->TRIGGER = '-P1D';
- $valarm7->TRIGGER['VALUE'] = 'DURATION';
- $valarm7->TRIGGER['RELATED']= 'END';
-
- $vevent7 = Component::create('VEVENT');
- $vevent7->DTSTART = '20120301T130000Z';
- $vevent7->DURATION = 'P30D';
- $vevent7->add($valarm7);
-
- $tests[] = array($valarm7, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), false);
- $tests[] = array($valarm7, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), true);
-
- // Relation to end time of event (No DTEND or DURATION)
- $valarm7 = Component::create('VALARM');
- $valarm7->TRIGGER = '-P1D';
- $valarm7->TRIGGER['VALUE'] = 'DURATION';
- $valarm7->TRIGGER['RELATED']= 'END';
-
- $vevent7 = Component::create('VEVENT');
- $vevent7->DTSTART = '20120301T130000Z';
- $vevent7->add($valarm7);
-
- $tests[] = array($valarm7, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), true);
- $tests[] = array($valarm7, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), false);
-
-
- return $tests;
- }
-
- /**
- * @expectedException LogicException
- */
- public function testInTimeRangeInvalidComponent() {
-
- $valarm = Component::create('VALARM');
- $valarm->TRIGGER = '-P1D';
- $valarm->TRIGGER['RELATED'] = 'END';
-
- $vjournal = Component::create('VJOURNAL');
- $vjournal->add($valarm);
-
- $valarm->isInTimeRange(new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'));
-
- }
-
- /**
- * This bug was found and reported on the mailing list.
- */
- public function testInTimeRangeBuggy() {
-
-$input = <<<BLA
-BEGIN:VCALENDAR
-BEGIN:VTODO
-DTSTAMP:20121003T064931Z
-UID:b848cb9a7bb16e464a06c222ca1f8102@examle.com
-STATUS:NEEDS-ACTION
-DUE:20121005T000000Z
-SUMMARY:Task 1
-CATEGORIES:AlarmCategory
-BEGIN:VALARM
-TRIGGER:-PT10M
-ACTION:DISPLAY
-DESCRIPTION:Task 1
-END:VALARM
-END:VTODO
-END:VCALENDAR
-BLA;
-
- $vobj = Reader::read($input);
-
- $this->assertTrue($vobj->VTODO->VALARM->isInTimeRange(new \DateTime('2012-10-01 00:00:00'), new \DateTime('2012-11-01 00:00:00')));
-
- }
-}
-
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Component/VCalendarTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/Component/VCalendarTest.php
deleted file mode 100644
index 1d7e0c603..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Component/VCalendarTest.php
+++ /dev/null
@@ -1,244 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Component;
-
-use Sabre\VObject;
-
-class VCalendarTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @dataProvider expandData
- */
- public function testExpand($input, $output) {
-
- $vcal = VObject\Reader::read($input);
- $vcal->expand(
- new \DateTime('2011-12-01'),
- new \DateTime('2011-12-31')
- );
-
- // This will normalize the output
- $output = VObject\Reader::read($output)->serialize();
-
- $this->assertEquals($output, $vcal->serialize());
-
- }
-
- public function expandData() {
-
- $tests = array();
-
- // No data
- $input = 'BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-VERSION:2.0
-END:VCALENDAR
-';
-
- $output = $input;
- $tests[] = array($input,$output);
-
-
- // Simple events
- $input = 'BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-VERSION:2.0
-BEGIN:VEVENT
-UID:bla
-SUMMARY:InExpand
-DTSTART;VALUE=DATE:20111202
-END:VEVENT
-BEGIN:VEVENT
-UID:bla2
-SUMMARY:NotInExpand
-DTSTART;VALUE=DATE:20120101
-END:VEVENT
-END:VCALENDAR
-';
-
- $output = 'BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-VERSION:2.0
-BEGIN:VEVENT
-UID:bla
-SUMMARY:InExpand
-DTSTART;VALUE=DATE:20111202
-END:VEVENT
-END:VCALENDAR
-';
-
- $tests[] = array($input, $output);
-
- // Removing timezone info
- $input = 'BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-VERSION:2.0
-BEGIN:VTIMEZONE
-TZID:Europe/Paris
-END:VTIMEZONE
-BEGIN:VEVENT
-UID:bla4
-SUMMARY:RemoveTZ info
-DTSTART;TZID=Europe/Paris:20111203T130102
-END:VEVENT
-END:VCALENDAR
-';
-
- $output = 'BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-VERSION:2.0
-BEGIN:VEVENT
-UID:bla4
-SUMMARY:RemoveTZ info
-DTSTART;VALUE=DATE-TIME:20111203T120102Z
-END:VEVENT
-END:VCALENDAR
-';
-
- $tests[] = array($input, $output);
-
- // Recurrence rule
- $input = 'BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-VERSION:2.0
-BEGIN:VEVENT
-UID:bla6
-SUMMARY:Testing RRule
-DTSTART:20111125T120000Z
-DTEND:20111125T130000Z
-RRULE:FREQ=WEEKLY
-END:VEVENT
-END:VCALENDAR
-';
-
- $output = 'BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-VERSION:2.0
-BEGIN:VEVENT
-UID:bla6
-SUMMARY:Testing RRule
-DTSTART;VALUE=DATE-TIME:20111202T120000Z
-DTEND;VALUE=DATE-TIME:20111202T130000Z
-RECURRENCE-ID:20111202T120000Z
-END:VEVENT
-BEGIN:VEVENT
-UID:bla6
-SUMMARY:Testing RRule
-DTSTART;VALUE=DATE-TIME:20111209T120000Z
-DTEND;VALUE=DATE-TIME:20111209T130000Z
-RECURRENCE-ID:20111209T120000Z
-END:VEVENT
-BEGIN:VEVENT
-UID:bla6
-SUMMARY:Testing RRule
-DTSTART;VALUE=DATE-TIME:20111216T120000Z
-DTEND;VALUE=DATE-TIME:20111216T130000Z
-RECURRENCE-ID:20111216T120000Z
-END:VEVENT
-BEGIN:VEVENT
-UID:bla6
-SUMMARY:Testing RRule
-DTSTART;VALUE=DATE-TIME:20111223T120000Z
-DTEND;VALUE=DATE-TIME:20111223T130000Z
-RECURRENCE-ID:20111223T120000Z
-END:VEVENT
-BEGIN:VEVENT
-UID:bla6
-SUMMARY:Testing RRule
-DTSTART;VALUE=DATE-TIME:20111230T120000Z
-DTEND;VALUE=DATE-TIME:20111230T130000Z
-RECURRENCE-ID:20111230T120000Z
-END:VEVENT
-END:VCALENDAR
-';
-
- // Recurrence rule + override
- $input = 'BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-VERSION:2.0
-BEGIN:VEVENT
-UID:bla6
-SUMMARY:Testing RRule2
-DTSTART:20111125T120000Z
-DTEND:20111125T130000Z
-RRULE:FREQ=WEEKLY
-END:VEVENT
-BEGIN:VEVENT
-UID:bla6
-RECURRENCE-ID:20111209T120000Z
-DTSTART:20111209T140000Z
-DTEND:20111209T150000Z
-SUMMARY:Override!
-END:VEVENT
-END:VCALENDAR
-';
-
- $output = 'BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-VERSION:2.0
-BEGIN:VEVENT
-UID:bla6
-SUMMARY:Testing RRule2
-DTSTART;VALUE=DATE-TIME:20111202T120000Z
-DTEND;VALUE=DATE-TIME:20111202T130000Z
-RECURRENCE-ID:20111202T120000Z
-END:VEVENT
-BEGIN:VEVENT
-UID:bla6
-RECURRENCE-ID:20111209T120000Z
-DTSTART:20111209T140000Z
-DTEND:20111209T150000Z
-SUMMARY:Override!
-END:VEVENT
-BEGIN:VEVENT
-UID:bla6
-SUMMARY:Testing RRule2
-DTSTART;VALUE=DATE-TIME:20111216T120000Z
-DTEND;VALUE=DATE-TIME:20111216T130000Z
-RECURRENCE-ID:20111216T120000Z
-END:VEVENT
-BEGIN:VEVENT
-UID:bla6
-SUMMARY:Testing RRule2
-DTSTART;VALUE=DATE-TIME:20111223T120000Z
-DTEND;VALUE=DATE-TIME:20111223T130000Z
-RECURRENCE-ID:20111223T120000Z
-END:VEVENT
-BEGIN:VEVENT
-UID:bla6
-SUMMARY:Testing RRule2
-DTSTART;VALUE=DATE-TIME:20111230T120000Z
-DTEND;VALUE=DATE-TIME:20111230T130000Z
-RECURRENCE-ID:20111230T120000Z
-END:VEVENT
-END:VCALENDAR
-';
-
- $tests[] = array($input, $output);
- return $tests;
-
- }
-
- /**
- * @expectedException LogicException
- */
- public function testBrokenEventExpand() {
-
- $input = 'BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-VERSION:2.0
-BEGIN:VEVENT
-RRULE:FREQ=WEEKLY
-DTSTART;VALUE=DATE:20111202
-END:VEVENT
-END:VCALENDAR
-';
- $vcal = VObject\Reader::read($input);
- $vcal->expand(
- new \DateTime('2011-12-01'),
- new \DateTime('2011-12-31')
- );
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Component/VCardTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/Component/VCardTest.php
deleted file mode 100644
index 584a007d9..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Component/VCardTest.php
+++ /dev/null
@@ -1,100 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Component;
-
-use Sabre\VObject;
-
-class VCardTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @dataProvider validateData
- */
- function testValidate($input, $expectedWarnings, $expectedRepairedOutput) {
-
- $vcard = VObject\Reader::read($input);
-
- $warnings = $vcard->validate();
-
- $warnMsg = array();
- foreach($warnings as $warning) {
- $warnMsg[] = $warning['message'];
- }
-
- $this->assertEquals($expectedWarnings, $warnMsg);
-
- $vcard->validate(VObject\Component::REPAIR);
-
- $this->assertEquals(
- $expectedRepairedOutput,
- $vcard->serialize()
- );
-
- }
-
- public function validateData() {
-
- $tests = array();
-
- // Correct
- $tests[] = array(
- "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nEND:VCARD\r\n",
- array(),
- "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nEND:VCARD\r\n",
- );
-
- // No VERSION
- $tests[] = array(
- "BEGIN:VCARD\r\nFN:John Doe\r\nEND:VCARD\r\n",
- array(
- 'The VERSION property must appear in the VCARD component exactly 1 time',
- ),
- "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nEND:VCARD\r\n",
- );
-
- // Unknown version
- $tests[] = array(
- "BEGIN:VCARD\r\nVERSION:2.2\r\nFN:John Doe\r\nEND:VCARD\r\n",
- array(
- 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.',
- ),
- "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nEND:VCARD\r\n",
- );
-
- // No FN
- $tests[] = array(
- "BEGIN:VCARD\r\nVERSION:4.0\r\nEND:VCARD\r\n",
- array(
- 'The FN property must appear in the VCARD component exactly 1 time',
- ),
- "BEGIN:VCARD\r\nVERSION:4.0\r\nEND:VCARD\r\n",
- );
- // No FN, N fallback
- $tests[] = array(
- "BEGIN:VCARD\r\nVERSION:4.0\r\nN:Doe;John;;;;;\r\nEND:VCARD\r\n",
- array(
- 'The FN property must appear in the VCARD component exactly 1 time',
- ),
- "BEGIN:VCARD\r\nVERSION:4.0\r\nN:Doe;John;;;;;\r\nFN:John Doe\r\nEND:VCARD\r\n",
- );
- // No FN, N fallback, no first name
- $tests[] = array(
- "BEGIN:VCARD\r\nVERSION:4.0\r\nN:Doe;;;;;;\r\nEND:VCARD\r\n",
- array(
- 'The FN property must appear in the VCARD component exactly 1 time',
- ),
- "BEGIN:VCARD\r\nVERSION:4.0\r\nN:Doe;;;;;;\r\nFN:Doe\r\nEND:VCARD\r\n",
- );
-
- // No FN, ORG fallback
- $tests[] = array(
- "BEGIN:VCARD\r\nVERSION:4.0\r\nORG:Acme Co.\r\nEND:VCARD\r\n",
- array(
- 'The FN property must appear in the VCARD component exactly 1 time',
- ),
- "BEGIN:VCARD\r\nVERSION:4.0\r\nORG:Acme Co.\r\nFN:Acme Co.\r\nEND:VCARD\r\n",
- );
- return $tests;
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Component/VEventTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/Component/VEventTest.php
deleted file mode 100644
index 616da4ac7..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Component/VEventTest.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Component;
-
-use Sabre\VObject;
-
-class VEventTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @dataProvider timeRangeTestData
- */
- public function testInTimeRange(VEvent $vevent,$start,$end,$outcome) {
-
- $this->assertEquals($outcome, $vevent->isInTimeRange($start, $end));
-
- }
-
- public function timeRangeTestData() {
-
- $tests = array();
-
- $vevent = new VEvent('VEVENT');
- $vevent->DTSTART = '20111223T120000Z';
- $tests[] = array($vevent, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vevent, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
-
- $vevent2 = clone $vevent;
- $vevent2->DTEND = '20111225T120000Z';
- $tests[] = array($vevent2, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vevent2, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
-
- $vevent3 = clone $vevent;
- $vevent3->DURATION = 'P1D';
- $tests[] = array($vevent3, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vevent3, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
-
- $vevent4 = clone $vevent;
- $vevent4->DTSTART = '20111225';
- $vevent4->DTSTART['VALUE'] = 'DATE';
- $tests[] = array($vevent4, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vevent4, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
- // Event with no end date should be treated as lasting the entire day.
- $tests[] = array($vevent4, new \DateTime('2011-12-25 16:00:00'), new \DateTime('2011-12-25 17:00:00'), true);
-
-
- $vevent5 = clone $vevent;
- $vevent5->DURATION = 'P1D';
- $vevent5->RRULE = 'FREQ=YEARLY';
- $tests[] = array($vevent5, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vevent5, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
- $tests[] = array($vevent5, new \DateTime('2013-12-01'), new \DateTime('2013-12-31'), true);
-
- $vevent6 = clone $vevent;
- $vevent6->DTSTART = '20111225';
- $vevent6->DTSTART['VALUE'] = 'DATE';
- $vevent6->DTEND = '20111225';
- $vevent6->DTEND['VALUE'] = 'DATE';
-
- $tests[] = array($vevent6, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vevent6, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
-
- // Added this test to ensure that recurrence rules with no DTEND also
- // get checked for the entire day.
- $vevent7 = clone $vevent;
- $vevent7->DTSTART = '20120101';
- $vevent7->DTSTART['VALUE'] = 'DATE';
- $vevent7->RRULE = 'FREQ=MONTHLY';
- $tests[] = array($vevent7, new \DateTime('2012-02-01 15:00:00'), new \DateTime('2012-02-02'), true);
- return $tests;
-
- }
-
-}
-
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Component/VFreeBusyTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/Component/VFreeBusyTest.php
deleted file mode 100644
index 031c3c684..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Component/VFreeBusyTest.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Component;
-use Sabre\VObject;
-
-class VFreeBusyTest extends \PHPUnit_Framework_TestCase {
-
- function testIsFree() {
-
- $input = <<<BLA
-BEGIN:VCALENDAR
-BEGIN:VFREEBUSY
-FREEBUSY;FBTYPE=FREE:20120912T000500Z/PT1H
-FREEBUSY;FBTYPE=BUSY:20120912T010000Z/20120912T020000Z
-FREEBUSY;FBTYPE=BUSY-TENTATIVE:20120912T020000Z/20120912T030000Z
-FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20120912T030000Z/20120912T040000Z
-FREEBUSY;FBTYPE=BUSY:20120912T050000Z/20120912T060000Z,20120912T080000Z/20120912T090000Z
-FREEBUSY;FBTYPE=BUSY:20120912T100000Z/PT1H
-END:VFREEBUSY
-END:VCALENDAR
-BLA;
-
- $obj = VObject\Reader::read($input);
- $vfb = $obj->VFREEBUSY;
-
- $tz = new \DateTimeZone('UTC');
-
- $this->assertFalse($vfb->isFree(new \DateTime('2012-09-12 01:15:00', $tz), new \DateTime('2012-09-12 01:45:00', $tz)));
- $this->assertFalse($vfb->isFree(new \DateTime('2012-09-12 08:05:00', $tz), new \DateTime('2012-09-12 08:10:00', $tz)));
- $this->assertFalse($vfb->isFree(new \DateTime('2012-09-12 10:15:00', $tz), new \DateTime('2012-09-12 10:45:00', $tz)));
-
- // Checking whether the end time is treated as non-inclusive
- $this->assertTrue($vfb->isFree(new \DateTime('2012-09-12 09:00:00', $tz), new \DateTime('2012-09-12 09:15:00', $tz)));
- $this->assertTrue($vfb->isFree(new \DateTime('2012-09-12 09:45:00', $tz), new \DateTime('2012-09-12 10:00:00', $tz)));
- $this->assertTrue($vfb->isFree(new \DateTime('2012-09-12 11:00:00', $tz), new \DateTime('2012-09-12 12:00:00', $tz)));
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Component/VJournalTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/Component/VJournalTest.php
deleted file mode 100644
index 46ecb992b..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Component/VJournalTest.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Component;
-
-use Sabre\VObject\Component;
-
-class VJournalTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @dataProvider timeRangeTestData
- */
- public function testInTimeRange(VJournal $vtodo,$start,$end,$outcome) {
-
- $this->assertEquals($outcome, $vtodo->isInTimeRange($start, $end));
-
- }
-
- public function timeRangeTestData() {
-
- $tests = array();
-
- $vjournal = Component::create('VJOURNAL');
- $vjournal->DTSTART = '20111223T120000Z';
- $tests[] = array($vjournal, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vjournal, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
-
- $vjournal2 = Component::create('VJOURNAL');
- $vjournal2->DTSTART = '20111223';
- $vjournal2->DTSTART['VALUE'] = 'DATE';
- $tests[] = array($vjournal2, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vjournal2, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
-
- $vjournal3 = Component::create('VJOURNAL');
- $tests[] = array($vjournal3, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), false);
- $tests[] = array($vjournal3, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
-
- return $tests;
- }
-
-}
-
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Component/VTodoTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/Component/VTodoTest.php
deleted file mode 100644
index a84da5cdf..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Component/VTodoTest.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Component;
-
-use Sabre\VObject\Component;
-
-class VTodoTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @dataProvider timeRangeTestData
- */
- public function testInTimeRange(VTodo $vtodo,$start,$end,$outcome) {
-
- $this->assertEquals($outcome, $vtodo->isInTimeRange($start, $end));
-
- }
-
- public function timeRangeTestData() {
-
- $tests = array();
-
- $vtodo = Component::create('VTODO');
- $vtodo->DTSTART = '20111223T120000Z';
- $tests[] = array($vtodo, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vtodo, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
-
- $vtodo2 = clone $vtodo;
- $vtodo2->DURATION = 'P1D';
- $tests[] = array($vtodo2, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vtodo2, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
-
- $vtodo3 = clone $vtodo;
- $vtodo3->DUE = '20111225';
- $tests[] = array($vtodo3, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vtodo3, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
-
- $vtodo4 = Component::create('VTODO');
- $vtodo4->DUE = '20111225';
- $tests[] = array($vtodo4, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vtodo4, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
-
- $vtodo5 = Component::create('VTODO');
- $vtodo5->COMPLETED = '20111225';
- $tests[] = array($vtodo5, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vtodo5, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
-
- $vtodo6 = Component::create('VTODO');
- $vtodo6->CREATED = '20111225';
- $tests[] = array($vtodo6, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vtodo6, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
-
- $vtodo7 = Component::create('VTODO');
- $vtodo7->CREATED = '20111225';
- $vtodo7->COMPLETED = '20111226';
- $tests[] = array($vtodo7, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vtodo7, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false);
-
- $vtodo7 = Component::create('VTODO');
- $tests[] = array($vtodo7, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true);
- $tests[] = array($vtodo7, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), true);
-
- return $tests;
-
- }
-
-}
-
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/ComponentTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/ComponentTest.php
deleted file mode 100644
index 07000bda0..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/ComponentTest.php
+++ /dev/null
@@ -1,413 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-class ComponentTest extends \PHPUnit_Framework_TestCase {
-
- function testIterate() {
-
- $comp = new Component('VCALENDAR');
-
- $sub = new Component('VEVENT');
- $comp->children[] = $sub;
-
- $sub = new Component('VTODO');
- $comp->children[] = $sub;
-
- $count = 0;
- foreach($comp->children() as $key=>$subcomponent) {
-
- $count++;
- $this->assertInstanceOf('Sabre\\VObject\\Component',$subcomponent);
-
- }
- $this->assertEquals(2,$count);
- $this->assertEquals(1,$key);
-
- }
-
- function testMagicGet() {
-
- $comp = new Component('VCALENDAR');
-
- $sub = new Component('VEVENT');
- $comp->children[] = $sub;
-
- $sub = new Component('VTODO');
- $comp->children[] = $sub;
-
- $event = $comp->vevent;
- $this->assertInstanceOf('Sabre\\VObject\\Component', $event);
- $this->assertEquals('VEVENT', $event->name);
-
- $this->assertInternalType('null', $comp->vjournal);
-
- }
-
- function testMagicGetGroups() {
-
- $comp = new Component('VCARD');
-
- $sub = new Property('GROUP1.EMAIL','1@1.com');
- $comp->children[] = $sub;
-
- $sub = new Property('GROUP2.EMAIL','2@2.com');
- $comp->children[] = $sub;
-
- $sub = new Property('EMAIL','3@3.com');
- $comp->children[] = $sub;
-
- $emails = $comp->email;
- $this->assertEquals(3, count($emails));
-
- $email1 = $comp->{"group1.email"};
- $this->assertEquals('EMAIL', $email1[0]->name);
- $this->assertEquals('GROUP1', $email1[0]->group);
-
- $email3 = $comp->{".email"};
- $this->assertEquals('EMAIL', $email3[0]->name);
- $this->assertEquals(null, $email3[0]->group);
-
- }
-
- function testMagicIsset() {
-
- $comp = new Component('VCALENDAR');
-
- $sub = new Component('VEVENT');
- $comp->children[] = $sub;
-
- $sub = new Component('VTODO');
- $comp->children[] = $sub;
-
- $this->assertTrue(isset($comp->vevent));
- $this->assertTrue(isset($comp->vtodo));
- $this->assertFalse(isset($comp->vjournal));
-
- }
-
- function testMagicSetScalar() {
-
- $comp = new Component('VCALENDAR');
- $comp->myProp = 'myValue';
-
- $this->assertInstanceOf('Sabre\\VObject\\Property',$comp->MYPROP);
- $this->assertEquals('myValue',$comp->MYPROP->value);
-
-
- }
-
- function testMagicSetScalarTwice() {
-
- $comp = new Component('VCALENDAR');
- $comp->myProp = 'myValue';
- $comp->myProp = 'myValue';
-
- $this->assertEquals(1,count($comp->children));
- $this->assertInstanceOf('Sabre\\VObject\\Property',$comp->MYPROP);
- $this->assertEquals('myValue',$comp->MYPROP->value);
-
- }
-
- function testMagicSetComponent() {
-
- $comp = new Component('VCALENDAR');
-
- // Note that 'myProp' is ignored here.
- $comp->myProp = new Component('VEVENT');
-
- $this->assertEquals(1, count($comp->children));
-
- $this->assertEquals('VEVENT',$comp->VEVENT->name);
-
- }
-
- function testMagicSetTwice() {
-
- $comp = new Component('VCALENDAR');
-
- $comp->VEVENT = new Component('VEVENT');
- $comp->VEVENT = new Component('VEVENT');
-
- $this->assertEquals(1, count($comp->children));
-
- $this->assertEquals('VEVENT',$comp->VEVENT->name);
-
- }
-
- function testArrayAccessGet() {
-
- $comp = new Component('VCALENDAR');
-
- $event = new Component('VEVENT');
- $event->summary = 'Event 1';
-
- $comp->add($event);
-
- $event2 = clone $event;
- $event2->summary = 'Event 2';
-
- $comp->add($event2);
-
- $this->assertEquals(2,count($comp->children()));
- $this->assertTrue($comp->vevent[1] instanceof Component);
- $this->assertEquals('Event 2', (string)$comp->vevent[1]->summary);
-
- }
-
- function testArrayAccessExists() {
-
- $comp = new Component('VCALENDAR');
-
- $event = new Component('VEVENT');
- $event->summary = 'Event 1';
-
- $comp->add($event);
-
- $event2 = clone $event;
- $event2->summary = 'Event 2';
-
- $comp->add($event2);
-
- $this->assertTrue(isset($comp->vevent[0]));
- $this->assertTrue(isset($comp->vevent[1]));
-
- }
-
- /**
- * @expectedException LogicException
- */
- function testArrayAccessSet() {
-
- $comp = new Component('VCALENDAR');
- $comp['hey'] = 'hi there';
-
- }
- /**
- * @expectedException LogicException
- */
- function testArrayAccessUnset() {
-
- $comp = new Component('VCALENDAR');
- unset($comp[0]);
-
- }
-
- function testAddScalar() {
-
- $comp = new Component('VCALENDAR');
-
- $comp->add('myprop','value');
-
- $this->assertEquals(1, count($comp->children));
-
- $this->assertTrue($comp->children[0] instanceof Property);
- $this->assertEquals('MYPROP',$comp->children[0]->name);
- $this->assertEquals('value',$comp->children[0]->value);
-
- }
-
- function testAddScalarParams() {
-
- $comp = Component::create('VCALENDAR');
-
- $comp->add('myprop','value',array('param1'=>'value1'));
-
- $this->assertEquals(1, count($comp->children));
-
- $this->assertTrue($comp->children[0] instanceof Property);
- $this->assertEquals('MYPROP',$comp->children[0]->name);
- $this->assertEquals('value',$comp->children[0]->value);
-
- $this->assertEquals(1, count($comp->children[0]->parameters));
-
- $this->assertTrue($comp->children[0]->parameters[0] instanceof Parameter);
- $this->assertEquals('PARAM1',$comp->children[0]->parameters[0]->name);
- $this->assertEquals('value1',$comp->children[0]->parameters[0]->value);
-
- }
-
-
- function testAddComponent() {
-
- $comp = new Component('VCALENDAR');
-
- $comp->add(new Component('VEVENT'));
-
- $this->assertEquals(1, count($comp->children));
-
- $this->assertEquals('VEVENT',$comp->VEVENT->name);
-
- }
-
- function testAddComponentTwice() {
-
- $comp = new Component('VCALENDAR');
-
- $comp->add(new Component('VEVENT'));
- $comp->add(new Component('VEVENT'));
-
- $this->assertEquals(2, count($comp->children));
-
- $this->assertEquals('VEVENT',$comp->VEVENT->name);
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testAddArgFail() {
-
- $comp = new Component('VCALENDAR');
- $comp->add(new Component('VEVENT'),'hello');
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testAddArgFail2() {
-
- $comp = new Component('VCALENDAR');
- $comp->add(array());
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testAddArgFail3() {
-
- $comp = new Component('VCALENDAR');
- $comp->add('hello',array());
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testMagicSetInvalid() {
-
- $comp = new Component('VCALENDAR');
-
- // Note that 'myProp' is ignored here.
- $comp->myProp = new \StdClass();
-
- $this->assertEquals(1, count($comp->children));
-
- $this->assertEquals('VEVENT',$comp->VEVENT->name);
-
- }
-
- function testMagicUnset() {
-
- $comp = new Component('VCALENDAR');
- $comp->add(new Component('VEVENT'));
-
- unset($comp->vevent);
-
- $this->assertEquals(array(), $comp->children);
-
- }
-
-
- function testCount() {
-
- $comp = new Component('VCALENDAR');
- $this->assertEquals(1,$comp->count());
-
- }
-
- function testChildren() {
-
- $comp = new Component('VCALENDAR');
-
- // Note that 'myProp' is ignored here.
- $comp->children = array(
- new Component('VEVENT'),
- new Component('VTODO')
- );
-
- $r = $comp->children();
- $this->assertTrue($r instanceof ElementList);
- $this->assertEquals(2,count($r));
- }
-
- function testGetComponents() {
-
- $comp = new Component('VCALENDAR');
-
- // Note that 'myProp' is ignored here.
- $comp->children = array(
- new Property('FOO','BAR'),
- new Component('VTODO')
- );
-
- $r = $comp->getComponents();
- $this->assertInternalType('array', $r);
- $this->assertEquals(1, count($r));
- $this->assertEquals('VTODO', $r[0]->name);
- }
-
- function testSerialize() {
-
- $comp = new Component('VCALENDAR');
- $this->assertEquals("BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n", $comp->serialize());
-
- }
-
- function testSerializeChildren() {
-
- $comp = new Component('VCALENDAR');
- $comp->children = array(
- new Component('VEVENT'),
- new Component('VTODO')
- );
-
- $str = $comp->serialize();
-
- $this->assertEquals("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n", $str);
-
- }
-
- function testSerializeOrderCompAndProp() {
-
- $comp = new Component('VCALENDAR');
- $comp->add(new Component('VEVENT'));
- $comp->add('PROP1','BLABLA');
- $comp->add('VERSION','2.0');
- $comp->add(new Component('VTIMEZONE'));
-
- $str = $comp->serialize();
-
- $this->assertEquals("BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPROP1:BLABLA\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", $str);
-
- }
-
- function testAnotherSerializeOrderProp() {
-
- $prop4s=array('1', '2', '3', '4', '5', '6', '7', '8', '9', '10');
-
- $comp = new Component('VCARD');
- $comp->__set('SOMEPROP','FOO');
- $comp->__set('ANOTHERPROP','FOO');
- $comp->__set('THIRDPROP','FOO');
- foreach ($prop4s as $prop4) {
- $comp->add('PROP4', 'FOO '.$prop4);
- }
- $comp->__set('PROPNUMBERFIVE', 'FOO');
- $comp->__set('PROPNUMBERSIX', 'FOO');
- $comp->__set('PROPNUMBERSEVEN', 'FOO');
- $comp->__set('PROPNUMBEREIGHT', 'FOO');
- $comp->__set('PROPNUMBERNINE', 'FOO');
- $comp->__set('PROPNUMBERTEN', 'FOO');
- $comp->__set('VERSION','2.0');
- $comp->__set('UID', 'FOO');
-
- $str = $comp->serialize();
-
- $this->assertEquals("BEGIN:VCARD\r\nVERSION:2.0\r\nSOMEPROP:FOO\r\nANOTHERPROP:FOO\r\nTHIRDPROP:FOO\r\nPROP4:FOO 1\r\nPROP4:FOO 2\r\nPROP4:FOO 3\r\nPROP4:FOO 4\r\nPROP4:FOO 5\r\nPROP4:FOO 6\r\nPROP4:FOO 7\r\nPROP4:FOO 8\r\nPROP4:FOO 9\r\nPROP4:FOO 10\r\nPROPNUMBERFIVE:FOO\r\nPROPNUMBERSIX:FOO\r\nPROPNUMBERSEVEN:FOO\r\nPROPNUMBEREIGHT:FOO\r\nPROPNUMBERNINE:FOO\r\nPROPNUMBERTEN:FOO\r\nUID:FOO\r\nEND:VCARD\r\n", $str);
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/DateTimeParserTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/DateTimeParserTest.php
deleted file mode 100644
index 6ea2faed9..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/DateTimeParserTest.php
+++ /dev/null
@@ -1,153 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-use DateTime;
-use DateTimeZone;
-use DateInterval;
-
-class DateTimeParserTest extends \PHPUnit_Framework_TestCase {
-
- function testParseICalendarDuration() {
-
- $this->assertEquals('+1 weeks', DateTimeParser::parseDuration('P1W',true));
- $this->assertEquals('+5 days', DateTimeParser::parseDuration('P5D',true));
- $this->assertEquals('+5 days 3 hours 50 minutes 12 seconds', DateTimeParser::parseDuration('P5DT3H50M12S',true));
- $this->assertEquals('-1 weeks 50 minutes', DateTimeParser::parseDuration('-P1WT50M',true));
- $this->assertEquals('+50 days 3 hours 2 seconds', DateTimeParser::parseDuration('+P50DT3H2S',true));
- $this->assertEquals(new DateInterval('PT0S'), DateTimeParser::parseDuration('PT0S'));
-
- }
-
- function testParseICalendarDurationDateInterval() {
-
- $expected = new DateInterval('P7D');
- $this->assertEquals($expected, DateTimeParser::parseDuration('P1W'));
- $this->assertEquals($expected, DateTimeParser::parse('P1W'));
-
- $expected = new DateInterval('PT3M');
- $expected->invert = true;
- $this->assertEquals($expected, DateTimeParser::parseDuration('-PT3M'));
-
- }
-
- /**
- * @expectedException LogicException
- */
- function testParseICalendarDurationFail() {
-
- DateTimeParser::parseDuration('P1X',true);
-
- }
-
- function testParseICalendarDateTime() {
-
- $dateTime = DateTimeParser::parseDateTime('20100316T141405');
-
- $compare = new DateTime('2010-03-16 14:14:05',new DateTimeZone('UTC'));
-
- $this->assertEquals($compare, $dateTime);
-
- }
-
- /**
- * @depends testParseICalendarDateTime
- * @expectedException LogicException
- */
- function testParseICalendarDateTimeBadFormat() {
-
- $dateTime = DateTimeParser::parseDateTime('20100316T141405 ');
-
- }
-
- /**
- * @depends testParseICalendarDateTime
- */
- function testParseICalendarDateTimeUTC() {
-
- $dateTime = DateTimeParser::parseDateTime('20100316T141405Z');
-
- $compare = new DateTime('2010-03-16 14:14:05',new DateTimeZone('UTC'));
- $this->assertEquals($compare, $dateTime);
-
- }
-
- /**
- * @depends testParseICalendarDateTime
- */
- function testParseICalendarDateTimeUTC2() {
-
- $dateTime = DateTimeParser::parseDateTime('20101211T160000Z');
-
- $compare = new DateTime('2010-12-11 16:00:00',new DateTimeZone('UTC'));
- $this->assertEquals($compare, $dateTime);
-
- }
-
- /**
- * @depends testParseICalendarDateTime
- */
- function testParseICalendarDateTimeCustomTimeZone() {
-
- $dateTime = DateTimeParser::parseDateTime('20100316T141405', new DateTimeZone('Europe/Amsterdam'));
-
- $compare = new DateTime('2010-03-16 13:14:05',new DateTimeZone('UTC'));
- $this->assertEquals($compare, $dateTime);
-
- }
-
- function testParseICalendarDate() {
-
- $dateTime = DateTimeParser::parseDate('20100316');
-
- $expected = new DateTime('2010-03-16 00:00:00',new DateTimeZone('UTC'));
-
- $this->assertEquals($expected, $dateTime);
-
- $dateTime = DateTimeParser::parse('20100316');
- $this->assertEquals($expected, $dateTime);
-
- }
-
- /**
- * TCheck if a date with year > 4000 will not throw an exception. iOS seems to use 45001231 in yearly recurring events
- */
- function testParseICalendarDateGreaterThan4000() {
-
- $dateTime = DateTimeParser::parseDate('45001231');
-
- $expected = new DateTime('4500-12-31 00:00:00',new DateTimeZone('UTC'));
-
- $this->assertEquals($expected, $dateTime);
-
- $dateTime = DateTimeParser::parse('45001231');
- $this->assertEquals($expected, $dateTime);
-
- }
-
- /**
- * Check if a datetime with year > 4000 will not throw an exception. iOS seems to use 45001231T235959 in yearly recurring events
- */
- function testParseICalendarDateTimeGreaterThan4000() {
-
- $dateTime = DateTimeParser::parseDateTime('45001231T235959');
-
- $expected = new DateTime('4500-12-31 23:59:59',new DateTimeZone('UTC'));
-
- $this->assertEquals($expected, $dateTime);
-
- $dateTime = DateTimeParser::parse('45001231T235959');
- $this->assertEquals($expected, $dateTime);
-
- }
-
- /**
- * @depends testParseICalendarDate
- * @expectedException LogicException
- */
- function testParseICalendarDateBadFormat() {
-
- $dateTime = DateTimeParser::parseDate('20100316T141405');
-
- }
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/DocumentTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/DocumentTest.php
deleted file mode 100644
index 5fd2a2a78..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/DocumentTest.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-class DocumentTest extends \PHPUnit_Framework_TestCase {
-
- function testCreateComponent() {
-
- $vcal = new Component\VCalendar();
-
- $event = $vcal->createComponent('VEVENT');
-
- $this->assertInstanceOf('Sabre\VObject\Component\VEvent', $event);
- $vcal->add($event);
-
- $prop = $vcal->createProperty('X-PROP','1234256',array('X-PARAM' => '3'));
- $this->assertInstanceOf('Sabre\VObject\Property', $prop);
-
- $event->add($prop);
-
- $out = $vcal->serialize();
- $this->assertEquals("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nX-PROP;X-PARAM=3:1234256\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", $out);
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/ElementListTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/ElementListTest.php
deleted file mode 100644
index 84e1bcbe9..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/ElementListTest.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-class ElementListTest extends \PHPUnit_Framework_TestCase {
-
- function testIterate() {
-
- $sub = new Component('VEVENT');
-
- $elems = array(
- $sub,
- clone $sub,
- clone $sub
- );
-
- $elemList = new ElementList($elems);
-
- $count = 0;
- foreach($elemList as $key=>$subcomponent) {
-
- $count++;
- $this->assertInstanceOf('Sabre\\VObject\\Component',$subcomponent);
-
- }
- $this->assertEquals(3,$count);
- $this->assertEquals(2,$key);
-
- }
-
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/EmClientTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/EmClientTest.php
deleted file mode 100644
index 69d410fe7..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/EmClientTest.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-class EmClientTest extends \PHPUnit_Framework_TestCase {
-
- function testParseTz() {
-
- $str = 'BEGIN:VCALENDAR
-X-WR-CALNAME:Blackhawks Schedule 2011-12
-X-APPLE-CALENDAR-COLOR:#E51717
-X-WR-TIMEZONE:America/Chicago
-CALSCALE:GREGORIAN
-PRODID:-//eM Client/4.0.13961.0
-VERSION:2.0
-BEGIN:VTIMEZONE
-TZID:America/Chicago
-BEGIN:DAYLIGHT
-TZOFFSETFROM:-0600
-RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
-DTSTART:20070311T020000
-TZNAME:CDT
-TZOFFSETTO:-0500
-END:DAYLIGHT
-BEGIN:STANDARD
-TZOFFSETFROM:-0500
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
-DTSTART:20071104T020000
-TZNAME:CST
-TZOFFSETTO:-0600
-END:STANDARD
-END:VTIMEZONE
-BEGIN:VEVENT
-CREATED:20110624T181236Z
-UID:be3bbfff-96e8-4c66-9908-ab791a62231d
-DTEND;TZID="America/Chicago":20111008T223000
-TRANSP:OPAQUE
-SUMMARY:Stars @ Blackhawks (Home Opener)
-DTSTART;TZID="America/Chicago":20111008T193000
-DTSTAMP:20120330T013232Z
-SEQUENCE:2
-X-MICROSOFT-CDO-BUSYSTATUS:BUSY
-LAST-MODIFIED:20120330T013237Z
-CLASS:PUBLIC
-END:VEVENT
-END:VCALENDAR';
-
- $vObject = Reader::read($str);
- $dt = $vObject->VEVENT->DTSTART->getDateTime();
- $this->assertEquals(new \DateTime('2011-10-08 19:30:00', new \DateTimeZone('America/Chicago')), $dt);
-
- }
-
-}
-
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/FreeBusyGeneratorTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/FreeBusyGeneratorTest.php
deleted file mode 100644
index 1f79e0a47..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/FreeBusyGeneratorTest.php
+++ /dev/null
@@ -1,246 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-class FreeBusyGeneratorTest extends \PHPUnit_Framework_TestCase {
-
- function getInput() {
-
- // shows up
-$blob1 = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-DTSTART:20110101T120000Z
-DTEND:20110101T130000Z
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- // opaque, shows up
-$blob2 = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-TRANSP:OPAQUE
-DTSTART:20110101T130000Z
-DTEND:20110101T140000Z
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- // transparent, hidden
-$blob3 = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-TRANSP:TRANSPARENT
-DTSTART:20110101T140000Z
-DTEND:20110101T150000Z
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- // cancelled, hidden
-$blob4 = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-STATUS:CANCELLED
-DTSTART:20110101T160000Z
-DTEND:20110101T170000Z
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- // tentative, shows up
-$blob5 = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-STATUS:TENTATIVE
-DTSTART:20110101T180000Z
-DTEND:20110101T190000Z
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- // outside of time-range, hidden
-$blob6 = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-DTSTART:20110101T090000Z
-DTEND:20110101T100000Z
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- // outside of time-range, hidden
-$blob7 = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-DTSTART:20110104T090000Z
-DTEND:20110104T100000Z
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- // using duration, shows up
-$blob8 = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-DTSTART:20110101T190000Z
-DURATION:PT1H
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- // Day-long event, shows up
-$blob9 = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-DTSTART;TYPE=DATE:20110102
-END:VEVENT
-END:VCALENDAR
-ICS;
-
-// No duration, does not show up
-$blob10 = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-DTSTART:20110101T200000Z
-END:VEVENT
-END:VCALENDAR
-ICS;
-
-// encoded as object, shows up
-$blob11 = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-DTSTART:20110101T210000Z
-DURATION:PT1H
-END:VEVENT
-END:VCALENDAR
-ICS;
-
-// Freebusy. Some parts show up
-$blob12 = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VFREEBUSY
-FREEBUSY:20110103T010000Z/20110103T020000Z
-FREEBUSY;FBTYPE=FREE:20110103T020000Z/20110103T030000Z
-FREEBUSY:20110103T030000Z/20110103T040000Z,20110103T040000Z/20110103T050000Z
-FREEBUSY:20120101T000000Z/20120101T010000Z
-FREEBUSY:20110103T050000Z/PT1H
-END:VFREEBUSY
-END:VCALENDAR
-ICS;
-
-// Yearly recurrence rule, shows up
-$blob13 = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-DTSTART:20100101T220000Z
-DTEND:20100101T230000Z
-RRULE:FREQ=YEARLY
-END:VEVENT
-END:VCALENDAR
-ICS;
-
-// Yearly recurrence rule + duration, shows up
-$blob14 = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-DTSTART:20100101T230000Z
-DURATION:PT1H
-RRULE:FREQ=YEARLY
-END:VEVENT
-END:VCALENDAR
-ICS;
-
-
- return array(
- $blob1,
- $blob2,
- $blob3,
- $blob4,
- $blob5,
- $blob6,
- $blob7,
- $blob8,
- $blob9,
- $blob10,
- Reader::read($blob11),
- $blob12,
- $blob13,
- $blob14,
- );
-
- }
-
- function testGenerator() {
-
- $gen = new FreeBusyGenerator(
- new \DateTime('20110101T110000Z', new \DateTimeZone('UTC')),
- new \DateTime('20110103T110000Z', new \DateTimeZone('UTC')),
- $this->getInput()
- );
-
- $result = $gen->getResult();
-
- $expected = array(
- '20110101T120000Z/20110101T130000Z',
- '20110101T130000Z/20110101T140000Z',
- '20110101T180000Z/20110101T190000Z',
- '20110101T190000Z/20110101T200000Z',
- '20110102T000000Z/20110103T000000Z',
- '20110101T210000Z/20110101T220000Z',
-
- '20110103T010000Z/20110103T020000Z',
- '20110103T030000Z/20110103T040000Z',
- '20110103T040000Z/20110103T050000Z',
- '20110103T050000Z/20110103T060000Z',
-
- '20110101T220000Z/20110101T230000Z',
- '20110101T230000Z/20110102T000000Z',
- );
-
- foreach($result->VFREEBUSY->FREEBUSY as $fb) {
-
- $this->assertContains((string)$fb, $expected);
-
- $k = array_search((string)$fb, $expected);
- unset($expected[$k]);
-
- }
- if (count($expected)>0) {
- $this->fail('There were elements in the expected array that were not found in the output: ' . "\n" . print_r($expected,true) . "\n" . $result->serialize());
-
- }
-
- }
-
- function testGeneratorBaseObject() {
-
- $obj = new Component('VCALENDAR');
- $obj->METHOD = 'PUBLISH';
-
- $gen = new FreeBusyGenerator();
- $gen->setObjects(array());
- $gen->setBaseObject($obj);
-
- $result = $gen->getResult();
-
- $this->assertEquals('PUBLISH', $result->METHOD->value);
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testInvalidArg() {
-
- $gen = new FreeBusyGenerator(
- new \DateTime('2012-01-01'),
- new \DateTime('2012-12-31'),
- new \StdClass()
- );
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Issue153Test.php b/vendor/sabre/vobject/tests/Sabre/VObject/Issue153Test.php
deleted file mode 100644
index 1cc14c161..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Issue153Test.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-class Issue153Test extends \PHPUnit_Framework_TestCase {
-
- function testRead() {
-
- $obj = Reader::read(file_get_contents(dirname(__FILE__) . '/issue153.vcf'));
- $this->assertEquals('Test Benutzer', (string)$obj->fn);
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Issue154Test.php b/vendor/sabre/vobject/tests/Sabre/VObject/Issue154Test.php
deleted file mode 100644
index ed9c7c3f4..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Issue154Test.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-class Issue154Test extends \PHPUnit_Framework_TestCase {
-
- function testStuff() {
-
- $vcard = new Component('VCARD');
- $vcard->VERSION = '3.0';
- $vcard->PHOTO = base64_encode('random_stuff');
- $vcard->PHOTO->add('BASE64',null);
- $vcard->UID = 'foo-bar';
-
- $result = $vcard->serialize();
- $expected = array(
- "BEGIN:VCARD",
- "VERSION:3.0",
- "PHOTO;BASE64:" . base64_encode('random_stuff'),
- "UID:foo-bar",
- "END:VCARD",
- "",
- );
-
- $this->assertEquals(implode("\r\n", $expected), $result);
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Issue48Test.php b/vendor/sabre/vobject/tests/Sabre/VObject/Issue48Test.php
deleted file mode 100644
index 980d432b9..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Issue48Test.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-use
- DateTime,
- DateTimeZone;
-
-class Issue48Test extends \PHPUnit_Framework_TestCase {
-
- function testExpand() {
-
- $input = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-UID:foo
-DTEND;TZID=Europe/Moscow:20130710T120000
-DTSTART;TZID=Europe/Moscow:20130710T110000
-RRULE:FREQ=DAILY;UNTIL=20130712T195959Z
-END:VEVENT
-BEGIN:VEVENT
-UID:foo
-DTEND;TZID=Europe/Moscow:20130713T120000
-DTSTART;TZID=Europe/Moscow:20130713T110000
-RECURRENCE-ID;TZID=Europe/Moscow:20130711T110000
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- $vcal = Reader::read($input);
- $this->assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal);
-
- $it = new RecurrenceIterator($vcal, 'foo');
-
- $result = iterator_to_array($it);
-
- $tz = new DateTimeZone('Europe/Moscow');
-
- $this->assertEquals(array(
- new DateTime('2013-07-10 11:00:00', $tz),
- new DateTime('2013-07-12 11:00:00', $tz),
- new DateTime('2013-07-13 11:00:00', $tz),
- ), $result);
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Issue50Test.php b/vendor/sabre/vobject/tests/Sabre/VObject/Issue50Test.php
deleted file mode 100644
index fdb012ba2..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Issue50Test.php
+++ /dev/null
@@ -1,128 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-use
- DateTime,
- DateTimeZone;
-
-class Issue50Test extends \PHPUnit_Framework_TestCase {
-
- function testExpand() {
-
- $input = <<<ICS
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
-BEGIN:VTIMEZONE
-TZID:Europe/Brussels
-X-LIC-LOCATION:Europe/Brussels
-BEGIN:DAYLIGHT
-TZOFFSETFROM:+0100
-TZOFFSETTO:+0200
-TZNAME:CEST
-DTSTART:19700329T020000
-RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
-END:DAYLIGHT
-BEGIN:STANDARD
-TZOFFSETFROM:+0200
-TZOFFSETTO:+0100
-TZNAME:CET
-DTSTART:19701025T030000
-RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
-END:STANDARD
-END:VTIMEZONE
-BEGIN:VEVENT
-CREATED:20130705T142510Z
-LAST-MODIFIED:20130715T132556Z
-DTSTAMP:20130715T132556Z
-UID:1aef0b27-3d92-4581-829a-11999dd36724
-SUMMARY:Werken
-RRULE:FREQ=DAILY;COUNT=5
-DTSTART;TZID=Europe/Brussels:20130715T090000
-DTEND;TZID=Europe/Brussels:20130715T170000
-LOCATION:Job
-DESCRIPTION:Vrij
-X-MOZ-GENERATION:9
-END:VEVENT
-BEGIN:VEVENT
-CREATED:20130715T081654Z
-LAST-MODIFIED:20130715T110931Z
-DTSTAMP:20130715T110931Z
-UID:1aef0b27-3d92-4581-829a-11999dd36724
-SUMMARY:Werken
-RECURRENCE-ID;TZID=Europe/Brussels:20130719T090000
-DTSTART;TZID=Europe/Brussels:20130719T070000
-DTEND;TZID=Europe/Brussels:20130719T150000
-SEQUENCE:1
-LOCATION:Job
-DESCRIPTION:Vrij
-X-MOZ-GENERATION:1
-END:VEVENT
-BEGIN:VEVENT
-CREATED:20130715T111654Z
-LAST-MODIFIED:20130715T132556Z
-DTSTAMP:20130715T132556Z
-UID:1aef0b27-3d92-4581-829a-11999dd36724
-SUMMARY:Werken
-RECURRENCE-ID;TZID=Europe/Brussels:20130716T090000
-DTSTART;TZID=Europe/Brussels:20130716T070000
-DTEND;TZID=Europe/Brussels:20130716T150000
-SEQUENCE:1
-LOCATION:Job
-X-MOZ-GENERATION:2
-END:VEVENT
-BEGIN:VEVENT
-CREATED:20130715T125942Z
-LAST-MODIFIED:20130715T130023Z
-DTSTAMP:20130715T130023Z
-UID:1aef0b27-3d92-4581-829a-11999dd36724
-SUMMARY:Werken
-RECURRENCE-ID;TZID=Europe/Brussels:20130717T090000
-DTSTART;TZID=Europe/Brussels:20130717T070000
-DTEND;TZID=Europe/Brussels:20130717T150000
-SEQUENCE:1
-LOCATION:Job
-X-MOZ-GENERATION:3
-END:VEVENT
-BEGIN:VEVENT
-CREATED:20130715T130024Z
-LAST-MODIFIED:20130715T130034Z
-DTSTAMP:20130715T130034Z
-UID:1aef0b27-3d92-4581-829a-11999dd36724
-SUMMARY:Werken
-RECURRENCE-ID;TZID=Europe/Brussels:20130718T090000
-DTSTART;TZID=Europe/Brussels:20130718T090000
-DTEND;TZID=Europe/Brussels:20130718T170000
-LOCATION:Job
-X-MOZ-GENERATION:5
-DESCRIPTION:Vrij
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- $vcal = Reader::read($input);
- $this->assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal);
-
- $it = new RecurrenceIterator($vcal, '1aef0b27-3d92-4581-829a-11999dd36724');
-
- $result = array();
- foreach($it as $instance) {
-
- $result[] = $instance;
-
- }
-
- $tz = new DateTimeZone('Europe/Brussels');
-
- $this->assertEquals(array(
- new DateTime('2013-07-15 09:00:00', $tz),
- new DateTime('2013-07-16 07:00:00', $tz),
- new DateTime('2013-07-17 07:00:00', $tz),
- new DateTime('2013-07-18 09:00:00', $tz),
- new DateTime('2013-07-19 07:00:00', $tz),
- ), $result);
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/ParameterTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/ParameterTest.php
deleted file mode 100644
index 90eb5d2aa..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/ParameterTest.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-class ParameterTest extends \PHPUnit_Framework_TestCase {
-
- function testSetup() {
-
- $param = new Parameter('name','value');
- $this->assertEquals('NAME',$param->name);
- $this->assertEquals('value',$param->value);
- $this->assertEquals('value',$param->getValue());
-
- }
-
- function testCastToString() {
-
- $param = new Parameter('name','value');
- $this->assertEquals('value',$param->__toString());
- $this->assertEquals('value',(string)$param);
-
- }
-
- function testSerialize() {
-
- $param = new Parameter('name','value');
- $this->assertEquals('NAME=value',$param->serialize());
-
- }
-
- function testSerializeEmpty() {
-
- $param = new Parameter('name',null);
- $this->assertEquals('NAME',$param->serialize());
-
- }
-
- function testSerializeColon() {
-
- $param = new Parameter('name','va:lue');
- $this->assertEquals('NAME="va:lue"',$param->serialize());
-
- }
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Property/CompoundTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/Property/CompoundTest.php
deleted file mode 100644
index 5d8cdaabb..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Property/CompoundTest.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Property;
-use Sabre\VObject\Component;
-
-class CompoundTest extends \PHPUnit_Framework_TestCase {
-
- function testSetParts() {
-
- $arr = array(
- 'ABC, Inc.',
- 'North American Division',
- 'Marketing;Sales',
- );
-
- $elem = new Compound('ORG');
- $elem->setParts($arr);
-
- $this->assertEquals('ABC\, Inc.;North American Division;Marketing\;Sales', $elem->value);
- $this->assertEquals(3, count($elem->getParts()));
- $parts = $elem->getParts();
- $this->assertEquals('Marketing;Sales', $parts[2]);
-
- }
-
- function testGetParts() {
-
- $str = 'ABC\, Inc.;North American Division;Marketing\;Sales';
-
- $elem = new Compound('ORG', $str);
-
- $this->assertEquals(3, count($elem->getParts()));
- $parts = $elem->getParts();
- $this->assertEquals('Marketing;Sales', $parts[2]);
- }
-
- function testGetPartsDefaultDelimiter() {
-
- $str = 'Hi!;Hello!';
-
- $elem = new Compound('X-FOO', $str);
-
- $this->assertEquals(array(
- 'Hi!',
- 'Hello!',
- ), $elem->getParts());
-
- }
-
- function testGetPartsNull() {
-
- $str = 'ABC\, Inc.;North American Division;Marketing\;Sales';
-
- $elem = new Compound('ORG', null);
-
- $this->assertEquals(0, count($elem->getParts()));
-
- }
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Property/DateTimeTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/Property/DateTimeTest.php
deleted file mode 100644
index b5b522e7d..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Property/DateTimeTest.php
+++ /dev/null
@@ -1,240 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Property;
-use Sabre\VObject\Component;
-
-class DateTimeTest extends \PHPUnit_Framework_TestCase {
-
- function testSetDateTime() {
-
- $tz = new \DateTimeZone('Europe/Amsterdam');
- $dt = new \DateTime('1985-07-04 01:30:00', $tz);
- $dt->setTimeZone($tz);
-
- $elem = new DateTime('DTSTART');
- $elem->setDateTime($dt);
-
- $this->assertEquals('19850704T013000', $elem->value);
- $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']);
- $this->assertEquals('DATE-TIME', (string)$elem['VALUE']);
-
- $this->assertTrue($elem->hasTime());
-
- }
-
- function testSetDateTimeLOCAL() {
-
- $tz = new \DateTimeZone('Europe/Amsterdam');
- $dt = new \DateTime('1985-07-04 01:30:00', $tz);
- $dt->setTimeZone($tz);
-
- $elem = new DateTime('DTSTART');
- $elem->setDateTime($dt, DateTime::LOCAL);
-
- $this->assertEquals('19850704T013000', $elem->value);
- $this->assertNull($elem['TZID']);
- $this->assertEquals('DATE-TIME', (string)$elem['VALUE']);
-
- $this->assertTrue($elem->hasTime());
- }
-
- function testSetDateTimeUTC() {
-
- $tz = new \DateTimeZone('GMT');
- $dt = new \DateTime('1985-07-04 01:30:00', $tz);
- $dt->setTimeZone($tz);
-
- $elem = new DateTime('DTSTART');
- $elem->setDateTime($dt, DateTime::UTC);
-
- $this->assertEquals('19850704T013000Z', $elem->value);
- $this->assertNull($elem['TZID']);
- $this->assertEquals('DATE-TIME', (string)$elem['VALUE']);
-
- $this->assertTrue($elem->hasTime());
- }
-
- function testSetDateTimeLOCALTZ() {
-
- $tz = new \DateTimeZone('Europe/Amsterdam');
- $dt = new \DateTime('1985-07-04 01:30:00', $tz);
- $dt->setTimeZone($tz);
-
- $elem = new DateTime('DTSTART');
- $elem->setDateTime($dt, DateTime::LOCALTZ);
-
- $this->assertEquals('19850704T013000', $elem->value);
- $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']);
- $this->assertEquals('DATE-TIME', (string)$elem['VALUE']);
-
- $this->assertTrue($elem->hasTime());
- }
-
- function testSetDateTimeDATE() {
-
- $tz = new \DateTimeZone('Europe/Amsterdam');
- $dt = new \DateTime('1985-07-04 01:30:00', $tz);
- $dt->setTimeZone($tz);
-
- $elem = new DateTime('DTSTART');
- $elem->setDateTime($dt, DateTime::DATE);
-
- $this->assertEquals('19850704', $elem->value);
- $this->assertNull($elem['TZID']);
- $this->assertEquals('DATE', (string)$elem['VALUE']);
-
- $this->assertFalse($elem->hasTime());
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testSetDateTimeInvalid() {
-
- $tz = new \DateTimeZone('Europe/Amsterdam');
- $dt = new \DateTime('1985-07-04 01:30:00', $tz);
- $dt->setTimeZone($tz);
-
- $elem = new DateTime('DTSTART');
- $elem->setDateTime($dt, 7);
-
- }
-
- function testGetDateTimeCached() {
-
- $tz = new \DateTimeZone('Europe/Amsterdam');
- $dt = new \DateTime('1985-07-04 01:30:00', $tz);
- $dt->setTimeZone($tz);
-
- $elem = new DateTime('DTSTART');
- $elem->setDateTime($dt);
-
- $this->assertEquals($elem->getDateTime(), $dt);
-
- }
-
- function testGetDateTimeDateNULL() {
-
- $elem = new DateTime('DTSTART');
- $dt = $elem->getDateTime();
-
- $this->assertNull($dt);
- $this->assertNull($elem->getDateType());
-
- }
-
- function testGetDateTimeDateDATE() {
-
- $elem = new DateTime('DTSTART','19850704');
- $dt = $elem->getDateTime();
-
- $this->assertInstanceOf('DateTime', $dt);
- $this->assertEquals('1985-07-04 00:00:00', $dt->format('Y-m-d H:i:s'));
- $this->assertEquals(DateTime::DATE, $elem->getDateType());
-
- }
-
-
- function testGetDateTimeDateLOCAL() {
-
- $elem = new DateTime('DTSTART','19850704T013000');
- $dt = $elem->getDateTime();
-
- $this->assertInstanceOf('DateTime', $dt);
- $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s'));
- $this->assertEquals(DateTime::LOCAL, $elem->getDateType());
-
- }
-
- function testGetDateTimeDateUTC() {
-
- $elem = new DateTime('DTSTART','19850704T013000Z');
- $dt = $elem->getDateTime();
-
- $this->assertInstanceOf('DateTime', $dt);
- $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s'));
- $this->assertEquals('UTC', $dt->getTimeZone()->getName());
- $this->assertEquals(DateTime::UTC, $elem->getDateType());
-
- }
-
- function testGetDateTimeDateLOCALTZ() {
-
- $elem = new DateTime('DTSTART','19850704T013000');
- $elem['TZID'] = 'Europe/Amsterdam';
-
- $dt = $elem->getDateTime();
-
- $this->assertInstanceOf('DateTime', $dt);
- $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s'));
- $this->assertEquals('Europe/Amsterdam', $dt->getTimeZone()->getName());
- $this->assertEquals(DateTime::LOCALTZ, $elem->getDateType());
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testGetDateTimeDateInvalid() {
-
- $elem = new DateTime('DTSTART','bla');
- $dt = $elem->getDateTime();
-
- }
-
- function testGetDateTimeWeirdTZ() {
-
- $elem = new DateTime('DTSTART','19850704T013000');
- $elem['TZID'] = '/freeassociation.sourceforge.net/Tzfile/Europe/Amsterdam';
-
-
- $event = new Component('VEVENT');
- $event->add($elem);
-
- $timezone = new Component('VTIMEZONE');
- $timezone->TZID = '/freeassociation.sourceforge.net/Tzfile/Europe/Amsterdam';
- $timezone->{'X-LIC-LOCATION'} = 'Europe/Amsterdam';
-
- $calendar = new Component('VCALENDAR');
- $calendar->add($event);
- $calendar->add($timezone);
-
- $dt = $elem->getDateTime();
-
- $this->assertInstanceOf('DateTime', $dt);
- $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s'));
- $this->assertEquals('Europe/Amsterdam', $dt->getTimeZone()->getName());
- $this->assertEquals(DateTime::LOCALTZ, $elem->getDateType());
-
- }
-
- function testGetDateTimeBadTimeZone() {
-
- $default = date_default_timezone_get();
- date_default_timezone_set('Canada/Eastern');
-
- $elem = new DateTime('DTSTART','19850704T013000');
- $elem['TZID'] = 'Moon';
-
-
- $event = new Component('VEVENT');
- $event->add($elem);
-
- $timezone = new Component('VTIMEZONE');
- $timezone->TZID = 'Moon';
- $timezone->{'X-LIC-LOCATION'} = 'Moon';
-
- $calendar = new Component('VCALENDAR');
- $calendar->add($event);
- $calendar->add($timezone);
-
- $dt = $elem->getDateTime();
-
- $this->assertInstanceOf('DateTime', $dt);
- $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s'));
- $this->assertEquals('Canada/Eastern', $dt->getTimeZone()->getName());
- $this->assertEquals(DateTime::LOCALTZ, $elem->getDateType());
- date_default_timezone_set($default);
-
- }
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Property/MultiDateTimeTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/Property/MultiDateTimeTest.php
deleted file mode 100644
index 177616652..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Property/MultiDateTimeTest.php
+++ /dev/null
@@ -1,208 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Property;
-
-class MultiDateTimeTest extends \PHPUnit_Framework_TestCase {
-
- function testSetDateTime() {
-
- $tz = new \DateTimeZone('Europe/Amsterdam');
- $dt1 = new \DateTime('1985-07-04 01:30:00', $tz);
- $dt2 = new \DateTime('1986-07-04 01:30:00', $tz);
- $dt1->setTimeZone($tz);
- $dt2->setTimeZone($tz);
-
- $elem = new MultiDateTime('DTSTART');
- $elem->setDateTimes(array($dt1,$dt2));
-
- $this->assertEquals('19850704T013000,19860704T013000', $elem->value);
- $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']);
- $this->assertEquals('DATE-TIME', (string)$elem['VALUE']);
-
- $this->assertTrue($elem->hasTime());
-
- }
-
- function testSetDateTimeLOCAL() {
-
- $tz = new \DateTimeZone('Europe/Amsterdam');
- $dt1 = new \DateTime('1985-07-04 01:30:00', $tz);
- $dt2 = new \DateTime('1986-07-04 01:30:00', $tz);
- $dt1->setTimeZone($tz);
- $dt2->setTimeZone($tz);
-
- $elem = new MultiDateTime('DTSTART');
- $elem->setDateTimes(array($dt1,$dt2), DateTime::LOCAL);
-
- $this->assertEquals('19850704T013000,19860704T013000', $elem->value);
- $this->assertNull($elem['TZID']);
- $this->assertEquals('DATE-TIME', (string)$elem['VALUE']);
-
- $this->assertTrue($elem->hasTime());
- }
-
- function testSetDateTimeUTC() {
-
- $tz = new \DateTimeZone('GMT');
- $dt1 = new \DateTime('1985-07-04 01:30:00', $tz);
- $dt2 = new \DateTime('1986-07-04 01:30:00', $tz);
- $dt1->setTimeZone($tz);
- $dt2->setTimeZone($tz);
-
- $elem = new MultiDateTime('DTSTART');
- $elem->setDateTimes(array($dt1,$dt2), DateTime::UTC);
-
- $this->assertEquals('19850704T013000Z,19860704T013000Z', $elem->value);
- $this->assertNull($elem['TZID']);
- $this->assertEquals('DATE-TIME', (string)$elem['VALUE']);
-
- $this->assertTrue($elem->hasTime());
- }
-
- function testSetDateTimeLOCALTZ() {
-
- $tz = new \DateTimeZone('Europe/Amsterdam');
- $dt1 = new \DateTime('1985-07-04 01:30:00', $tz);
- $dt2 = new \DateTime('1986-07-04 01:30:00', $tz);
- $dt1->setTimeZone($tz);
- $dt2->setTimeZone($tz);
-
- $elem = new MultiDateTime('DTSTART');
- $elem->setDateTimes(array($dt1,$dt2), DateTime::LOCALTZ);
-
- $this->assertEquals('19850704T013000,19860704T013000', $elem->value);
- $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']);
- $this->assertEquals('DATE-TIME', (string)$elem['VALUE']);
-
- $this->assertTrue($elem->hasTime());
- }
-
- function testSetDateTimeDATE() {
-
- $tz = new \DateTimeZone('Europe/Amsterdam');
- $dt1 = new \DateTime('1985-07-04 01:30:00', $tz);
- $dt2 = new \DateTime('1986-07-04 01:30:00', $tz);
- $dt1->settimezone($tz);
- $dt2->settimezone($tz);
-
- $elem = new MultiDateTime('DTSTART');
- $elem->setDateTimes(array($dt1,$dt2), DateTime::DATE);
-
- $this->assertEquals('19850704,19860704', $elem->value);
- $this->assertNull($elem['TZID']);
- $this->assertEquals('DATE', (string)$elem['VALUE']);
-
- $this->assertFalse($elem->hasTime());
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testSetDateTimeInvalid() {
-
- $tz = new \DateTimeZone('Europe/Amsterdam');
- $dt = new \DateTime('1985-07-04 01:30:00', $tz);
- $dt->setTimeZone($tz);
-
- $elem = new MultiDateTime('DTSTART');
- $elem->setDateTimes(array($dt), 7);
-
- }
-
- function testGetDateTimeCached() {
-
- $tz = new \DateTimeZone('Europe/Amsterdam');
- $dt1 = new \DateTime('1985-07-04 01:30:00', $tz);
- $dt2 = new \DateTime('1986-07-04 01:30:00', $tz);
- $dt1->settimezone($tz);
- $dt2->settimezone($tz);
-
- $elem = new MultiDateTime('DTSTART');
- $elem->setDateTimes(array($dt1,$dt2));
-
- $this->assertEquals($elem->getDateTimes(), array($dt1,$dt2));
-
- }
-
- function testGetDateTimeDateNULL() {
-
- $elem = new MultiDateTime('DTSTART');
- $dt = $elem->getDateTimes();
-
- $this->assertNull($dt);
- $this->assertNull($elem->getDateType());
-
- }
-
- function testGetDateTimeDateDATE() {
-
- $elem = new MultiDateTime('DTSTART','19850704,19860704');
- $dt = $elem->getDateTimes();
-
- $this->assertEquals('1985-07-04 00:00:00', $dt[0]->format('Y-m-d H:i:s'));
- $this->assertEquals('1986-07-04 00:00:00', $dt[1]->format('Y-m-d H:i:s'));
- $this->assertEquals(DateTime::DATE, $elem->getDateType());
-
- }
-
- function testGetDateTimeDateDATEReverse() {
-
- $elem = new MultiDateTime('DTSTART','19850704,19860704');
-
- $this->assertEquals(DateTime::DATE, $elem->getDateType());
-
- $dt = $elem->getDateTimes();
- $this->assertEquals('1985-07-04 00:00:00', $dt[0]->format('Y-m-d H:i:s'));
- $this->assertEquals('1986-07-04 00:00:00', $dt[1]->format('Y-m-d H:i:s'));
-
- }
-
-
- function testGetDateTimeDateLOCAL() {
-
- $elem = new DateTime('DTSTART','19850704T013000');
- $dt = $elem->getDateTime();
-
- $this->assertInstanceOf('DateTime', $dt);
- $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s'));
- $this->assertEquals(DateTime::LOCAL, $elem->getDateType());
-
- }
-
- function testGetDateTimeDateUTC() {
-
- $elem = new DateTime('DTSTART','19850704T013000Z');
- $dt = $elem->getDateTime();
-
- $this->assertInstanceOf('DateTime', $dt);
- $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s'));
- $this->assertEquals('UTC', $dt->getTimeZone()->getName());
- $this->assertEquals(DateTime::UTC, $elem->getDateType());
-
- }
-
- function testGetDateTimeDateLOCALTZ() {
-
- $elem = new DateTime('DTSTART','19850704T013000');
- $elem['TZID'] = 'Europe/Amsterdam';
-
- $dt = $elem->getDateTime();
-
- $this->assertInstanceOf('DateTime', $dt);
- $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s'));
- $this->assertEquals('Europe/Amsterdam', $dt->getTimeZone()->getName());
- $this->assertEquals(DateTime::LOCALTZ, $elem->getDateType());
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testGetDateTimeDateInvalid() {
-
- $elem = new DateTime('DTSTART','bla');
- $dt = $elem->getDateTime();
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/PropertyTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/PropertyTest.php
deleted file mode 100644
index 3bb289567..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/PropertyTest.php
+++ /dev/null
@@ -1,324 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-class PropertyTest extends \PHPUnit_Framework_TestCase {
-
- public function testToString() {
-
- $property = new Property('propname','propvalue');
- $this->assertEquals('PROPNAME', $property->name);
- $this->assertEquals('propvalue', $property->value);
- $this->assertEquals('propvalue', $property->__toString());
- $this->assertEquals('propvalue', (string)$property);
- $this->assertEquals('propvalue', $property->getValue());
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- public function testCreateNonScalar() {
-
- $property = new Property('propname',array());
-
- }
-
- public function testParameterExists() {
-
- $property = new Property('propname','propvalue');
- $property->parameters[] = new Parameter('paramname','paramvalue');
-
- $this->assertTrue(isset($property['PARAMNAME']));
- $this->assertTrue(isset($property['paramname']));
- $this->assertFalse(isset($property['foo']));
-
- }
-
- public function testParameterGet() {
-
- $property = new Property('propname','propvalue');
- $property->parameters[] = new Parameter('paramname','paramvalue');
-
- $this->assertInstanceOf('Sabre\\VObject\\Parameter',$property['paramname']);
-
- }
-
- public function testParameterNotExists() {
-
- $property = new Property('propname','propvalue');
- $property->parameters[] = new Parameter('paramname','paramvalue');
-
- $this->assertInternalType('null',$property['foo']);
-
- }
-
- public function testParameterMultiple() {
-
- $property = new Property('propname','propvalue');
- $property->parameters[] = new Parameter('paramname','paramvalue');
- $property->parameters[] = new Parameter('paramname','paramvalue');
-
- $this->assertInstanceOf('Sabre\\VObject\\Parameter',$property['paramname']);
- $this->assertEquals(2,count($property['paramname']));
-
- }
-
- public function testSetParameterAsString() {
-
- $property = new Property('propname','propvalue');
- $property['paramname'] = 'paramvalue';
-
- $this->assertEquals(1,count($property->parameters));
- $this->assertInstanceOf('Sabre\\VObject\\Parameter', $property->parameters[0]);
- $this->assertEquals('PARAMNAME',$property->parameters[0]->name);
- $this->assertEquals('paramvalue',$property->parameters[0]->value);
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- public function testSetParameterAsStringNoKey() {
-
- $property = new Property('propname','propvalue');
- $property[] = 'paramvalue';
-
- }
-
- public function testSetParameterObject() {
-
- $property = new Property('propname','propvalue');
- $param = new Parameter('paramname','paramvalue');
-
- $property[] = $param;
-
- $this->assertEquals(1,count($property->parameters));
- $this->assertEquals($param, $property->parameters[0]);
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- public function testSetParameterObjectWithKey() {
-
- $property = new Property('propname','propvalue');
- $param = new Parameter('paramname','paramvalue');
-
- $property['key'] = $param;
-
- }
-
-
- /**
- * @expectedException InvalidArgumentException
- */
- public function testSetParameterObjectRandomObject() {
-
- $property = new Property('propname','propvalue');
- $property[] = new \StdClass();
-
- }
-
- public function testUnsetParameter() {
-
- $property = new Property('propname','propvalue');
- $param = new Parameter('paramname','paramvalue');
- $property->parameters[] = $param;
-
- unset($property['PARAMNAME']);
- $this->assertEquals(0,count($property->parameters));
-
- }
-
- public function testParamCount() {
-
- $property = new Property('propname','propvalue');
- $param = new Parameter('paramname','paramvalue');
- $property->parameters[] = $param;
- $property->parameters[] = clone $param;
-
- $this->assertEquals(2,count($property->parameters));
-
- }
-
- public function testSerialize() {
-
- $property = new Property('propname','propvalue');
-
- $this->assertEquals("PROPNAME:propvalue\r\n",$property->serialize());
-
- }
-
- public function testSerializeParam() {
-
- $property = new Property('propname','propvalue');
- $property->parameters[] = new Parameter('paramname','paramvalue');
- $property->parameters[] = new Parameter('paramname2','paramvalue2');
-
- $this->assertEquals("PROPNAME;PARAMNAME=paramvalue;PARAMNAME2=paramvalue2:propvalue\r\n",$property->serialize());
-
- }
-
- public function testSerializeNewLine() {
-
- $property = new Property('propname',"line1\nline2");
-
- $this->assertEquals("PROPNAME:line1\\nline2\r\n",$property->serialize());
-
- }
-
- public function testSerializeLongLine() {
-
- $value = str_repeat('!',200);
- $property = new Property('propname',$value);
-
- $expected = "PROPNAME:" . str_repeat('!',66) . "\r\n " . str_repeat('!',74) . "\r\n " . str_repeat('!',60) . "\r\n";
-
- $this->assertEquals($expected,$property->serialize());
-
- }
-
- public function testSerializeUTF8LineFold() {
-
- $value = str_repeat('!',65) . "\xc3\xa4bla"; // inserted umlaut-a
- $property = new Property('propname', $value);
- $expected = "PROPNAME:" . str_repeat('!',65) . "\r\n \xc3\xa4bla\r\n";
- $this->assertEquals($expected, $property->serialize());
-
- }
-
- public function testGetIterator() {
-
- $it = new ElementList(array());
- $property = new Property('propname','propvalue');
- $property->setIterator($it);
- $this->assertEquals($it,$property->getIterator());
-
- }
-
-
- public function testGetIteratorDefault() {
-
- $property = new Property('propname','propvalue');
- $it = $property->getIterator();
- $this->assertTrue($it instanceof ElementList);
- $this->assertEquals(1,count($it));
-
- }
-
- function testAddScalar() {
-
- $property = new Property('EMAIL');
-
- $property->add('myparam','value');
-
- $this->assertEquals(1, count($property->parameters));
-
- $this->assertTrue($property->parameters[0] instanceof Parameter);
- $this->assertEquals('MYPARAM',$property->parameters[0]->name);
- $this->assertEquals('value',$property->parameters[0]->value);
-
- }
-
- function testAddParameter() {
-
- $prop = new Property('EMAIL');
-
- $prop->add(new Parameter('MYPARAM','value'));
-
- $this->assertEquals(1, count($prop->parameters));
- $this->assertEquals('MYPARAM',$prop['myparam']->name);
-
- }
-
- function testAddParameterTwice() {
-
- $prop = new Property('EMAIL');
-
- $prop->add(new Parameter('MYPARAM', 'value1'));
- $prop->add(new Parameter('MYPARAM', 'value2'));
-
- $this->assertEquals(2, count($prop->parameters));
-
- $this->assertEquals('MYPARAM',$prop['MYPARAM']->name);
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testAddArgFail() {
-
- $prop = new Property('EMAIL');
- $prop->add(new Parameter('MPARAM'),'hello');
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testAddArgFail2() {
-
- $property = new Property('EMAIL','value');
- $property->add(array());
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testAddArgFail3() {
-
- $property = new Property('EMAIL','value');
- $property->add('hello',array());
-
- }
-
- function testClone() {
-
- $property = new Property('EMAIL','value');
- $property['FOO'] = 'BAR';
-
- $property2 = clone $property;
-
- $property['FOO'] = 'BAZ';
- $this->assertEquals('BAR', (string)$property2['FOO']);
-
- }
-
- function testCreateParams() {
-
- $property = Property::create('X-PROP', 'value', array(
- 'param1' => 'value1',
- 'param2' => array('value2', 'value3')
- ));
-
- $this->assertEquals(1, count($property['PARAM1']));
- $this->assertEquals(2, count($property['PARAM2']));
-
- }
-
- function testValidateNonUTF8() {
-
- $property = Property::create('X-PROP', "Bla\x00");
- $result = $property->validate(Property::REPAIR);
-
- $this->assertEquals('Property is not valid UTF-8!', $result[0]['message']);
- $this->assertEquals('Bla', $property->value);
-
- }
-
-
- function testValidateBadPropertyName() {
-
- $property = Property::create("X_*&PROP*", "Bla");
- $result = $property->validate(Property::REPAIR);
-
- $this->assertEquals($result[0]['message'], 'The propertyname: X_*&PROP* contains invalid characters. Only A-Z, 0-9 and - are allowed');
- $this->assertEquals('X-PROP', $property->name);
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/ReaderTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/ReaderTest.php
deleted file mode 100644
index 0969c6e52..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/ReaderTest.php
+++ /dev/null
@@ -1,367 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-class ReaderTest extends \PHPUnit_Framework_TestCase {
-
- function testReadComponent() {
-
- $data = "BEGIN:VCALENDAR\r\nEND:VCALENDAR";
-
- $result = Reader::read($data);
-
- $this->assertInstanceOf('Sabre\\VObject\\Component', $result);
- $this->assertEquals('VCALENDAR', $result->name);
- $this->assertEquals(0, count($result->children));
-
- }
-
- function testReadComponentUnixNewLine() {
-
- $data = "BEGIN:VCALENDAR\nEND:VCALENDAR";
-
- $result = Reader::read($data);
-
- $this->assertInstanceOf('Sabre\\VObject\\Component', $result);
- $this->assertEquals('VCALENDAR', $result->name);
- $this->assertEquals(0, count($result->children));
-
- }
-
- function testReadComponentMacNewLine() {
-
- $data = "BEGIN:VCALENDAR\rEND:VCALENDAR";
-
- $result = Reader::read($data);
-
- $this->assertInstanceOf('Sabre\\VObject\\Component', $result);
- $this->assertEquals('VCALENDAR', $result->name);
- $this->assertEquals(0, count($result->children));
-
- }
-
- function testReadComponentLineFold() {
-
- $data = "BEGIN:\r\n\tVCALENDAR\r\nE\r\n ND:VCALENDAR";
-
- $result = Reader::read($data);
-
- $this->assertInstanceOf('Sabre\\VObject\\Component', $result);
- $this->assertEquals('VCALENDAR', $result->name);
- $this->assertEquals(0, count($result->children));
-
- }
-
- /**
- * @expectedException Sabre\VObject\ParseException
- */
- function testReadCorruptComponent() {
-
- $data = "BEGIN:VCALENDAR\r\nEND:FOO";
-
- $result = Reader::read($data);
-
- }
-
- function testReadProperty() {
-
- $data = "PROPNAME:propValue";
- $result = Reader::read($data);
-
- $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
- $this->assertEquals('PROPNAME', $result->name);
- $this->assertEquals('propValue', $result->value);
-
- }
-
- function testReadPropertyWithNewLine() {
-
- $data = 'PROPNAME:Line1\\nLine2\\NLine3\\\\Not the 4th line!';
- $result = Reader::read($data);
-
- $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
- $this->assertEquals('PROPNAME', $result->name);
- $this->assertEquals("Line1\nLine2\nLine3\\Not the 4th line!", $result->value);
-
- }
-
- function testReadMappedProperty() {
-
- $data = "DTSTART:20110529";
- $result = Reader::read($data);
-
- $this->assertInstanceOf('Sabre\\VObject\\Property\\DateTime', $result);
- $this->assertEquals('DTSTART', $result->name);
- $this->assertEquals('20110529', $result->value);
-
- }
-
- function testReadMappedPropertyGrouped() {
-
- $data = "foo.DTSTART:20110529";
- $result = Reader::read($data);
-
- $this->assertInstanceOf('Sabre\\VObject\\Property\\DateTime', $result);
- $this->assertEquals('DTSTART', $result->name);
- $this->assertEquals('20110529', $result->value);
-
- }
-
-
- /**
- * @expectedException Sabre\VObject\ParseException
- */
- function testReadBrokenLine() {
-
- $data = "PROPNAME;propValue";
- $result = Reader::read($data);
-
- }
-
- function testReadPropertyInComponent() {
-
- $data = array(
- "BEGIN:VCALENDAR",
- "PROPNAME:propValue",
- "END:VCALENDAR"
- );
-
- $result = Reader::read(implode("\r\n",$data));
-
- $this->assertInstanceOf('Sabre\\VObject\\Component', $result);
- $this->assertEquals('VCALENDAR', $result->name);
- $this->assertEquals(1, count($result->children));
- $this->assertInstanceOf('Sabre\\VObject\\Property', $result->children[0]);
- $this->assertEquals('PROPNAME', $result->children[0]->name);
- $this->assertEquals('propValue', $result->children[0]->value);
-
- }
- function testReadNestedComponent() {
-
- $data = array(
- "BEGIN:VCALENDAR",
- "BEGIN:VTIMEZONE",
- "BEGIN:DAYLIGHT",
- "END:DAYLIGHT",
- "END:VTIMEZONE",
- "END:VCALENDAR"
- );
-
- $result = Reader::read(implode("\r\n",$data));
-
- $this->assertInstanceOf('Sabre\\VObject\\Component', $result);
- $this->assertEquals('VCALENDAR', $result->name);
- $this->assertEquals(1, count($result->children));
- $this->assertInstanceOf('Sabre\\VObject\\Component', $result->children[0]);
- $this->assertEquals('VTIMEZONE', $result->children[0]->name);
- $this->assertEquals(1, count($result->children[0]->children));
- $this->assertInstanceOf('Sabre\\VObject\\Component', $result->children[0]->children[0]);
- $this->assertEquals('DAYLIGHT', $result->children[0]->children[0]->name);
-
-
- }
-
- function testReadPropertyParameter() {
-
- $data = "PROPNAME;PARAMNAME=paramvalue:propValue";
- $result = Reader::read($data);
-
- $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
- $this->assertEquals('PROPNAME', $result->name);
- $this->assertEquals('propValue', $result->value);
- $this->assertEquals(1, count($result->parameters));
- $this->assertEquals('PARAMNAME', $result->parameters[0]->name);
- $this->assertEquals('paramvalue', $result->parameters[0]->value);
-
- }
-
- function testReadPropertyNoValue() {
-
- $data = "PROPNAME;PARAMNAME:propValue";
- $result = Reader::read($data);
-
- $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
- $this->assertEquals('PROPNAME', $result->name);
- $this->assertEquals('propValue', $result->value);
- $this->assertEquals(1, count($result->parameters));
- $this->assertEquals('PARAMNAME', $result->parameters[0]->name);
-
- $this->assertNull($result->parameters[0]->value);
-
- }
-
- function testReadPropertyParameterExtraColon() {
-
- $data = "PROPNAME;PARAMNAME=paramvalue:propValue:anotherrandomstring";
- $result = Reader::read($data);
-
- $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
- $this->assertEquals('PROPNAME', $result->name);
- $this->assertEquals('propValue:anotherrandomstring', $result->value);
- $this->assertEquals(1, count($result->parameters));
- $this->assertEquals('PARAMNAME', $result->parameters[0]->name);
- $this->assertEquals('paramvalue', $result->parameters[0]->value);
-
- }
-
- function testReadProperty2Parameters() {
-
- $data = "PROPNAME;PARAMNAME=paramvalue;PARAMNAME2=paramvalue2:propValue";
- $result = Reader::read($data);
-
- $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
- $this->assertEquals('PROPNAME', $result->name);
- $this->assertEquals('propValue', $result->value);
- $this->assertEquals(2, count($result->parameters));
- $this->assertEquals('PARAMNAME', $result->parameters[0]->name);
- $this->assertEquals('paramvalue', $result->parameters[0]->value);
- $this->assertEquals('PARAMNAME2', $result->parameters[1]->name);
- $this->assertEquals('paramvalue2', $result->parameters[1]->value);
-
- }
-
- function testReadPropertyParameterQuoted() {
-
- $data = "PROPNAME;PARAMNAME=\"paramvalue\":propValue";
- $result = Reader::read($data);
-
- $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
- $this->assertEquals('PROPNAME', $result->name);
- $this->assertEquals('propValue', $result->value);
- $this->assertEquals(1, count($result->parameters));
- $this->assertEquals('PARAMNAME', $result->parameters[0]->name);
- $this->assertEquals('paramvalue', $result->parameters[0]->value);
-
- }
- function testReadPropertyParameterNewLines() {
-
- $data = "PROPNAME;PARAMNAME=paramvalue1\\nvalue2\\\\nvalue3:propValue";
- $result = Reader::read($data);
-
- $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
- $this->assertEquals('PROPNAME', $result->name);
- $this->assertEquals('propValue', $result->value);
-
- $this->assertEquals(1, count($result->parameters));
- $this->assertEquals('PARAMNAME', $result->parameters[0]->name);
- $this->assertEquals("paramvalue1\nvalue2\\nvalue3", $result->parameters[0]->value);
-
- }
-
- function testReadPropertyParameterQuotedColon() {
-
- $data = "PROPNAME;PARAMNAME=\"param:value\":propValue";
- $result = Reader::read($data);
-
- $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
- $this->assertEquals('PROPNAME', $result->name);
- $this->assertEquals('propValue', $result->value);
- $this->assertEquals(1, count($result->parameters));
- $this->assertEquals('PARAMNAME', $result->parameters[0]->name);
- $this->assertEquals('param:value', $result->parameters[0]->value);
-
- }
-
- function testReadForgiving() {
-
- $data = array(
- "BEGIN:VCALENDAR",
- "X_PROP:propValue",
- "END:VCALENDAR"
- );
-
- $caught = false;
- try {
- $result = Reader::read(implode("\r\n",$data));
- } catch (ParseException $e) {
- $caught = true;
- }
-
- $this->assertEquals(true, $caught);
-
- $result = Reader::read(implode("\r\n",$data), Reader::OPTION_FORGIVING);
-
- $expected = implode("\r\n", array(
- "BEGIN:VCALENDAR",
- "X_PROP:propValue",
- "END:VCALENDAR",
- ""
- ));
-
- $this->assertEquals($expected, $result->serialize());
-
-
- }
-
- function testReadWithInvalidLine() {
-
- $data = array(
- "BEGIN:VCALENDAR",
- "DESCRIPTION:propValue",
- "Yes, we've actually seen a file with non-idented property values on multiple lines",
- "END:VCALENDAR"
- );
-
- $caught = false;
- try {
- $result = Reader::read(implode("\r\n",$data));
- } catch (ParseException $e) {
- $caught = true;
- }
-
- $this->assertEquals(true, $caught);
-
- $result = Reader::read(implode("\r\n",$data), Reader::OPTION_IGNORE_INVALID_LINES);
-
- $expected = implode("\r\n", array(
- "BEGIN:VCALENDAR",
- "DESCRIPTION:propValue",
- "END:VCALENDAR",
- ""
- ));
-
- $this->assertEquals($expected, $result->serialize());
-
-
- }
-
- /**
- * Reported as Issue 32.
- *
- * @expectedException \Sabre\VObject\ParseException
- */
- function testReadIncompleteFile() {
-
- $input = <<<ICS
-BEGIN:VCALENDAR
-VERSION:1.0
-BEGIN:VEVENT
-X-FUNAMBOL-FOLDER:DEFAULT_FOLDER
-X-FUNAMBOL-ALLDAY:0
-DTSTART:20111017T110000Z
-DTEND:20111017T123000Z
-X-MICROSOFT-CDO-BUSYSTATUS:BUSY
-CATEGORIES:
-LOCATION;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:Netviewer Meeting
-PRIORITY:1
-STATUS:3
-X-MICROSOFT-CDO-REPLYTIME:20111017T064200Z
-SUMMARY;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:Kopieren: test
-CLASS:PUBLIC
-AALARM:
-RRULE:
-X-FUNAMBOL-BILLINGINFO:
-X-FUNAMBOL-COMPANIES:
-X-FUNAMBOL-MILEAGE:
-X-FUNAMBOL-NOAGING:0
-ATTENDEE;STATUS=NEEDS ACTION;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:'Heino' heino@test.com
-ATTENDEE;STATUS=NEEDS ACTION;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:'Markus' test@test.com
-ATTENDEE;STATUS=NEEDS AC
-ICS;
-
- Reader::read($input);
-
- }
-
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorFifthTuesdayProblemTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorFifthTuesdayProblemTest.php
deleted file mode 100644
index 069832a0f..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorFifthTuesdayProblemTest.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-class RecurrenceIteratorFifthTuesdayProblemTest extends \PHPUnit_Framework_TestCase {
-
- function testGetDTEnd() {
-
- $ics = <<<ICS
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Apple Inc.//iCal 4.0.4//EN
-CALSCALE:GREGORIAN
-BEGIN:VEVENT
-TRANSP:OPAQUE
-DTEND;TZID=America/New_York:20070925T170000
-UID:uuid
-DTSTAMP:19700101T000000Z
-LOCATION:
-DESCRIPTION:
-STATUS:CONFIRMED
-SEQUENCE:18
-SUMMARY:Stuff
-DTSTART;TZID=America/New_York:20070925T160000
-CREATED:20071004T144642Z
-RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=20071030T035959Z;BYDAY=5TU
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- $vObject = Reader::read($ics);
- $it = new RecurrenceIterator($vObject, (string)$vObject->VEVENT->UID);
-
- while($it->valid()) {
- $it->next();
- }
-
- // If we got here, it means we were successful. The bug that was in the
- // system before would fail on the 5th tuesday of the month, if the 5th
- // tuesday did not exist.
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorIncorrectExpandTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorIncorrectExpandTest.php
deleted file mode 100644
index 9adc8537f..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorIncorrectExpandTest.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-use
- DateTime,
- DateTimeZone;
-
-/**
- * This is a unittest for Issue #53.
- */
-class RecurrenceIteratorIncorrectExpandTest extends \PHPUnit_Framework_TestCase {
-
- function testExpand() {
-
- $input = <<<ICS
-BEGIN:VCALENDAR
-VERSION:2.0
-BEGIN:VEVENT
-UID:foo
-DTSTART:20130711T050000Z
-DTEND:20130711T053000Z
-RRULE:FREQ=DAILY;INTERVAL=1;COUNT=2
-END:VEVENT
-BEGIN:VEVENT
-UID:foo
-DTSTART:20130719T050000Z
-DTEND:20130719T053000Z
-RECURRENCE-ID:20130712T050000Z
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- $vcal = Reader::read($input);
- $this->assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal);
-
- $vcal->expand(new DateTime('2011-01-01'), new DateTime('2014-01-01'));
-
- $result = $vcal->serialize();
-
- $output = <<<ICS
-BEGIN:VCALENDAR
-VERSION:2.0
-BEGIN:VEVENT
-UID:foo
-DTSTART;VALUE=DATE-TIME:20130711T050000Z
-DTEND;VALUE=DATE-TIME:20130711T053000Z
-END:VEVENT
-BEGIN:VEVENT
-UID:foo
-DTSTART:20130719T050000Z
-DTEND:20130719T053000Z
-RECURRENCE-ID:20130712T050000Z
-END:VEVENT
-END:VCALENDAR
-
-ICS;
- $this->assertEquals($output, str_replace("\r", "", $result));
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorInfiniteLoopProblemTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorInfiniteLoopProblemTest.php
deleted file mode 100644
index 670c39bae..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorInfiniteLoopProblemTest.php
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-use DateTime;
-use DateTimeZone;
-
-class RecurrenceIteratorInfiniteLoopProblemTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * This bug came from a Fruux customer. This would result in a never-ending
- * request.
- */
- function testFastForwardTooFar() {
-
- $ev = Component::create('VEVENT');
- $ev->DTSTART = '20090420T180000Z';
- $ev->RRULE = 'FREQ=WEEKLY;BYDAY=MO;UNTIL=20090704T205959Z;INTERVAL=1';
-
- $this->assertFalse($ev->isInTimeRange(new DateTime('2012-01-01 12:00:00'),new DateTime('3000-01-01 00:00:00')));
-
- }
-
- /**
- * Different bug, also likely an infinite loop.
- */
- function testYearlyByMonthLoop() {
-
- $ev = Component::create('VEVENT');
- $ev->UID = 'uuid';
- $ev->DTSTART = '20120101T154500';
- $ev->DTSTART['TZID'] = 'Europe/Berlin';
- $ev->RRULE = 'FREQ=YEARLY;INTERVAL=1;UNTIL=20120203T225959Z;BYMONTH=2;BYSETPOS=1;BYDAY=SU,MO,TU,WE,TH,FR,SA';
- $ev->DTEND = '20120101T164500';
- $ev->DTEND['TZID'] = 'Europe/Berlin';
-
- // This recurrence rule by itself is a yearly rule that should happen
- // every february.
- //
- // The BYDAY part expands this to every day of the month, but the
- // BYSETPOS limits this to only the 1st day of the month. Very crazy
- // way to specify this, and could have certainly been a lot easier.
- $cal = Component::create('VCALENDAR');
- $cal->add($ev);
-
- $it = new RecurrenceIterator($cal,'uuid');
- $it->fastForward(new DateTime('2012-01-29 23:00:00', new DateTimeZone('UTC')));
-
- $collect = array();
-
- while($it->valid()) {
- $collect[] = $it->getDTSTART();
- if ($it->getDTSTART() > new DateTime('2013-02-05 22:59:59', new DateTimeZone('UTC'))) {
- break;
- }
- $it->next();
-
- }
-
- $this->assertEquals(
- array(new DateTime('2012-02-01 15:45:00', new DateTimeZone('Europe/Berlin'))),
- $collect
- );
-
- }
-
- /**
- * Something, somewhere produced an ics with an interval set to 0. Because
- * this means we increase the current day (or week, month) by 0, this also
- * results in an infinite loop.
- *
- * @expectedException InvalidArgumentException
- * @return void
- */
- function testZeroInterval() {
-
- $ev = Component::create('VEVENT');
- $ev->UID = 'uuid';
- $ev->DTSTART = '20120824T145700Z';
- $ev->RRULE = 'FREQ=YEARLY;INTERVAL=0';
- $cal = Component::create('VCALENDAR');
- $cal->add($ev);
-
- $it = new RecurrenceIterator($cal,'uuid');
- $it->fastForward(new DateTime('2013-01-01 23:00:00', new DateTimeZone('UTC')));
-
- // if we got this far.. it means we are no longer infinitely looping
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorMinusOneProblemTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorMinusOneProblemTest.php
deleted file mode 100644
index 2c17f9f6b..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorMinusOneProblemTest.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-class RecurrenceIteratorMinusOneProblemTest extends \PHPUnit_Framework_TestCase {
-
- function testMinusOne() {
-
- $ics = <<<ICS
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-DTSTAMP:20120314T203127Z
-UID:foo
-SUMMARY:foo
-RRULE:FREQ=YEARLY;UNTIL=20120314
-DTSTART;VALUE=DATE:20120315
-DTEND;VALUE=DATE:20120316
-SEQUENCE:1
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- $vObject = Reader::read($ics);
- $it = new RecurrenceIterator($vObject, (string)$vObject->VEVENT->UID);
-
- $this->assertTrue($it->valid());
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorMissingOverriddenTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorMissingOverriddenTest.php
deleted file mode 100644
index f311329db..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorMissingOverriddenTest.php
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-use
- DateTime,
- DateTimeZone;
-
-class RecurrenceIteratorMissingOverriddenTest extends \PHPUnit_Framework_TestCase {
-
- function testExpand() {
-
- $input = <<<ICS
-BEGIN:VCALENDAR
-VERSION:2.0
-BEGIN:VEVENT
-UID:foo
-DTSTART:20130727T120000Z
-DURATION:PT1H
-RRULE:FREQ=DAILY;COUNT=2
-SUMMARY:A
-END:VEVENT
-BEGIN:VEVENT
-RECURRENCE-ID:20130728T120000Z
-UID:foo
-DTSTART:20140101T120000Z
-DURATION:PT1H
-SUMMARY:B
-END:VEVENT
-END:VCALENDAR
-ICS;
-
- $vcal = Reader::read($input);
- $this->assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal);
-
- $vcal->expand(new DateTime('2011-01-01'), new DateTime('2015-01-01'));
-
- $result = $vcal->serialize();
-
- $output = <<<ICS
-BEGIN:VCALENDAR
-VERSION:2.0
-BEGIN:VEVENT
-UID:foo
-DTSTART;VALUE=DATE-TIME:20130727T120000Z
-DURATION:PT1H
-SUMMARY:A
-END:VEVENT
-BEGIN:VEVENT
-RECURRENCE-ID:20130728T120000Z
-UID:foo
-DTSTART:20140101T120000Z
-DURATION:PT1H
-SUMMARY:B
-END:VEVENT
-END:VCALENDAR
-
-ICS;
- $this->assertEquals($output, str_replace("\r","",$result));
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorTest.php
deleted file mode 100644
index 5988f16e2..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorTest.php
+++ /dev/null
@@ -1,1425 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-use DateTime;
-use DateTimeZone;
-
-class RecurrenceIteratorTest extends \PHPUnit_Framework_TestCase {
-
- function testValues() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=DAILY;BYHOUR=10;BYMINUTE=5;BYSECOND=16;BYWEEKNO=32;BYYEARDAY=100,200';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-10-07'),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertTrue($it->isInfinite());
- $this->assertEquals(array(10), $it->byHour);
- $this->assertEquals(array(5), $it->byMinute);
- $this->assertEquals(array(16), $it->bySecond);
- $this->assertEquals(array(32), $it->byWeekNo);
- $this->assertEquals(array(100,200), $it->byYearDay);
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- * @depends testValues
- */
- function testInvalidFreq() {
-
- $ev = new Component('VEVENT');
- $ev->RRULE = 'FREQ=SMONTHLY;INTERVAL=3;UNTIL=20111025T000000Z';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-10-07'),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testVCalendarNoUID() {
-
- $vcal = new Component('VCALENDAR');
- $it = new RecurrenceIterator($vcal);
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testVCalendarInvalidUID() {
-
- $vcal = new Component('VCALENDAR');
- $it = new RecurrenceIterator($vcal,'foo');
-
- }
-
- /**
- * @depends testValues
- */
- function testHourly() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=HOURLY;INTERVAL=3;UNTIL=20111025T000000Z';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-10-07 12:00:00', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,$ev->uid);
-
- $this->assertEquals('hourly', $it->frequency);
- $this->assertEquals(3, $it->interval);
- $this->assertEquals(new DateTime('2011-10-25', new DateTimeZone('UTC')), $it->until);
-
- // Max is to prevent overflow
- $max = 12;
- $result = array();
- foreach($it as $item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-10-07 12:00:00', $tz),
- new DateTime('2011-10-07 15:00:00', $tz),
- new DateTime('2011-10-07 18:00:00', $tz),
- new DateTime('2011-10-07 21:00:00', $tz),
- new DateTime('2011-10-08 00:00:00', $tz),
- new DateTime('2011-10-08 03:00:00', $tz),
- new DateTime('2011-10-08 06:00:00', $tz),
- new DateTime('2011-10-08 09:00:00', $tz),
- new DateTime('2011-10-08 12:00:00', $tz),
- new DateTime('2011-10-08 15:00:00', $tz),
- new DateTime('2011-10-08 18:00:00', $tz),
- new DateTime('2011-10-08 21:00:00', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testDaily() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=DAILY;INTERVAL=3;UNTIL=20111025T000000Z';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,$ev->uid);
-
- $this->assertEquals('daily', $it->frequency);
- $this->assertEquals(3, $it->interval);
- $this->assertEquals(new DateTime('2011-10-25', new DateTimeZone('UTC')), $it->until);
-
- // Max is to prevent overflow
- $max = 12;
- $result = array();
- foreach($it as $item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-10-07', $tz),
- new DateTime('2011-10-10', $tz),
- new DateTime('2011-10-13', $tz),
- new DateTime('2011-10-16', $tz),
- new DateTime('2011-10-19', $tz),
- new DateTime('2011-10-22', $tz),
- new DateTime('2011-10-25', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testNoRRULE() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,$ev->uid);
-
- $this->assertEquals('daily', $it->frequency);
- $this->assertEquals(1, $it->interval);
-
- // Max is to prevent overflow
- $max = 12;
- $result = array();
- foreach($it as $item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-10-07', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testDailyByDayByHour() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=DAILY;BYDAY=SA,SU;BYHOUR=6,7';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-10-08 06:00:00', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('daily', $it->frequency);
- $this->assertEquals(1, $it->interval);
- $this->assertEquals(array('6','7'), $it->byHour);
- $this->assertEquals(array('SA','SU'), $it->byDay);
-
- // Grabbing the next 12 items
- $max = 12;
- $result = array();
- foreach($it as $item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new datetime('2011-10-08 06:00:00', $tz),
- new datetime('2011-10-08 07:00:00', $tz),
- new datetime('2011-10-09 06:00:00', $tz),
- new datetime('2011-10-09 07:00:00', $tz),
- new datetime('2011-10-15 06:00:00', $tz),
- new datetime('2011-10-15 07:00:00', $tz),
- new datetime('2011-10-16 06:00:00', $tz),
- new datetime('2011-10-16 07:00:00', $tz),
- new datetime('2011-10-22 06:00:00', $tz),
- new datetime('2011-10-22 07:00:00', $tz),
- new datetime('2011-10-23 06:00:00', $tz),
- new datetime('2011-10-23 07:00:00', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testDailyByHour() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=DAILY;INTERVAL=2;BYHOUR=10,11,12,13,14,15';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2012-10-11 12:00:00', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('daily', $it->frequency);
- $this->assertEquals(2, $it->interval);
- $this->assertEquals(array('10','11','12','13','14','15'), $it->byHour);
-
- // Grabbing the next 12 items
- $max = 12;
- $result = array();
- foreach($it as $item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new datetime('2012-10-11 12:00:00', $tz),
- new datetime('2012-10-11 13:00:00', $tz),
- new datetime('2012-10-11 14:00:00', $tz),
- new datetime('2012-10-11 15:00:00', $tz),
- new datetime('2012-10-13 10:00:00', $tz),
- new datetime('2012-10-13 11:00:00', $tz),
- new datetime('2012-10-13 12:00:00', $tz),
- new datetime('2012-10-13 13:00:00', $tz),
- new datetime('2012-10-13 14:00:00', $tz),
- new datetime('2012-10-13 15:00:00', $tz),
- new datetime('2012-10-15 10:00:00', $tz),
- new datetime('2012-10-15 11:00:00', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testDailyByDay() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=DAILY;INTERVAL=2;BYDAY=TU,WE,FR';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('daily', $it->frequency);
- $this->assertEquals(2, $it->interval);
- $this->assertEquals(array('TU','WE','FR'), $it->byDay);
-
- // Grabbing the next 12 items
- $max = 12;
- $result = array();
- foreach($it as $item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-10-07', $tz),
- new DateTime('2011-10-11', $tz),
- new DateTime('2011-10-19', $tz),
- new DateTime('2011-10-21', $tz),
- new DateTime('2011-10-25', $tz),
- new DateTime('2011-11-02', $tz),
- new DateTime('2011-11-04', $tz),
- new DateTime('2011-11-08', $tz),
- new DateTime('2011-11-16', $tz),
- new DateTime('2011-11-18', $tz),
- new DateTime('2011-11-22', $tz),
- new DateTime('2011-11-30', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testWeekly() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;COUNT=10';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('weekly', $it->frequency);
- $this->assertEquals(2, $it->interval);
- $this->assertEquals(10, $it->count);
-
- // Max is to prevent overflow
- $max = 12;
- $result = array();
- foreach($it as $item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-10-07', $tz),
- new DateTime('2011-10-21', $tz),
- new DateTime('2011-11-04', $tz),
- new DateTime('2011-11-18', $tz),
- new DateTime('2011-12-02', $tz),
- new DateTime('2011-12-16', $tz),
- new DateTime('2011-12-30', $tz),
- new DateTime('2012-01-13', $tz),
- new DateTime('2012-01-27', $tz),
- new DateTime('2012-02-10', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testWeeklyByDayByHour() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=MO;BYHOUR=8,9,10';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-10-07 08:00:00', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('weekly', $it->frequency);
- $this->assertEquals(2, $it->interval);
- $this->assertEquals(array('TU','WE','FR'), $it->byDay);
- $this->assertEquals(array('8','9','10'), $it->byHour);
- $this->assertEquals('MO', $it->weekStart);
-
- // Grabbing the next 12 items
- $max = 15;
- $result = array();
- foreach($it as $item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-10-07 08:00:00', $tz),
- new DateTime('2011-10-07 09:00:00', $tz),
- new DateTime('2011-10-07 10:00:00', $tz),
- new DateTime('2011-10-18 08:00:00', $tz),
- new DateTime('2011-10-18 09:00:00', $tz),
- new DateTime('2011-10-18 10:00:00', $tz),
- new DateTime('2011-10-19 08:00:00', $tz),
- new DateTime('2011-10-19 09:00:00', $tz),
- new DateTime('2011-10-19 10:00:00', $tz),
- new DateTime('2011-10-21 08:00:00', $tz),
- new DateTime('2011-10-21 09:00:00', $tz),
- new DateTime('2011-10-21 10:00:00', $tz),
- new DateTime('2011-11-01 08:00:00', $tz),
- new DateTime('2011-11-01 09:00:00', $tz),
- new DateTime('2011-11-01 10:00:00', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testWeeklyByDaySpecificHour() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-10-07 18:00:00', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('weekly', $it->frequency);
- $this->assertEquals(2, $it->interval);
- $this->assertEquals(array('TU','WE','FR'), $it->byDay);
- $this->assertEquals('SU', $it->weekStart);
-
- // Grabbing the next 12 items
- $max = 12;
- $result = array();
- foreach($it as $item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-10-07 18:00:00', $tz),
- new DateTime('2011-10-18 18:00:00', $tz),
- new DateTime('2011-10-19 18:00:00', $tz),
- new DateTime('2011-10-21 18:00:00', $tz),
- new DateTime('2011-11-01 18:00:00', $tz),
- new DateTime('2011-11-02 18:00:00', $tz),
- new DateTime('2011-11-04 18:00:00', $tz),
- new DateTime('2011-11-15 18:00:00', $tz),
- new DateTime('2011-11-16 18:00:00', $tz),
- new DateTime('2011-11-18 18:00:00', $tz),
- new DateTime('2011-11-29 18:00:00', $tz),
- new DateTime('2011-11-30 18:00:00', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testWeeklyByDay() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('weekly', $it->frequency);
- $this->assertEquals(2, $it->interval);
- $this->assertEquals(array('TU','WE','FR'), $it->byDay);
- $this->assertEquals('SU', $it->weekStart);
-
- // Grabbing the next 12 items
- $max = 12;
- $result = array();
- foreach($it as $item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-10-07', $tz),
- new DateTime('2011-10-18', $tz),
- new DateTime('2011-10-19', $tz),
- new DateTime('2011-10-21', $tz),
- new DateTime('2011-11-01', $tz),
- new DateTime('2011-11-02', $tz),
- new DateTime('2011-11-04', $tz),
- new DateTime('2011-11-15', $tz),
- new DateTime('2011-11-16', $tz),
- new DateTime('2011-11-18', $tz),
- new DateTime('2011-11-29', $tz),
- new DateTime('2011-11-30', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testMonthly() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=3;COUNT=5';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-12-05', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('monthly', $it->frequency);
- $this->assertEquals(3, $it->interval);
- $this->assertEquals(5, $it->count);
-
- $max = 14;
- $result = array();
- foreach($it as $item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-12-05', $tz),
- new DateTime('2012-03-05', $tz),
- new DateTime('2012-06-05', $tz),
- new DateTime('2012-09-05', $tz),
- new DateTime('2012-12-05', $tz),
- ),
- $result
- );
-
-
- }
-
- /**
- * @depends testValues
- */
- function testMonthlyEndOfMonth() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=2;COUNT=12';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-12-31', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('monthly', $it->frequency);
- $this->assertEquals(2, $it->interval);
- $this->assertEquals(12, $it->count);
-
- $max = 14;
- $result = array();
- foreach($it as $item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-12-31', $tz),
- new DateTime('2012-08-31', $tz),
- new DateTime('2012-10-31', $tz),
- new DateTime('2012-12-31', $tz),
- new DateTime('2013-08-31', $tz),
- new DateTime('2013-10-31', $tz),
- new DateTime('2013-12-31', $tz),
- new DateTime('2014-08-31', $tz),
- new DateTime('2014-10-31', $tz),
- new DateTime('2014-12-31', $tz),
- new DateTime('2015-08-31', $tz),
- new DateTime('2015-10-31', $tz),
- ),
- $result
- );
-
-
- }
-
- /**
- * @depends testValues
- */
- function testMonthlyByMonthDay() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=5;COUNT=9;BYMONTHDAY=1,31,-7';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-01-01', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('monthly', $it->frequency);
- $this->assertEquals(5, $it->interval);
- $this->assertEquals(9, $it->count);
- $this->assertEquals(array(1, 31, -7), $it->byMonthDay);
-
- $max = 14;
- $result = array();
- foreach($it as $item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-01-01', $tz),
- new DateTime('2011-01-25', $tz),
- new DateTime('2011-01-31', $tz),
- new DateTime('2011-06-01', $tz),
- new DateTime('2011-06-24', $tz),
- new DateTime('2011-11-01', $tz),
- new DateTime('2011-11-24', $tz),
- new DateTime('2012-04-01', $tz),
- new DateTime('2012-04-24', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testMonthlyByDay() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=2;COUNT=16;BYDAY=MO,-2TU,+1WE,3TH';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-01-03', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('monthly', $it->frequency);
- $this->assertEquals(2, $it->interval);
- $this->assertEquals(16, $it->count);
- $this->assertEquals(array('MO','-2TU','+1WE','3TH'), $it->byDay);
-
- $max = 20;
- $result = array();
- foreach($it as $k=>$item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-01-03', $tz),
- new DateTime('2011-01-05', $tz),
- new DateTime('2011-01-10', $tz),
- new DateTime('2011-01-17', $tz),
- new DateTime('2011-01-18', $tz),
- new DateTime('2011-01-20', $tz),
- new DateTime('2011-01-24', $tz),
- new DateTime('2011-01-31', $tz),
- new DateTime('2011-03-02', $tz),
- new DateTime('2011-03-07', $tz),
- new DateTime('2011-03-14', $tz),
- new DateTime('2011-03-17', $tz),
- new DateTime('2011-03-21', $tz),
- new DateTime('2011-03-22', $tz),
- new DateTime('2011-03-28', $tz),
- new DateTime('2011-05-02', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testMonthlyByDayByMonthDay() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=MONTHLY;COUNT=10;BYDAY=MO;BYMONTHDAY=1';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-08-01', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('monthly', $it->frequency);
- $this->assertEquals(1, $it->interval);
- $this->assertEquals(10, $it->count);
- $this->assertEquals(array('MO'), $it->byDay);
- $this->assertEquals(array(1), $it->byMonthDay);
-
- $max = 20;
- $result = array();
- foreach($it as $k=>$item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-08-01', $tz),
- new DateTime('2012-10-01', $tz),
- new DateTime('2013-04-01', $tz),
- new DateTime('2013-07-01', $tz),
- new DateTime('2014-09-01', $tz),
- new DateTime('2014-12-01', $tz),
- new DateTime('2015-06-01', $tz),
- new DateTime('2016-02-01', $tz),
- new DateTime('2016-08-01', $tz),
- new DateTime('2017-05-01', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testMonthlyByDayBySetPos() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=MONTHLY;COUNT=10;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1,-1';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-01-03', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('monthly', $it->frequency);
- $this->assertEquals(1, $it->interval);
- $this->assertEquals(10, $it->count);
- $this->assertEquals(array('MO','TU','WE','TH','FR'), $it->byDay);
- $this->assertEquals(array(1,-1), $it->bySetPos);
-
- $max = 20;
- $result = array();
- foreach($it as $k=>$item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-01-03', $tz),
- new DateTime('2011-01-31', $tz),
- new DateTime('2011-02-01', $tz),
- new DateTime('2011-02-28', $tz),
- new DateTime('2011-03-01', $tz),
- new DateTime('2011-03-31', $tz),
- new DateTime('2011-04-01', $tz),
- new DateTime('2011-04-29', $tz),
- new DateTime('2011-05-02', $tz),
- new DateTime('2011-05-31', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testYearly() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=YEARLY;COUNT=10;INTERVAL=3';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-01-01', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('yearly', $it->frequency);
- $this->assertEquals(3, $it->interval);
- $this->assertEquals(10, $it->count);
-
- $max = 20;
- $result = array();
- foreach($it as $k=>$item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-01-01', $tz),
- new DateTime('2014-01-01', $tz),
- new DateTime('2017-01-01', $tz),
- new DateTime('2020-01-01', $tz),
- new DateTime('2023-01-01', $tz),
- new DateTime('2026-01-01', $tz),
- new DateTime('2029-01-01', $tz),
- new DateTime('2032-01-01', $tz),
- new DateTime('2035-01-01', $tz),
- new DateTime('2038-01-01', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testYearlyLeapYear() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=YEARLY;COUNT=3';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2012-02-29', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('yearly', $it->frequency);
- $this->assertEquals(3, $it->count);
-
- $max = 20;
- $result = array();
- foreach($it as $k=>$item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2012-02-29', $tz),
- new DateTime('2016-02-29', $tz),
- new DateTime('2020-02-29', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testYearlyByMonth() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=4;BYMONTH=4,10';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-04-07', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('yearly', $it->frequency);
- $this->assertEquals(4, $it->interval);
- $this->assertEquals(8, $it->count);
- $this->assertEquals(array(4,10), $it->byMonth);
-
- $max = 20;
- $result = array();
- foreach($it as $k=>$item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-04-07', $tz),
- new DateTime('2011-10-07', $tz),
- new DateTime('2015-04-07', $tz),
- new DateTime('2015-10-07', $tz),
- new DateTime('2019-04-07', $tz),
- new DateTime('2019-10-07', $tz),
- new DateTime('2023-04-07', $tz),
- new DateTime('2023-10-07', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testYearlyByMonthByDay() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-04-04', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('yearly', $it->frequency);
- $this->assertEquals(5, $it->interval);
- $this->assertEquals(8, $it->count);
- $this->assertEquals(array(4,10), $it->byMonth);
- $this->assertEquals(array('1MO','-1SU'), $it->byDay);
-
- $max = 20;
- $result = array();
- foreach($it as $k=>$item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $tz = new DateTimeZone('UTC');
-
- $this->assertEquals(
- array(
- new DateTime('2011-04-04', $tz),
- new DateTime('2011-04-24', $tz),
- new DateTime('2011-10-03', $tz),
- new DateTime('2011-10-30', $tz),
- new DateTime('2016-04-04', $tz),
- new DateTime('2016-04-24', $tz),
- new DateTime('2016-10-03', $tz),
- new DateTime('2016-10-30', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testFastForward() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU';
- $dtStart = new Property\DateTime('DTSTART');
- $dtStart->setDateTime(new DateTime('2011-04-04', new DateTimeZone('UTC')),Property\DateTime::UTC);
-
- $ev->add($dtStart);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- // The idea is that we're fast-forwarding too far in the future, so
- // there will be no results left.
- $it->fastForward(new DateTime('2020-05-05', new DateTimeZone('UTC')));
-
- $max = 20;
- $result = array();
- while($item = $it->current()) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
- $it->next();
-
- }
-
- $tz = new DateTimeZone('UTC');
- $this->assertEquals(array(), $result);
-
- }
-
- /**
- * @depends testValues
- */
- function testComplexExclusions() {
-
- $ev = new Component('VEVENT');
- $ev->UID = 'bla';
- $ev->RRULE = 'FREQ=YEARLY;COUNT=10';
- $dtStart = new Property\DateTime('DTSTART');
-
- $tz = new DateTimeZone('Canada/Eastern');
- $dtStart->setDateTime(new DateTime('2011-01-01 13:50:20', $tz),Property\DateTime::LOCALTZ);
-
- $exDate1 = new Property\MultiDateTime('EXDATE');
- $exDate1->setDateTimes(array(new DateTime('2012-01-01 13:50:20', $tz), new DateTime('2014-01-01 13:50:20', $tz)), Property\DateTime::LOCALTZ);
- $exDate2 = new Property\MultiDateTime('EXDATE');
- $exDate2->setDateTimes(array(new DateTime('2016-01-01 13:50:20', $tz)), Property\DateTime::LOCALTZ);
-
- $ev->add($dtStart);
- $ev->add($exDate1);
- $ev->add($exDate2);
-
- $vcal = Component::create('VCALENDAR');
- $vcal->add($ev);
-
- $it = new RecurrenceIterator($vcal,(string)$ev->uid);
-
- $this->assertEquals('yearly', $it->frequency);
- $this->assertEquals(1, $it->interval);
- $this->assertEquals(10, $it->count);
-
- $max = 20;
- $result = array();
- foreach($it as $k=>$item) {
-
- $result[] = $item;
- $max--;
-
- if (!$max) break;
-
- }
-
- $this->assertEquals(
- array(
- new DateTime('2011-01-01 13:50:20', $tz),
- new DateTime('2013-01-01 13:50:20', $tz),
- new DateTime('2015-01-01 13:50:20', $tz),
- new DateTime('2017-01-01 13:50:20', $tz),
- new DateTime('2018-01-01 13:50:20', $tz),
- new DateTime('2019-01-01 13:50:20', $tz),
- new DateTime('2020-01-01 13:50:20', $tz),
- ),
- $result
- );
-
- }
-
- /**
- * @depends testValues
- */
- function testOverridenEvent() {
-
- $vcal = Component::create('VCALENDAR');
-
- $ev1 = Component::create('VEVENT');
- $ev1->UID = 'overridden';
- $ev1->RRULE = 'FREQ=DAILY;COUNT=10';
- $ev1->DTSTART = '20120107T120000Z';
- $ev1->SUMMARY = 'baseEvent';
-
- $vcal->add($ev1);
-
- // ev2 overrides an event, and puts it on 2pm instead.
- $ev2 = Component::create('VEVENT');
- $ev2->UID = 'overridden';
- $ev2->{'RECURRENCE-ID'} = '20120110T120000Z';
- $ev2->DTSTART = '20120110T140000Z';
- $ev2->SUMMARY = 'Event 2';
-
- $vcal->add($ev2);
-
- // ev3 overrides an event, and puts it 2 days and 2 hours later
- $ev3 = Component::create('VEVENT');
- $ev3->UID = 'overridden';
- $ev3->{'RECURRENCE-ID'} = '20120113T120000Z';
- $ev3->DTSTART = '20120115T140000Z';
- $ev3->SUMMARY = 'Event 3';
-
- $vcal->add($ev3);
-
- $it = new RecurrenceIterator($vcal,'overridden');
-
- $dates = array();
- $summaries = array();
- while($it->valid()) {
-
- $dates[] = $it->getDTStart();
- $summaries[] = (string)$it->getEventObject()->SUMMARY;
- $it->next();
-
- }
-
- $tz = new DateTimeZone('UTC');
- $this->assertEquals(array(
- new DateTime('2012-01-07 12:00:00',$tz),
- new DateTime('2012-01-08 12:00:00',$tz),
- new DateTime('2012-01-09 12:00:00',$tz),
- new DateTime('2012-01-10 14:00:00',$tz),
- new DateTime('2012-01-11 12:00:00',$tz),
- new DateTime('2012-01-12 12:00:00',$tz),
- new DateTime('2012-01-14 12:00:00',$tz),
- new DateTime('2012-01-15 12:00:00',$tz),
- new DateTime('2012-01-15 14:00:00',$tz),
- new DateTime('2012-01-16 12:00:00',$tz),
- ), $dates);
-
- $this->assertEquals(array(
- 'baseEvent',
- 'baseEvent',
- 'baseEvent',
- 'Event 2',
- 'baseEvent',
- 'baseEvent',
- 'baseEvent',
- 'baseEvent',
- 'Event 3',
- 'baseEvent',
- ), $summaries);
-
- }
-
- /**
- * @depends testValues
- */
- function testOverridenEvent2() {
-
- $vcal = Component::create('VCALENDAR');
-
- $ev1 = Component::create('VEVENT');
- $ev1->UID = 'overridden';
- $ev1->RRULE = 'FREQ=WEEKLY;COUNT=3';
- $ev1->DTSTART = '20120112T120000Z';
- $ev1->SUMMARY = 'baseEvent';
-
- $vcal->add($ev1);
-
- // ev2 overrides an event, and puts it 6 days earlier instead.
- $ev2 = Component::create('VEVENT');
- $ev2->UID = 'overridden';
- $ev2->{'RECURRENCE-ID'} = '20120119T120000Z';
- $ev2->DTSTART = '20120113T120000Z';
- $ev2->SUMMARY = 'Override!';
-
- $vcal->add($ev2);
-
- $it = new RecurrenceIterator($vcal,'overridden');
-
- $dates = array();
- $summaries = array();
- while($it->valid()) {
-
- $dates[] = $it->getDTStart();
- $summaries[] = (string)$it->getEventObject()->SUMMARY;
- $it->next();
-
- }
-
- $tz = new DateTimeZone('UTC');
- $this->assertEquals(array(
- new DateTime('2012-01-12 12:00:00',$tz),
- new DateTime('2012-01-13 12:00:00',$tz),
- new DateTime('2012-01-26 12:00:00',$tz),
-
- ), $dates);
-
- $this->assertEquals(array(
- 'baseEvent',
- 'Override!',
- 'baseEvent',
- ), $summaries);
-
- }
-
- /**
- * @depends testValues
- */
- function testOverridenEventNoValuesExpected() {
-
- $vcal = Component::create('VCALENDAR');
-
- $ev1 = Component::create('VEVENT');
- $ev1->UID = 'overridden';
- $ev1->RRULE = 'FREQ=WEEKLY;COUNT=3';
- $ev1->DTSTART = '20120124T120000Z';
- $ev1->SUMMARY = 'baseEvent';
-
- $vcal->add($ev1);
-
- // ev2 overrides an event, and puts it 6 days earlier instead.
- $ev2 = Component::create('VEVENT');
- $ev2->UID = 'overridden';
- $ev2->{'RECURRENCE-ID'} = '20120131T120000Z';
- $ev2->DTSTART = '20120125T120000Z';
- $ev2->SUMMARY = 'Override!';
-
- $vcal->add($ev2);
-
- $it = new RecurrenceIterator($vcal,'overridden');
-
- $dates = array();
- $summaries = array();
-
- // The reported problem was specifically related to the VCALENDAR
- // expansion. In this parcitular case, we had to forward to the 28th of
- // january.
- $it->fastForward(new DateTime('2012-01-28 23:00:00'));
-
- // We stop the loop when it hits the 6th of februari. Normally this
- // iterator would hit 24, 25 (overriden from 31) and 7 feb but because
- // we 'filter' from the 28th till the 6th, we should get 0 results.
- while($it->valid() && $it->getDTSTart() < new DateTime('2012-02-06 23:00:00')) {
-
- $dates[] = $it->getDTStart();
- $summaries[] = (string)$it->getEventObject()->SUMMARY;
- $it->next();
-
- }
-
- $this->assertEquals(array(), $dates);
- $this->assertEquals(array(), $summaries);
-
- }
-}
-
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/SlashRTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/SlashRTest.php
deleted file mode 100644
index ebbfb04a7..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/SlashRTest.php
+++ /dev/null
@@ -1,19 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-/**
- * This issue was pointed out in Issue 55. \r should be stripped completely
- * when encoding property values.
- */
-class SlashRTest extends \PHPUnit_Framework_TestCase {
-
- function testEncode() {
-
- $prop = new \Sabre\VObject\Property('test', "abc\r\ndef");
- $this->assertEquals("TEST:abc\\ndef\r\n", $prop->serialize());
-
- }
-
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Splitter/ICalendarTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/Splitter/ICalendarTest.php
deleted file mode 100644
index 43613350f..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Splitter/ICalendarTest.php
+++ /dev/null
@@ -1,283 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Splitter;
-
-use Sabre\VObject;
-
-class ICalendarSplitterTest extends \PHPUnit_Framework_TestCase {
-
- protected $version;
-
- function setup() {
- $this->version = VObject\Version::VERSION;
- }
-
- function createStream($data) {
-
- $stream = fopen('php://memory','r+');
- fwrite($stream, $data);
- rewind($stream);
- return $stream;
-
- }
-
- function testICalendarImportValidEvent() {
-
- $data = <<<EOT
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-UID:foo
-END:VEVENT
-END:VCALENDAR
-EOT;
- $tempFile = $this->createStream($data);
-
- $objects = new ICalendar($tempFile);
-
- $return = "";
- while($object=$objects->getNext()) {
- $return .= $object->serialize();
- }
- $this->assertEquals(array(), VObject\Reader::read($return)->validate());
- }
-
- function testICalendarImportEndOfData() {
- $data = <<<EOT
-BEGIN:VCALENDAR
-BEGIN:VEVENT
-UID:foo
-END:VEVENT
-END:VCALENDAR
-EOT;
- $tempFile = $this->createStream($data);
-
- $objects = new ICalendar($tempFile);
-
- $return = "";
- while($object=$objects->getNext()) {
- $return .= $object->serialize();
- }
- $this->assertNull($object=$objects->getNext());
- }
-
- /**
- * @expectedException Sabre\VObject\ParseException
- */
- function testICalendarImportInvalidEvent() {
- $data = <<<EOT
-EOT;
- $tempFile = $this->createStream($data);
-
- $objects = new ICalendar($tempFile);
- }
-
- function testICalendarImportMultipleValidEvents() {
-
- $event[] = <<<EOT
-BEGIN:VEVENT
-UID:foo1
-END:VEVENT
-EOT;
-
-$event[] = <<<EOT
-BEGIN:VEVENT
-UID:foo2
-END:VEVENT
-EOT;
-
- $data = <<<EOT
-BEGIN:VCALENDAR
-$event[0]
-$event[1]
-END:VCALENDAR
-
-EOT;
- $tempFile = $this->createStream($data);
-
- $objects = new ICalendar($tempFile);
-
- $return = "";
- $i = 0;
- while($object=$objects->getNext()) {
-
- $expected = <<<EOT
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Sabre//Sabre VObject $this->version//EN
-CALSCALE:GREGORIAN
-$event[$i]
-END:VCALENDAR
-
-EOT;
-
- $return .= $object->serialize();
- $expected = str_replace("\n", "\r\n", $expected);
- $this->assertEquals($expected, $object->serialize());
- $i++;
- }
- $this->assertEquals(array(), VObject\Reader::read($return)->validate());
- }
-
- function testICalendarImportEventWithoutUID() {
-
- $data = <<<EOT
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Sabre//Sabre VObject $this->version//EN
-CALSCALE:GREGORIAN
-BEGIN:VEVENT
-END:VEVENT
-END:VCALENDAR
-
-EOT;
- $tempFile = $this->createStream($data);
-
- $objects = new ICalendar($tempFile);
-
- $return = "";
- while($object=$objects->getNext()) {
- $expected = str_replace("\n", "\r\n", $data);
- $this->assertEquals($expected, $object->serialize());
- $return .= $object->serialize();
- }
-
- $this->assertEquals(array(), VObject\Reader::read($return)->validate());
- }
-
- function testICalendarImportMultipleVTIMEZONESAndMultipleValidEvents() {
-
- $timezones = <<<EOT
-BEGIN:VTIMEZONE
-TZID:Europe/Berlin
-BEGIN:DAYLIGHT
-TZOFFSETFROM:+0100
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
-DTSTART:19810329T020000
-TZNAME:MESZ
-TZOFFSETTO:+0200
-END:DAYLIGHT
-BEGIN:STANDARD
-TZOFFSETFROM:+0200
-RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
-DTSTART:19961027T030000
-TZNAME:MEZ
-TZOFFSETTO:+0100
-END:STANDARD
-END:VTIMEZONE
-BEGIN:VTIMEZONE
-TZID:Europe/London
-BEGIN:DAYLIGHT
-TZOFFSETFROM:+0000
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
-DTSTART:19810329T010000
-TZNAME:GMT+01:00
-TZOFFSETTO:+0100
-END:DAYLIGHT
-BEGIN:STANDARD
-TZOFFSETFROM:+0100
-RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
-DTSTART:19961027T020000
-TZNAME:GMT
-TZOFFSETTO:+0000
-END:STANDARD
-END:VTIMEZONE
-EOT;
-
- $event[] = <<<EOT
-BEGIN:VEVENT
-UID:foo1
-END:VEVENT
-EOT;
-
- $event[] = <<<EOT
-BEGIN:VEVENT
-UID:foo2
-END:VEVENT
-EOT;
-
- $event[] = <<<EOT
-BEGIN:VEVENT
-UID:foo3
-END:VEVENT
-EOT;
-
- $data = <<<EOT
-BEGIN:VCALENDAR
-$timezones
-$event[0]
-$event[1]
-$event[2]
-END:VCALENDAR
-
-EOT;
- $tempFile = $this->createStream($data);
-
- $objects = new ICalendar($tempFile);
-
- $return = "";
- $i = 0;
- while($object=$objects->getNext()) {
-
- $expected = <<<EOT
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Sabre//Sabre VObject $this->version//EN
-CALSCALE:GREGORIAN
-$timezones
-$event[$i]
-END:VCALENDAR
-
-EOT;
- $expected = str_replace("\n", "\r\n", $expected);
-
- $this->assertEquals($expected, $object->serialize());
- $return .= $object->serialize();
- $i++;
-
- }
-
- $this->assertEquals(array(), VObject\Reader::read($return)->validate());
- $this->assertEquals(array(), VObject\Reader::read($return)->validate());
- }
-
- function testICalendarImportWithOutVTIMEZONES() {
-
- $data = <<<EOT
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Apple Inc.//Mac OS X 10.8//EN
-CALSCALE:GREGORIAN
-BEGIN:VEVENT
-CREATED:20120605T072109Z
-UID:D6716295-C10F-4B20-82F9-E1A3026C7DCF
-DTEND;VALUE=DATE:20120717
-TRANSP:TRANSPARENT
-SUMMARY:Start Vorbereitung
-DTSTART;VALUE=DATE:20120716
-DTSTAMP:20120605T072115Z
-SEQUENCE:2
-BEGIN:VALARM
-X-WR-ALARMUID:A99EDA6A-35EB-4446-B8BC-CDA3C60C627D
-UID:A99EDA6A-35EB-4446-B8BC-CDA3C60C627D
-TRIGGER:-PT15H
-X-APPLE-DEFAULT-ALARM:TRUE
-ATTACH;VALUE=URI:Basso
-ACTION:AUDIO
-END:VALARM
-END:VEVENT
-END:VCALENDAR
-
-EOT;
- $tempFile = $this->createStream($data);
-
- $objects = new ICalendar($tempFile);
-
- $return = "";
- while($object=$objects->getNext()) {
- $return .= $object->serialize();
- }
-
- $this->assertEquals(array(), VObject\Reader::read($return)->validate());
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/Splitter/VCardTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/Splitter/VCardTest.php
deleted file mode 100644
index b6b41925f..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/Splitter/VCardTest.php
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-
-namespace Sabre\VObject\Splitter;
-
-use Sabre\VObject;
-
-class VCardSplitterTest extends \PHPUnit_Framework_TestCase {
-
- function createStream($data) {
-
- $stream = fopen('php://memory','r+');
- fwrite($stream, $data);
- rewind($stream);
- return $stream;
-
- }
-
- function testVCardImportValidVCard() {
- $data = <<<EOT
-BEGIN:VCARD
-UID:foo
-END:VCARD
-EOT;
- $tempFile = $this->createStream($data);
-
- $objects = new VCard($tempFile);
-
- $return = "";
- while($object=$objects->getNext()) {
- $return .= $object->serialize();
- }
-
- VObject\Reader::read($return);
- }
-
- function testVCardImportValidVCardsWithCategories() {
- $data = <<<EOT
-BEGIN:VCARD
-UID:card-in-foo1-and-foo2
-CATEGORIES:foo1\,foo2
-END:VCARD
-BEGIN:VCARD
-UID:card-in-foo1
-CATEGORIES:foo1
-END:VCARD
-BEGIN:VCARD
-UID:card-in-foo3
-CATEGORIES:foo3
-END:VCARD
-BEGIN:VCARD
-UID:card-in-foo1-and-foo3
-CATEGORIES:foo1\,foo3
-END:VCARD
-EOT;
- $tempFile = $this->createStream($data);
-
- $objects = new VCard($tempFile);
-
- $return = "";
- while($object=$objects->getNext()) {
- $return .= $object->serialize();
- }
-
- VObject\Reader::read($return);
- }
-
- function testVCardImportEndOfData() {
- $data = <<<EOT
-BEGIN:VCARD
-UID:foo
-END:VCARD
-EOT;
- $tempFile = $this->createStream($data);
-
- $objects = new VCard($tempFile);
- $object=$objects->getNext();
-
- $this->assertFalse($object=$objects->getNext());
-
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testVCardImportCheckInvalidArgumentException() {
- $data = <<<EOT
-BEGIN:FOO
-END:FOO
-EOT;
- $tempFile = $this->createStream($data);
-
- $objects = new VCard($tempFile);
- while($object=$objects->getNext()) {
- $return .= $object->serialize();
- }
-
- }
-
- function testVCardImportMultipleValidVCards() {
- $data = <<<EOT
-BEGIN:VCARD
-UID:foo
-END:VCARD
-BEGIN:VCARD
-UID:foo
-END:VCARD
-EOT;
- $tempFile = $this->createStream($data);
-
- $objects = new VCard($tempFile);
-
- $return = "";
- while($object=$objects->getNext()) {
- $return .= $object->serialize();
- }
-
- VObject\Reader::read($return);
- }
-
- function testVCardImportVCardWithoutUID() {
- $data = <<<EOT
-BEGIN:VCARD
-END:VCARD
-EOT;
- $tempFile = $this->createStream($data);
-
- $objects = new VCard($tempFile);
-
- $return = "";
- while($object=$objects->getNext()) {
- $return .= $object->serialize();
- }
-
- VObject\Reader::read($return);
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/StringUtilTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/StringUtilTest.php
deleted file mode 100644
index 59a83d294..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/StringUtilTest.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-class StringUtilTest extends \PHPUnit_Framework_TestCase {
-
- function testNonUTF8() {
-
- $string = StringUtil::isUTF8(chr('0xbf'));
-
- $this->assertEquals(false, $string);
-
- }
-
- function testIsUTF8() {
-
- $string = StringUtil::isUTF8('I 💚 SabreDAV');
-
- $this->assertEquals(true, $string);
-
- }
-
- function testUTF8ControlChar() {
-
- $string = StringUtil::isUTF8(chr('0x00'));
-
- $this->assertEquals(false, $string);
-
- }
-
- function testConvertToUTF8nonUTF8() {
-
- $string = StringUtil::convertToUTF8(chr('0xbf'));
-
- $this->assertEquals(utf8_encode(chr('0xbf')), $string);
-
- }
-
- function testConvertToUTF8IsUTF8() {
-
- $string = StringUtil::convertToUTF8('I 💚 SabreDAV');
-
- $this->assertEquals('I 💚 SabreDAV', $string);
-
- }
-
- function testConvertToUTF8ControlChar() {
-
- $string = StringUtil::convertToUTF8(chr(0x00));
-
- $this->assertEquals('', $string);
-
- }
-
-
-
-
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/TimeZoneUtilTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/TimeZoneUtilTest.php
deleted file mode 100644
index b898e8d26..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/TimeZoneUtilTest.php
+++ /dev/null
@@ -1,306 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-class TimezoneUtilTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * @dataProvider getMapping
- */
- function testCorrectTZ($timezoneName) {
-
- $tz = new \DateTimeZone($timezoneName);
-
- }
-
- function getMapping() {
-
- // PHPUNit requires an array of arrays
- return array_map(
- function($value) {
- return array($value);
- },
- TimeZoneUtil::$map
- );
-
- }
-
- function testExchangeMap() {
-
- $vobj = <<<HI
-BEGIN:VCALENDAR
-METHOD:REQUEST
-VERSION:2.0
-BEGIN:VTIMEZONE
-TZID:foo
-X-MICROSOFT-CDO-TZID:2
-BEGIN:STANDARD
-DTSTART:16010101T030000
-TZOFFSETFROM:+0200
-TZOFFSETTO:+0100
-RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:16010101T020000
-TZOFFSETFROM:+0100
-TZOFFSETTO:+0200
-RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
-END:DAYLIGHT
-END:VTIMEZONE
-BEGIN:VEVENT
-DTSTAMP:20120416T092149Z
-DTSTART;TZID="foo":20120418T1
- 00000
-SUMMARY:Begin Unterhaltsreinigung
-UID:040000008200E00074C5B7101A82E0080000000010DA091DC31BCD01000000000000000
- 0100000008FECD2E607780649BE5A4C9EE6418CBC
- 000
-END:VEVENT
-END:VCALENDAR
-HI;
-
- $tz = TimeZoneUtil::getTimeZone('foo', Reader::read($vobj));
- $ex = new \DateTimeZone('Europe/Lisbon');
-
- $this->assertEquals($ex->getName(), $tz->getName());
-
- }
-
- function testWetherMicrosoftIsStillInsane() {
-
- $vobj = <<<HI
-BEGIN:VCALENDAR
-METHOD:REQUEST
-VERSION:2.0
-BEGIN:VTIMEZONE
-TZID:(GMT+01.00) Sarajevo/Warsaw/Zagreb
-X-MICROSOFT-CDO-TZID:2
-BEGIN:STANDARD
-DTSTART:16010101T030000
-TZOFFSETFROM:+0200
-TZOFFSETTO:+0100
-RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
-END:STANDARD
-END:VTIMEZONE
-END:VCALENDAR
-HI;
-
- $tz = TimeZoneUtil::getTimeZone('(GMT+01.00) Sarajevo/Warsaw/Zagreb', Reader::read($vobj));
- $ex = new \DateTimeZone('Europe/Sarajevo');
-
- $this->assertEquals($ex->getName(), $tz->getName());
-
- }
-
- function testUnknownExchangeId() {
-
- $vobj = <<<HI
-BEGIN:VCALENDAR
-METHOD:REQUEST
-VERSION:2.0
-BEGIN:VTIMEZONE
-TZID:foo
-X-MICROSOFT-CDO-TZID:2000
-BEGIN:STANDARD
-DTSTART:16010101T030000
-TZOFFSETFROM:+0200
-TZOFFSETTO:+0100
-RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:16010101T020000
-TZOFFSETFROM:+0100
-TZOFFSETTO:+0200
-RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
-END:DAYLIGHT
-END:VTIMEZONE
-BEGIN:VEVENT
-DTSTAMP:20120416T092149Z
-DTSTART;TZID="foo":20120418T1
- 00000
-SUMMARY:Begin Unterhaltsreinigung
-UID:040000008200E00074C5B7101A82E0080000000010DA091DC31BCD01000000000000000
- 0100000008FECD2E607780649BE5A4C9EE6418CBC
-DTEND;TZID="Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb":20120418T103
- 000
-END:VEVENT
-END:VCALENDAR
-HI;
-
- $tz = TimeZoneUtil::getTimeZone('foo', Reader::read($vobj));
- $ex = new \DateTimeZone(date_default_timezone_get());
- $this->assertEquals($ex->getName(), $tz->getName());
-
- }
-
- function testWindowsTimeZone() {
-
- $tz = TimeZoneUtil::getTimeZone('Eastern Standard Time');
- $ex = new \DateTimeZone('America/New_York');
- $this->assertEquals($ex->getName(), $tz->getName());
-
- }
-
- function testTimezoneOffset() {
-
- $tz = TimeZoneUtil::getTimeZone('GMT-0400', null, true);
-
- if (version_compare(PHP_VERSION, '5.5.10', '>=')) {
- $ex = new \DateTimeZone('-04:00');
- } else {
- $ex = new \DateTimeZone('Etc/GMT-4');
- }
- $this->assertEquals($ex->getName(), $tz->getName());
-
- }
-
- /**
- * @expectedException InvalidArgumentException
- */
- function testTimezoneFail() {
-
- $tz = TimeZoneUtil::getTimeZone('FooBar',null,true);
-
- }
-
- function testFallBack() {
-
- $vobj = <<<HI
-BEGIN:VCALENDAR
-METHOD:REQUEST
-VERSION:2.0
-BEGIN:VTIMEZONE
-TZID:foo
-BEGIN:STANDARD
-DTSTART:16010101T030000
-TZOFFSETFROM:+0200
-TZOFFSETTO:+0100
-RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:16010101T020000
-TZOFFSETFROM:+0100
-TZOFFSETTO:+0200
-RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
-END:DAYLIGHT
-END:VTIMEZONE
-BEGIN:VEVENT
-DTSTAMP:20120416T092149Z
-DTSTART;TZID="foo":20120418T1
- 00000
-SUMMARY:Begin Unterhaltsreinigung
-UID:040000008200E00074C5B7101A82E0080000000010DA091DC31BCD01000000000000000
- 0100000008FECD2E607780649BE5A4C9EE6418CBC
- 000
-END:VEVENT
-END:VCALENDAR
-HI;
-
- $tz = TimeZoneUtil::getTimeZone('foo', Reader::read($vobj));
- $ex = new \DateTimeZone(date_default_timezone_get());
- $this->assertEquals($ex->getName(), $tz->getName());
-
- }
-
- function testLjubljanaBug() {
-
- $vobj = <<<HI
-BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-PRODID:-//Ximian//NONSGML Evolution Calendar//EN
-VERSION:2.0
-BEGIN:VTIMEZONE
-TZID:/freeassociation.sourceforge.net/Tzfile/Europe/Ljubljana
-X-LIC-LOCATION:Europe/Ljubljana
-BEGIN:STANDARD
-TZNAME:CET
-DTSTART:19701028T030000
-RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
-TZOFFSETFROM:+0200
-TZOFFSETTO:+0100
-END:STANDARD
-BEGIN:DAYLIGHT
-TZNAME:CEST
-DTSTART:19700325T020000
-RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
-TZOFFSETFROM:+0100
-TZOFFSETTO:+0200
-END:DAYLIGHT
-END:VTIMEZONE
-BEGIN:VEVENT
-UID:foo
-DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/Europe/Ljubljana:
- 20121003T080000
-DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/Europe/Ljubljana:
- 20121003T083000
-TRANSP:OPAQUE
-SEQUENCE:2
-SUMMARY:testing
-CREATED:20121002T172613Z
-LAST-MODIFIED:20121002T172613Z
-END:VEVENT
-END:VCALENDAR
-
-HI;
-
-
- $tz = TimeZoneUtil::getTimeZone('/freeassociation.sourceforge.net/Tzfile/Europe/Ljubljana', Reader::read($vobj));
- $ex = new \DateTimeZone('Europe/Ljubljana');
- $this->assertEquals($ex->getName(), $tz->getName());
-
- }
-
- function testWeirdSystemVLICs() {
-
-$vobj = <<<HI
-BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-PRODID:-//Ximian//NONSGML Evolution Calendar//EN
-VERSION:2.0
-BEGIN:VTIMEZONE
-TZID:/freeassociation.sourceforge.net/Tzfile/SystemV/EST5EDT
-X-LIC-LOCATION:SystemV/EST5EDT
-BEGIN:STANDARD
-TZNAME:EST
-DTSTART:19701104T020000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-TZNAME:EDT
-DTSTART:19700311T020000
-RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-END:VTIMEZONE
-BEGIN:VEVENT
-UID:20121026T021107Z-6301-1000-1-0@chAir
-DTSTAMP:20120905T172126Z
-DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/SystemV/EST5EDT:
- 20121026T153000
-DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/SystemV/EST5EDT:
- 20121026T160000
-TRANSP:OPAQUE
-SEQUENCE:5
-SUMMARY:pick up Ibby
-CLASS:PUBLIC
-CREATED:20121026T021108Z
-LAST-MODIFIED:20121026T021118Z
-X-EVOLUTION-MOVE-CALENDAR:1
-END:VEVENT
-END:VCALENDAR
-HI;
-
- $tz = TimeZoneUtil::getTimeZone('/freeassociation.sourceforge.net/Tzfile/SystemV/EST5EDT', Reader::read($vobj), true);
- if (version_compare(PHP_VERSION, '5.5.10', '>=')) {
- $ex = new \DateTimeZone('America/New_York');
- } else {
- $ex = new \DateTimeZone('EST5EDT');
- }
- $this->assertEquals($ex->getName(), $tz->getName());
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/VersionTest.php b/vendor/sabre/vobject/tests/Sabre/VObject/VersionTest.php
deleted file mode 100644
index ae6855e85..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/VersionTest.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-namespace Sabre\VObject;
-
-class VersionTest extends \PHPUnit_Framework_TestCase {
-
- function testString() {
-
- $v = Version::VERSION;
- $this->assertEquals(-1, version_compare('0.9.0',$v));
-
- $s = Version::STABILITY;
- $this->assertTrue($s == 'alpha' || $s == 'beta' || $s =='stable');
-
- }
-
-}
diff --git a/vendor/sabre/vobject/tests/Sabre/VObject/issue153.vcf b/vendor/sabre/vobject/tests/Sabre/VObject/issue153.vcf
deleted file mode 100644
index 5fb0fa297..000000000
--- a/vendor/sabre/vobject/tests/Sabre/VObject/issue153.vcf
+++ /dev/null
@@ -1,352 +0,0 @@
-BEGIN:VCARD
-VERSION:3.0
-N:Benutzer;Test;;;
-FN:Test Benutzer
-PHOTO;BASE64:
- /9j/4AAQSkZJRgABAQAAAQABAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQA
- AAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAABQKADAAQAAAABAAABQAAAAAD/2wBD
- AAIBAQIBAQICAQICAgICAwUDAwMDAwYEBAMFBwYHBwcGBgYHCAsJBwgKCAYGCQ0JCgsLDAwMBwkN
- Dg0MDgsMDAv/2wBDAQICAgMCAwUDAwULCAYICwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL
- CwsLCwsLCwsLCwsLCwsLCwsLCwv/wAARCAFAAUADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAA
- AAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKB
- kaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZn
- aGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT
- 1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcI
- CQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAV
- YnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6
- goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk
- 5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8J7JbO8tYo1tIFCDLOVG5qfdaVZRwmSOFWzyA
- F4H1rLt5WViMhdp6HgmtKK8O3B+4Rhx6fSgBI9FtjaNN5aErwRjilSys7lFAt41xyTtqc2yJCVlY
- 7eqgGqv2jyLcebjZnGPWncdzT0+w0u5eQXtrGiBcIyoPmNMXwpb/AGMTSRRbH6YAyPwqK21GKdfL
- BAVfu+1SQX4jnjKFsp03dPypCKN9oEaKSkC7R0bGKpnSlSPdHErZOORXV3Ouy337sCLB6kpx+FY0
- t+VfyrgcbuCB1oAfoMemrcImq2sZX+I7ATXS618PdK1DRlvvDEaMq5LoV2nisx4LVrUfu5BOePau
- m8EQS6PY3HmFXjljKhTzjOf1oA4mz8OxvMrLbW5RD8wbByKg1LRrRriRYY408w/KAMba1pRaWt/H
- a6a7CVm2u7N8lUPEujzaRekzSK6tgqVNAGNBZJauY5Yon92GTRJp0ROY0Un0A4q3c2odkaYOMjii
- KL7NIDGcj1NDAZBplmmWv1xnoFHStfS/DFpewqYoYm3DutZ8lv8AapdyOqk8EVteEbSe3KBSrDrQ
- BT8S+HbawiiWGCAPjsuMnPesqHS4JSFlSMP7DitbXbvfrkkM2eGw3p+FMfTh5X+hr8w7t3oAhOhW
- u8MkMZUY3fL0Heo9UsrN5FFrbxKmMBgoG41fWFra0Acjpzg9aoXjtgRoo29vagCoun27kbY059qn
- bwykskYjRArdTT7GEl2UqMr2q/JtVU27iR15NADdK8DC/wBPle2iicxNg5ALH6Umm6FZ/a3ttQt4
- g2Cqnb0PbJ+tamn3j6ZCW0nILfeBORWVfO4dhLw7fMW7560AZuqeHf7MuTFcRpv6qVGVx70q2Eci
- QwyW0SsPvOqjJrUtb6S9tHQKGeMZYuM8VUs7gRxbrncy9mWgB1x4QtTHvsQWkHJVhhax3tkhugHh
- UkfeAXIFdPZ3v2uxkQ9G4jI6/j+tYun3r2Fy6yxeb2Py5IoAqXenJ5xaGNNvXH/1qcLSGeBdkSg9
- CcdaswC3be0pfexOMnpn2qaS1KQkQASKoydvLCgDNi09RKTNCuO2BxVjSobc6gqXMERQHkleDUsc
- u9VADbG6qOWAp11bLbptkjlCkZRsde9AFi5sbO3kKfZYTnkHaOlVbuO2F5thtYcADjaKXUpHj8ku
- Co2VDFL5wLeg696YFwQ2z7Qtlb8HJO0c1Zsr7T7a9kL6XazZ4CmMFRWfHdkEgjGRjPpU9raP5LSP
- j5h2pAWdQ0+z1KdG+y21qvcRqBn8qXSvC+iTu63ssqyE/IAuR+NQwSrGm1g+c8E9qiSQW9wPNYYP
- OR2oAW68GNa28k3lwGNHwvzDJGfSqM9nHBgm3j59QMVdmma4zIjsUBHy5OKp6o8s2BJjZjjAoAro
- /nysbgYY9zWmLPCR+WQQwyaz4k2F/Pbft/GtKxvUeFN+B2x+NAEptsWpZSdo9etZe8su2X7pPFdU
- LeOazKqVwevNYt7pw5EA5HIxQBQA8tAIeGz1NWIJvJlhW5OQBzjrUMR/eN9pwoXjB4qQ3ERJeYcy
- 9P8AZoA0jf8AmybVxsHAFS6jp63ixmwjIwOfrWfaou12GcDpmt/w5qJhXc6hh2GM0AZkHiRpblVl
- G0RjGMdxXQ+H/E0Rm+bjdw1crqEHm3EksY4Y9PTmq0cskc42qUOfpmgDovHOhLBOZ9O+aEnIUdRW
- QZft1sgum/1Ywua3fDfiFDL5WoEPEwxzzirPizwTFPZC60kYUjcAp4NAHPSq91EoRS3061DHD9nb
- 94Mkfw020v57GbcCRt4IIqzNcedIH2jc3JyOaAIYrRZmJxtNdB4fkGn2hluBgBR+NZ2n2X9ozAQD
- 5qvaxGbKIRXkuFU4C96AMDxBKZdQkuEUkStuUegpNM1eWScAkqpHTHNPlwbjMzExZ4Pal1PS/s6+
- dY/6vuwPSgC9G8c0A+1xEknrnpUVxaeXNm2dVUfjVazvEZAEkMrccZzV1YYyBIhJP8SZ6fhQBSmV
- 4JfMVT+96UJdSQdcMO4A6fjVmTUoJiqTOMJ/q+elRyQs0TtaxF0PVhzmgCzpd55r7YI2HHPTmrV0
- sDTF7gnJXGO4OKyNKgn80NbFhjoBzWjqdg6SISPmIBOaAKVnI1leyhsMJOD7CqOqRtZqotjiFulW
- rhsSMshKH1ogsZbmF475TKifdf0oApabevHIAhCYOdxp0t59luS0I+995uxqpdRyWsrqmXGeCR/K
- rVlZfaogqv8AvD/CaAIY42kV3K5zzn1p9jNLp6u/A80YPNWWsJNPAVpC4JAZT2HfFWJoVmVVjhVk
- HTPrQBPoi2wsoo4APtBHL+tP1mS5uVEFxgJGNqH15plp5WmyBriMRsowM8UybXTNdbrpd6A/KKAD
- xbJAGs44FIPlnd9c/wD16ynt/LiDW2SR2qa5vP7RnMs6BNuQMd6jhkAUb2K8+tADYp0fhj8w6itC
- yQ3CFYeAOoqi8Uew+UMuf4u9T2NwIW+UgMetO4FmS6RJ1ik6HqxHAqC+gimUiA8DvjrU0kcE8ieY
- itu+8c0+bShaWxksSZoM4b0SkBTgha0cq33Cuc1SvrrLFV6jpWqbuGe1HnnDdAKy7i3WSY7OT2NN
- AMulWSV8ZDNzxV7SlbaFjClx69Kpww7W3ct7jpUtnNJHd5UjZnt1NIDdt7h7NQ7qGfpt7VR1XVEh
- dhEpP94/4VpafexTy7ZlbBGDVHxFbQh1j04HaOTkdKAM5ZVlYso3E+tVp4w8gx0Bqd7QxNu+6D6V
- DIoVySxAx2NAFyNmli2pjYBz61paW3lWrFS3BwP8/hWJbTBFJy2D6HgfWtiTWPsqxraBHyOeBg0A
- RSoLSTdIepzz0606exTWyQGMXljORTNT1B7+ECZR5fHzDqapfbHjbFkTsIwSTQA43ptyyS44Paun
- 8N64Z7Bre4YlZBtU5+7XLTQbjwN4Pb+IfWn2lw9uyrIw2Z5HpQBv3GirHc7LxWVZOVI71FNp7WDg
- QYlIIGD6VvaPdi+tljb5yeAzcn8DT9YtbPSpVhDM87jJ3Htjnn6UAUIrJreD7Si7MDoKhv8AUxqt
- pGt5GqIOr9zRfLM8ZFgZGtex2nGe4zWKN8rsDhYx2JpJ3Atx+HxcRSzWcpcL/CRwaj0zW1sQy3cS
- nsFPSoYJpbIl7dm8tT8wzV7+0hqEO1Y4lQ9cqMn9KoCp9kW7kaaxU+Yx+5j5etWrb/RGxfr5bkdu
- lW7KFILpfspDbVyc1fjNnrLtHqOYWP8AFjGfxpAc/e6Ql/GzW4AfqBWfpupS6Xer5vPlHmMjg10V
- 5pp0u4JhYNGvAYHrUn2WLWrVo41AvSMRZAC/8CPr1oAvafdWOuNG+lqDekY+zg8MPXPX/wDXWZrF
- tcWNw0erKElB4Rf4R6c1BpqyaBdbrnEcwyAc4x06H0rQS9a9jUTgOXPzMwycexoAw7u1jYb3zkU3
- Srtgdk54PFamv2C2pDQbWjcfKCeSa56aJld23YA6ZOKFqBrXGjjULuOKxKuZOTn+H/OKwr/ztOvs
- uCrg7RgVLYapPbXAEW4EkHJNdBNBH4gtgyhFmXuw60AVpbT7VpiPJ94jLetQWsDRSIYz8mec1c0+
- 1nexdrw7GjJXk/epsFtDPG0bOdw+b5SaAKWsXA+14Y71FQi5S4RvlAC8A0y5hHmHarhvQ9BVGSQx
- sUXPHX3oAmDCJ8rzgHg96gQ+ZGWbg9vahNRG7EnalkkF6hEXyD270MCWF3aEhdue1OsmNnMAih/r
- VaBgAUY8561PaubdnMxJXseuKANhIY5Assp2v12itZtAgubEi2nb5xuKYHWubstQaO6SVzujTqpP
- X8K2rXWLRF8xZJPMfjAzgUAcxcNiaRSpUocc96sW+yNgZCMVF4lvJdRvTOYkj52jbgZ98D6VWmlY
- 2qCUnJOKaVwCzviibANwYc8Utkdl7tbKhjxmpUspvm8tgn16ipigSEG4G4pxu9TSA27GeFbRlGGm
- P3cdhUN8GEP2hV3JjafrWfpU/wBmuAcZLA4/Sr1trkarJHcRmSEZO3uTQBmrcbZCLoDZ2x1qOHSi
- yebJIAPQipp4kmbzI1EQJ6GtCxsoHP8Ap91GB2yDQBlSWO+M/ZsBHHzZ71XkfMIWNgGU9vSt3U9N
- t9m21uonz0Iz/hVCfRkjg82FhtHDGgCuZ8EMjDZjBzSZ8pAwU7XbGT0pWtEjjAZgV4PFOml2QKqk
- OoOcU1qBNYRSrdkrhw3BIrah8KwXoV/m3PyVzyDWNp999kccgZq/ea7PFAGgZlJ6EUgN23thpdi4
- V1Eucr7ev9K53V/ER1a/MkuWdBtG04zioLrXJ5wDK2XAxmqVqmZ2YPtHJ/GgDsvC3i0ppr2d2ish
- yFAHIz706bRLNdOPnErKw4y3NcvZ3pjA8o4kB61o3OpSX9nbx3QIkU/MwoAj/sGaPzFjlWSJjk46
- ioYYwqssjIHHAHpWm4ESN9nYDIFZV+I7uVI1wrY5b1oAtafcvb3W4MM9Nx6U/VZpNRys54ToU4zW
- KXaDKrJuC8cVdtpi1gzs43HNAD9N195bdYtRIUR4wD1NX2KuA9uThuSQelcsZwzq9xyzfezV/SdX
- e3m8pXJhkPKkUAdYZk8RywjVVJES7U2cE/WtA+HDHohuY3Uxg7RF/GeaPBlxaawMW6rHKnAU9SOO
- lX/FFv8A2bpzTQk+cpAAz93nrQBx+r4c5CODEOA3Y+wrKu5V1C1GFKznkk9K6Wzv49fs8Xf7y7DY
- MhGNgrmtX0s2t66WknnKvUp0/WgCnbrJFdot0NwJxkDFdDYp86oMjjIArJivxbR7LuMyEjKitS21
- MW8auuW44H93/PFAG15aXdr5Uv7uULkA/wCFc+Yvstw0at8+eoq/p+rm6vRJMNwIx9KranYySXSy
- WEZZHOCw7UARXFyj5STAk7ntWVf2gALLyfUVoataLbfLO2SO/Ws2c+VwhLK3QDpQBmz2xAyCG56d
- 6uWPlnCkFcjoTzUBkMc/3cZpwn8oZkDFs8HsKALN1apDIHOeaiLkRkMOtSXE6yxAsRUcdxldswIJ
- HANMCuJW8xQgOP51oacWPPGAeRUUOIZQzDhecd6mbIcbPusM0gLmq6bHPohlhDeZuH4c1zzF1+Rs
- HByDXTae0s0IhjjZg3GPWqOs+HpLCTbNGyb+cHrQBZitjPEzW/LL97vinw2v2m2aORec9AKXQbsw
- ygBBiX72TWxfaS8kiGFQAwz8vWkncDlbqNraT5cjb/n+lMGckx8kjOa1tU2TxkPkMpxyKyrhJ4Wa
- KIDbTAkgvIp7URzgBwe/BpZYrd4vmZWNZ81x5cgBXDdzVlIvtUOGIBHpQA2aEROpR8DsB2q3bvG9
- iySzEsTkLnrVMqViCZzt7nrT7GBVuQRnODQA6Q+Sx80A4HApEJB3BAR9K19EmhkvCJ0ZsKe3tUc8
- Mc1yy7cpn6YoAzoUiclnYYY8AHpUl8zRxqpPy9qtC2tULgSMAvQ460lzIl9b7YiDt4GaAKMMQlJ5
- z9Kj8gIW5yKnS3Crlzhh6d6k0mbyZT565Q5z60ANtrRpPmhzWhbwy7DJcDhhwMdKlt7aK+gb+z33
- yKdxVuMCqaz5cqGYfWgB6yu8rBB8o6Gs/UpjGQXBGPTvVmSfyImyepqrqjbIw3WgCDz1ib9yOTg4
- NbVlNBJYvlVBHt1rBaPzQWU4IHSn2FwRJslJxQA6e3M0O4oAzdB6VXR2iKGQENGOK0ms1eAkFjF/
- BjrVGaAo371smgC7pety2kwl06Vo5AOWXmuwm+Itv4g8Ota30aWlySAJQfmkP/1zXIeG4Y5SVBB3
- evamXGly2tydwG0nKkHpQBZ86fRbpBLI252y4PGRWhO8Ml1IbJhHn+BTnNU9O1oRwvDqqhB2lHJP
- 4U6awb+z4JdKbzdh5ZurDHtQBat5LaRHiaOP7QejEZKD/Oauy+FI7W3Bsroyhxkq3QH8q5a7ujM8
- nWOQnBqTR9burCT98xdR60AbbaHc6ZG3ymJsZC/3hVnw/fNIXt7hygHzZp2oeIBqCxzqfmCgEe3+
- RVdrmLVAEtf3bxfOW/ve36UAV7+7DXMu5Q4/Os2e3eRWkiAGOijtWrPodxfQmeNVAPOPWsppJIpi
- JxsKcY9aAMwRyTSbpflx68VOYvOXb97OKtXAiZdzkqT0AGc037BIIRLHjsR60AVprZrZwGj4qTY0
- xyRj3PUVMJDduFfqvFRzxJCzrCzEr60ALEu+YI53c4qeGB7lGCnBU4FUopTBLvfk1at9R2sAMjNA
- GtaXsnhy2FzPHvC46jgnNQ33imTXrkz3oVFAwo9Kfrtq03hAzEfJ5gyc81hWM5hhKrhgT0NPcByS
- P5g2uVI98Vp6X4uuNGlyzCQIQR0bI7/1rNQxqW+05J7Y4qK5ZYUP2ZCW9TSA7SR9M8V30X9nMFZw
- WfcNi5qPWPDtjo0pE7O03U/Mf055rmtFmN9E0DEox+atPWbiW7lSO8Ja4jQbcDC4A9PXFADYtM0+
- 6nc3u7aOm3IP6Vnak9tYt/xL/M445zTIbieOdmWNsE46cip42EkyC4hYx469KAFsrT7XEJgFPOT6
- 1s+H9PD3XlzxnL/MDtqn9pghgb7GjL/eJORWqfEnmrA9oFRoxjJ5BoAp6NqDW2pzRXtuyIAw3FMf
- rVS4iF08pydmeCDxWvqeuC+Ro9qglcMw71mwReXD5aAlFJPPU0AZ0cEsbkSZKH15FD2xJJiJVj6c
- VfnzLGEXAA71PFpDPaebE6/KOh60AYVws8TBgrFe57CmHUG25RVJA7AVozzSLbNvX5T1AHNY/m/Z
- nPlqwDetAEtvqzJNu3FZBwQBjI96vPqkd3mRtokH31UYx+VZqWruxaFl+frkZxT1tvs1ujJgEH5m
- PR/pQAXl2S371XAHI+Wkaf7VD8hGR2arKySylRccQ98DmiS0jifdsdgeODQBQd9x3IBx1xTYlBm3
- En86sXUAwPswKg9QeaBErIEj6nrQC0NHRtUjt0K3AHzDABGcVW1fTzJL51jyOpz0NVooispebBI4
- wK2YFEthk8qR07igDAgJil+TKtnnHFaP2h5yI3ZsgdSfaqd2P3im3BGM9aktsjmRgCOaAJZrMwR7
- 3A5PT0pdMvZtOning+byzuVDyh/A8VHczSzDPy7RwOKgiuHEewjKeoFAzp7TUNM8XXEw8RhYNQmP
- 7ny18uNeOM7cCtMfDiS8uY0tDEYghyynjPbn864htP8ANhLIehzWzovxDvtFsDB9+PI4I/rQI0r3
- wNc6DO0N2VaQqW2q24YxmqFhYRgE/vkkDfMGBBP4GrSeJ7tZd6SxvIfmK4yQP84p0XiyC71gS65G
- 00zAKGX5Qv4UAbFpd28WnIsBLsDzmub1+AXt1LJEoQqfu4xu+lbWsWgs4/NsCXjPIbqK5+5kklmE
- rDD54BFAGb5cjybCrAnnB6ipEvXil2sM4GMVpFY7m4UNmNyOWJ4qteaM0BISVZe+RQBFHC2/zISg
- B69KlIVhIHA3HuR70lqotlBulY5P4Vcls44k3u6N5oyoHb60wM6O1SRir5LemOKv2vhuW4iLg7VA
- 6k4FTR2ax4aaVIwR3HWqGua5PcQm1WRBH6jqaQFzWbE2nhzynuIi+8HaHyKweJSEQEN6jpVcKyOw
- cMVznOeKmtZvOPDKuOKAJbi0JYFf4eue9IW8sncfvdqnlvVFyFyu09abI0bysMZx0oArC4eCTcgb
- juK2dNvE1N1M0ohljGQzc5A7cfSs6aweWAk7kTuapQysIT9mOSvG49aAOkvzLMxk06QNuG1l7j3r
- PlnnJAuGJij+nNQ6XqT7wEYqyn5v9utLULaW7j321uiEjLqMkKKAIotbghb/AI8hKGPIBHNXLG6t
- 7uzk3RLbKG/iP+Fc+8f2d1eFztzyD2q5p2oCFWRoxOX52nPFAGgLyC2lyZFKdB70r69buxRJBHjr
- nvWVdeXLE7xE8fwnoPpVKZUnQPkBhwRmgDq7a9tLyARWiiWYngL1qG4gurJ28+NowO2a5a3v3smD
- aa5WUd1HNbC6zI0KSX13JO7D5lbHFAE4V7pi0b5x1GazdUtXSM7v4iPw5rQ0/XrcXX75FgUdxzuq
- /qFrp+sWRe3uDkc4BFAHLRDY42ycd6uPOXiiV+RGPlWnXOg3IQvEmIB/Ft6/jUUEZmMcgydvzECg
- C1G2+Ly3YAvyM9qY88kaFcmmp807uwPJ4FS3do+Fzn5ulAFVrjbgS8Z4yah2C03SMffNWZdPknVA
- iluQOnHWmX9pILvyY13HHK46UAVre7LSyOCTmtjSiy7VijLeZ0IqO08OzPIUiTI74Ga6bRP7O01F
- h1KYJOv3V4BoA4zU1lExMrkbOAvpVcSifhjgrzmtjxPp7pO7SggOcqfUViy25hG5fSgC8rrLAojb
- d7d6SexlEgwpRfTNV7e5LFBbKAwPNWHeX7TguxI7GmBPBExhaNVIJ6egqOVknO1fkx1J61aj1gLC
- UEKlk4LVWvozC67kCFxkD1pAQ24e3uDLC3z9CR3H/wCqrczJdOGiOxvYc5/CocMYhtUBj3xU8Qjk
- XbKPIZOjqclvzoAu2HiO60xPKvd7wY/1fGBWnJo8WuW6y6XIPMYZEAzuH9KxISonAuzuRzgk9qtR
- 79KmMuhTt5cRyxznFADLzS2tMw6pAY5OoDEZ/Sm20TQQ74YwVQckGtMatB4kUpqreVIRw5+8aqXF
- jc6bAsbD9yThWz94UAOmmjvrRCMJjOQRVS0sD9pLyABM5Of6Vdtrdn+RUGcZqO6uRBG0MuFI79KA
- MfV7r7ZqDI7kohAVT6U2eJNimJQOuTnpSXFussrMvBz1pJov3YUsR9O9ABblRncQ3bAqY2EUwIiA
- Vqr20ojfYqZx3q9bSKAGcYJPIoAoq7OCEQBffrRDGEcleM8nNPjuGkhHmbB74ApvmxltsuTnuDQA
- +SFEjDwu5buD0qpLL5vMg2kEdOlXECMAyZGOMMePyprQRI5N0rt3BXO326UAV4b0Wt0pC5HrXS2W
- qq9zE7jcO+OhFc81kbg7iMqeAFHSpLa8eymaNOUIwD6UAavjPQYYybq1bBmXcF9O39Kw4iXdDKcE
- DAxW3q7NdWELISdiYIz71kz6ZNZNHI0cjqQfujIFAEtzAtu/7vODzmqlyzNyAo9vWp7uWSWJd+AM
- jjGGqOWCSWRVVW2+uKAKskpWU5TP0p8c+ExsPPNTmCVD+5U/QrzRJHJGymeOQc45HFAFczh497KR
- jirWlEsAudvII9znitEeBp7yAPZvEVPJUsP5ZqCO3j0yYDUNwliI6dOPpQBt/wDCR3Wj6eHFujvI
- do3DIX9KoHXoL6J11CJYZAONlaWueIYtY8Nwx6ZHu2MdxVeTXKG0eaXKRuCeuBQB0mn+HRe2Yeze
- MqRkFmwfyra0rwsIrRmvZICcgDLVw7xXFuFd2uEQfeAJAxUkkjSxh4J7gjPAErf40Abvjq1i0y4S
- KByCdrfL+FUI7SR4Wc+WzMOCW5qhf3Mt9cCV2ZiihRk5qpdTSBgRI+R2DnFAFw2k6AqJZMjuD1qn
- cxzyyAkPuiP3ieT/AJzV+01R7a2RpMZPVmGQ1WVuTqLDCptcfMBwRQBEkst/YMCSTH8vJqtJaoYQ
- JPv1o+ZDZKAo+UnBpmrCBpRNp4/0crgZ9f8A9dAzCdGgkOynxSus2xjkj+L1qW5/fxYj+8D+NRWz
- R4fzCd2O9Ai0lzI6mPaMOcZqW4uI7rbtJ3IMc1XScKqncQT0olPlKWfBz6UATKjSDcmdoFWtPCyR
- kzckHiqUV0623lKVIPzHHWp7Ic/vSRz0zQBcCqdyT4J7YqC3uZdKv1a2UupO7B6H2NMglMUsmcnd
- 0Lc4q3BmaMBiDjr60AWJRBfyb9P2RueWJ6KfQVLHqMdtcEysxJXayN0x0yKyWihWQBdwTOSdxHNb
- zWEF5ErXhX7QQAMNge2f0oAnhs4rq2kksHwirkg9SfauXnJnmL3AbL9jXSRWh0N28x1cEfMqtnA/
- Cs+70+O9/fWRIb+76fhSTuBimbyyyKDgnipLk7AML1pZbCWO7Hnjn26U6ZykRL+veqAryuvm/Jwf
- Sk3mo2AyHyCT6Ux5pLU5Gwg88gGkBPNAILUO3KmooyjL8ueegzTvPMsRjG4qBwKrW1sxJZzsIPGa
- AJbmfp5q7MZx71NZawEi8qZSyHg4NRGLzCPtB3eme1R3Nutocodyd8UAaVtqEUDlI8/N3PaqV2Ht
- X2x4lIOSwHFSWkEFyo+cD1BpbmNbNdkh20AMh1UiJ1c9RzWj/wAJa1vYiK1RmRvvetY5gDENxgnp
- UlhN5TiI4O4845oAmu51lXzFDGQ8jnpTra4uJkBAOQavXvhG8tIhPawvJAfmY9gKE1COwgIiAZiO
- 3rQBV866T52Qsw6YrXguZNTs0WSJ8IPnHr9KwZNamNumZSpPU4pbPxBeRy/uJjtXqfWgDodMtnXK
- QjYeo3VnalpiXjMzXMKS9O9VV1ydCXkmLY/SorWwTVJTmQEt81AHTeCY49Mik+0SJKmOg71W1bxH
- HLdgaXaSRNnjdzWapGlBBG2ec4GKtQ6yZD5hjLMvbIzQBfutWC2ajV4ywwN2OM/Sql/JY2kKGzU/
- McnBBqlf3Lam5e8lKMv3Yz2FU4VjgzsGQ3WgDa0ya0u7kxzgqCCcn1q43hizkEjRkOoXcAOua5Ka
- 6Mc3ygEVb0nW57ac/ZC4Xuo5zQBBeZjcwuMxRn5fUUmnySx6kv2cgg98deK1LjT31pTLpymSVuWi
- Xqv17U2GzFgFBUCVOo7igCTT7cnTp/ty5ZnyCvGOKz2uwimOY7geQB0FWY7tzu8xiqk8A96qOvmy
- MSowOc0AVpkkgk3uAiP39KkjtonYtnO4cKOP1q1Z3K+X5V2N6OeM8gfWiewaxiKhDsAyJB2oAk0u
- 1juAwniYshwoB61FLZfaJDv/AHWexpulXRNwpjkP7s8nu1Wd4uC7zfezxQBTjxZTHzlMigbdy8Up
- YXEv7nPvk1aNqbhDhgARnFZMCvbzuWZgc/nQBo2l6qs63AJA6VIsiG4DI4jXP8XeqcbrK5JH3xkH
- 0pWhWVR52CF6UAa8kUd7H8rD5f1p5txHAfNPasWRCjgh8D0BrV0a+DgCdfM3DaB9RigCml/JFPyB
- 159xV+C/wfNHAbtUN9orxO3k5dhycfw1XmT7JarIjb1k6U2BcuNSVGDSAPu6be1QTXcO0CVSwbPA
- 7VRtpftEmxW2Mx6HvUv2V1J2jkdaQBFJB5jBVYemetRyW6SqTKCfTFNllCHBX5vWkLBPvk4NADTG
- 0ePKB5qdLN5NjycqvNQIpZAFVj71LsaJQBuGaAH3aCVwycKODUMsZgJjxv8AXIzUs0DpHhmBycjm
- gOd37wdRjNAFETeTcARAbSeTViApfrhjufHXNJNCsUu18Z61Xit3Q5JxQBdW0MYKyn5hSf2BPIjS
- 24I29T6f5xUMMrs5HOF71ooVmtMyu3ynAAzQBqeCfG7aaPsmuYkiYFG3HseKq67YQW2rSNpLCS0l
- GQ5GSh74xWZc2SyxK4OZl5x7d/0rV0K+j+xPFOu4Pwpx0oAo3OnFreM7AR9Kp/2eYpxtyCx6VoXd
- g2nSlQzMh6UxJdjqSpKgfN6mgCOLSZGkKyYw/wCn+c1YltRodoWA+Y8Z+taPhWz866DQqxLdmq34
- x0ZbS23yY3NgkUAcZcSyrjcc7zw3YU62meOeTazdOhrZ07TYLkYvSFVfmqveQWkDj7CW9zg0AZs9
- 8wbO3L8ZpvmGRsyZQDsO9WLu0EwZojwMc1DJCrsA5we1AFmGVZLc7Y1bA6nvU1gIyNzgxtnoKr7I
- NgHO8dx0pJ3AYG3UnHegDRS+NpL5lsxh3dQverj38OtL/pKCKSPhWU/f+tYEt98xMnC9qgludrrJ
- GzFl7DvQBq6pYNGdzHGO3aqS33kEBhlSME0+01z7OcXGXRupJ5H0q5fafFqNuJLLnofmGDRsBmJe
- DzMEZGevpW7o8sN/bzLqTBML8oB71k/2YYh83FQRqbdtr7sDv60AX7jSo4ZsiVo067hj9anuNHey
- jVizMj8gkdaqQyi+UxjO7O0A96tXDz6rEFucp5HygUANGEQKjDJGaqzWbzgyn5QOPY1p2xZtOaGN
- VMo5BPoKqxa1NHHtmij+Q4xkUAUraZFiYScMOgNMf76CIZHf2q5KRq8arEjK4OTsGaki0oKwAEhP
- uDmgCohEsqq/O6rrMNMj3AEdgfQmn3tqUgEcaYz1JFMtLdn0wpFGxYHhjQBa026M0XM2WQ/NnHzU
- 6Yw6tCPt6rbpH0CdvzrPtrZ45ceU4cHk9qtzW6XLOjqwY9+1AEa+HWun8zR28xU5LAZx+VLaGSV9
- jrkr145amvEY4hGkjKMg5XoPY/571vaHFDr95HHqDMkoU4C9G+uKAOevoo5iSBjBxVYwLdRkL1Xt
- XSeK/CdzpkjRMqyJ95SjbsD3rmJbUwoeuGOCfSgC9eWc9rcbbdA0KHPmhcq39Ka8e9DkBS5zk1X0
- /wAR3dvEtuTm3AwVzW/D4w0xIEivbOaSTAVWBAH40AYMu6CZDkFcHcTz6UrtkYlwVHIwOtb91olr
- qtuRZSL5h5EX8VY97pc1jKAqZ2jB/wA/nQBRJhubjE4YOOnNMC+S+DzmrMkIA819wPTbjmqwfzcM
- 4w3vQA9mbYwgIz/ENvSm2t+6jZsYKeTkVYjn/eqwGAOp9aeW+2sdkgVf5UAQLKY5MHGferNv+6IM
- XT07CmyaeZIS1vtmkUdQKbZ+akOZoyqMe45oAvRzjUJPLLgSds8/zqyPDzwETagy4U8YwARWMbcw
- NuDDePenPrbXEfkTn5hwrdqAO709LPSbbzlZdvqD0Ncnr/iufX793uWQrGdmFGBjpmstdQeFRHKx
- 2Nn5f73+f61E7iLCxDnrjvQBaubtNypAxyRzg0q263DMsJIzzyc1mwyDeSD82e9XIGUIrSyBNw+X
- 2+tAD3tSpcFvufrVZbdL2XbnDdjnGKnhs2nkYtcIEJ6461HMiJIApBVe5HWgB8mmtpzDzSrrkZYU
- 65mRGYoBgirEkCStiJlC7c5IqjLNsYhtu0d6AKkshbAZcAdc81Gdwb5SD6cVZjYy5WXBVu/pWppn
- h63urfdLdxR47MDk0AYjnhehxntVq11OVANuTj8q2/8AhBZ7mwkm00CYKQBtHXrWe+kTWS7J4zE+
- OQ1ACQX/ANrkC3DD0wODV280KQwM0jxheueKdZWcCrvkjYYHUHvRe6jFLapHtLKeDjg0AVrDQ5xd
- xuhIUEMHx8pH1roZtH+2W+dPIbHDMOcms+81YNoqWltlFKhQD1HNP0e5udHsHFkcyMRkDoaALUPh
- aa1n8yUgqRgjPOO/eq+reDkvHzoQYIB85JzzW5HBLqWmCSWQJM3UEdB3/Sk0S3uNPmIkBlgJyXAw
- o/Ci4EHh3QYfDsfm3mHklGGLdFqS91HSYpvMw0jjkhTx/KqXjLUg8hihYiMn746H6Vg+QYxuV9vH
- 1oA3xrem38TNe28rqp+VUyD+gpbTU7O6ylvEYoEBPzjDAjp2HeuUk1aeyfNqMH+8BTrvVhqEAMuP
- O7n1oA3X1Q3U0klp5S7OGHFZt7rj4DwxlTJ6riqMTiDZsHTn6/WpbfU5EP8AxMVMqdFIOMfWgCZb
- lpEO/GDgn9K6bwZpktjcC7lUsAMYPvj/AArBi0lrpc2sqbZsHbjkV20SvDp8UUZBcDp60AY+ueIZ
- dIu3Frh0lbD+YNxAPXBPSqLrpuunyNPBSSM7mZyQpJ/KtWQ2uqvNDcjypQjAFjnJx0rhNYhntbvy
- 7jcucgIe9AEUMOy5ImYgg4xViVVa4UFSoToc9a6DxZoEdqv2rTsHzDlx/dFcujFpG27vlPGe9AEi
- anPpV359o7b143jqo/yP0rWs/FSavF9l1JltlB3tOerd+axl3XGfMXC9896iu7UbtyYIxg0AdTc2
- Vrqe3+zZxIF4Uj+I1S1Hwpexu0kts8aL7Vg2t9JZ8REjJ+UD+Guh0TxjeaW3/EwAuFAxh260AY8y
- ujfLkBOCOuabHcqgCxYAbrz0rsbSysfHdzks1rO33Y0AwTWd4h+D2r6M5mmt0ER5D85P1oAxLfWZ
- LSYrbnAb5eKnudVnyELFkHOcCqUmjzRzBWyD9K6W38JtLo6TtkLzmgDHtryGZiZUDZqDU1Vl3wp8
- g+9jsf8AOKmGnw2cpE8jFR1I7VdGjRXMQa0kdoSPmHrQBn6bYnWz5NydjgZVgORWeztBK8ZBJQld
- x6nFdZ4ZtoNI1QPI7O+OB7VX8faO9rdC7ESrC4BJHqaAOcgUTtuORiraW0M9yiXLAIeoPc+1RWar
- u6Haxq7e6ekEZkBGzGVz1ptgVprUw3ku3iJDgDPUYFEzAwZRN2CDgUw3JEkezD7+xolvytwn2pVV
- RkADv060gLVlMk4aLIDHp7+1Vbu1+yzgThiHOOelElyIZl8v5CDkVtxWkGtaYs0bMblCcr/KgDCe
- 3LzsN20L2HepUQJnHI9KsX+gT29pHKCd79qWw0u4aPcwU4796AL+meIr2G1aDSbiWHOMhR1qxZXz
- xXBl1n/iYBBlg/FR6VZW1nciS9mdJADgYGO1Q3pIOOu5hz60AO1vxLDqluP7Pt47eJSQ2KzvtiSg
- eWuPpU89gsfzH5cc+1ZaSpbXRZT8tAGjjz237gNuPwrc0O48uUPOM4GBXORXC3HmJD1bB/QVZivZ
- fLwp+71oA6fVfEiwXC+UBGjfKTj14qZbi7gtJWjkY2zx5C9s4rnbCRdZiaOUkFQTke3P9KbYa1c6
- XcBARLEWxhzwBU2AotqzH5Ls5YdFPOKmiu1KgxfvCOqHrXTL4EXxLbl9MO6bGRkYzXPal4TuNLu2
- ju/3csfUD9KoDO19yChhO3OcqO1VoZEUbHVckZL9x3q09s8a5uDkZxUDWX2i4OzgHvQBLCwkwyEF
- c4z6VNDZm7utkROCfwqCzAhuGRhhV/WtR5okjjkQ7ST2oAlSRtMdUjHzR1p2OuOI2Ly4kHQViS3K
- iYBMsW5zSNF9klEjPnPSgC1dzm4uVKSMZd4JP41oeJPD8+r6ZHLbwmW5H3yCMqvr/Os6xu/tDfvU
- CqSOfWuj0yf7OxLO2CAG9x6UAZs6vcIqSiVw3GQMisR7RVvpFkGFU46e1dN4c1hYmCXm0quDIO9c
- 54quVl16drdDHGzZX6UAV5bTzWIi4Ws6/DQEoQSpI5q9BfywxkS7WU9OOlMa3F8hG7bj5sn86AKc
- ErggKVA96lFwLcYHX3NQPAHnYD5e26pAnluA/JoAu6JevFqsEqs4YN0HQV39p8aL+CJVnWKWOP5c
- OAf6VwCzrbxAIMMefpT48zEFD9RQB6hZ+PNE8YqsfiJFt5GOC0abcH6ioPF+i2/hiGK50xmuLOQ4
- AjO9s/T8a8wlzLIdxKkHIwcc1s6R43vdJi2xurxsdriQbto9RnpQBal1C1urtzcIVjfqu3FRMNM8
- zbpplViehyAKnuU0/X4N+ixtFdR/67e2fN+g4xzWPcWzWFyDL8gP3Qw+9+NAGhqulSWzpJHt/wBn
- Bzj2NejeHLG28f8Ahox6/HsmA2DHBGO9eTrrksUTKSOD0Par+n/EnVdMRVsZYgpHIK9u9KwEvjn4
- eTeF9UY2Jie3HI+bJFc6b6eMkt909j2rsrTxpYa7bGHWYpXlc8Ord/yrOu/B8gEjQul3Ao6RjLL9
- cGhaAcu0skr7mK8HtTjEAcMMk881Zm0l7JXxg7uQBywqqzysygDBPr1qgHSWqzANL6UunXjWBOxW
- KsaZcggbu4HSlindrf5ANxNIDqblPteiWrESNC2fujJ7Vd0bRY7KLfZswWYZYSdT2/pWJ4Q8ST21
- 1b2krIYj8pBFdd4k024ht0nsdpjA4AHNAHO6npkSs2SwPase6ieJcSYdenB+atGbWykgF9G2cHvi
- qGqMxiWW0GFyCSRnFAFeSN4yGiLE9we1QXYEhzMo+bnAqaC9YzbpSGY8CoL/ACwDQ80AV1mxdJwQ
- q9h1qd71WHU/QdqgDO0gJAyevFE4WI8dW60AafhzUHt5v3ZAzxVzXNFku/38Odg9KwbK4ELA4z+N
- ddourgQKJsMv92gCr4Y8Qy6VGUmkdLcDjn5/8a6vS5tM8SWTG3kkaZeP3xIyfxrmPEuk/ZXF9akG
- CY/LHj7tZy38tvcxSwnYw7DpQB0viLwrIigwhcHqAeKxDpbmcgJtKjOfStXRPHgjlEeuAzZ6bf4e
- lajX+navE4gZIyQcFmxQBxd5ZPG+9iuDxmqitHGR5oO09M+tdDqmjNsDl90YPBHSsJ4N7uH7dOOt
- MByxj+EkE/d5qwYGkUNu+VetUgxVz6gVNAryx7Y84J5PpSAeZWjG8A/Lg1sabqn2hF8wnniqPkK6
- qk/z/TilaEWo/cgqKANPSbRba8zM6MXGDzVPxHYPPOzOOVPy471R03XmSRXlQEHv6VstqaakgJKh
- h0X1oA5jBjYrP8uTkA9TQ0qoxLHqPyrQ1+z6TMu104x65/8A1ViSsVc5GdwoAseWbkDyQWC01QVv
- S+5WGcbe9OguTFZqIjhxnPHWnWTCO6LyKjPnpQBDfs4n3sMc8Y7VPBKWT922498U7X0RCjRnJmAL
- KP4aq2rtA/ycBu5HXFAGkYg0GT8rY5J5qIw5jyMORxU28zwAou5jxj1pnktAzCUlT1xQBHFP/Z8w
- dpNsg6ccj8a6jQPFNjqdqbfxJbvPM/yxTE/LF9c1zsNsJ1U3EYIP8VPe1iicCORsnnHTBoAtat4Z
- mS92Wn79WBK7aw0ia3uXW4jdChxkjvW/Z+KLjTZFd4hKwyAc44qy+nwazpxEOPNdvMdx1UdTQBzb
- AbSNyqGPf+lWvDPiW58IXDtZzOIpRiVVON4qS/0ePcG04/aYV4Z8YwaoPGJrgq2AqnAPY0AdVdww
- eJLX7XoxSKfbnyRwzn61zGooyMzsreYpwQTyn+P/ANap9NvX0S4DQtzu7dhW/rel2viWzWfRiPtC
- L88a/wAfuaAOQEvyDepIOOamtbFJZWKzrH7Gpk02QRBLgYYHkDtSTaf5LBgM7u1AEVxbS2aiSNfm
- xw3St7RfiTLFZi2vUe4VRt44xWJDczTzoLoFgvO096bMomlkaJfI5ztFAG7Jqdlrcm2WNYHA+82C
- KidbiCAoVLWzfKoHOawo1dyGO4bQcc9frWppOvSwQLDcDzQSOvbmgCjcWBQsqDYwOTmo44BdAZfG
- OeuK1NYdZLjzCdu8dAKzpLYQt+6OKAK88ciXREQ3AY5/Ckmt3dlMoznPSrMU2zJxgD2zSSRmX5kY
- gdiO9AFWO3KSDgqMjrXQ6fYuUAjG3HO7rWRawNeSDLYKnHPeunVG0bR4ruTnc20g96AHxn7ZbNA7
- qzgcVzup2s2mzOl0CAT8jYzvrb1TxpZ3tgr6fBFFL/EUqpp+pJqpxeqJAPulucfSgDDfcjgxAqSP
- mB60xXXlZFBPXpV2+tms5W2oTnpk1nht0uZCAfTFAG9oOvCJBb6jueJj8qj+Grer6XFCqvHMvHTA
- zmuajlMUmWHznoKvQ6tLDEPtKeZnsT0oAkaBVLGX7x54qOG6NvkEEA/rV2dYLi08y3fMhH3e4rMR
- mkDLOMkHg9KALcN7vXI4Iq9ZyG5jw7An1rFuWMWMAopxTzqMkIxZAuOpINAD7ZAcg9F6VqaXdRFg
- pX5h92sPzRbfKQdvr61c0+4MjDyxsYHkkUAdA2lvdQ+ZcDIPGOuawNY0wWNywjwVbocdK2E1ubTF
- +T5gw5yM1Lc2kOqaX5kXMxG4nPT8KAOSUSKu5VGM03aZmRo22k9Tird26Fgp+6hwcVAZfNmCnBVu
- mKAJp7N71FDcuOI8d6pJlLlt+d44PoK0dTZLKCI2HmCZQCd33c+1R6iqXKpJBu34+bPQGmBNpzND
- bgH7zHjPapLiXMhEvzMRwarQXG+ILcfMP7w7VZjdHj+QgMOmaQCRF7AsVBZO2am2G5t2kIAJ9O1V
- 2vzM21l+UU9Cjj5M8eh4NAAIXjUeRl8/pUa6k1hGFtWyG6n+lWYX25Y8dsUs9t5tkVkK7Tz7+tAE
- 9l4hAj8q/RUf+Db0P1qZ/DUWrTO0paK9cfLGg+Qn61zc0SeYc53DgVr+HNfk0u623LgwSDaxHLY9
- QaYFa80a60G58vU1VmbqF5AFWdC1k6PqaTW6qyEbSD+FdRJd2s8IikZJbO46MTmRB7nr2/WsrxD4
- QjtohLo+9kHXPb0pAd6uh6Lrekm6hkkQSRgNtQfK/p+dc1f/AAsuGUnSWSVScgynbisHQfGFxpki
- RKw8tRyD0z/nNWPFHji/1lFihkCxKMAocUAaNt8NNSt3bzYrYsnT5xTLvwZYQTIuqzlLh/vqigqP
- xrk/7QuIwRHcXG4jnMpP9ary3kzhvtUkrSH7p3E0AdXqPgvT1vI47K4kfcCcYAx0/wAar2ngu2uW
- ZIJX3pnjHFc3DqUikfPIGHU5PFb2ka3PDe7dPZGGzGW7/wCc0AX7LRLSzcxb3eXrhhxVG78JeVcA
- bvvcVfEgudqaoyrOrbiV9Pwpmo311pMnmWmySH3w1AGRrXh6TRfLMq8yfcHGPxqxZ6fpmnmNddml
- jlk5+RQRx/8ArqO51ptT3vMwWU9iOF/CsOZHnkIkYu3YnmgDo7qPTtPszcWTu5LcAr1ycVl6p4hk
- 1BRbsCEXkCqEGqz20wEWGEZGAeRxVy+vRqV2JpUVJiACQMAUAZ0+mvaNuuz88hwAOmaktbt7C4Ub
- c8jvW5rGkp/YUEsRM0nLSf7PFYogSWEF/lJ6CgDWcjXyuMhwOAO9Y09hLbSyKy9+pqzpM9xo90Jr
- co2OMMM5ropr2PxBYGK7VVXBbIXG4jnrQByUI8xSADs6HPWpPLIjGxssvr3pxQmcqx+VGwFHenJI
- gOF5oAW0jZB5nQnnH6Usnzjrg0rW2/8AeISD1x2pWR5VySNo60AQBX2EzHIXpSQJ5kjOOFpLgrtI
- iLFvWi2Y3CFYuoNAEt4myTBBQ46Gq6OyHKjGTzSyyyXUm+/cnHc0+PY42RtuDcDigDS03UzdQlHG
- WHFSw3/2CX99lo+hA64NUorOeyG9FJA68VJFaLqNu0hkIlXkgelAF3VtEjvNMF1pKOctyPTFc/bw
- tGVeMfMRzW54f119M8yJ2IjlGzk9B/k1p6f4fsmi2xXsUmeP88U7gYV5Et3aQlWCsox+NR2eUnWG
- 7bdvrZ1TRY7FXjuQsatzHJ7VkyeXbxnz38xl6NmkBFfiXR3MDKQjHI9xUMV0ijMnNdBZWbeJbUcC
- SZU+U454rFu/DF7byNJcW0qxqeeOtAE0EcbI+4nax49qnKNY7CCG46Vjw3DRHO1gtaNrqPnBRKu1
- R0Y80AXYDHPAzlPmzzTWG2Evn8KafMMWIsFfamKxcAyjAHbNAFSeRJpOBg0xrXykVjyp6VLqFv5b
- AqwTI6dal02ZZ5VjuMNGentQBJZxXFtFuUZDcitDSPFrwOYrkFkfj6Vl30l7p87RpKRDn92eoIqG
- 31gRxk3qMzqRnmgC/wCJtIa2uzLYfMjgEj2rNs70woyIMjPLHtW7Y3y38gkUnGBke1R6p4dS/mNx
- obeZgfvIVH3Pf3oAz7W3EmGzgrSSRqszF13+4/hqOOLdGSrk5HO0d6WCUxYaUMYhw4HegCM6TLcy
- Ztkd0wckd6jtZZbPiI+aqnlem2tTStXNvcbYZyiSA4QcdMf41Y8Taf8A2dZieGMR7sAkc7s8H+dA
- GVJqTT3AKtjIxtrStNVy/kyLuUj1rAlhG4NtKqOc/wB+l+2SpP8AcKMn3s07gdJdeHPtLRS2zpCr
- csD171laro72bGSFWZRwzHpQdUe8hTDEMg5xU0N7Pcx7GVpIf4lzSAwlk2yAoevUDpWpa2hvYeTg
- 0mo2UM8w8lPs4HUDvRpsFz9oYW6NKB07U0BbjvptGhkgJDRMu01VLRyyIYQSgA3HstVdVMiSlZyx
- bPKiksbyS1hdWUmKQ5K0gJpt8UgAw69iKn0/UyJdrdOmKIPIvW/cyLEqj7p4zUEUIEr+blHXJBx1
- oAk1O28q6VoSFVhk1GbZQ25TzUlvcfakIucKAcAnqaWK1cyFkQlB70AJvJdNq5I4+tBcbCnCjv71
- LIVcAowVhxj0qO2t9zkXHKt0bsKAIpbPIHlKWUjk06wgaNiqIBzViF/kKKwBHA9aguI5oX3REk9j
- TQErWypGPOGc/pTLTy47gMFyob5fetB7EmcG3G6N8hSTjNWRpgsws/y7ouWB70gKd5dGSRcfKnIP
- HFXrHSYL61e4kfyVVcYA61lC7OrxurAKxbIHtUtxfC2sTDA/A49KAEazRmkEw+TqG9as+H7YSTeX
- bvu7ccYrIt7qRdobPLc59K6jw9pf2KUXcJBVjuI/z9aALF88MsJh1AiRoPl54Iqt5GmXUG3ABx1x
- 0/WneMbGfTryO8VB5d2N6qfTJHP5VBoNtFqUb/b28uU/d2d6AJLPV4dGtP8AQyokHGKgu/Fwu9wl
- PXgj0pmpaSmnOxmYEdu5rOht2knZ4FX3oAimiju3AtlAznrVWSAW7OC2HQ/d7VdNjLaMjurbSeMC
- s+4WS41BjyEB5zQBcgnk2ARnJbqKZcydmZt3fFVxB+9DRkjHfNWLh/KKGTp/6FQBGLg3C5PzFeBT
- LeT5yEzlB0p1zb7wGtzt9RTNhWVQOHPWgDc0iUajbPbTgM5GE9aydTtPKk8sKcDrk9adZX5+0FLc
- FZM/K1dPpmgReJLR2nOyZDhQT1z60AYWgXYtrvy5cFXBXA9+OtGpLceH9YIsZ3BwGI4+YHsaNR09
- 9C1ERTFTMjBgE6YyO9S+IoDqHlag5++RGPfGKALelpb+IbtA+Ldk+ZkXofxqHxFpn2Vpv7OXdGOW
- 56Vk3GpCBQB8pB429a0bHXN8kX2gKY1ILju1AGakfmFfJXLN0/z+VdZYQG503yda5xyPp/8AqqXw
- 2LKJJvsqbjIdwDL936Viarq8u9nhA8sNg88/TFAGrdeFbeWBHscSL/AM9DWRqnhObyS7KUYdfetH
- wkx1Gdnm3rECAB6Vu674psYbIRxeZuHBJHWgDzZw2nybQMluDVnT9T2PsJK56Ve1OS1vJ/OhOfXj
- pWVdWctu/mJhgTxQBeYrOS0xAxTojJHKHspCQ3GPSqaXCTuqpnf+lTQIJ5XRXwy0AaN7YxzWzT3I
- /fSHp6VnS2LI8Yt13kj5ucAU17me4hYbvkHXJ5qvJfDMYDNlevqeaAJTAVJGBuHPFSWuoMN32iNW
- UgjOelVo5vNUvg8HGKVollOIG4HNAGhb6dHewhrVy8gPK4qaFTZZRssT1GKzLWd7C5zDlS1a9rq5
- vU2uFAIznuaAK93po2GSIEjqefu1C8QZApc+uBxWnbQpeyCG1OB1cnjmi5sUuTlxgpTQFBAYCWEQ
- bjrmmsHvDypH0qYqYGPlk56DPSnWFuz3BN2MCkB0niGK10bw/ExCyMxwhVskH8K5O98SPfWixqPm
- AxkjBNEkkz2iQSzgqn3U54rPm4RkY4YEfhQBd0gPBMGnwc8fSpvElpFBIGU5Y4Ix0qjcanIkKBG5
- 7VGzPdIHvF3P9aAHpGtymc4Ira0fU5YYUG7KA5P0rAEgjOFjfHtVqzndD8ilFkGKAPTri4h1fRrW
- DVAojmjwjdwPY/XNcJK6aTfubdjhDgc9a19PnbUYLW2upsRJ8o61S8WeH1sryKJ2AeRSUb1oApTX
- TXpaQMWJGcdal8PSf6UTcj5WOKz5YW0zgTKZG44Bq4THLpSqj7LhWJdsdfSgDo9e16OGFba0ji3p
- wZCBzXOoYZp2N2u0Mecd6Zp12cIbkfIBzTbwRG53W4wp5oAbeWVmgY2ZYeuTVC4SWFAzjdGO5qws
- HmK28jaTVi1vhaR+XfRGeJhtVR69jz6dfwpgZEcrPcAp92pl2IzMxLuRwamfSJZCXtnRhnLgcFR6
- VWc7J9mNpbtikAW9w0MheQj5ea3NG1Y2sPmWhCvjuf5Vk7UadY48RseW960rDS11C3b7EMzL3oAt
- 6hpn9pZu4GzGq7djH5g2PzpPDsMV/Y3Fveg/uVZl+vNJYRy2KhXfcB972q5aRw310/2eZLbcuCWH
- X8qaA4yTeT845B4qaEqjZlVtzflV+80qY31z/Z8T3ENqMs8ZAAGcd6zoZMncEwH6H0pAdDpusLZQ
- 7Rjc3ApkFoZJHmY4iAPXpms8R7oh/Gc5HtXQaALbUtGMN6ApPHrzQA/TvEdsdOWD92rRk8gcmud8
- QXkl1cZzlfapr3QP7NujGjfKTlSKzr2Jmdgx/wBX096AIkn8ucBQQjdat/bWMLZKOOnOOKzdjL0P
- BoiXe2Cu7vQBpxC0KAyK2488Hiql3LskbaDtbpjrV+3tlubYC2TExGBVe+tJNOAF4PmHNAFO0meG
- R1bI9jU0iK23zcbsdagWYO+xOH7mrkMWYcNgkUAQwKGA4JC5pzyFmPlEADt61asYIgSJWA3dOKv6
- zosFpdxPaBGVlG445BwKAMwuWADAbqs6eI/3hl++Pu1cj8NFyrRncAdxb0psElpY37NMhljD4YKe
- poAsWmm/aIjKknlsvUnoalhtHLcbiueucA1Uu9UMs8wt4SsOfkUnkCrOmXcotj9rkV0HSLnmgDoD
- 4JSXSzPNNFJhdwCkZX9a5+K9gD+XPgDdjNTpez6ZZywwPskcZbk/KK5qZ2llPmvvYnrQATr8zE5D
- N1zxRbou7951anhZNYuUVFw7dvSp59IltXdZ1IZKAGvpLNGfLAfufaqDCSKUEkgdMkVd07VWs7oG
- XLL0x60+7ePUjyCpByMUAV3bBGxsk1ZikV4gAMkHOKpzW5SUmN849qjjnlil3KODxj0oA6KykW7t
- yJW8pk4BFdxrGhwax4TS5JWWaEBEY9QDn/CvNrPUfJmBcZDHLV0s2vsfDMwt2ZYy4z7cGgDHv9NK
- yjfD+8bgYFUNRtTps4S6HlkjIBPU/wCcVeN86xKZmJlyMc5p/ifU5L/RYVmto9wJUyZ5oAy01Dfb
- qZV2xnoKbfX6NEv2ZcHHWmPLFJYQx2ZLTL1U1EIJA+2bAJ6Y5oAIboyDb0PU1c8xLkBJLna4Hy44
- 5x06VAbZbdcyZ3elNBXeCRjnOaAG2808N5syYmJ7fx+5q7tW5QCZQso/iqsULT7rXLr6k4xVi0dX
- +9kmgBlxpbI7SxqZAoGWz0p+i3txZ3AezJAHXjrWlZ26mFyzEnPC+vStzTLO3vZ1M8Yjwp6Hr0oA
- 5/xFqyrIggQKrLlsdc96xpQZ5wySbu2DVnVYQ9/MJCSitxVOQFW4G1aAOm+H3iGPSbie1upBDBqC
- CKRugwOfwrI8VWsenazNHZtvs0fEb/3h6j171Elg02N65x6Gt200i18VwwwXcjQ3Fou2NQMiTvye
- 3WgDn4riKEhkfKf3h6+9aFlGLeyS8eT5DIMoDnv3FXZ9I0iwhJFxJLMpwY2ACg1TvvISzMs77S5w
- EUcUAW9dH9qW6y6ZKBgcgdawoNOu7iWMmNiWOMDtT4Jxb5e1bKuMEHsfWpNM1ZrG4WWFmct0BHSg
- CprWivp0u193mMeR6VHa2jmQbVH0zV3WNRkv5mkn5YnjFRJGBMjRMScdKANvR7OO1u4pS+SGGV68
- d61/GnhSHUYReQyqsZXiPI64rK0S5hRNzfePXvWr5w1KIwwucAccUAefW1q8kqiT+WK0RpdzFFuE
- bFT0bHBqxrFj/Z87LjDZ/Km2ctw7Kgk3KO3SgDPQPuHmqNynv2rRs7hrhjDIcDqD6VPeafDfWbbC
- UnUjav8AeHfn8qsaL4bl2pLcYWJT85PYdzQBq6dfjRtKX7QnmC4JQH07f1rIl0SztbsSrcoQnJQH
- qaseJ7mBVT7PIXtDwrYwQ3esOO4RrxvLZmjI+90P5UAXrm881T9lHOeAOareXPH+8BKOB19Kb9rF
- pcq0ILDPc8mp7m+S6k3fdKj7vWgB8Gtj7Oq3AZ3fCs7DmorqxQTbl+oAqJJlu4gJMKwIxT3kNq+H
- G5/7o7D1zTA7Pwpd6NBrk5vQwMv3Pl+7UnjAwwXX7tFe3l5UjBbHvXP3GnCOxhuo2IL1G+qPcFYX
- cknoT/n2pbgVZtGFxZvNbH5VOBk+vt+FZ8lrPakrcqyHGcEYzWidWS3lCxAlVPUdDWxf6pa6nLH/
- AGlH99QoI4wTwKbA45pHEirjk1asbxYZCsoDYH1rV17wyumSKVbeGG4Y6gVk/wBn7UdgCpPc0gLw
- aEwtLKMDtWhoNykVwHdd8JGCjDIrDkSW1g2zOhVhkVLo+puSVlKlccYoA6Dxf4PbSLRb21wto7DG
- W7ntj61mpKdXtxaOQvlfMCSBuJrqLfWIfEvhg2muKzQoN4CnBJHT9cVyU5hEjNbB0CHABPNAGTPa
- fZriQONjqcZ6flUtqqB1SRmMr/dJzWlDaLrEUh1Qbnx+628ZNZE1s9nfctxEccjpQBO9tLcy7Zjw
- vfNQ31q9oee3A75qe2Yyzby5OKiutRMsjKQDg4FG4EVvEyfM5xnsD1q5bbzKHBAB9KrCJN4YMd3p
- V+wt8szRZUCnYDXsWSGPz7jGI+SMVVuvErXKEWuRk9QMYqXVyLXTUyRmRcmsSC4EAO8D2pAXxbma
- IMR8w7+tVdRtkUAT9ew71as7wsF2nFGsKodDOMzHo/YU0rgULe7j098qW545Gaki1FIbwzeYyzfw
- EdvyqkyGSfaw+bvRcQLayqyEnAyaQHR6gi6/pXnBER0IGFHzN15rnmlXyTGRuQHByeQau2GrS20G
- 9OhO3H1//VWhf6RprXbXmnrMtuYsOjNk78DkfiDQBi2rpHIVQjb1otHPnBZAMAdRVUQiW6Bgyis2
- Buq29q2nXJjn/eDsycUAOLCG8yg9zkcVCzeVIZY+cenekN0LqYRSHAHA9aLMCOTy5BlTyPegCxa6
- ltkL2+ORzxjFWbTXpLSV3Y84+XFVJvLilKjgVFMpAyBxQBq6prEF7bQSzA+ZJ97jpVRGjDbUJAB+
- U+tUywlJUdE6VteHLK3kuoDqQZ0zyAcYFAG3feVo+io90u2d13R/LyR35rm77VZNSmzC5SEj5hnH
- 14/Otu+hv/FN3gTWywW4KRqQM4/OsUeFZp5miaVAc9R0oAaXWa0EUWCIjuA9PeqEMbCYM3G77oAr
- bi8Gz2YDmeLc3ygev61X1CxnnuTE8TvPb9fKXigDMuIJFlBdtzHnAPSrEF0IwDCm5hw2VNRzxTWt
- 0BeKVMnTIxj8KZ/ahtgY49uT7UAX7VH1K63oERVOTxiuu0ex0nS7L7chJkm+R1kwwyPQZrh4JJDw
- zbVbk4/OrNpefLsnyyg5UUAf/9k=
-END:VCARD
diff --git a/vendor/sabre/vobject/tests/bootstrap.php b/vendor/sabre/vobject/tests/bootstrap.php
index 3608abec1..14281e218 100644
--- a/vendor/sabre/vobject/tests/bootstrap.php
+++ b/vendor/sabre/vobject/tests/bootstrap.php
@@ -2,14 +2,24 @@
date_default_timezone_set('UTC');
-$try = array(
+$try = [
__DIR__ . '/../vendor/autoload.php',
__DIR__ . '/../../../autoload.php',
-);
+];
-foreach($try as $path) {
+foreach ($try as $path) {
if (file_exists($path)) {
- include $path;
+ $autoLoader = include $path;
break;
}
}
+
+$autoLoader->addPsr4('Sabre\\VObject\\', __DIR__ . '/VObject');
+
+if (!defined('SABRE_TEMPDIR')) {
+ define('SABRE_TEMPDIR', __DIR__ . '/temp/');
+}
+
+if (!file_exists(SABRE_TEMPDIR)) {
+ mkdir(SABRE_TEMPDIR);
+}
diff --git a/vendor/sabre/vobject/tests/phpunit.xml b/vendor/sabre/vobject/tests/phpunit.xml
index 8aeb65aa0..46dad6a3d 100644
--- a/vendor/sabre/vobject/tests/phpunit.xml
+++ b/vendor/sabre/vobject/tests/phpunit.xml
@@ -4,14 +4,20 @@
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
+ beStrictAboutTestsThatDoNotTestAnything="true"
+ beStrictAboutOutputDuringTests="true"
+ beStrictAboutTestSize="true"
>
- <testsuite name="Sabre_VObject">
- <directory>Sabre/</directory>
+ <testsuite name="Sabre\VObject">
+ <directory>VObject/</directory>
</testsuite>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
- <directory suffix=".php">../lib/</directory>
- </whitelist>
+ <directory suffix=".php">../lib/</directory>
+ <exclude>
+ <file>../lib/Sabre/VObject/includes.php</file>
+ </exclude>
+ </whitelist>
</filter>
</phpunit>
diff --git a/vendor/sabre/xml/.gitignore b/vendor/sabre/xml/.gitignore
new file mode 100644
index 000000000..accb586c7
--- /dev/null
+++ b/vendor/sabre/xml/.gitignore
@@ -0,0 +1,9 @@
+vendor
+composer.lock
+tests/cov
+.*.swp
+
+# Composer binaries
+bin/phpunit
+bin/php-cs-fixer
+bin/sabre-cs-fixer
diff --git a/vendor/sabre/xml/.travis.yml b/vendor/sabre/xml/.travis.yml
new file mode 100644
index 000000000..19a61a2c8
--- /dev/null
+++ b/vendor/sabre/xml/.travis.yml
@@ -0,0 +1,20 @@
+language: php
+php:
+ - 5.4
+ - 5.5
+ - 5.6
+ - 7
+ - hhvm
+
+matrix:
+ fast_finish: true
+
+sudo: false
+
+cache: vendor
+
+script:
+ - ./bin/phpunit --configuration tests/phpunit.xml.dist
+ - ./bin/sabre-cs-fixer fix . --dry-run --diff
+
+before_script: composer install
diff --git a/vendor/sabre/xml/CHANGELOG.md b/vendor/sabre/xml/CHANGELOG.md
new file mode 100644
index 000000000..3d8eb0fcb
--- /dev/null
+++ b/vendor/sabre/xml/CHANGELOG.md
@@ -0,0 +1,208 @@
+ChangeLog
+=========
+
+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/vendor/sabre/xml/LICENSE b/vendor/sabre/xml/LICENSE
new file mode 100644
index 000000000..c9faf409b
--- /dev/null
+++ b/vendor/sabre/xml/LICENSE
@@ -0,0 +1,27 @@
+Copyright (C) 2009-2015 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 Sabre 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/vendor/sabre/xml/README.md b/vendor/sabre/xml/README.md
new file mode 100644
index 000000000..e6fc4db5f
--- /dev/null
+++ b/vendor/sabre/xml/README.md
@@ -0,0 +1,25 @@
+sabre/xml
+=========
+
+[![Build Status](https://secure.travis-ci.org/fruux/sabre-xml.svg?branch=master)](http://travis-ci.org/fruux/sabre-xml)
+
+The sabre/xml library is a specialized XML reader and writer.
+
+Documentation
+-------------
+
+* [Introduction](http://sabre.io/xml/).
+* [Installation](http://sabre.io/xml/install/).
+* [Reading XML](http://sabre.io/xml/reading/).
+* [Writing XML](http://sabre.io/xml/writing/).
+
+
+Support
+-------
+
+Head over to the [SabreDAV mailing list](http://groups.google.com/group/sabredav-discuss) for any questions.
+
+Made at fruux
+-------------
+
+This library is being developed by [fruux](https://fruux.com/). Drop us a line for commercial services or enterprise support.
diff --git a/vendor/sabre/xml/bin/.empty b/vendor/sabre/xml/bin/.empty
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/vendor/sabre/xml/bin/.empty
diff --git a/vendor/sabre/xml/lib/ContextStackTrait.php b/vendor/sabre/xml/lib/ContextStackTrait.php
new file mode 100644
index 000000000..ee3a3baca
--- /dev/null
+++ b/vendor/sabre/xml/lib/ContextStackTrait.php
@@ -0,0 +1,123 @@
+<?php
+
+namespace Sabre\Xml;
+
+/**
+ * Context Stack
+ *
+ * The Context maintains information about a document during either reading or
+ * writing.
+ *
+ * During this process, it may be neccesary to override this context
+ * information.
+ *
+ * This trait allows easy access to the context, and allows the end-user to
+ * override its settings for document fragments, and easily restore it again
+ * later.
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+trait ContextStackTrait {
+
+ /**
+ * This is the element map. It contains a list of XML elements (in clark
+ * notation) as keys and PHP class names as values.
+ *
+ * The PHP class names must implement Sabre\Xml\Element.
+ *
+ * Values may also be a callable. In that case the function will be called
+ * directly.
+ *
+ * @var array
+ */
+ public $elementMap = [];
+
+ /**
+ * A contextUri pointing to the document being parsed / written.
+ * This uri may be used to resolve relative urls that may appear in the
+ * document.
+ *
+ * The reader and writer don't use this property, but as it's an extremely
+ * common use-case for parsing XML documents, it's added here as a
+ * convenience.
+ *
+ * @var string
+ */
+ public $contextUri;
+
+ /**
+ * This is a list of namespaces that you want to give default prefixes.
+ *
+ * You must make sure you create this entire list before starting to write.
+ * They should be registered on the root element.
+ *
+ * @var array
+ */
+ public $namespaceMap = [];
+
+ /**
+ * This is a list of custom serializers for specific classes.
+ *
+ * The writer may use this if you attempt to serialize an object with a
+ * class that does not implement XmlSerializable.
+ *
+ * Instead it will look at this classmap to see if there is a custom
+ * serializer here. This is useful if you don't want your value objects
+ * to be responsible for serializing themselves.
+ *
+ * The keys in this classmap need to be fully qualified PHP class names,
+ * the values must be callbacks. The callbacks take two arguments. The
+ * writer class, and the value that must be written.
+ *
+ * function (Writer $writer, object $value)
+ *
+ * @var array
+ */
+ public $classMap = [];
+
+ /**
+ * Backups of previous contexts.
+ *
+ * @var array
+ */
+ protected $contextStack = [];
+
+ /**
+ * Create a new "context".
+ *
+ * This allows you to safely modify the elementMap, contextUri or
+ * namespaceMap. After you're done, you can restore the old data again
+ * with popContext.
+ *
+ * @return null
+ */
+ function pushContext() {
+
+ $this->contextStack[] = [
+ $this->elementMap,
+ $this->contextUri,
+ $this->namespaceMap,
+ $this->classMap
+ ];
+
+ }
+
+ /**
+ * Restore the previous "context".
+ *
+ * @return null
+ */
+ function popContext() {
+
+ list(
+ $this->elementMap,
+ $this->contextUri,
+ $this->namespaceMap,
+ $this->classMap
+ ) = array_pop($this->contextStack);
+
+ }
+
+}
diff --git a/vendor/sabre/xml/lib/Deserializer/functions.php b/vendor/sabre/xml/lib/Deserializer/functions.php
new file mode 100644
index 000000000..fe88a6db8
--- /dev/null
+++ b/vendor/sabre/xml/lib/Deserializer/functions.php
@@ -0,0 +1,255 @@
+<?php
+
+namespace Sabre\Xml\Deserializer;
+
+use Sabre\Xml\Reader;
+
+/**
+ * This class provides a number of 'deserializer' helper functions.
+ * These can be used to easily specify custom deserializers for specific
+ * XML elements.
+ *
+ * You can either use these functions from within the $elementMap in the
+ * Service or Reader class, or you can call them from within your own
+ * deserializer functions.
+ */
+
+/*
+ * The 'keyValue' deserializer parses all child elements, and outputs them as
+ * a "key=>value" array.
+ *
+ * For example, keyvalue will parse:
+ *
+ * <?xml version="1.0"?>
+ * <s:root xmlns:s="http://sabredav.org/ns">
+ * <s:elem1>value1</s:elem1>
+ * <s:elem2>value2</s:elem2>
+ * <s:elem3 />
+ * </s:root>
+ *
+ * Into:
+ *
+ * [
+ * "{http://sabredav.org/ns}elem1" => "value1",
+ * "{http://sabredav.org/ns}elem2" => "value2",
+ * "{http://sabredav.org/ns}elem3" => null,
+ * ];
+ *
+ * If you specify the 'namespace' argument, the deserializer will remove
+ * the namespaces of the keys that match that namespace.
+ *
+ * For example, if you call keyValue like this:
+ *
+ * keyValue($reader, 'http://sabredav.org/ns')
+ *
+ * it's output will instead be:
+ *
+ * [
+ * "elem1" => "value1",
+ * "elem2" => "value2",
+ * "elem3" => null,
+ * ];
+ *
+ * Attributes will be removed from the top-level elements. If elements with
+ * the same name appear twice in the list, only the last one will be kept.
+ *
+ *
+ * @param Reader $reader
+ * @param string $namespace
+ * @return array
+ */
+function keyValue(Reader $reader, $namespace = null) {
+
+ // If there's no children, we don't do anything.
+ if ($reader->isEmptyElement) {
+ $reader->next();
+ return [];
+ }
+
+ $values = [];
+
+ $reader->read();
+ do {
+
+ if ($reader->nodeType === Reader::ELEMENT) {
+ if ($namespace !== null && $reader->namespaceURI === $namespace) {
+ $values[$reader->localName] = $reader->parseCurrentElement()['value'];
+ } else {
+ $clark = $reader->getClark();
+ $values[$clark] = $reader->parseCurrentElement()['value'];
+ }
+ } else {
+ $reader->read();
+ }
+ } while ($reader->nodeType !== Reader::END_ELEMENT);
+
+ $reader->read();
+
+ return $values;
+
+}
+
+/**
+ * The 'enum' deserializer parses elements into a simple list
+ * without values or attributes.
+ *
+ * For example, Elements will parse:
+ *
+ * <?xml version="1.0"? >
+ * <s:root xmlns:s="http://sabredav.org/ns">
+ * <s:elem1 />
+ * <s:elem2 />
+ * <s:elem3 />
+ * <s:elem4>content</s:elem4>
+ * <s:elem5 attr="val" />
+ * </s:root>
+ *
+ * Into:
+ *
+ * [
+ * "{http://sabredav.org/ns}elem1",
+ * "{http://sabredav.org/ns}elem2",
+ * "{http://sabredav.org/ns}elem3",
+ * "{http://sabredav.org/ns}elem4",
+ * "{http://sabredav.org/ns}elem5",
+ * ];
+ *
+ * This is useful for 'enum'-like structures.
+ *
+ * If the $namespace argument is specified, it will strip the namespace
+ * for all elements that match that.
+ *
+ * For example,
+ *
+ * enum($reader, 'http://sabredav.org/ns')
+ *
+ * would return:
+ *
+ * [
+ * "elem1",
+ * "elem2",
+ * "elem3",
+ * "elem4",
+ * "elem5",
+ * ];
+ *
+ * @param Reader $reader
+ * @param string $namespace
+ * @return string[]
+ */
+function enum(Reader $reader, $namespace = null) {
+
+ // If there's no children, we don't do anything.
+ if ($reader->isEmptyElement) {
+ $reader->next();
+ return [];
+ }
+ $reader->read();
+ $currentDepth = $reader->depth;
+
+ $values = [];
+ do {
+
+ if ($reader->nodeType !== Reader::ELEMENT) {
+ continue;
+ }
+ if (!is_null($namespace) && $namespace === $reader->namespaceURI) {
+ $values[] = $reader->localName;
+ } else {
+ $values[] = $reader->getClark();
+ }
+
+ } while ($reader->depth >= $currentDepth && $reader->next());
+
+ $reader->next();
+ return $values;
+
+}
+
+/**
+ * The valueObject deserializer turns an xml element into a PHP object of
+ * a specific class.
+ *
+ * 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) {
+
+ $valueObject = new $className();
+ if ($reader->isEmptyElement) {
+ $reader->next();
+ return $valueObject;
+ }
+
+ $defaultProperties = get_class_vars($className);
+
+ $reader->read();
+ do {
+
+ if ($reader->nodeType === Reader::ELEMENT && $reader->namespaceURI == $namespace) {
+
+ if (property_exists($valueObject, $reader->localName)) {
+ if (is_array($defaultProperties[$reader->localName])) {
+ $valueObject->{$reader->localName}[] = $reader->parseCurrentElement()['value'];
+ } else {
+ $valueObject->{$reader->localName} = $reader->parseCurrentElement()['value'];
+ }
+ } else {
+ // Ignore property
+ $reader->next();
+ }
+ } else {
+ $reader->read();
+ }
+ } while ($reader->nodeType !== Reader::END_ELEMENT);
+
+ $reader->read();
+ return $valueObject;
+
+}
+
+/*
+ * This deserializer helps you deserialize xml structures that look like
+ * this:
+ *
+ * <collection>
+ * <item>...</item>
+ * <item>...</item>
+ * <item>...</item>
+ * </collection>
+ *
+ * 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:
+ *
+ * $reader->elementMap['{}collection'] = function($reader) {
+ * return repeatingElements($reader, '{}item');
+ * }
+ *
+ * The repeatingElements deserializer simply returns everything as an array.
+ *
+ * @param Reader $reader
+ * @param string $childElementName Element name in clark-notation
+ * @return array
+ */
+function repeatingElements(Reader $reader, $childElementName) {
+
+ $result = [];
+
+ foreach ($reader->parseGetElements() as $element) {
+
+ if ($element['name'] === $childElementName) {
+ $result[] = $element['value'];
+ }
+
+ }
+
+ return $result;
+
+}
diff --git a/vendor/sabre/xml/lib/Element.php b/vendor/sabre/xml/lib/Element.php
new file mode 100644
index 000000000..dd89c5888
--- /dev/null
+++ b/vendor/sabre/xml/lib/Element.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Sabre\Xml;
+
+/**
+ * This is the XML element interface.
+ *
+ * Elements are responsible for serializing and deserializing part of an XML
+ * document into PHP values.
+ *
+ * It combines XmlSerializable and XmlDeserializable into one logical class
+ * that does both.
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface Element extends XmlSerializable, XmlDeserializable {
+
+}
diff --git a/vendor/sabre/xml/lib/Element/Base.php b/vendor/sabre/xml/lib/Element/Base.php
new file mode 100644
index 000000000..f59ba49a0
--- /dev/null
+++ b/vendor/sabre/xml/lib/Element/Base.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace Sabre\Xml\Element;
+
+use Sabre\Xml;
+
+/**
+ * The Base XML element is the standard parser & generator that's used by the
+ * XML reader and writer.
+ *
+ * It spits out a simple PHP array structure during deserialization, that can
+ * also be directly injected back into Writer::write.
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Base implements Xml\Element {
+
+ /**
+ * PHP value to serialize.
+ *
+ * @var mixed
+ */
+ protected $value;
+
+ /**
+ * Constructor
+ *
+ * @param mixed $value
+ */
+ function __construct($value = null) {
+
+ $this->value = $value;
+
+ }
+
+ /**
+ * The xmlSerialize metod 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 XmlSerializable should only ever write what's considered
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ $writer->write($this->value);
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, 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
+ * free to return other data as well.
+ *
+ * Important note 2: You are responsible for advancing the reader to the
+ * next element. Not doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $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) {
+
+ $subTree = $reader->parseInnerTree();
+ return $subTree;
+
+ }
+
+}
diff --git a/vendor/sabre/xml/lib/Element/Cdata.php b/vendor/sabre/xml/lib/Element/Cdata.php
new file mode 100644
index 000000000..5f42c4c6e
--- /dev/null
+++ b/vendor/sabre/xml/lib/Element/Cdata.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Sabre\Xml\Element;
+
+use Sabre\Xml;
+
+/**
+ * CDATA element.
+ *
+ * This element allows you to easily inject CDATA.
+ *
+ * Note that we strongly recommend avoiding CDATA nodes, unless you definitely
+ * know what you're doing, or you're working with unchangable systems that
+ * require CDATA.
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Cdata implements Xml\XmlSerializable
+{
+ /**
+ * CDATA element value.
+ *
+ * @var string
+ */
+ protected $value;
+
+ /**
+ * Constructor
+ *
+ * @param string $value
+ */
+ function __construct($value)
+ {
+ $this->value = $value;
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ $writer->writeCData($this->value);
+
+ }
+
+}
diff --git a/vendor/sabre/xml/lib/Element/Elements.php b/vendor/sabre/xml/lib/Element/Elements.php
new file mode 100644
index 000000000..9eefd1bf8
--- /dev/null
+++ b/vendor/sabre/xml/lib/Element/Elements.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Sabre\Xml\Element;
+
+use Sabre\Xml;
+use Sabre\Xml\Deserializer;
+use Sabre\Xml\Serializer;
+
+/**
+ * 'Elements' is a simple list of elements, without values or attributes.
+ * For example, Elements will parse:
+ *
+ * <?xml version="1.0"?>
+ * <s:root xmlns:s="http://sabredav.org/ns">
+ * <s:elem1 />
+ * <s:elem2 />
+ * <s:elem3 />
+ * <s:elem4>content</s:elem4>
+ * <s:elem5 attr="val" />
+ * </s:root>
+ *
+ * Into:
+ *
+ * [
+ * "{http://sabredav.org/ns}elem1",
+ * "{http://sabredav.org/ns}elem2",
+ * "{http://sabredav.org/ns}elem3",
+ * "{http://sabredav.org/ns}elem4",
+ * "{http://sabredav.org/ns}elem5",
+ * ];
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Elements implements Xml\Element {
+
+ /**
+ * Value to serialize
+ *
+ * @var array
+ */
+ protected $value;
+
+ /**
+ * Constructor
+ *
+ * @param array $value
+ */
+ function __construct(array $value = []) {
+
+ $this->value = $value;
+
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ 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
+ * 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
+ * free to return other data as well.
+ *
+ * Important note 2: You are responsible for advancing the reader to the
+ * next element. Not doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $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) {
+
+ return Deserializer\enum($reader);
+
+ }
+
+}
diff --git a/vendor/sabre/xml/lib/Element/KeyValue.php b/vendor/sabre/xml/lib/Element/KeyValue.php
new file mode 100644
index 000000000..7ce53bf4c
--- /dev/null
+++ b/vendor/sabre/xml/lib/Element/KeyValue.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Sabre\Xml\Element;
+
+use Sabre\Xml;
+use Sabre\Xml\Deserializer;
+
+/**
+ * 'KeyValue' parses out all child elements from a single node, and outputs a
+ * key=>value struct.
+ *
+ * Attributes will be removed, and duplicate child elements are discarded.
+ * Complex values within the elements will be parsed by the 'standard' parser.
+ *
+ * For example, KeyValue will parse:
+ *
+ * <?xml version="1.0"?>
+ * <s:root xmlns:s="http://sabredav.org/ns">
+ * <s:elem1>value1</s:elem1>
+ * <s:elem2>value2</s:elem2>
+ * <s:elem3 />
+ * </s:root>
+ *
+ * Into:
+ *
+ * [
+ * "{http://sabredav.org/ns}elem1" => "value1",
+ * "{http://sabredav.org/ns}elem2" => "value2",
+ * "{http://sabredav.org/ns}elem3" => null,
+ * ];
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class KeyValue implements Xml\Element {
+
+ /**
+ * Value to serialize
+ *
+ * @var array
+ */
+ protected $value;
+
+ /**
+ * Constructor
+ *
+ * @param array $value
+ */
+ function __construct(array $value = []) {
+
+ $this->value = $value;
+
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ $writer->write($this->value);
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called staticly, 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
+ * free to return other data as well.
+ *
+ * Important note 2: You are responsible for advancing the reader to the
+ * next element. Not doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $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) {
+
+ return Deserializer\keyValue($reader);
+
+ }
+
+}
diff --git a/vendor/sabre/xml/lib/Element/Uri.php b/vendor/sabre/xml/lib/Element/Uri.php
new file mode 100644
index 000000000..8f45c0027
--- /dev/null
+++ b/vendor/sabre/xml/lib/Element/Uri.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace Sabre\Xml\Element;
+
+use Sabre\Xml;
+
+/**
+ * Uri element.
+ *
+ * This represents a single uri. An example of how this may be encoded:
+ *
+ * <link>/foo/bar</link>
+ * <d:href xmlns:d="DAV:">http://example.org/hi</d:href>
+ *
+ * If the uri is relative, it will be automatically expanded to an absolute
+ * url during writing and reading, if the contextUri property is set on the
+ * reader and/or writer.
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Uri implements Xml\Element {
+
+ /**
+ * Uri element value.
+ *
+ * @var string
+ */
+ protected $value;
+
+ /**
+ * Constructor
+ *
+ * @param string $value
+ */
+ function __construct($value)
+ {
+ $this->value = $value;
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ $writer->text(
+ \Sabre\Uri\resolve(
+ $writer->contextUri,
+ $this->value
+ )
+ );
+
+ }
+
+ /**
+ * This method is called during xml parsing.
+ *
+ * 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
+ * free to return other data as well.
+ *
+ * Important note 2: You are responsible for advancing the reader to the
+ * next element. Not doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $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) {
+
+ return new self(
+ \Sabre\Uri\resolve(
+ $reader->contextUri,
+ $reader->readText()
+ )
+ );
+
+ }
+
+}
diff --git a/vendor/sabre/xml/lib/Element/XmlFragment.php b/vendor/sabre/xml/lib/Element/XmlFragment.php
new file mode 100644
index 000000000..0abfac132
--- /dev/null
+++ b/vendor/sabre/xml/lib/Element/XmlFragment.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace Sabre\Xml\Element;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+use Sabre\Xml\Element;
+
+/**
+ * The XmlFragment element allows you to extract a portion of your xml tree,
+ * and get a well-formed xml string.
+ *
+ * This goes a bit beyond `innerXml` and friends, as we'll also match all the
+ * correct namespaces.
+ *
+ * Please note that the XML fragment:
+ *
+ * 1. Will not have an <?xml declaration.
+ * 2. Or a DTD
+ * 3. It will have all the relevant xmlns attributes.
+ * 4. It may not have a root element.
+ */
+class XmlFragment implements Element {
+
+ protected $xml;
+
+ function __construct($xml) {
+
+ $this->xml = $xml;
+
+ }
+
+ function getXml() {
+
+ return $this->xml;
+
+ }
+
+ /**
+ * The xmlSerialize metod 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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) {
+
+ $reader = new Reader();
+
+ // Wrapping the xml in a container, so root-less values can still be
+ // parsed.
+ $xml = <<<XML
+<?xml version="1.0"?>
+<xml-fragment xmlns="http://sabre.io/ns">{$this->getXml()}</xml-fragment>
+XML;
+
+ $reader->xml($xml);
+
+ while ($reader->read()) {
+
+ if ($reader->depth < 1) {
+ // Skipping the root node.
+ continue;
+ }
+
+ switch ($reader->nodeType) {
+
+ case Reader::ELEMENT :
+ $writer->startElement(
+ $reader->getClark()
+ );
+ $empty = $reader->isEmptyElement;
+ while ($reader->moveToNextAttribute()) {
+ switch ($reader->namespaceURI) {
+ case '' :
+ $writer->writeAttribute($reader->localName, $reader->value);
+ break;
+ case 'http://www.w3.org/2000/xmlns/' :
+ // Skip namespace declarations
+ break;
+ default :
+ $writer->writeAttribute($reader->getClark(), $reader->value);
+ break;
+ }
+ }
+ if ($empty) {
+ $writer->endElement();
+ }
+ break;
+ case Reader::CDATA :
+ case Reader::TEXT :
+ $writer->text(
+ $reader->value
+ );
+ break;
+ 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
+ * 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $result = new self($reader->readInnerXml());
+ $reader->next();
+ return $result;
+
+ }
+
+}
diff --git a/vendor/sabre/xml/lib/LibXMLException.php b/vendor/sabre/xml/lib/LibXMLException.php
new file mode 100644
index 000000000..f0190eb51
--- /dev/null
+++ b/vendor/sabre/xml/lib/LibXMLException.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Sabre\Xml;
+
+use
+ LibXMLError;
+
+/**
+ * This exception is thrown when the Readers runs into a parsing error.
+ *
+ * This exception effectively wraps 1 or more LibXMLError objects.
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class LibXMLException extends ParseException {
+
+ /**
+ * The error list.
+ *
+ * @var LibXMLError[]
+ */
+ protected $errors;
+
+ /**
+ * Creates the exception.
+ *
+ * You should pass a list of LibXMLError objects in its constructor.
+ *
+ * @param LibXMLError[] $errors
+ * @param int $code
+ * @param Exception $previousException
+ */
+ function __construct(array $errors, $code = null, Exception $previousException = null) {
+
+ $this->errors = $errors;
+ parent::__construct($errors[0]->message . ' on line ' . $errors[0]->line . ', column ' . $errors[0]->column, $code, $previousException);
+
+ }
+
+ /**
+ * Returns the LibXML errors
+ *
+ * @return void
+ */
+ function getErrors() {
+
+ return $this->errors;
+
+ }
+
+}
diff --git a/vendor/sabre/xml/lib/ParseException.php b/vendor/sabre/xml/lib/ParseException.php
new file mode 100644
index 000000000..3a6883b2f
--- /dev/null
+++ b/vendor/sabre/xml/lib/ParseException.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Sabre\Xml;
+
+use
+ Exception;
+
+/**
+ * This is a base exception for any exception related to parsing xml files.
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ParseException extends Exception {
+
+}
diff --git a/vendor/sabre/xml/lib/Reader.php b/vendor/sabre/xml/lib/Reader.php
new file mode 100644
index 000000000..7cba76c59
--- /dev/null
+++ b/vendor/sabre/xml/lib/Reader.php
@@ -0,0 +1,308 @@
+<?php
+
+namespace Sabre\Xml;
+
+use XMLReader;
+
+/**
+ * The Reader class expands upon PHP's built-in XMLReader.
+ *
+ * The intended usage, is to assign certain XML elements to PHP classes. These
+ * need to be registered using the $elementMap public property.
+ *
+ * After this is done, a single call to parse() will parse the entire document,
+ * and delegate sub-sections of the document to element classes.
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Reader extends XMLReader {
+
+ use ContextStackTrait;
+
+ /**
+ * Returns the current nodename in clark-notation.
+ *
+ * For example: "{http://www.w3.org/2005/Atom}feed".
+ * Or if no namespace is defined: "{}feed".
+ *
+ * This method returns null if we're not currently on an element.
+ *
+ * @return string|null
+ */
+ function getClark() {
+
+ if (! $this->localName) {
+ return null;
+ }
+
+ return '{' . $this->namespaceURI . '}' . $this->localName;
+
+ }
+
+ /**
+ * Reads the entire document.
+ *
+ * This function returns an array with the following three elements:
+ * * name - The root element name.
+ * * value - The value for the root element.
+ * * attributes - An array of attributes.
+ *
+ * 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);
+ $previousSetting = libxml_use_internal_errors(true);
+
+ // 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
+ }
+ $result = $this->parseCurrentElement();
+
+ $errors = libxml_get_errors();
+ libxml_clear_errors();
+ libxml_use_internal_errors($previousSetting);
+ libxml_disable_entity_loader($previousEntityState);
+
+ if ($errors) {
+ throw new LibXMLException($errors);
+ }
+
+ return $result;
+ }
+
+
+
+ /**
+ * parseGetElements parses everything in the current sub-tree,
+ * and returns a 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
+ * 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) {
+
+ $result = $this->parseInnerTree($elementMap);
+ if (!is_array($result)) {
+ return [];
+ }
+ return $result;
+
+ }
+
+ /**
+ * Parses all elements below the current element.
+ *
+ * This method will return a string if this was a text-node, or an array if
+ * there were sub-elements.
+ *
+ * If there's both text and sub-elements, the text 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|string
+ */
+ function parseInnerTree(array $elementMap = null) {
+
+ $text = null;
+ $elements = [];
+
+ if ($this->nodeType === self::ELEMENT && $this->isEmptyElement) {
+ // Easy!
+ $this->next();
+ return null;
+ }
+
+ if (!is_null($elementMap)) {
+ $this->pushContext();
+ $this->elementMap = $elementMap;
+ }
+
+ // Really sorry about the silence operator, seems like I have no
+ // choice. See:
+ //
+ // https://bugs.php.net/bug.php?id=64230
+ if (!@$this->read()) return false;
+
+ while (true) {
+
+ if (!$this->isValid()) {
+
+ $errors = libxml_get_errors();
+
+ if ($errors) {
+ libxml_clear_errors();
+ throw new LibXMLException($errors);
+ }
+ }
+
+ switch ($this->nodeType) {
+ case self::ELEMENT :
+ $elements[] = $this->parseCurrentElement();
+ break;
+ case self::TEXT :
+ case self::CDATA :
+ $text .= $this->value;
+ $this->read();
+ break;
+ case self::END_ELEMENT :
+ // Ensuring we are moving the cursor after the end element.
+ $this->read();
+ break 2;
+ 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 :
+ // Advance to the next element
+ $this->read();
+ break;
+ }
+
+ }
+
+ if (!is_null($elementMap)) {
+ $this->popContext();
+ }
+ return ($elements ? $elements : $text);
+
+ }
+
+ /**
+ * Reads all text below the current element, and returns this as a string.
+ *
+ * @return string
+ */
+ function readText() {
+
+ $result = '';
+ $previousDepth = $this->depth;
+
+ while ($this->read() && $this->depth != $previousDepth) {
+ if (in_array($this->nodeType, [XMLReader::TEXT, XMLReader::CDATA, XMLReader::WHITESPACE])) {
+ $result .= $this->value;
+ }
+ }
+ return $result;
+
+ }
+
+ /**
+ * Parses the current XML element.
+ *
+ * This method returns arn array with 3 properties:
+ * * name - A clark-notation XML element name.
+ * * value - The parsed value.
+ * * attributes - A key-value list of attributes.
+ *
+ * @return array
+ */
+ function parseCurrentElement() {
+
+ $name = $this->getClark();
+
+ $attributes = [];
+
+ if ($this->hasAttributes) {
+ $attributes = $this->parseAttributes();
+ }
+
+ $value = call_user_func(
+ $this->getDeserializerForElementName($name),
+ $this
+ );
+
+ return [
+ '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
+ */
+ function parseAttributes() {
+
+ $attributes = [];
+
+ while ($this->moveToNextAttribute()) {
+ if ($this->namespaceURI) {
+
+ // Ignoring 'xmlns', it doesn't make any sense.
+ if ($this->namespaceURI === 'http://www.w3.org/2000/xmlns/') {
+ continue;
+ }
+
+ $name = $this->getClark();
+ $attributes[$name] = $this->value;
+
+ } else {
+ $attributes[$this->localName] = $this->value;
+ }
+ }
+ $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
+ */
+ function getDeserializerForElementName($name) {
+
+ if (!array_key_exists($name, $this->elementMap)) {
+ return ['Sabre\\Xml\\Element\\Base', 'xmlDeserialize'];
+ }
+
+ $deserializer = $this->elementMap[$name];
+ if (is_subclass_of($deserializer, 'Sabre\\Xml\\XmlDeserializable')) {
+ return [ $deserializer, 'xmlDeserialize' ];
+ }
+
+ if (is_callable($deserializer)) {
+ return $deserializer;
+ }
+
+ $type = gettype($deserializer);
+ if ($type === 'string') {
+ $type .= ' (' . $deserializer . ')';
+ } elseif ($type === 'object') {
+ $type .= ' (' . get_class($deserializer) . ')';
+ }
+ throw new \LogicException('Could not use this type as a deserializer: ' . $type . ' for element: ' . $name);
+
+ }
+
+}
diff --git a/vendor/sabre/xml/lib/Serializer/functions.php b/vendor/sabre/xml/lib/Serializer/functions.php
new file mode 100644
index 000000000..21448017d
--- /dev/null
+++ b/vendor/sabre/xml/lib/Serializer/functions.php
@@ -0,0 +1,249 @@
+<?php
+
+namespace Sabre\Xml\Serializer;
+
+use InvalidArgumentException;
+use Sabre\Xml\Writer;
+use Sabre\Xml\XmlSerializable;
+
+/**
+ * This file provides a number of 'serializer' helper functions.
+ *
+ * These helper functions can be used to easily xml-encode common PHP
+ * data structures, or can be placed in the $classMap.
+ */
+
+/**
+ * The 'enum' serializer writes simple list of elements.
+ *
+ * For example, calling:
+ *
+ * enum($writer, [
+ * "{http://sabredav.org/ns}elem1",
+ * "{http://sabredav.org/ns}elem2",
+ * "{http://sabredav.org/ns}elem3",
+ * "{http://sabredav.org/ns}elem4",
+ * "{http://sabredav.org/ns}elem5",
+ * ]);
+ *
+ * Will generate something like this (if the correct namespace is declared):
+ *
+ * <s:elem1 />
+ * <s:elem2 />
+ * <s:elem3 />
+ * <s:elem4>content</s:elem4>
+ * <s:elem5 attr="val" />
+ *
+ * @param Writer $writer
+ * @param string[] $values
+ * @return void
+ */
+function enum(Writer $writer, array $values) {
+
+ foreach ($values as $value) {
+ $writer->writeElement($value);
+ }
+}
+
+/**
+ * The valueObject serializer turns a simple PHP object into a classname.
+ *
+ * Every public property will be encoded as an xml element with the same
+ * name, in the XML namespace as specified.
+ *
+ * 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) {
+ 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);
+ }
+
+ } elseif ($val !== null) {
+ $writer->writeElement('{' . $namespace . '}' . $key, $val);
+ }
+ }
+}
+
+
+/**
+ * This serializer helps you serialize xml structures that look like
+ * this:
+ *
+ * <collection>
+ * <item>...</item>
+ * <item>...</item>
+ * <item>...</item>
+ * </collection>
+ *
+ * In that previous example, this serializer just serializes the item element,
+ * 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) {
+
+ foreach ($items as $item) {
+ $writer->writeElement($childElementName, $item);
+ }
+
+}
+
+/**
+ * This function is the 'default' serializer that is able to serialize most
+ * things, and delegates to other serializers if needed.
+ *
+ * The standardSerializer supports a wide-array of values.
+ *
+ * $value may be a string or integer, it will just write out the string as text.
+ * $value may be an instance of XmlSerializable or Element, in which case it
+ * calls it's xmlSerialize() method.
+ * $value may be a PHP callback/function/closure, in case we call the callback
+ * and give it the Writer as an argument.
+ * $value may be a an object, and if it's in the classMap we automatically call
+ * the correct serializer for it.
+ * $value may be null, in which case we do nothing.
+ *
+ * If $value is an array, the array must look like this:
+ *
+ * [
+ * [
+ * 'name' => '{namespaceUri}element-name',
+ * 'value' => '...',
+ * 'attributes' => [ 'attName' => 'attValue' ]
+ * ]
+ * [,
+ * 'name' => '{namespaceUri}element-name2',
+ * 'value' => '...',
+ * ]
+ * ]
+ *
+ * This would result in xml like:
+ *
+ * <element-name xmlns="namespaceUri" attName="attValue">
+ * ...
+ * </element-name>
+ * <element-name2>
+ * ...
+ * </element-name2>
+ *
+ * The value property may be any value standardSerializer supports, so you can
+ * nest data-structures this way. Both value and attributes are optional.
+ *
+ * Alternatively, you can also specify the array using this syntax:
+ *
+ * [
+ * [
+ * '{namespaceUri}element-name' => '...',
+ * '{namespaceUri}element-name2' => '...',
+ * ]
+ * ]
+ *
+ * This is excellent for simple key->value structures, and here you can also
+ * specify anything for the value.
+ *
+ * You can even mix the two array syntaxes.
+ *
+ * @param Writer $writer
+ * @param string|int|float|bool|array|object
+ * @return void
+ */
+function standardSerializer(Writer $writer, $value) {
+
+ if (is_scalar($value)) {
+
+ // String, integer, float, boolean
+ $writer->text($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'.
+
+ $name = $value['name'];
+ $attributes = isset($value['attributes']) ? $value['attributes'] : [];
+ $value = isset($value['value']) ? $value['value'] : null;
+
+ $writer->startElement($name);
+ $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);
+ $writer->writeAttributes($item['attributes']);
+ if (isset($item['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));
+
+ }
+ }
+
+ } 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));
+
+ }
+
+}
diff --git a/vendor/sabre/xml/lib/Service.php b/vendor/sabre/xml/lib/Service.php
new file mode 100644
index 000000000..b2603a4c7
--- /dev/null
+++ b/vendor/sabre/xml/lib/Service.php
@@ -0,0 +1,291 @@
+<?php
+
+namespace Sabre\Xml;
+
+/**
+ * XML parsing and writing service.
+ *
+ * You are encouraged to make a instance of this for your application and
+ * potentially extend it, as a central API point for dealing with xml and
+ * configuring the reader and writer.
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Service {
+
+ /**
+ * This is the element map. It contains a list of XML elements (in clark
+ * notation) as keys and PHP class names as values.
+ *
+ * The PHP class names must implement Sabre\Xml\Element.
+ *
+ * Values may also be a callable. In that case the function will be called
+ * directly.
+ *
+ * @var array
+ */
+ public $elementMap = [];
+
+ /**
+ * This is a list of namespaces that you want to give default prefixes.
+ *
+ * You must make sure you create this entire list before starting to write.
+ * They should be registered on the root element.
+ *
+ * @var array
+ */
+ public $namespaceMap = [];
+
+ /**
+ * This is a list of custom serializers for specific classes.
+ *
+ * The writer may use this if you attempt to serialize an object with a
+ * class that does not implement XmlSerializable.
+ *
+ * Instead it will look at this classmap to see if there is a custom
+ * serializer here. This is useful if you don't want your value objects
+ * to be responsible for serializing themselves.
+ *
+ * The keys in this classmap need to be fully qualified PHP class names,
+ * the values must be callbacks. The callbacks take two arguments. The
+ * writer class, and the value that must be written.
+ *
+ * function (Writer $writer, object $value)
+ *
+ * @var array
+ */
+ public $classMap = [];
+
+ /**
+ * Returns a fresh XML Reader
+ *
+ * @return Reader
+ */
+ function getReader() {
+
+ $r = new Reader();
+ $r->elementMap = $this->elementMap;
+ return $r;
+
+ }
+
+ /**
+ * Returns a fresh xml writer
+ *
+ * @return Writer
+ */
+ function getWriter() {
+
+ $w = new Writer();
+ $w->namespaceMap = $this->namespaceMap;
+ $w->classMap = $this->classMap;
+ return $w;
+
+ }
+
+ /**
+ * Parses a document in full.
+ *
+ * Input may be specified as a string or readable stream resource.
+ * The returned value is the value of the root document.
+ *
+ * Specifying the $contextUri allows the parser to figure out what the URI
+ * of the document was. This allows relative URIs within the document to be
+ * expanded easily.
+ *
+ * The $rootElementName is specified by reference and will be populated
+ * 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) {
+
+ if (is_resource($input)) {
+ // Unfortunately the XMLReader doesn't support streams. When it
+ // does, we can optimize this.
+ $input = stream_get_contents($input);
+ }
+ $r = $this->getReader();
+ $r->contextUri = $contextUri;
+ $r->xml($input);
+
+ $result = $r->parse();
+ $rootElementName = $result['name'];
+ return $result['value'];
+
+ }
+
+ /**
+ * Parses a document in full, and specify what the expected root element
+ * name is.
+ *
+ * This function works similar to parse, but the difference is that the
+ * user can specify what the expected name of the root element should be,
+ * in clark notation.
+ *
+ * This is useful in cases where you expected a specific document to be
+ * passed, and reduces the amount of if statements.
+ *
+ * It's also possible to pass an array of expected rootElements if your
+ * code may expect more than one document type.
+ *
+ * @param string|string[] $rootElementName
+ * @param string|resource $input
+ * @param string|null $contextUri
+ * @return void
+ */
+ function expect($rootElementName, $input, $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);
+ }
+ $r = $this->getReader();
+ $r->contextUri = $contextUri;
+ $r->xml($input);
+
+ $result = $r->parse();
+ if (!in_array($result['name'], (array)$rootElementName, true)) {
+ throw new ParseException('Expected ' . implode(' or ', (array)$rootElementName) . ' but received ' . $result['name'] . ' as the root element');
+ }
+ return $result['value'];
+
+ }
+
+ /**
+ * Generates an XML document in one go.
+ *
+ * The $rootElement must be specified in clark notation.
+ * The value must be a string, an array or an object implementing
+ * XmlSerializable. Basically, anything that's supported by the Writer
+ * object.
+ *
+ * $contextUri can be used to specify a sort of 'root' of the PHP application,
+ * in case the xml document is used as a http response.
+ *
+ * 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
+ */
+ function write($rootElementName, $value, $contextUri = null) {
+
+ $w = $this->getWriter();
+ $w->openMemory();
+ $w->contextUri = $contextUri;
+ $w->setIndent(true);
+ $w->startDocument();
+ $w->writeElement($rootElementName, $value);
+ return $w->outputMemory();
+
+ }
+
+ /**
+ * Map an xml element to a PHP class.
+ *
+ * Calling this function will automatically setup the Reader and Writer
+ * classes to turn a specific XML element to a PHP class.
+ *
+ * For example, given a class such as :
+ *
+ * class Author {
+ * public $firstName;
+ * public $lastName;
+ * }
+ *
+ * and an XML element such as:
+ *
+ * <author xmlns="http://example.org/ns">
+ * <firstName>...</firstName>
+ * <lastName>...</lastName>
+ * </author>
+ *
+ * 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) {
+ list($namespace) = self::parseClarkNotation($elementName);
+
+ $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) {
+ return \Sabre\Xml\Serializer\valueObject($writer, $valueObject, $namespace);
+ };
+ $this->valueObjectMap[$className] = $elementName;
+ }
+
+ /**
+ * Writes a value object.
+ *
+ * This function largely behaves similar to write(), except that it's
+ * intended specifically to serialize a Value Object into an XML document.
+ *
+ * The ValueObject must have been previously registered using
+ * mapValueObject().
+ *
+ * @param object $object
+ * @param string $contextUri
+ * @return void
+ */
+ function writeValueObject($object, $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.');
+ }
+ return $this->write(
+ $this->valueObjectMap[get_class($object)],
+ $object,
+ $contextUri
+ );
+
+ }
+
+ /**
+ * Parses a clark-notation string, and returns the namespace and element
+ * name components.
+ *
+ * If the string was invalid, it will throw an InvalidArgumentException.
+ *
+ * @param string $str
+ * @throws InvalidArgumentException
+ * @return array
+ */
+ static function parseClarkNotation($str) {
+ static $cache = [];
+
+ if (!isset($cache[$str])) {
+
+ if (!preg_match('/^{([^}]*)}(.*)$/', $str, $matches)) {
+ throw new \InvalidArgumentException('\'' . $str . '\' is not a valid clark-notation formatted string');
+ }
+
+ $cache[$str] = [
+ $matches[1],
+ $matches[2]
+ ];
+ }
+
+ return $cache[$str];
+ }
+
+ /**
+ * A list of classes and which XML elements they map to.
+ */
+ protected $valueObjectMap = [];
+
+}
diff --git a/vendor/sabre/xml/lib/Version.php b/vendor/sabre/xml/lib/Version.php
new file mode 100644
index 000000000..f199e7158
--- /dev/null
+++ b/vendor/sabre/xml/lib/Version.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Sabre\Xml;
+
+/**
+ * This class contains the version number for this package.
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/
+ */
+class Version {
+
+ /**
+ * Full version number
+ */
+ const VERSION = '1.4.1';
+
+}
diff --git a/vendor/sabre/xml/lib/Writer.php b/vendor/sabre/xml/lib/Writer.php
new file mode 100644
index 000000000..adfbe0cb0
--- /dev/null
+++ b/vendor/sabre/xml/lib/Writer.php
@@ -0,0 +1,266 @@
+<?php
+
+namespace Sabre\Xml;
+
+use XMLWriter;
+
+/**
+ * The XML Writer class.
+ *
+ * This class works exactly as PHP's built-in XMLWriter, with a few additions.
+ *
+ * Namespaces can be registered beforehand, globally. When the first element is
+ * written, namespaces will automatically be declared.
+ *
+ * The writeAttribute, startElement and writeElement can now take a
+ * clark-notation element name (example: {http://www.w3.org/2005/Atom}link).
+ *
+ * If, when writing the namespace is a known one a prefix will automatically be
+ * selected, otherwise a random prefix will be generated.
+ *
+ * Instead of standard string values, the writer can take Element classes (as
+ * defined by this library) to delegate the serialization.
+ *
+ * The write() method can take array structures to quickly write out simple xml
+ * trees.
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Writer extends XMLWriter {
+
+ use ContextStackTrait;
+
+ /**
+ * Any namespace that the writer is asked to write, will be added here.
+ *
+ * Any of these elements will get a new namespace definition *every single
+ * time* they are used, but this array allows the writer to make sure that
+ * the prefixes are consistent anyway.
+ *
+ * @var array
+ */
+ protected $adhocNamespaces = [];
+
+ /**
+ * When the first element is written, this flag is set to true.
+ *
+ * This ensures that the namespaces in the namespaces map are only written
+ * once.
+ *
+ * @var bool
+ */
+ protected $namespacesWritten = false;
+
+ /**
+ * Writes a value to the output stream.
+ *
+ * The following values are supported:
+ * 1. Scalar values will be written as-is, as text.
+ * 2. Null values will be skipped (resulting in a short xml tag).
+ * 3. If a value is an instance of an Element class, writing will be
+ * delegated to the object.
+ * 4. If a value is an array, two formats are supported.
+ *
+ * Array format 1:
+ * [
+ * "{namespace}name1" => "..",
+ * "{namespace}name2" => "..",
+ * ]
+ *
+ * One element will be created for each key in this array. The values of
+ * this array support any format this method supports (this method is
+ * called recursively).
+ *
+ * Array format 2:
+ *
+ * [
+ * [
+ * "name" => "{namespace}name1"
+ * "value" => "..",
+ * "attributes" => [
+ * "attr" => "attribute value",
+ * ]
+ * ],
+ * [
+ * "name" => "{namespace}name1"
+ * "value" => "..",
+ * "attributes" => [
+ * "attr" => "attribute value",
+ * ]
+ * ]
+ * ]
+ *
+ * @param mixed $value
+ * @return void
+ */
+ function write($value) {
+
+ Serializer\standardSerializer($this, $value);
+
+ }
+
+ /**
+ * Opens a new element.
+ *
+ * You can either just use a local elementname, or you can use clark-
+ * notation to start a new element.
+ *
+ * Example:
+ *
+ * $writer->startElement('{http://www.w3.org/2005/Atom}entry');
+ *
+ * Would result in something like:
+ *
+ * <entry xmlns="http://w3.org/2005/Atom">
+ *
+ * @param string $name
+ * @return bool
+ */
+ function startElement($name) {
+
+ 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],
+ $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) {
+ $result = $this->startElement($localName);
+ $this->writeAttribute('xmlns', '');
+ } else {
+ if (!isset($this->adhocNamespaces[$namespace])) {
+ $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->namespacesWritten = true;
+
+ }
+
+ return $result;
+
+ }
+
+ /**
+ * Write a full element tag and it's contents.
+ *
+ * This method automatically closes the element as well.
+ *
+ * The element name may be specified in clark-notation.
+ *
+ * Examples:
+ *
+ * $writer->writeElement('{http://www.w3.org/2005/Atom}author',null);
+ * becomes:
+ * <author xmlns="http://www.w3.org/2005" />
+ *
+ * $writer->writeElement('{http://www.w3.org/2005/Atom}author', [
+ * '{http://www.w3.org/2005/Atom}name' => 'Evert Pot',
+ * ]);
+ * becomes:
+ * <author xmlns="http://www.w3.org/2005" /><name>Evert Pot</name></author>
+ *
+ * @param string $name
+ * @param string $content
+ * @return bool
+ */
+ function writeElement($name, $content = null) {
+
+ $this->startElement($name);
+ if (!is_null($content)) {
+ $this->write($content);
+ }
+ $this->endElement();
+
+ }
+
+ /**
+ * Writes a list of attributes.
+ *
+ * Attributes are specified as a key->value array.
+ *
+ * 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) {
+
+ foreach ($attributes as $name => $value) {
+ $this->writeAttribute($name, $value);
+ }
+
+ }
+
+ /**
+ * Writes a new attribute.
+ *
+ * The name may be specified in clark-notation.
+ *
+ * Returns true when successful.
+ *
+ * @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 {
+ return parent::writeAttribute($name, $value);
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/xml/lib/XmlDeserializable.php b/vendor/sabre/xml/lib/XmlDeserializable.php
new file mode 100644
index 000000000..fa857e82c
--- /dev/null
+++ b/vendor/sabre/xml/lib/XmlDeserializable.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Sabre\Xml;
+
+/**
+ * Implementing the XmlDeserializable interface allows you to use a class as a
+ * deserializer for a specific element.
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface XmlDeserializable {
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * 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
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader);
+
+}
diff --git a/vendor/sabre/xml/lib/XmlSerializable.php b/vendor/sabre/xml/lib/XmlSerializable.php
new file mode 100644
index 000000000..3e2c528b9
--- /dev/null
+++ b/vendor/sabre/xml/lib/XmlSerializable.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Sabre\Xml;
+
+/**
+ * Objects implementing XmlSerializable can control how they are represented in
+ * Xml.
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface XmlSerializable {
+
+ /**
+ * 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
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing 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);
+
+}