From bda23e67da5bc00d6c032ce252e798f63f93039c Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Fri, 12 Sep 2025 20:20:27 -0400 Subject: [PATCH 01/11] Added ktor-version in dedicated package --- build.gradle.kts | 6 + gradle/libs.versions.toml | 7 + .../dav4jvm/ktor/CallbackInterfaces.kt | 48 + .../at/bitfire/dav4jvm/ktor/DavAddressBook.kt | 153 ++ .../at/bitfire/dav4jvm/ktor/DavCalendar.kt | 194 +++ .../at/bitfire/dav4jvm/ktor/DavCollection.kt | 109 ++ .../at/bitfire/dav4jvm/ktor/DavResource.kt | 856 ++++++++++ .../kotlin/at/bitfire/dav4jvm/ktor/Error.kt | 61 + .../at/bitfire/dav4jvm/ktor/HttpUtils.kt | 170 ++ .../at/bitfire/dav4jvm/ktor/PropStat.kt | 58 + .../at/bitfire/dav4jvm/ktor/Property.kt | 75 + .../bitfire/dav4jvm/ktor/PropertyFactory.kt | 35 + .../bitfire/dav4jvm/ktor/PropertyRegistry.kt | 150 ++ .../bitfire/dav4jvm/ktor/QuotedStringUtils.kt | 40 + .../at/bitfire/dav4jvm/ktor/Response.kt | 243 +++ .../at/bitfire/dav4jvm/ktor/UrlUtils.kt | 107 ++ .../at/bitfire/dav4jvm/ktor/XmlReader.kt | 182 +++ .../at/bitfire/dav4jvm/ktor/XmlUtils.kt | 76 + .../ktor/exception/ConflictException.kt | 22 + .../dav4jvm/ktor/exception/DavException.kt | 84 + .../ktor/exception/ForbiddenException.kt | 22 + .../dav4jvm/ktor/exception/GoneException.kt | 22 + .../dav4jvm/ktor/exception/HttpException.kt | 75 + .../ktor/exception/HttpResponseInfo.kt | 146 ++ .../exception/InvalidPropertyException.kt | 18 + .../ktor/exception/NotFoundException.kt | 22 + .../exception/PreconditionFailedException.kt | 22 + .../exception/ServiceUnavailableException.kt | 86 + .../ktor/exception/UnauthorizedException.kt | 22 + .../ktor/property/caldav/CalendarColor.kt | 70 + .../ktor/property/caldav/CalendarData.kt | 42 + .../property/caldav/CalendarDescription.kt | 38 + .../ktor/property/caldav/CalendarHomeSet.kt | 37 + .../property/caldav/CalendarProxyReadFor.kt | 35 + .../property/caldav/CalendarProxyWriteFor.kt | 35 + .../ktor/property/caldav/CalendarTimezone.kt | 37 + .../property/caldav/CalendarTimezoneId.kt | 37 + .../property/caldav/CalendarUserAddressSet.kt | 35 + .../dav4jvm/ktor/property/caldav/GetCTag.kt | 35 + .../ktor/property/caldav/MaxResourceSize.kt | 32 + .../ktor/property/caldav/ScheduleTag.kt | 51 + .../dav4jvm/ktor/property/caldav/Source.kt | 37 + .../caldav/SupportedCalendarComponentSet.kt | 76 + .../property/caldav/SupportedCalendarData.kt | 51 + .../dav4jvm/ktor/property/caldav/namespace.kt | 19 + .../ktor/property/carddav/AddressData.kt | 44 + .../carddav/AddressbookDescription.kt | 37 + .../property/carddav/AddressbookHomeSet.kt | 37 + .../ktor/property/carddav/MaxResourceSize.kt | 38 + .../property/carddav/SupportedAddressData.kt | 54 + .../ktor/property/carddav/namespace.kt | 16 + .../ktor/property/common/HrefListProperty.kt | 48 + .../dav4jvm/ktor/property/push/AuthSecret.kt | 44 + .../ktor/property/push/ContentUpdate.kt | 67 + .../ktor/property/push/PropertyUpdate.kt | 60 + .../dav4jvm/ktor/property/push/PushMessage.kt | 68 + .../ktor/property/push/PushRegister.kt | 77 + .../ktor/property/push/PushResource.kt | 54 + .../ktor/property/push/PushTransport.kt | 18 + .../ktor/property/push/PushTransports.kt | 56 + .../ktor/property/push/Subscription.kt | 52 + .../property/push/SubscriptionPublicKey.kt | 49 + .../ktor/property/push/SupportedTriggers.kt | 64 + .../dav4jvm/ktor/property/push/Topic.kt | 44 + .../dav4jvm/ktor/property/push/Trigger.kt | 59 + .../ktor/property/push/VapidPublicKey.kt | 49 + .../dav4jvm/ktor/property/push/WebPush.kt | 53 + .../ktor/property/push/WebPushSubscription.kt | 62 + .../dav4jvm/ktor/property/push/namespace.kt | 19 + .../dav4jvm/ktor/property/webdav/AddMember.kt | 41 + .../ktor/property/webdav/CreationDate.kt | 38 + .../property/webdav/CurrentUserPrincipal.kt | 48 + .../webdav/CurrentUserPrivilegeSet.kt | 98 ++ .../dav4jvm/ktor/property/webdav/Depth.kt | 51 + .../ktor/property/webdav/DisplayName.kt | 40 + .../ktor/property/webdav/GetContentLength.kt | 38 + .../ktor/property/webdav/GetContentType.kt | 48 + .../dav4jvm/ktor/property/webdav/GetETag.kt | 93 ++ .../ktor/property/webdav/GetLastModified.kt | 44 + .../ktor/property/webdav/GroupMembership.kt | 37 + .../dav4jvm/ktor/property/webdav/Owner.kt | 40 + .../property/webdav/QuotaAvailableBytes.kt | 38 + .../ktor/property/webdav/QuotaUsedBytes.kt | 38 + .../ktor/property/webdav/ResourceType.kt | 75 + .../property/webdav/SupportedReportSet.kt | 60 + .../dav4jvm/ktor/property/webdav/SyncLevel.kt | 46 + .../dav4jvm/ktor/property/webdav/SyncToken.kt | 40 + .../dav4jvm/ktor/property/webdav/namespace.kt | 16 + .../bitfire/dav4jvm/ktor/DavCalendarTest.kt | 68 + .../bitfire/dav4jvm/ktor/DavCollectionTest.kt | 291 ++++ .../bitfire/dav4jvm/ktor/DavResourceTest.kt | 1393 +++++++++++++++++ .../at/bitfire/dav4jvm/ktor/ErrorTest.kt | 24 + .../at/bitfire/dav4jvm/ktor/HttpUtilsTest.kt | 324 ++++ .../at/bitfire/dav4jvm/ktor/PropertyTest.kt | 69 + .../dav4jvm/ktor/QuotedStringUtilsTest.kt | 39 + .../at/bitfire/dav4jvm/ktor/UrlUtilsTest.kt | 67 + .../at/bitfire/dav4jvm/ktor/XmlReaderTest.kt | 164 ++ .../at/bitfire/dav4jvm/ktor/XmlUtilsTest.kt | 29 + .../ktor/exception/DavExceptionTest.kt | 111 ++ .../ktor/exception/HttpExceptionTest.kt | 145 ++ .../ktor/exception/HttpResponseInfoTest.kt | 216 +++ .../ServiceUnavailableExceptionTest.kt | 99 ++ .../ktor/property/CalendarDescriptionTest.kt | 26 + .../dav4jvm/ktor/property/GetETagTest.kt | 61 + .../dav4jvm/ktor/property/OwnerTest.kt | 64 + .../dav4jvm/ktor/property/PropertyTest.kt | 30 + .../dav4jvm/ktor/property/push/WebPushTest.kt | 101 ++ 107 files changed, 9197 insertions(+) create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/CallbackInterfaces.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/DavAddressBook.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCalendar.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCollection.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/DavResource.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/Error.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/HttpUtils.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/PropStat.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/Property.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/PropertyFactory.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/PropertyRegistry.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/QuotedStringUtils.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/Response.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/UrlUtils.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/XmlReader.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/XmlUtils.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ConflictException.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/DavException.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ForbiddenException.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/GoneException.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpException.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfo.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/InvalidPropertyException.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/NotFoundException.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/PreconditionFailedException.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableException.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/UnauthorizedException.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarColor.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarData.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarDescription.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarHomeSet.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarProxyReadFor.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarProxyWriteFor.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarTimezone.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarTimezoneId.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarUserAddressSet.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/GetCTag.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/MaxResourceSize.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/ScheduleTag.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/Source.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/SupportedCalendarComponentSet.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/SupportedCalendarData.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/namespace.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressData.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressbookDescription.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressbookHomeSet.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/MaxResourceSize.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/SupportedAddressData.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/namespace.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/common/HrefListProperty.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/AuthSecret.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/ContentUpdate.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PropertyUpdate.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushMessage.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushRegister.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushResource.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushTransport.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushTransports.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Subscription.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/SubscriptionPublicKey.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/SupportedTriggers.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Topic.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Trigger.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/VapidPublicKey.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPush.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPushSubscription.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/namespace.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/AddMember.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CreationDate.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CurrentUserPrincipal.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CurrentUserPrivilegeSet.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/Depth.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/DisplayName.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetContentLength.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetContentType.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetETag.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetLastModified.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GroupMembership.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/Owner.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/QuotaAvailableBytes.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/QuotaUsedBytes.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/ResourceType.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SupportedReportSet.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SyncLevel.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SyncToken.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/namespace.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/DavCalendarTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/DavCollectionTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/DavResourceTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/ErrorTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/HttpUtilsTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/PropertyTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/QuotedStringUtilsTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/UrlUtilsTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/XmlReaderTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/XmlUtilsTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/DavExceptionTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpExceptionTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfoTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableExceptionTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/property/CalendarDescriptionTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/property/GetETagTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/property/OwnerTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/property/PropertyTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPushTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 11a43421..5da048ae 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -58,6 +58,12 @@ dependencies { api(libs.spotbugs.annotations) api(libs.xpp3) + implementation(libs.ktor.client.core) + implementation(libs.ktor.client.logging) + implementation(libs.ktor.server.http.redirect) + implementation(libs.slf4j) + testImplementation(libs.junit4) + testImplementation(libs.ktor.client.mock) testImplementation(libs.okhttp.mockwebserver) } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 765db255..246ba45b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,11 +5,18 @@ kotlin = "2.2.10" okhttpVersion = "5.1.0" spotbugs = "4.9.4" xpp3Version = "1.1.6" +ktor = "3.2.1" +slf4j = "1.7.36" [libraries] junit4 = { module = "junit:junit", version.ref = "junit4" } +ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } +ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } +ktor-server-http-redirect = { module = "io.ktor:ktor-server-http-redirect", version.ref = "ktor" } +ktor-client-mock = { module = "io.ktor:ktor-client-mock", version.ref = "ktor" } okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttpVersion" } okhttp-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver3", version.ref = "okhttpVersion" } +slf4j = { module = "org.slf4j:slf4j-android", version.ref = "slf4j" } spotbugs-annotations = { module = "com.github.spotbugs:spotbugs-annotations", version.ref = "spotbugs" } xpp3 = { module = "org.ogce:xpp3", version.ref = "xpp3Version" } diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/CallbackInterfaces.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/CallbackInterfaces.kt new file mode 100644 index 00000000..0fa61734 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/CallbackInterfaces.kt @@ -0,0 +1,48 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import io.ktor.client.statement.HttpResponse + +/** + * Callback for the OPTIONS request. + */ +fun interface CapabilitiesCallback { + fun onCapabilities(davCapabilities: Set, response: HttpResponse) +} + +/** + * Callback for 207 Multi-Status responses. + */ +fun interface MultiResponseCallback { + /** + * Called for every `` element in the `` body. For instance, + * in response to a `PROPFIND` request, this callback will be called once for every found + * member resource. + * + * Known collections have [response] `href` with trailing slash, see [Response.parse] for details. + * + * @param response the parsed response (including URL) + * @param relation relation of the response to the called resource + */ + fun onResponse(response: Response, relation: Response.HrefRelation) +} + +/** + * Callback for HTTP responses. + */ +fun interface ResponseCallback { + /** + * Called for a HTTP response. Typically this is only called for successful/redirect + * responses because HTTP errors throw an exception before this callback is called. + */ + fun onResponse(response: HttpResponse) +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavAddressBook.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavAddressBook.kt new file mode 100644 index 00000000..875ce564 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavAddressBook.kt @@ -0,0 +1,153 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.XmlUtils.insertTag +import at.bitfire.dav4jvm.ktor.property.carddav.AddressData +import at.bitfire.dav4jvm.ktor.property.carddav.NS_CARDDAV +import at.bitfire.dav4jvm.ktor.property.webdav.GetContentType +import at.bitfire.dav4jvm.ktor.property.webdav.GetETag +import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV +import io.ktor.client.HttpClient +import io.ktor.client.request.prepareRequest +import io.ktor.client.request.setBody +import io.ktor.client.request.url +import io.ktor.http.ContentType +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpMethod +import io.ktor.http.Url +import io.ktor.util.logging.Logger +import org.slf4j.LoggerFactory +import java.io.StringWriter + +@Suppress("unused") +class DavAddressBook @JvmOverloads constructor( + httpClient: HttpClient, + location: Url, + logger: Logger = LoggerFactory.getLogger(DavAddressBook::javaClass.name) +): DavCollection(httpClient, location, logger) { + + companion object { + val MIME_JCARD = ContentType.Companion.parse("application/vcard+json") + val MIME_VCARD3_UTF8 = ContentType.Companion.parse("text/vcard;charset=utf-8") + val MIME_VCARD4 = ContentType.Companion.parse("text/vcard;version=4.0") + + val ADDRESSBOOK_QUERY = Property.Name(NS_CARDDAV, "addressbook-query") + val ADDRESSBOOK_MULTIGET = Property.Name(NS_CARDDAV, "addressbook-multiget") + val FILTER = Property.Name(NS_CARDDAV, "filter") + } + + /** + * Sends an addressbook-query REPORT request to the resource. + * + * @param callback called for every WebDAV response XML element in the result + * + * @return list of properties which have been received in the Multi-Status response, but + * are not part of response XML elements + * + * @throws java.io.IOException on I/O error + * @throws at.bitfire.dav4jvm.ktor.exception.HttpException on HTTP error + * @throws at.bitfire.dav4jvm.ktor.exception.DavException on WebDAV error + */ + suspend fun addressbookQuery(callback: MultiResponseCallback): List { + /* + + */ + val serializer = XmlUtils.newSerializer() + val writer = StringWriter() + serializer.setOutput(writer) + serializer.startDocument("UTF-8", null) + serializer.setPrefix("", NS_WEBDAV) + serializer.setPrefix("CARD", NS_CARDDAV) + serializer.insertTag(ADDRESSBOOK_QUERY) { + insertTag(PROP) { + insertTag(GetETag.Companion.NAME) + } + insertTag(FILTER) + } + serializer.endDocument() + + followRedirects { + httpClient.prepareRequest { + url(location) + method = HttpMethod.Companion.parse("REPORT") + headers.append(HttpHeaders.ContentType, MIME_XML.toString()) + setBody(writer.toString()) + headers.append(HttpHeaders.Depth, "1") + }.execute() + }.let { response -> + return processMultiStatus(response, callback) + } + } + + /** + * Sends an addressbook-multiget REPORT request to the resource. + * + * @param urls list of vCard URLs to be requested + * @param contentType MIME type of requested format; may be "text/vcard" for vCard or + * "application/vcard+json" for jCard. *null*: don't request specific representation type + * @param version vCard version subtype of the requested format. Should only be specified together with a [contentType] of "text/vcard". + * Currently only useful value: "4.0" for vCard 4. *null*: don't request specific version + * @param callback called for every WebDAV response XML element in the result + * + * @return list of properties which have been received in the Multi-Status response, but + * are not part of response XML elements + * + * @throws java.io.IOException on I/O error + * @throws at.bitfire.dav4jvm.ktor.exception.HttpException on HTTP error + * @throws at.bitfire.dav4jvm.ktor.exception.DavException on WebDAV error + */ + suspend fun multiget(urls: List, contentType: String? = null, version: String? = null, callback: MultiResponseCallback): List { + /* + */ + val serializer = XmlUtils.newSerializer() + val writer = StringWriter() + serializer.setOutput(writer) + serializer.startDocument("UTF-8", null) + serializer.setPrefix("", NS_WEBDAV) + serializer.setPrefix("CARD", NS_CARDDAV) + serializer.insertTag(ADDRESSBOOK_MULTIGET) { + insertTag(PROP) { + insertTag(GetContentType.Companion.NAME) + insertTag(GetETag.Companion.NAME) + insertTag(AddressData.Companion.NAME) { + if (contentType != null) + attribute(null, AddressData.Companion.CONTENT_TYPE, contentType) + if (version != null) + attribute(null, AddressData.Companion.VERSION, version) + } + } + for (url in urls) + insertTag(HREF) { + text(url.encodedPath) + } + } + serializer.endDocument() + + followRedirects { + httpClient.prepareRequest { + url(location) + method = HttpMethod.Companion.parse("REPORT") + setBody(writer.toString()) + headers.append(HttpHeaders.ContentType, MIME_XML.toString()) + headers.append(HttpHeaders.Depth, "0") + }.execute() + }.let { response -> + return processMultiStatus(response, callback) + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCalendar.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCalendar.kt new file mode 100644 index 00000000..aea672b5 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCalendar.kt @@ -0,0 +1,194 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.XmlUtils.insertTag +import at.bitfire.dav4jvm.ktor.property.caldav.CalendarData +import at.bitfire.dav4jvm.ktor.property.caldav.NS_CALDAV +import at.bitfire.dav4jvm.ktor.property.caldav.ScheduleTag +import at.bitfire.dav4jvm.ktor.property.webdav.GetContentType +import at.bitfire.dav4jvm.ktor.property.webdav.GetETag +import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV +import io.ktor.client.HttpClient +import io.ktor.client.request.prepareRequest +import io.ktor.client.request.setBody +import io.ktor.client.request.url +import io.ktor.http.ContentType +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpMethod +import io.ktor.http.Url +import io.ktor.util.logging.Logger +import org.slf4j.LoggerFactory +import java.io.StringWriter +import java.time.Instant +import java.time.ZoneOffset +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.util.Locale + +@Suppress("unused") +class DavCalendar @JvmOverloads constructor( + httpClient: HttpClient, + location: Url, + logger: Logger = LoggerFactory.getLogger(DavCalendar::javaClass.name) +): DavCollection(httpClient, location, logger) { + + companion object { + val MIME_ICALENDAR = ContentType.Companion.parse("text/calendar") + val MIME_ICALENDAR_UTF8 = ContentType.Companion.parse("text/calendar;charset=utf-8") + + val CALENDAR_QUERY = Property.Name(NS_CALDAV, "calendar-query") + val CALENDAR_MULTIGET = Property.Name(NS_CALDAV, "calendar-multiget") + + val FILTER = Property.Name(NS_CALDAV, "filter") + val COMP_FILTER = Property.Name(NS_CALDAV, "comp-filter") + const val COMP_FILTER_NAME = "name" + val TIME_RANGE = Property.Name(NS_CALDAV, "time-range") + const val TIME_RANGE_START = "start" + const val TIME_RANGE_END = "end" + + private val timeFormatUTC = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmssVV", Locale.US) + } + + + /** + * Sends a calendar-query REPORT to the resource. + * + * @param component requested component name (like VEVENT or VTODO) + * @param start time-range filter: start date (optional) + * @param end time-range filter: end date (optional) + * @param callback called for every WebDAV response XML element in the result + * + * @return list of properties which have been received in the Multi-Status response, but + * are not part of response XML elements + * + * @throws java.io.IOException on I/O error + * @throws at.bitfire.dav4jvm.ktor.exception.HttpException on HTTP error + * @throws at.bitfire.dav4jvm.ktor.exception.DavException on WebDAV error + */ + suspend fun calendarQuery(component: String, start: Instant?, end: Instant?, callback: MultiResponseCallback): List { + /* + + + + name value: a calendar object or calendar component + type (e.g., VEVENT) + + */ + val serializer = XmlUtils.newSerializer() + val writer = StringWriter() + serializer.setOutput(writer) + serializer.startDocument("UTF-8", null) + serializer.setPrefix("", NS_WEBDAV) + serializer.setPrefix("CAL", NS_CALDAV) + serializer.insertTag(CALENDAR_QUERY) { + insertTag(PROP) { + insertTag(GetETag.Companion.NAME) + } + insertTag(FILTER) { + insertTag(COMP_FILTER) { + attribute(null, COMP_FILTER_NAME, "VCALENDAR") + insertTag(COMP_FILTER) { + attribute(null, COMP_FILTER_NAME, component) + if (start != null || end != null) { + insertTag(TIME_RANGE) { + if (start != null) + attribute(null, TIME_RANGE_START, timeFormatUTC.format( + ZonedDateTime.ofInstant(start, ZoneOffset.UTC) + )) + if (end != null) + attribute(null, TIME_RANGE_END, timeFormatUTC.format( + ZonedDateTime.ofInstant(end, ZoneOffset.UTC) + )) + } + } + } + } + } + } + serializer.endDocument() + + followRedirects { + httpClient.prepareRequest { + url(location) + method = HttpMethod.Companion.parse("REPORT") + setBody(writer.toString()) + headers.append(HttpHeaders.ContentType, MIME_XML.toString()) + headers.append(HttpHeaders.Depth, "1") + }.execute() + }.let { response -> + return processMultiStatus(response, callback) + } + } + + /** + * Sends a calendar-multiget REPORT to the resource. Received responses are sent + * to the callback, whether they are successful (2xx) or not. + * + * @param urls list of iCalendar URLs to be requested + * @param contentType MIME type of requested format; may be "text/calendar" for iCalendar or + * "application/calendar+json" for jCard. *null*: don't request specific representation type + * @param version Version subtype of the requested format, like "2.0" for iCalendar 2. *null*: don't request specific version + * @param callback called for every WebDAV response XML element in the result + * + * @return list of properties which have been received in the Multi-Status response, but + * are not part of response XML elements + * + * @throws java.io.IOException on I/O error + * @throws at.bitfire.dav4jvm.ktor.exception.HttpException on HTTP error + * @throws at.bitfire.dav4jvm.ktor.exception.DavException on WebDAV error + */ + suspend fun multiget(urls: List, contentType: String? = null, version: String? = null, callback: MultiResponseCallback): List { + /* + */ + val serializer = XmlUtils.newSerializer() + val writer = StringWriter() + serializer.setOutput(writer) + serializer.startDocument("UTF-8", null) + serializer.setPrefix("", NS_WEBDAV) + serializer.setPrefix("CAL", NS_CALDAV) + serializer.insertTag(CALENDAR_MULTIGET) { + insertTag(PROP) { + insertTag(GetContentType.Companion.NAME) // to determine the character set + insertTag(GetETag.Companion.NAME) + insertTag(ScheduleTag.Companion.NAME) + insertTag(CalendarData.Companion.NAME) { + if (contentType != null) + attribute(null, CalendarData.Companion.CONTENT_TYPE, contentType) + if (version != null) + attribute(null, CalendarData.Companion.VERSION, version) + } + } + for (url in urls) + insertTag(HREF) { + serializer.text(url.encodedPath) + } + } + serializer.endDocument() + + followRedirects { + httpClient.prepareRequest { + url(location) + method = HttpMethod.Companion.parse("REPORT") + setBody(writer.toString()) + headers.append(HttpHeaders.ContentType, MIME_XML.toString()) + }.execute() + }.let { response -> + return processMultiStatus(response, callback) + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCollection.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCollection.kt new file mode 100644 index 00000000..905e811c --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCollection.kt @@ -0,0 +1,109 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.XmlUtils.insertTag +import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.ktor.property.webdav.SyncToken +import io.ktor.client.HttpClient +import io.ktor.client.request.header +import io.ktor.client.request.prepareRequest +import io.ktor.client.request.setBody +import io.ktor.client.request.url +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpMethod +import io.ktor.http.Url +import io.ktor.util.logging.Logger +import org.slf4j.LoggerFactory +import java.io.StringWriter + +/** + * Represents a WebDAV collection. + */ +open class DavCollection @JvmOverloads constructor( + httpClient: HttpClient, + location: Url, + logger: Logger = LoggerFactory.getLogger(DavCollection::class.java.name) +): DavResource(httpClient, location, logger) { + + companion object { + val SYNC_COLLECTION = Property.Name(NS_WEBDAV, "sync-collection") + val SYNC_LEVEL = Property.Name(NS_WEBDAV, "sync-level") + val LIMIT = Property.Name(NS_WEBDAV, "limit") + val NRESULTS = Property.Name(NS_WEBDAV, "nresults") + } + + /** + * Sends a REPORT sync-collection request. + * + * @param syncToken sync-token to be sent with the request + * @param infiniteDepth sync-level to be sent with the request: false = "1", true = "infinite" + * @param limit maximum number of results (may cause truncation) + * @param properties WebDAV properties to be requested + * @param callback called for every WebDAV response XML element in the result + * + * @return list of properties which have been received in the Multi-Status response, but + * are not part of response XML elements (like `sync-token` which is returned as [at.bitfire.dav4jvm.ktor.property.webdav.SyncToken]) + * + * @throws java.io.IOException on I/O error + * @throws at.bitfire.dav4jvm.ktor.exception.HttpException on HTTP error + * @throws at.bitfire.dav4jvm.ktor.exception.DavException on WebDAV error + */ + suspend fun reportChanges(syncToken: String?, infiniteDepth: Boolean, limit: Int?, vararg properties: Property.Name, callback: MultiResponseCallback): List { + /* + + + + + + + + + */ + val serializer = XmlUtils.newSerializer() + val writer = StringWriter() + serializer.setOutput(writer) + serializer.startDocument("UTF-8", null) + serializer.setPrefix("", NS_WEBDAV) + serializer.insertTag(SYNC_COLLECTION) { + insertTag(SyncToken.Companion.NAME) { + if (syncToken != null) + text(syncToken) + } + insertTag(SYNC_LEVEL) { + text(if (infiniteDepth) "infinite" else "1") + } + if (limit != null) + insertTag(LIMIT) { + insertTag(NRESULTS) { + text(limit.toString()) + } + } + insertTag(PROP) { + for (prop in properties) + insertTag(prop) + } + } + serializer.endDocument() + + followRedirects { + httpClient.prepareRequest { + url(location) + method = HttpMethod.Companion.parse("REPORT") + setBody(writer.toString()) + header(HttpHeaders.ContentType, MIME_XML) + header(HttpHeaders.Depth, "0") + }.execute() + }.let { response -> + return processMultiStatus(response, callback) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavResource.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavResource.kt new file mode 100644 index 00000000..98e532fa --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavResource.kt @@ -0,0 +1,856 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.Response.Companion.MULTISTATUS +import at.bitfire.dav4jvm.ktor.Response.Companion.RESPONSE +import at.bitfire.dav4jvm.ktor.XmlUtils.insertTag +import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.ktor.exception.ConflictException +import at.bitfire.dav4jvm.ktor.exception.DavException +import at.bitfire.dav4jvm.ktor.exception.ForbiddenException +import at.bitfire.dav4jvm.ktor.exception.GoneException +import at.bitfire.dav4jvm.ktor.exception.HttpException +import at.bitfire.dav4jvm.ktor.exception.NotFoundException +import at.bitfire.dav4jvm.ktor.exception.PreconditionFailedException +import at.bitfire.dav4jvm.ktor.exception.ServiceUnavailableException +import at.bitfire.dav4jvm.ktor.exception.UnauthorizedException +import at.bitfire.dav4jvm.ktor.property.caldav.NS_CALDAV +import at.bitfire.dav4jvm.ktor.property.carddav.NS_CARDDAV +import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.ktor.property.webdav.SyncToken +import io.ktor.client.HttpClient +import io.ktor.client.request.header +import io.ktor.client.request.prepareRequest +import io.ktor.client.request.setBody +import io.ktor.client.request.url +import io.ktor.client.statement.HttpResponse +import io.ktor.client.statement.bodyAsBytes +import io.ktor.client.statement.bodyAsChannel +import io.ktor.http.ContentType +import io.ktor.http.Headers +import io.ktor.http.HeadersBuilder +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpMethod +import io.ktor.http.HttpStatusCode +import io.ktor.http.URLBuilder +import io.ktor.http.Url +import io.ktor.http.append +import io.ktor.http.contentType +import io.ktor.http.isSecure +import io.ktor.http.isSuccess +import io.ktor.http.takeFrom +import io.ktor.http.withCharset +import io.ktor.util.appendAll +import io.ktor.util.logging.Logger +import io.ktor.utils.io.core.readFully +import io.ktor.utils.io.readBuffer +import org.slf4j.LoggerFactory +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.EOFException +import java.io.IOException +import java.io.Reader +import java.io.StringWriter +import kotlin.collections.iterator +import kotlin.collections.plusAssign + + +/** + * Represents a WebDAV resource at the given location and allows WebDAV + * requests to be performed on this resource. + * + * Requests are executed synchronously (blocking). If no error occurs, the given + * callback will be called. Otherwise, an exception is thrown. *These callbacks + * don't need to close the response.* + * + * To cancel a request, interrupt the thread. This will cause the requests to + * throw `InterruptedException` or `InterruptedIOException`. + * + * ATTENTION: dav4jvm handles redirects itself. Make sure followRedirects is set to FALSE for the httpClient. + * + * @param httpClient [HttpClient] to access this object (must not follow redirects) + * @param location location of the WebDAV resource + * @param logger will be used for logging + */ +open class DavResource @JvmOverloads constructor( + val httpClient: HttpClient, + location: Url, + val logger: Logger = LoggerFactory.getLogger(DavResource::class.java.name) +) { + + companion object { + const val MAX_REDIRECTS = 5 + + val MIME_XML = ContentType.Application.Xml.withCharset(Charsets.UTF_8) + + val PROPFIND = Property.Name(NS_WEBDAV, "propfind") + val PROPERTYUPDATE = Property.Name(NS_WEBDAV, "propertyupdate") + val SET = Property.Name(NS_WEBDAV, "set") + val REMOVE = Property.Name(NS_WEBDAV, "remove") + val PROP = Property.Name(NS_WEBDAV, "prop") + val HREF = Property.Name(NS_WEBDAV, "href") + + val XML_SIGNATURE = ", + removeProperties: List + ): String { + // build XML request body + val serializer = XmlUtils.newSerializer() + val writer = StringWriter() + serializer.setOutput(writer) + serializer.setPrefix("d", NS_WEBDAV) + serializer.startDocument("UTF-8", null) + serializer.insertTag(PROPERTYUPDATE) { + // DAV:set + if (setProperties.isNotEmpty()) { + serializer.insertTag(SET) { + for (prop in setProperties) { + serializer.insertTag(PROP) { + serializer.insertTag(prop.key) { + text(prop.value) + } + } + } + } + } + + // DAV:remove + if (removeProperties.isNotEmpty()) { + serializer.insertTag(REMOVE) { + for (prop in removeProperties) { + insertTag(PROP) { + insertTag(prop) + } + } + } + } + } + + serializer.endDocument() + return writer.toString() + } + + } + + /** + * URL of this resource (changes when being redirected by server) + */ + var location: Url + private set // allow internal modification only (for redirects) + + init { + this.location = location + } + + override fun toString() = location.toString() + + + /** + * Gets the file name of this resource. See [HttpUtils.fileName] for details. + */ + fun fileName() = HttpUtils.fileName(location) + + + /** + * Sends an OPTIONS request to this resource without HTTP compression (because some servers have + * broken compression for OPTIONS). Follows up to [MAX_REDIRECTS] redirects when set. + * + * @param followRedirects whether redirects should be followed (default: *false*) + * @param callback called with server response unless an exception is thrown + * + * @throws IOException on I/O error + * @throws HttpException on HTTP error + * @throws DavException on HTTPS -> HTTP redirect + */ + @Throws(IOException::class, HttpException::class) + suspend fun options(followRedirects: Boolean = false, callback: CapabilitiesCallback) { + val request = httpClient.prepareRequest { + url(location) + method = HttpMethod.Options + headers.append(HttpHeaders.ContentLength, "0") + headers.append(HttpHeaders.AcceptEncoding, "identity") + } + val response = if (followRedirects) + followRedirects { request.execute() } + else + request.execute() + + checkStatus(response) + callback.onCapabilities( + HttpUtils.listHeader(response, "DAV").map { it.trim() }.toSet(), + response + ) + + } + + /** + * Sends a MOVE request to this resource. Follows up to [MAX_REDIRECTS] redirects. + * Updates [location] on success. + * + * @param destination where the resource shall be moved to + * @param overwrite whether resources are overwritten when they already exist in destination + * + * @throws IOException on I/O error + * @throws HttpException on HTTP error + * @throws DavException on WebDAV error or HTTPS -> HTTP redirect + */ + @Throws(IOException::class, HttpException::class, DavException::class) + suspend fun move(destination: Url, overwrite: Boolean, callback: ResponseCallback) { + + followRedirects { + httpClient.prepareRequest { + url(location) + method = HttpMethod.parse("MOVE") + headers.append(HttpHeaders.ContentLength, "0") + headers.append(HttpHeaders.Destination, destination.toString()) + if (!overwrite) // RFC 4918 9.9.3 and 10.6, default value: T + headers.append(HttpHeaders.Overwrite, "F") + }.execute() + }.let { response -> + checkStatus(response) + if (response.status == HttpStatusCode.MultiStatus) + /* Multiple resources were to be affected by the MOVE, but errors on some + of them prevented the operation from taking place. + [_] (RFC 4918 9.9.4. Status Codes for MOVE Method) */ + throw HttpException(response) + + // update location + val nPath = response.headers[HttpHeaders.Location] ?: destination.toString() + location = URLBuilder(location).takeFrom(nPath).build() + + callback.onResponse(response) + } + } + + /** + * Sends a COPY request for this resource. Follows up to [MAX_REDIRECTS] redirects. + * + * @param destination where the resource shall be copied to + * @param overwrite whether resources are overwritten when they already exist in destination + * + * @throws IOException on I/O error + * @throws HttpException on HTTP error + * @throws DavException on WebDAV error or HTTPS -> HTTP redirect + */ + @Throws(IOException::class, HttpException::class, DavException::class) + suspend fun copy(destination: Url, overwrite: Boolean, callback: ResponseCallback) { + + followRedirects { + httpClient.prepareRequest { + url(location) + method = HttpMethod.parse("COPY") + headers.append(HttpHeaders.ContentLength, "0") + headers.append(HttpHeaders.Destination, destination.toString()) + if (!overwrite) // RFC 4918 9.9.3 and 10.6, default value: T + headers.append("Overwrite", "F") + }.execute() + }.let { response -> + checkStatus(response) + if(response.status == HttpStatusCode.MultiStatus) + /* Multiple resources were to be affected by the COPY, but errors on some + of them prevented the operation from taking place. + [_] (RFC 4918 9.8.5. Status Codes for COPY Method) */ + throw HttpException(response) + + callback.onResponse(response) + } + } + + /** + * Sends a MKCOL request to this resource. Follows up to [MAX_REDIRECTS] redirects. + * Because the target [location] is by definition a collection, a trailing slash + * is appended (unless [location] already has a trailing slash). + * + * @param xmlBody optional request body (used for MKCALENDAR or Extended MKCOL) + * @param method HTTP MKCOL method (`MKCOL` by default, may for instance be `MKCALENDAR`) + * @param headers additional headers to send with the request + * @param callback called for the response + * + * @throws IOException on I/O error + * @throws HttpException on HTTP error + * @throws DavException on HTTPS -> HTTP redirect + */ + @Throws(IOException::class, HttpException::class) + suspend fun mkCol(xmlBody: String?, method: String = "MKCOL", headersOptional: Headers? = null, callback: ResponseCallback) { + + followRedirects { + httpClient.prepareRequest { + this.method = HttpMethod.parse(method) + setBody(xmlBody) + headers.append(HttpHeaders.ContentType, MIME_XML) + url(UrlUtils.withTrailingSlash(location)) + if (headersOptional != null) + headers.appendAll(headersOptional) + }.execute() + }.let { response -> + checkStatus(response) + callback.onResponse(response) + } + } + + /** + * Sends a HEAD request to the resource. + * + * Follows up to [MAX_REDIRECTS] redirects. + * + * @param callback called with server response unless an exception is thrown + * + * @throws IOException on I/O error + * @throws HttpException on HTTP error + * @throws DavException on HTTPS -> HTTP redirect + */ + suspend fun head(callback: ResponseCallback) { + + followRedirects { + httpClient.prepareRequest { + method = HttpMethod.Head + url(location) + }.execute() + }.let { response -> + checkStatus(response) + callback.onResponse(response) + } + + /* TODO @ricki, second call omitted, not sure if this was done like that on purpose? + followRedirects { + httpClient.newCall( + Request.Builder() + .head() + .url(location) + .build() + ).execute() + }.use { response -> + checkStatus(response) + callback.onResponse(response) + } + */ + } + + /** + * Sends a GET request to the resource. Follows up to [MAX_REDIRECTS] redirects. + * + * Note: Add `Accept-Encoding: identity` to [headers] if you want to disable compression + * (compression might change the returned ETag). + * + * @param accept value of `Accept` header (always sent for clarity; use */* if you don't care) + * @param headers additional headers to send with the request + * + * @return HttpResponse + * + * @throws IOException on I/O error + * @throws HttpException on HTTP error + * @throws DavException on HTTPS -> HTTP redirect + */ + suspend fun get(accept: String, headers: Headers?): HttpResponse = + followRedirects { + httpClient.prepareRequest { + method = HttpMethod.Get + url(location) + if (headers != null) + this.headers.appendAll(headers) + header(HttpHeaders.Accept, accept) + }.execute() + } + + + /** + * Sends a GET request to the resource. Sends `Accept-Encoding: identity` to disable + * compression, because compression might change the ETag. + * + * Follows up to [MAX_REDIRECTS] redirects. + * + * @param accept value of `Accept` header (always sent for clarity; use */* if you don't care) + * @param callback called with server response unless an exception is thrown + * + * @throws IOException on I/O error + * @throws HttpException on HTTP error + * @throws DavException on HTTPS -> HTTP redirect + */ + @Deprecated("Use get(accept, headers, callback) with explicit Accept-Encoding instead") + @Throws(IOException::class, HttpException::class) + suspend fun get(accept: String, callback: ResponseCallback) = + get(accept, Headers.build { append(HttpHeaders.AcceptEncoding, "identity") }, callback) + + /** + * Sends a GET request to the resource. Follows up to [MAX_REDIRECTS] redirects. + * + * Note: Add `Accept-Encoding: identity` to [headers] if you want to disable compression + * (compression might change the returned ETag). + * + * @param accept value of `Accept` header (always sent for clarity; use */* if you don't care) + * @param headers additional headers to send with the request + * @param callback called with server response unless an exception is thrown + * + * @throws IOException on I/O error + * @throws HttpException on HTTP error + * @throws DavException on HTTPS -> HTTP redirect + */ + suspend fun get(accept: String, headers: Headers?, callback: ResponseCallback) { + get(accept, headers).let { response -> + checkStatus(response) + callback.onResponse(response) + } + } + + /** + * Sends a GET request to the resource for a specific byte range. Make sure to check the + * response code: servers may return the whole resource with 200 or partials with 206. + * + * Follows up to [MAX_REDIRECTS] redirects. + * + * @param accept value of `Accept` header (always sent for clarity; use */* if you don't care) + * @param offset zero-based index of first byte to request + * @param size number of bytes to request + * @param headers additional headers to send with the request + * @param callback called with server response unless an exception is thrown + * + * @throws IOException on I/O error + * @throws HttpException on HTTP error + * @throws DavException on high-level errors + */ + @Throws(IOException::class, HttpException::class) + suspend fun getRange(accept: String, offset: Long, size: Int, headers: Headers? = null, callback: ResponseCallback) { + followRedirects { + httpClient.prepareRequest { + method = HttpMethod.Get + url(location) + if (headers != null) + this.headers.appendAll(headers) + val lastIndex = offset + size - 1 + this.headers.append(HttpHeaders.Accept, accept) + this.headers.append(HttpHeaders.Range, "bytes=$offset-$lastIndex") + }.execute() + }.let { response -> + checkStatus(response) + callback.onResponse(response) + } + } + + /** + * Sends a GET request to the resource. Follows up to [MAX_REDIRECTS] redirects. + */ + @Throws(IOException::class, HttpException::class) + suspend fun post(body: String, ifNoneMatch: Boolean = false, headers: Headers? = null, callback: ResponseCallback) { + + followRedirects { + httpClient.prepareRequest { + method = HttpMethod.Post + url(location) + this.setBody(body) // TODO: check in detail if this is correct, changed to String instead of HttpRequest + if (ifNoneMatch) + this.headers.append(HttpHeaders.IfNoneMatch, "*") + if (headers?.isEmpty() == false) + this.headers.appendAll(headers) + }.execute() + }.let { response -> + checkStatus(response) + callback.onResponse(response) + } + } + + /** + * Sends a PUT request to the resource. Follows up to [MAX_REDIRECTS] redirects. + * + * When the server returns an ETag, it is stored in response properties. + * + * @param body new resource body to upload + * @param ifETag value of `If-Match` header to set, or null to omit + * @param ifScheduleTag value of `If-Schedule-Tag-Match` header to set, or null to omit + * @param ifNoneMatch indicates whether `If-None-Match: *` ("don't overwrite anything existing") header shall be sent + * @param headers additional headers to send + * @param callback called with server response unless an exception is thrown + * + * @throws IOException on I/O error + * @throws HttpException on HTTP error + * @throws DavException on HTTPS -> HTTP redirect + */ + @Throws(IOException::class, HttpException::class) + suspend fun put( + body: String, // TODO: Changed to String, maybe a problem for DAVx5? The ContentType is anyway defined in headers + headers: Headers = HeadersBuilder().build(), + ifETag: String? = null, + ifScheduleTag: String? = null, + ifNoneMatch: Boolean = false, + callback: ResponseCallback + ) { + + followRedirects { + httpClient.prepareRequest { + method = HttpMethod.Put + //header(HttpHeaders.ContentType, contentType) + setBody(body) + url(location) + if (ifETag != null) + // only overwrite specific version + header(HttpHeaders.IfMatch, QuotedStringUtils.asQuotedString(ifETag)) + if (ifScheduleTag != null) + // only overwrite specific version + header(HttpHeaders.IfScheduleTagMatch, QuotedStringUtils.asQuotedString(ifScheduleTag)) + if (ifNoneMatch) + // don't overwrite anything existing + header(HttpHeaders.IfNoneMatch, "*") + // TODO: Check with Ricki: Is it okay to use just header here? Or should we use append? + }.execute() + }.let { response -> + checkStatus(response) + callback.onResponse(response) + } + } + + /** + * Sends a DELETE request to the resource. Warning: Sending this request to a collection will + * delete the collection with all its contents! + * + * Follows up to [MAX_REDIRECTS] redirects. + * + * @param ifETag value of `If-Match` header to set, or null to omit + * @param ifScheduleTag value of `If-Schedule-Tag-Match` header to set, or null to omit + * @param headers additional headers to send + * @param callback called with server response unless an exception is thrown + * + * @throws IOException on I/O error + * @throws HttpException on HTTP errors, or when 207 Multi-Status is returned + * (because then there was probably a problem with a member resource) + * @throws DavException on HTTPS -> HTTP redirect + */ + @Throws(IOException::class, HttpException::class) + suspend fun delete( + ifETag: String? = null, + ifScheduleTag: String? = null, + headers: Map = emptyMap(), + callback: ResponseCallback + ) { + followRedirects { + httpClient.prepareRequest { + method = HttpMethod.Delete + url(location) + if (ifETag != null) + header(HttpHeaders.IfMatch, QuotedStringUtils.asQuotedString(ifETag)) + if (ifScheduleTag != null) + header(HttpHeaders.IfScheduleTagMatch, QuotedStringUtils.asQuotedString(ifScheduleTag)) + this.headers.appendAll(headers) // TODO: check with Ricki if the previous two headers also shouldn't be appended! + }.execute() + }.let { response -> + checkStatus(response) + + if (response.status == HttpStatusCode.MultiStatus) + /* If an error occurs deleting a member resource (a resource other than + the resource identified in the Request-URI), then the response can be + a 207 (Multi-Status). […] (RFC 4918 9.6.1. DELETE for Collections) */ + throw HttpException(response) + + callback.onResponse(response) + } + } + + /** + * Sends a PROPFIND request to the resource. Expects and processes a 207 Multi-Status response. + * + * Follows up to [MAX_REDIRECTS] redirects. + * + * @param depth "Depth" header to send (-1 for `infinity`) + * @param reqProp properties to request + * @param callback called for every XML response element in the Multi-Status response + * + * @throws IOException on I/O error + * @throws HttpException on HTTP error + * @throws DavException on WebDAV error (like no 207 Multi-Status response) or HTTPS -> HTTP redirect + */ + @Throws(IOException::class, HttpException::class, DavException::class) + suspend fun propfind(depth: Int, vararg reqProp: Property.Name, callback: MultiResponseCallback) { + // build XML request body + val serializer = XmlUtils.newSerializer() + val writer = StringWriter() + serializer.setOutput(writer) + serializer.setPrefix("", NS_WEBDAV) + serializer.setPrefix("CAL", NS_CALDAV) + serializer.setPrefix("CARD", NS_CARDDAV) + serializer.startDocument("UTF-8", null) + serializer.insertTag(PROPFIND) { + insertTag(PROP) { + for (prop in reqProp) + insertTag(prop) + } + } + serializer.endDocument() + + followRedirects { + httpClient.prepareRequest { + url(location) + method = HttpMethod.parse("PROPFIND") + setBody(writer.toString()) + header(HttpHeaders.ContentType, MIME_XML) + header(HttpHeaders.Depth, if (depth >= 0) depth.toString() else "infinity") + }.execute() + }.let { response -> + processMultiStatus(response, callback) + } + } + + /** + * Sends a PROPPATCH request to the server in order to set and remove properties. + * + * @param setProperties map of properties that shall be set (values currently have to be strings) + * @param removeProperties list of names of properties that shall be removed + * @param callback called for every XML response element in the Multi-Status response + * + * Follows up to [MAX_REDIRECTS] redirects. + * + * Currently expects a 207 Multi-Status response although servers are allowed to + * return other values, too. + * + * @throws IOException on I/O error + * @throws HttpException on HTTP error + * @throws DavException on WebDAV error (like no 207 Multi-Status response) or HTTPS -> HTTP redirect + */ + suspend fun proppatch( + setProperties: Map, + removeProperties: List, + callback: MultiResponseCallback + ) { + val rqBody = createProppatchXml(setProperties, removeProperties) + + followRedirects { + httpClient.prepareRequest { + url(location) + method = HttpMethod.parse("PROPPATCH") + setBody(rqBody) + header(HttpHeaders.ContentType, MIME_XML) + }.execute() + }.let { response -> + // TODO handle not only 207 Multi-Status + // http://www.webdav.org/specs/rfc4918.html#PROPPATCH-status + + processMultiStatus(response, callback) + } + } + + /** + * Sends a SEARCH request (RFC 5323) with the given body to the server. + * + * Follows up to [MAX_REDIRECTS] redirects. Expects a 207 Multi-Status response. + * + * @param search search request body (XML format, DAV:searchrequest or DAV:query-schema-discovery) + * @param callback called for every XML response element in the Multi-Status response + * + * @throws IOException on I/O error + * @throws HttpException on HTTP error + * @throws DavException on WebDAV error (like no 207 Multi-Status response) or HTTPS -> HTTP redirect + */ + suspend fun search(search: String, callback: MultiResponseCallback) { + followRedirects { + httpClient.prepareRequest { + url(location) + method = HttpMethod.parse("SEARCH") + setBody(search) + header(HttpHeaders.ContentType, MIME_XML) + }.execute() + }.let { response -> + processMultiStatus(response, callback) + } + } + + + // status handling + + + /** + * Checks the status from an HTTP response and throws an exception in case of an error. + * + * @throws HttpException in case of an HTTP error + */ + protected fun checkStatus(response: HttpResponse) { + if (response.status.isSuccess()) + // everything OK + return + + throw when (response.status.value) { + 401 -> UnauthorizedException(response) + 403 -> ForbiddenException(response) + 404 -> NotFoundException(response) + 409 -> ConflictException(response) + 410 -> GoneException(response) + 412 -> PreconditionFailedException(response) + 503 -> ServiceUnavailableException(response) + else -> HttpException(response) + } + } + + /** + * Send a request and follows up to [MAX_REDIRECTS] redirects. + * + * @param sendRequest called to send the request (may be called multiple times) + * + * @return response of the last request (whether it is a redirect or not) + * + * @throws DavException on HTTPS -> HTTP redirect + */ + internal suspend fun followRedirects(sendRequest: suspend () -> HttpResponse): HttpResponse { + + lateinit var response: HttpResponse + for (attempt in 1..MAX_REDIRECTS) { + response = sendRequest() + if (response.status in listOf( + HttpStatusCode.PermanentRedirect, + HttpStatusCode.TemporaryRedirect, + HttpStatusCode.MultipleChoices, + HttpStatusCode.MovedPermanently, + HttpStatusCode.Found, + HttpStatusCode.SeeOther) + ) //if is redirect, based on okhttp3/Response.kt: HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER + // handle 3xx Redirection + response.let { + val target = it.headers[HttpHeaders.Location]?.let { newLocation -> + URLBuilder(location).takeFrom(newLocation).build() + } + if (target != null) { + logger.info("Redirected, new location = $target") // TODO: Is logger.info ok here? + + if (location.protocol.isSecure() && !target.protocol.isSecure()) + throw DavException("Received redirect from HTTPS to HTTP") + + location = target + } else + throw DavException("Redirected without new Location") + } + else + break + } + return response + } + + /** + * Validates a 207 Multi-Status response. + * + * @param response will be checked for Multi-Status response + * + * @throws DavException if the response is not a Multi-Status response + */ + suspend fun assertMultiStatus(httpResponse: HttpResponse) { + val response = httpResponse + + if (response.status != HttpStatusCode.MultiStatus) + throw DavException("Expected 207 Multi-Status, got ${response.status.value} ${response.status.description}", response = response) + + val bodyChannel = response.bodyAsChannel() + + response.contentType()?.let { mimeType -> // is response.contentType() ok here? Or must it be the content type of the body? + if (((!ContentType.Application.contains(mimeType) && !ContentType.Text.contains(mimeType))) || mimeType.contentSubtype != "xml") { + /* Content-Type is not application/xml or text/xml although that is expected here. + Some broken servers return an XML response with some other MIME type. So we try to see + whether the response is maybe XML although the Content-Type is something else. */ + try { + val firstBytes = ByteArray(XML_SIGNATURE.size) + bodyChannel.readBuffer().peek().readFully(firstBytes) + if (XML_SIGNATURE.contentEquals(firstBytes)) { + logger.warn("Received 207 Multi-Status that seems to be XML but has MIME type $mimeType") + + // response is OK, return and do not throw Exception below + return + } + } catch (e: Exception) { + logger.warn("Couldn't scan for XML signature", e) + } + + throw DavException("Received non-XML 207 Multi-Status", response = response) + } + } ?: logger.warn("Received 207 Multi-Status without Content-Type, assuming XML") + } + + + // Multi-Status handling + + /** + * Processes a Multi-Status response. + * + * @param response response which is expected to contain a Multi-Status response + * @param callback called for every XML response element in the Multi-Status response + * + * @return list of properties which have been received in the Multi-Status response, but + * are not part of response XML elements (like `sync-token` which is returned as [SyncToken]) + * + * @throws IOException on I/O error + * @throws HttpException on HTTP error + * @throws DavException on WebDAV error (for instance, when the response is not a Multi-Status response) + */ + protected suspend fun processMultiStatus(response: HttpResponse, callback: MultiResponseCallback): List { + checkStatus(response) + assertMultiStatus(response) + return processMultiStatus(response.bodyAsBytes().inputStream().reader(), callback) + } + + /** + * Processes a Multi-Status response. + * + * @param reader the Multi-Status response is read from this + * @param callback called for every XML response element in the Multi-Status response + * + * @return list of properties which have been received in the Multi-Status response, but + * are not part of response XML elements (like `sync-token` which is returned as [SyncToken]) + * + * @throws IOException on I/O error + * @throws HttpException on HTTP error + * @throws DavException on WebDAV error (like an invalid XML response) + */ + protected fun processMultiStatus(reader: Reader, callback: MultiResponseCallback): List { + val responseProperties = mutableListOf() + val parser = XmlUtils.newPullParser() + + fun parseMultiStatus(): List { + // + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) + when (parser.propertyName()) { + RESPONSE -> + Response.parse(parser, location, callback) + SyncToken.NAME -> + XmlReader(parser).readText()?.let { + responseProperties += SyncToken(it) + } + } + eventType = parser.next() + } + + return responseProperties + } + + try { + parser.setInput(reader) + + var eventType = parser.eventType + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG && parser.depth == 1) + if (parser.propertyName() == MULTISTATUS) + return parseMultiStatus() + // ignore further elements + eventType = parser.next() + } + + throw DavException("Multi-Status response didn't contain multistatus XML element") + + } catch (e: EOFException) { + throw DavException("Incomplete multistatus XML element", e) + } catch (e: XmlPullParserException) { + throw DavException("Couldn't parse multistatus XML element", e) + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/Error.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/Error.kt new file mode 100644 index 00000000..feef3824 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/Error.kt @@ -0,0 +1,61 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + + +import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV +import org.xmlpull.v1.XmlPullParser +import java.io.Serializable + +/** + * Represents an XML precondition/postcondition error. Every error has a name, which is the XML element + * name. Subclassed errors may have more specific information available. + * + * At the moment, there is no logic for subclassing errors. + * + * @param name property name for the XML error element + */ +data class Error( + val name: Property.Name +): Serializable { + + companion object { + + val NAME = Property.Name(NS_WEBDAV, "error") + + fun parseError(parser: XmlPullParser): List { + val names = mutableSetOf() + + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) + names += Property.Name(parser.namespace, parser.name) + eventType = parser.next() + } + + return names.map { Error(it) } + } + + + // some pre-defined errors + + val NEED_PRIVILEGES = Error(Property.Name(NS_WEBDAV, "need-privileges")) + val VALID_SYNC_TOKEN = Error(Property.Name(NS_WEBDAV, "valid-sync-token")) + + } + + override fun equals(other: Any?) = + (other is Error) && other.name == name + + override fun hashCode() = name.hashCode() + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/HttpUtils.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/HttpUtils.kt new file mode 100644 index 00000000..760272c0 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/HttpUtils.kt @@ -0,0 +1,170 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.HttpUtils.httpDateFormat +import io.ktor.client.statement.HttpResponse +import io.ktor.http.HttpStatusCode +import io.ktor.http.Url +import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.time.format.DateTimeParseException +import java.util.Locale +import java.util.logging.Logger + +object HttpUtils { + + /** + * Preferred HTTP date/time format, see RFC 7231 7.1.1.1 IMF-fixdate + */ + private const val httpDateFormatStr = "EEE, dd MMM yyyy HH:mm:ss ZZZZ" + private val httpDateFormat = DateTimeFormatter.ofPattern(httpDateFormatStr, Locale.US) + + val INVALID_STATUS = HttpStatusCode( 500, "Invalid status line") + + + private val logger + get() = Logger.getLogger(javaClass.name) + + + /** + * Gets the resource name (the last segment of the path) from an URL. + * Empty if the resource is the base directory. + * + * * `dir` for `https://example.com/dir/` + * * `file` for `https://example.com/file` + * * `` for `https://example.com` or `https://example.com/` + * + * @return resource name + */ + fun fileName(url: Url): String = url.segments.lastOrNull() ?: "" // segments excludes empty segments + + /** + * Gets all values of a header that is defined as a list [RFC 9110 5.6.1], + * regardless of they're sent as one line or as multiple lines. + * + * For instance, regardless of whether a server sends: + * + * ``` + * DAV: 1 + * DAV: 2 + * ``` + * + * or + * + * ``` + * DAV: 1, 2 + * ``` + * + * this method would return `arrayOf("1","2")` for the `DAV` header. + * + * @param response the HTTP response to evaluate + * @param name header name (for instance: `DAV`) + * + * @return all values for the given header name + */ + fun listHeader(response: HttpResponse, name: String): Array { + val value = response.headers.getAll(name)?.joinToString(",") + return value?.split(',')?.map { it.trim() }?.filter { it.isNotEmpty() }?.toTypedArray() ?: emptyArray() + } + + + /** + * Formats a date for use in HTTP headers using [httpDateFormat]. + * + * @param date date to be formatted + * + * @return date in HTTP-date format + */ + fun formatDate(date: Instant): String = + ZonedDateTime.ofInstant(date, ZoneOffset.UTC).format(httpDateFormat) + + /** + * Parses a HTTP-date according to RFC 7231 section 7.1.1.1. + * + * @param dateStr date-time formatted in one of the three accepted formats: + * + * - preferred format (`IMF-fixdate`) + * - obsolete RFC 850 format + * - ANSI C's `asctime()` format + * + * @return date-time, or null if date could not be parsed + */ + fun parseDate(dateStr: String): Instant? { + val zonedFormats = arrayOf( + // preferred format + httpDateFormat, + + // obsolete RFC 850 format + DateTimeFormatter.ofPattern("EEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US), + ) + + // try the two formats with zone info + for (format in zonedFormats) + try { + return ZonedDateTime.parse(dateStr, format).toInstant() + } catch (ignored: DateTimeParseException) { + } + + // try ANSI C's asctime() format + try { + val formatC = DateTimeFormatter.ofPattern("EEE MMM ppd HH:mm:ss yyyy", Locale.US) + val local = LocalDateTime.parse(dateStr, formatC) + return local.atZone(ZoneOffset.UTC).toInstant() + } catch (ignored: DateTimeParseException) { + } + + // no success in parsing + logger.warning("Couldn't parse HTTP date: $dateStr, ignoring") + return null + } + + /** + * Parses an HTTP status line. + * + * It supports both full status lines like "HTTP/1.1 200 OK" + * and partial ones like "200 OK" or just "200". + * + * If the status line cannot be parsed, an [HttpStatusCode] object + * with the value 500 "Invalid status line" is returned. + * + * @param statusText the status line to parse. + * @return an [HttpStatusCode] object representing the parsed status. + */ + fun parseStatusLine(statusText: String): HttpStatusCode { + + + val parts = statusText.split(" ", limit = 3) + return if (parts.size >= 2 && parts[0].startsWith("HTTP/")) { // Full status line like "HTTP/1.1 200 OK" + val statusCode = parts[1].toIntOrNull() + val description = if (parts.size > 2) parts[2] else "" + if (statusCode != null && statusCode in 1..999) { + HttpStatusCode(statusCode, description) + } else { + INVALID_STATUS + } + } else if (parts.isNotEmpty()) { // Potentially just "200 OK" or "200" + val statusCode = parts[0].toIntOrNull() + val description = if (parts.size > 1) parts.drop(1).joinToString(" ") else HttpStatusCode.allStatusCodes.find { it.value == statusCode }?.description ?: "" + if (statusCode != null && statusCode in 1..999) { + HttpStatusCode(statusCode, description) + } else { + INVALID_STATUS + } + } else { + INVALID_STATUS + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropStat.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropStat.kt new file mode 100644 index 00000000..3fad6a3d --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropStat.kt @@ -0,0 +1,58 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.Response.Companion.STATUS +import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV +import io.ktor.http.HttpStatusCode +import org.xmlpull.v1.XmlPullParser +import java.util.LinkedList + +/** + * Represents a WebDAV propstat XML element. + * + * + */ +data class PropStat( + val properties: List, + val status: HttpStatusCode, + val error: List? = null +) { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "propstat") + + private val ASSUMING_OK = HttpStatusCode(200, "Assuming OK") + + fun parse(parser: XmlPullParser): PropStat { + val depth = parser.depth + + var status: HttpStatusCode? = null + val prop = LinkedList() + + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) + when (parser.propertyName()) { + DavResource.Companion.PROP -> + prop.addAll(Property.parse(parser)) + STATUS -> status = HttpUtils.parseStatusLine(parser.nextText()) } + eventType = parser.next() + } + + return PropStat(prop, status ?: ASSUMING_OK) + } + + } +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/Property.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/Property.kt new file mode 100644 index 00000000..74769398 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/Property.kt @@ -0,0 +1,75 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.exception.InvalidPropertyException +import org.slf4j.LoggerFactory +import org.xmlpull.v1.XmlPullParser +import java.io.Serializable +import java.util.LinkedList + +/** + * Represents a WebDAV property. + * + * Every [Property] must define a static field (use `@JvmStatic`) called `NAME` of type [Property.Name], + * which will be accessed by reflection. + * + * Every [Property] should be a data class in order to be able to compare it against others, and convert to a useful + * string for debugging. + */ +interface Property { + + data class Name( + val namespace: String, + val name: String + ): Serializable { + + override fun toString() = "$namespace:$name" + + } + + companion object { + + fun parse(parser: XmlPullParser): List { + val logger = LoggerFactory.getLogger(Property::javaClass.name) + + // + val depth = parser.depth + val properties = LinkedList() + + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { + val depthBeforeParsing = parser.depth + val name = Name(parser.namespace, parser.name) + + try { + val property = PropertyRegistry.create(name, parser) + assert(parser.depth == depthBeforeParsing) + + if (property != null) { + properties.add(property) + } else + logger.info("Ignoring unknown property $name") + } catch (e: InvalidPropertyException) { + logger.warn("Ignoring invalid property", e) + } + } + + eventType = parser.next() + } + + return properties + } + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropertyFactory.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropertyFactory.kt new file mode 100644 index 00000000..d8eaf459 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropertyFactory.kt @@ -0,0 +1,35 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException + +interface PropertyFactory { + + /** + * Name of the Property the factory creates, + * e.g. `Property.Name("DAV:", "displayname")` if the factory creates + * [at.bitfire.dav4jvm.ktor.property.webdav.DisplayName] objects) + */ + fun getName(): Property.Name + + /** + * Parses XML of a property and returns its data class. + * + * Implementations shouldn't make assumptions on which sub-properties are available + * or not and in doubt return an empty [Property]. + * + * @throws XmlPullParserException in case of parsing errors + */ + fun create(parser: XmlPullParser): Property + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropertyRegistry.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropertyRegistry.kt new file mode 100644 index 00000000..2afcf367 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropertyRegistry.kt @@ -0,0 +1,150 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.property.caldav.CalendarColor +import at.bitfire.dav4jvm.ktor.property.caldav.CalendarData +import at.bitfire.dav4jvm.ktor.property.caldav.CalendarDescription +import at.bitfire.dav4jvm.ktor.property.caldav.CalendarHomeSet +import at.bitfire.dav4jvm.ktor.property.caldav.CalendarProxyReadFor +import at.bitfire.dav4jvm.ktor.property.caldav.CalendarProxyWriteFor +import at.bitfire.dav4jvm.ktor.property.caldav.CalendarTimezone +import at.bitfire.dav4jvm.ktor.property.caldav.CalendarTimezoneId +import at.bitfire.dav4jvm.ktor.property.caldav.CalendarUserAddressSet +import at.bitfire.dav4jvm.ktor.property.caldav.GetCTag +import at.bitfire.dav4jvm.ktor.property.caldav.MaxResourceSize +import at.bitfire.dav4jvm.ktor.property.caldav.ScheduleTag +import at.bitfire.dav4jvm.ktor.property.caldav.Source +import at.bitfire.dav4jvm.ktor.property.caldav.SupportedCalendarComponentSet +import at.bitfire.dav4jvm.ktor.property.caldav.SupportedCalendarData +import at.bitfire.dav4jvm.ktor.property.carddav.AddressData +import at.bitfire.dav4jvm.ktor.property.carddav.AddressbookDescription +import at.bitfire.dav4jvm.ktor.property.carddav.AddressbookHomeSet +import at.bitfire.dav4jvm.ktor.property.carddav.SupportedAddressData +import at.bitfire.dav4jvm.ktor.property.push.PushMessage +import at.bitfire.dav4jvm.ktor.property.push.PushRegister +import at.bitfire.dav4jvm.ktor.property.push.PushTransports +import at.bitfire.dav4jvm.ktor.property.push.Subscription +import at.bitfire.dav4jvm.ktor.property.push.Topic +import at.bitfire.dav4jvm.ktor.property.push.WebPushSubscription +import at.bitfire.dav4jvm.ktor.property.webdav.AddMember +import at.bitfire.dav4jvm.ktor.property.webdav.CreationDate +import at.bitfire.dav4jvm.ktor.property.webdav.CurrentUserPrincipal +import at.bitfire.dav4jvm.ktor.property.webdav.CurrentUserPrivilegeSet +import at.bitfire.dav4jvm.ktor.property.webdav.DisplayName +import at.bitfire.dav4jvm.ktor.property.webdav.GetContentLength +import at.bitfire.dav4jvm.ktor.property.webdav.GetContentType +import at.bitfire.dav4jvm.ktor.property.webdav.GetETag +import at.bitfire.dav4jvm.ktor.property.webdav.GetLastModified +import at.bitfire.dav4jvm.ktor.property.webdav.GroupMembership +import at.bitfire.dav4jvm.ktor.property.webdav.Owner +import at.bitfire.dav4jvm.ktor.property.webdav.QuotaAvailableBytes +import at.bitfire.dav4jvm.ktor.property.webdav.QuotaUsedBytes +import at.bitfire.dav4jvm.ktor.property.webdav.ResourceType +import at.bitfire.dav4jvm.ktor.property.webdav.SupportedReportSet +import at.bitfire.dav4jvm.ktor.property.webdav.SyncToken +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.util.logging.Level +import java.util.logging.Logger + +object PropertyRegistry { + + private val factories = mutableMapOf() + private val logger + get() = Logger.getLogger(javaClass.name) + + + init { + logger.info("Registering DAV property factories") + registerDefaultFactories() + } + + private fun registerDefaultFactories() { + register(listOf( + AddMember.Factory, + AddressbookDescription.Factory, + AddressbookHomeSet.Factory, + AddressData.Factory, + CalendarColor.Factory, + CalendarData.Factory, + CalendarDescription.Factory, + CalendarHomeSet.Factory, + CalendarProxyReadFor.Factory, + CalendarProxyWriteFor.Factory, + CalendarTimezone.Factory, + CalendarTimezoneId.Factory, + CalendarUserAddressSet.Factory, + CreationDate.Factory, + CurrentUserPrincipal.Factory, + CurrentUserPrivilegeSet.Factory, + DisplayName.Factory, + GetContentLength.Factory, + GetContentType.Factory, + GetCTag.Factory, + GetETag.Factory, + GetLastModified.Factory, + GroupMembership.Factory, + MaxResourceSize.Factory, + at.bitfire.dav4jvm.ktor.property.carddav.MaxResourceSize.Factory, + Owner.Factory, + PushMessage.Factory, + PushRegister.Factory, + PushTransports.Factory, + QuotaAvailableBytes.Factory, + QuotaUsedBytes.Factory, + ResourceType.Factory, + ScheduleTag.Factory, + Source.Factory, + Subscription.Factory, + SupportedAddressData.Factory, + SupportedCalendarComponentSet.Factory, + SupportedCalendarData.Factory, + SupportedReportSet.Factory, + SyncToken.Factory, + Topic.Factory, + WebPushSubscription.Factory + )) + } + + + /** + * Registers a property factory, so that objects for all WebDAV properties which are handled + * by this factory can be created. + * + * @param factory property factory to be registered + */ + fun register(factory: PropertyFactory) { + logger.fine("Registering ${factory::class.java.name} for ${factory.getName()}") + factories[factory.getName()] = factory + } + + /** + * Registers some property factories, so that objects for all WebDAV properties which are handled + * by these factories can be created. + + * @param factories property factories to be registered + */ + fun register(factories: Iterable) { + factories.forEach { + register(it) + } + } + + fun create(name: Property.Name, parser: XmlPullParser) = + try { + factories[name]?.create(parser) + } catch (e: XmlPullParserException) { + logger.log(Level.WARNING, "Couldn't parse $name", e) + null + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/QuotedStringUtils.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/QuotedStringUtils.kt new file mode 100644 index 00000000..b3385cf2 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/QuotedStringUtils.kt @@ -0,0 +1,40 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +object QuotedStringUtils { + + fun asQuotedString(raw: String) = + "\"" + raw.replace("\\" ,"\\\\").replace("\"", "\\\"") + "\"" + + fun decodeQuotedString(quoted: String): String { + /* quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) + qdtext = > + quoted-pair = "\" CHAR + */ + + val len = quoted.length + if (len >= 2 && quoted[0] == '"' && quoted[len-1] == '"') { + val result = StringBuffer(len) + var pos = 1 + while (pos < len-1) { + var c = quoted[pos] + if (c == '\\' && pos != len-2) + c = quoted[++pos] + result.append(c) + pos++ + } + return result.toString() + } else + return quoted + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/Response.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/Response.kt new file mode 100644 index 00000000..891cbf6f --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/Response.kt @@ -0,0 +1,243 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.ktor.property.webdav.ResourceType +import io.ktor.http.HttpStatusCode +import io.ktor.http.URLBuilder +import io.ktor.http.Url +import io.ktor.http.isSuccess +import io.ktor.http.takeFrom +import org.xmlpull.v1.XmlPullParser +import java.util.logging.Logger + +/** + * Represents a WebDAV response XML Element. + * + * + */ +@Suppress("unused") +data class Response( + /** + * URL of the requested resource. For instance, if `this` is a result + * of a PROPFIND request, the `requestedUrl` would be the URL where the + * PROPFIND request has been sent to (usually the collection URL). + */ + val requestedUrl: Url, + + /** + * URL of this response (`href` element) + */ + val href: Url, + + /** + * status of this response (`status` XML element) + */ + val status: HttpStatusCode?, + + /** + * property/status elements (`propstat` XML elements) + */ + val propstat: List, + + /** + * list of precondition/postcondition elements (`error` XML elements) + */ + val error: List? = null, + + /** + * new location of this response (`location` XML element), used for redirects + */ + val newLocation: Url? = null +) { + + enum class HrefRelation { + SELF, MEMBER, OTHER + } + + /** + * All properties from propstat elements with empty status or status code 2xx. + */ + val properties: List by lazy { + if (isSuccess()) + propstat.filter { it.status.isSuccess() }.map { it.properties }.flatten() + else + emptyList() + } + + /** + * Convenience method to get a certain property with empty status or status code 2xx + * from the current response. + */ + operator fun get(clazz: Class) = + properties.filterIsInstance(clazz).firstOrNull() + + /** + * Returns whether the request was successful. + * + * @return true: no status XML element or status code 2xx; false: otherwise + */ + fun isSuccess() = status == null || status.isSuccess() + + /** + * Returns the name (last path segment) of the resource. + */ + fun hrefName() = HttpUtils.fileName(href) + + + companion object { + + val RESPONSE = Property.Name(NS_WEBDAV, "response") + val MULTISTATUS = Property.Name(NS_WEBDAV, "multistatus") + val STATUS = Property.Name(NS_WEBDAV, "status") + val LOCATION = Property.Name(NS_WEBDAV, "location") + + /** + * Parses an XML response element and calls the [callback] for it (when it has a ``). + * The arguments of the [MultiResponseCallback.onResponse] are set accordingly. + * + * If the [at.bitfire.dav4jvm.ktor.property.webdav.ResourceType] of the queried resource is known (= was queried and returned by the server) + * and it contains [at.bitfire.dav4jvm.ktor.property.webdav.ResourceType.Companion.COLLECTION], the `href` property of the callback will automatically + * have a trailing slash. + * + * So if you want PROPFIND results to have a trailing slash when they are collections, make sure + * that you query [at.bitfire.dav4jvm.ktor.property.webdav.ResourceType]. + */ + fun parse(parser: XmlPullParser, location: Url, callback: MultiResponseCallback) { + val logger = Logger.getLogger(Response::javaClass.name) + + val depth = parser.depth + + var hrefOrNull: Url? = null + var status: HttpStatusCode? = null + val propStat = mutableListOf() + var error: List? = null + var newLocation: Url? = null + + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth+1) + when (parser.propertyName()) { + DavResource.Companion.HREF -> { + var sHref = parser.nextText() + var hierarchical = false + if (!sHref.startsWith("/")) { + /* According to RFC 4918 8.3 URL Handling, only absolute paths are allowed as relative + URLs. However, some servers reply with relative paths. */ + val firstColon = sHref.indexOf(':') + if (firstColon != -1) { + /* There are some servers which return not only relative paths, but relative paths like "a:b.vcf", + which would be interpreted as scheme: "a", scheme-specific part: "b.vcf" normally. + For maximum compatibility, we prefix all relative paths which contain ":" (but not "://"), + with "./" to allow resolving by HttpUrl. */ + try { + if (sHref.substring(firstColon, firstColon + 3) == "://") + hierarchical = true + } catch (e: IndexOutOfBoundsException) { + // no "://" + } + if (!hierarchical) + sHref = "./$sHref" + + } + } + + + if(!hierarchical) { + val urlBuilder = URLBuilder(location).takeFrom(sHref) + urlBuilder.pathSegments = urlBuilder.pathSegments.filterNot { it == "." } // Drop segments that are "./" + hrefOrNull = urlBuilder.build() + } else { + hrefOrNull = URLBuilder(location).takeFrom(sHref).build() + } + } + STATUS -> + status = HttpUtils.parseStatusLine(parser.nextText()) + PropStat.Companion.NAME -> + PropStat.Companion.parse(parser).let { propStat += it } + Error.NAME -> + error = Error.parseError(parser) + LOCATION -> + newLocation = Url(parser.nextText()) // TODO: Need to catch exception here? + } + eventType = parser.next() + } + + if (hrefOrNull == null) { + logger.warning("Ignoring XML response element without valid href") + return + } + var href: Url = hrefOrNull // guaranteed to be not null + + // if we know this resource is a collection, make sure href has a trailing slash + // (for clarity and resolving relative paths) + propStat.filter { it.status.isSuccess() } + .map { it.properties } + .filterIsInstance() + .firstOrNull() + ?.let { type -> + if (type.types.contains(ResourceType.Companion.COLLECTION)) + href = UrlUtils.withTrailingSlash(href) + } + + //log.log(Level.FINE, "Received properties for $href", if (status != null) status else propStat) + + // Which resource does this represent? + val relation = when { + UrlUtils.omitTrailingSlash(href).equalsForWebDAV(UrlUtils.omitTrailingSlash(location)) -> + HrefRelation.SELF + + else -> { + if (location.protocol.name == href.protocol.name && location.host == href.host && location.port == href.port) { + val locationSegments = location.segments + val hrefSegments = href.segments + + // don't compare trailing slash segment ("") + var nBasePathSegments = locationSegments.size + if (locationSegments[nBasePathSegments - 1] == "") // TODO: Ricki, Not sure if this is still needed + nBasePathSegments-- + + /* example: locationSegments = [ "davCollection", "" ] + nBasePathSegments = 1 + hrefSegments = [ "davCollection", "aMember" ] + */ + var relation = HrefRelation.OTHER + if (hrefSegments.size > nBasePathSegments) { + val sameBasePath = (0 until nBasePathSegments).none { locationSegments[it] != hrefSegments[it] } + if (sameBasePath) + relation = HrefRelation.MEMBER + } + + relation + } else + HrefRelation.OTHER + } + } + + callback.onResponse( + Response( + requestedUrl = location, + href = href, + status = status, + propstat = propStat, + error = error, + newLocation = newLocation + ), + relation + ) + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/UrlUtils.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/UrlUtils.kt new file mode 100644 index 00000000..b94c9f38 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/UrlUtils.kt @@ -0,0 +1,107 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.UrlUtils.omitTrailingSlash +import at.bitfire.dav4jvm.ktor.UrlUtils.withTrailingSlash +import io.ktor.http.URLBuilder +import io.ktor.http.Url + +object UrlUtils { + + /** + * Gets the first-level domain name (without subdomains) from a host name. + * Also removes trailing dots. + * + * @param host name (e.g. `www.example.com.`) + * + * @return domain name (e.g. `example.com`) + */ + fun hostToDomain(host: String?): String? { + if (host == null) + return null + + // remove optional dot at end + val withoutTrailingDot = host.removeSuffix(".") + + // split into labels + val labels = withoutTrailingDot.split('.') + return if (labels.size >= 2) { + labels[labels.size - 2] + "." + labels[labels.size - 1] + } else + withoutTrailingDot + } + + /** + * Ensures that a given URL doesn't have a trailing slash after member names. + * If the path is the root path (`/`), the slash is preserved. + * + * @param url URL to process (e.g. 'http://host/test1/') + * + * @return URL without trailing slash (except when the path is the root path), e.g. `http://host/test1` + */ + fun omitTrailingSlash(url: Url): Url { + val hasTrailingSlash = url.rawSegments.last() == "" + + return if (hasTrailingSlash) + URLBuilder(url).apply { pathSegments = pathSegments.dropLast(1) }.build() + else + url + } + + /** + * Ensures that a given URL has a trailing slash after member names. + * + * @param url URL to process (e.g. 'http://host/test1') + * + * @return URL with trailing slash, e.g. `http://host/test1/` + */ + fun withTrailingSlash(url: Url): Url { + val hasTrailingSlash = url.rawSegments.last() == "" + + return if (hasTrailingSlash) + url + else + URLBuilder(url).apply { pathSegments += "" }.build() + } + +} + +/** + * Compares two [Url]s in WebDAV context. If two URLs are considered *equal*, both + * represent the same WebDAV resource. + * + * The fragment of an URL is ignored, e.g. `http://host:80/folder1` and `http://HOST/folder1#somefragment` + * are considered to be equal. + * + * [Url] is less strict than [java.net.URI] and allows for instance (not encoded) square brackets in the path. + * So this method compares the protocol, host (case insensitive), port and rawSegments + * and and returns true if all are the same. + * + * Attention: **This method does not deal with trailing slashes**, so if you want to compare collection URLs, + * make sure they both (don't) have a trailing slash before calling this method, for instance + * with [omitTrailingSlash] or [withTrailingSlash]. + * + * @param other the URL to compare the current object with + * + * @return whether the URLs are considered to represent the same WebDAV resource + */ +fun Url.equalsForWebDAV(other: Url): Boolean { + // if Ktor thinks the two URLs are equal, they're in any case + // (and it's a simple String comparison) + if (this == other) + return true + + return this.protocol == other.protocol + && this.host.lowercase() == other.host.lowercase() + && this.port == other.port + && this.rawSegments == other.rawSegments +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/XmlReader.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/XmlReader.kt new file mode 100644 index 00000000..6b08672f --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/XmlReader.kt @@ -0,0 +1,182 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.ktor.property.caldav.SupportedCalendarData +import io.ktor.http.BadContentTypeFormatException +import io.ktor.http.ContentType +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException +import java.time.Instant +import java.util.logging.Level +import java.util.logging.Logger + +/** + * Reads/processes XML tags which are used for WebDAV. + * + * @param parser The parser to read from. + */ +class XmlReader( + private val parser: XmlPullParser +) { + + // base processing + + /** + * Reads child elements of the current element. Whenever a direct child with the given name is found, + * [processor] is called for each one. + */ + @Throws(IOException::class, XmlPullParserException::class) + fun processTag(name: Property.Name, processor: XmlReader.() -> Unit) { + val depth = parser.depth + var eventType = parser.eventType + while (!((eventType == XmlPullParser.END_TAG || eventType == XmlPullParser.END_DOCUMENT) && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1 && parser.propertyName() == name) + processor() + eventType = parser.next() + } + } + + /** + * Reads the inline text of the current element. + * + * For instance, if the parser is at the beginning of this XML: + * + * ``` + * text + * ``` + * + * this function will return "text". + * + * @return text or `null` if no text is found + */ + @Throws(IOException::class, XmlPullParserException::class) + fun readText(): String? { + var text: String? = null + + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.TEXT && parser.depth == depth) + text = parser.text + eventType = parser.next() + } + + return text + } + + /** + * Reads child elements of the current element. When a direct child with the given name is found, + * its text is returned. + * + * @param name The name of the tag to read. + * @return The text inside the tag, or `null` if the tag is not found. + */ + @Throws(IOException::class, XmlPullParserException::class) + fun readTextProperty(name: Property.Name): String? { + var result: String? = null + + val depth = parser.depth + var eventType = parser.eventType + while (!((eventType == XmlPullParser.END_TAG || eventType == XmlPullParser.END_DOCUMENT) && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1 && parser.propertyName() == name && result == null) + result = parser.nextText() + eventType = parser.next() + } + return result + } + + /** + * Reads child elements of the current element. Whenever a direct child with the given name is + * found, its text is added to the given list. + * + * @param name The name of the tag to read. + * @param list The list to add the text to. + */ + @Throws(IOException::class, XmlPullParserException::class) + fun readTextPropertyList(name: Property.Name, list: MutableCollection) { + val depth = parser.depth + var eventType = parser.eventType + while (!((eventType == XmlPullParser.END_TAG || eventType == XmlPullParser.END_DOCUMENT) && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1 && parser.propertyName() == name) + list.add(parser.nextText()) + eventType = parser.next() + } + } + + + // extended processing (uses readText etc.) + + /** + * Uses [readText] to read the tag's value (which is expected to be in _HTTP-date_ format), and converts + * it into an [Instant] using [HttpUtils.parseDate]. + * + * If the conversion fails for any reason, null is returned, and a message is displayed in log. + */ + fun readHttpDate(): Instant? { + return readText()?.let { rawDate -> + val date = HttpUtils.parseDate(rawDate) + if (date != null) + date + else { + val logger = Logger.getLogger(javaClass.name) + logger.warning("Couldn't parse HTTP-date") + null + } + } + } + + /** + * Uses [readText] to read the tag's value (which is expected to be a number), and converts it + * into a [Long] with [String.toLong]. + * + * If the conversion fails for any reason, null is returned, and a message is displayed in log. + */ + fun readLong(): Long? { + return readText()?.let { valueStr -> + try { + valueStr.toLong() + } catch(e: NumberFormatException) { + val logger = Logger.getLogger(javaClass.name) + logger.log(Level.WARNING, "Couldn't parse as Long: $valueStr", e) + null + } + } + } + + /** + * Processes all the tags named [tagName], and sends every tag that has the [at.bitfire.dav4jvm.ktor.property.caldav.SupportedCalendarData.Companion.CONTENT_TYPE] + * attribute with [onNewType]. + * + * @param tagName The name of the tag that contains the [at.bitfire.dav4jvm.ktor.property.caldav.SupportedCalendarData.Companion.CONTENT_TYPE] attribute value. + * @param onNewType Called every time a new [ContentType] is found. + */ + fun readContentTypes(tagName: Property.Name, onNewType: (ContentType) -> Unit) { + try { + processTag(tagName) { + parser.getAttributeValue(null, SupportedCalendarData.Companion.CONTENT_TYPE)?.let { contentType -> + var type = contentType + parser.getAttributeValue(null, SupportedCalendarData.Companion.VERSION)?.let { version -> type += "; version=$version" } + ContentType.parse(type).let(onNewType) + } + } + } catch(e: XmlPullParserException) { + val logger = Logger.getLogger(javaClass.name) + logger.log(Level.SEVERE, "Couldn't parse content types", e) + } catch (e: BadContentTypeFormatException) { + val logger = Logger.getLogger(javaClass.name) + logger.log(Level.SEVERE, "Couldn't parse content types", e) + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/XmlUtils.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/XmlUtils.kt new file mode 100644 index 00000000..63ace56f --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/XmlUtils.kt @@ -0,0 +1,76 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.XmlUtils.FEATURE_RELAXED +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import org.xmlpull.v1.XmlPullParserFactory +import org.xmlpull.v1.XmlSerializer + +object XmlUtils { + + /** + * Requests the parser to be as lenient as possible when parsing invalid XML. + * + * See [https://www.xmlpull.org/](xmlpull.org) and specific implementations, for instance + * [Android XML](https://developer.android.com/reference/android/util/Xml#FEATURE_RELAXED) + */ + private const val FEATURE_RELAXED = "http://xmlpull.org/v1/doc/features.html#relaxed" + + /** [XmlPullParserFactory] that is namespace-aware and does relaxed parsing */ + private val relaxedFactory = + XmlPullParserFactory.newInstance().apply { + isNamespaceAware = true + setFeature(FEATURE_RELAXED, true) + } + + /** [XmlPullParserFactory] that is namespace-aware */ + private val standardFactory: XmlPullParserFactory = + XmlPullParserFactory.newInstance().apply { + isNamespaceAware = true + } + + /** + * Creates a new [XmlPullParser]. + * + * First tries to create a namespace-aware parser that supports [FEATURE_RELAXED]. If that + * fails, it falls back to a namespace-aware parser without relaxed parsing. + * + * @throws XmlPullParserException when no parser could be created + */ + fun newPullParser(): XmlPullParser = + try { + relaxedFactory.newPullParser() + } catch (_: XmlPullParserException) { + // FEATURE_RELAXED may not be supported, try without it + null + } + ?: standardFactory.newPullParser() + + fun newSerializer(): XmlSerializer = standardFactory.newSerializer() + + + fun XmlSerializer.insertTag(name: Property.Name, contentGenerator: XmlSerializer.() -> Unit = {}) { + startTag(name.namespace, name.name) + contentGenerator(this) + endTag(name.namespace, name.name) + } + + fun XmlPullParser.propertyName(): Property.Name { + val propNs = namespace + val propName = name + if (propNs == null || propName == null) + throw IllegalStateException("Current event must be START_TAG or END_TAG") + return Property.Name(propNs, propName) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ConflictException.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ConflictException.kt new file mode 100644 index 00000000..c635aab6 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ConflictException.kt @@ -0,0 +1,22 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.exception + +import io.ktor.client.statement.HttpResponse +import io.ktor.http.HttpStatusCode + +class ConflictException: HttpException { + + constructor(response: HttpResponse) : super(response) { + if (response.status.value != HttpStatusCode.Conflict.value) + throw IllegalArgumentException("Status code must be 409") + } +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/DavException.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/DavException.kt new file mode 100644 index 00000000..cde80a83 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/DavException.kt @@ -0,0 +1,84 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.exception + +import at.bitfire.dav4jvm.ktor.Error +import io.ktor.client.statement.HttpResponse +import javax.annotation.WillNotClose + +/** + * Signals that an error occurred during a WebDAV-related operation. + * + * This could be a logical error like when a required ETag has not been received, but also an explicit HTTP error + * (usually with a subclass of [HttpException], which in turn extends this class). + * + * Often, HTTP response bodies contain valuable information about the error in text format (for instance, a HTML page + * that contains details about the error) and/or as `` XML elements. However, such response bodies + * are sometimes very large. + * + * So, if possible and useful, a size-limited excerpt of the associated HTTP request and response can be + * attached and subsequently included in application-level debug info or shown to the user. + * + * Note: [Exception] is serializable, so objects of this class must contain only serializable objects. + * + * @param statusCode status code of associated HTTP response + * @param requestExcerpt cached excerpt of associated HTTP request body + * @param responseExcerpt cached excerpt of associated HTTP response body + * @param errors precondition/postcondition XML elements which have been found in the XML response + */ +open class DavException( + message: String? = null, + cause: Throwable? = null, + open val statusCode: Int? = null, + val requestExcerpt: String? = null, + val responseExcerpt: String? = null, + val errors: List = emptyList() +): Exception(message, cause) { + + // constructor from Response + + /** + * Takes the request, response and errors from a given HTTP response. + * + * @param message optional exception message + * @param cause optional exception cause + * @param response response to extract status code and request/response excerpt from (if possible) + */ + constructor( + message: String, + cause: Throwable? = null, + @WillNotClose response: HttpResponse + ) : this(message, cause, HttpResponseInfo.fromResponse(response)) + + private constructor( + message: String?, + cause: Throwable? = null, + httpResponseInfo: HttpResponseInfo + ): this( + message = message, + cause = cause, + statusCode = httpResponseInfo.statusCode, + requestExcerpt = httpResponseInfo.requestExcerpt, + responseExcerpt = httpResponseInfo.responseExcerpt, + errors = httpResponseInfo.errors + ) + + + companion object { + + /** + * maximum size of extracted response body + */ + const val MAX_EXCERPT_SIZE = 20*1024 + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ForbiddenException.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ForbiddenException.kt new file mode 100644 index 00000000..1b3d7357 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ForbiddenException.kt @@ -0,0 +1,22 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.exception + +import io.ktor.client.statement.HttpResponse +import io.ktor.http.HttpStatusCode + +class ForbiddenException: HttpException { + + constructor(response: HttpResponse) : super(response) { + if (response.status.value != HttpStatusCode.Forbidden.value) + throw IllegalArgumentException("Status code must be 403") + } +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/GoneException.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/GoneException.kt new file mode 100644 index 00000000..4d0ed1f9 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/GoneException.kt @@ -0,0 +1,22 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.exception + +import io.ktor.client.statement.HttpResponse +import io.ktor.http.HttpStatusCode + +class GoneException: HttpException { + + constructor(response: HttpResponse) : super(response) { + if (response.status.value != HttpStatusCode.Gone.value) + throw IllegalArgumentException("Status code must be 410") + } +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpException.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpException.kt new file mode 100644 index 00000000..1b1bdc3c --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpException.kt @@ -0,0 +1,75 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.exception + +import at.bitfire.dav4jvm.ktor.Error +import at.bitfire.dav4jvm.ktor.exception.DavException +import at.bitfire.dav4jvm.ktor.exception.HttpResponseInfo +import io.ktor.client.statement.HttpResponse +import javax.annotation.WillNotClose + + +/** + * Signals that a HTTP error was sent by the server in the context of a WebDAV operation. + */ +open class HttpException( + message: String? = null, + cause: Throwable? = null, + override val statusCode: Int, + requestExcerpt: String?, + responseExcerpt: String?, + errors: List = emptyList() +): DavException(message, cause, statusCode, requestExcerpt, responseExcerpt, errors) { + + // constructor from Response + + /** + * Takes the request, response and errors from a given HTTP response. + * + * @param response response to extract status code and request/response excerpt from (if possible) + * @param message optional exception message + * @param cause optional exception cause + */ + constructor( + @WillNotClose response: HttpResponse, + message: String = "HTTP ${response.status.value} ${response.status.description}", + cause: Throwable? = null + ) : this(HttpResponseInfo.fromResponse(response), message, cause) + + private constructor( + httpResponseInfo: HttpResponseInfo, + message: String?, + cause: Throwable? = null + ): this( + message = message, + cause = cause, + statusCode = httpResponseInfo.statusCode, + requestExcerpt = httpResponseInfo.requestExcerpt, + responseExcerpt = httpResponseInfo.responseExcerpt, + errors = httpResponseInfo.errors + ) + + + // status code classes + + /** Whether the [statusCode] is 3xx and thus indicates a redirection. */ + val isRedirect + get() = statusCode / 100 == 3 + + /** Whether the [statusCode] is 4xx and thus indicates a client error. */ + val isClientError + get() = statusCode / 100 == 4 + + /** Whether the [statusCode] is 5xx and thus indicates a server error. */ + val isServerError + get() = statusCode / 100 == 5 + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfo.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfo.kt new file mode 100644 index 00000000..2063b8ff --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfo.kt @@ -0,0 +1,146 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.exception + +import at.bitfire.dav4jvm.ktor.Error +import at.bitfire.dav4jvm.ktor.XmlUtils +import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.ktor.exception.DavException.Companion.MAX_EXCERPT_SIZE +import io.ktor.client.statement.HttpResponse +import io.ktor.client.statement.bodyAsChannel +import io.ktor.client.statement.request +import io.ktor.content.ByteArrayContent +import io.ktor.content.TextContent +import io.ktor.http.ContentType +import io.ktor.http.charset +import io.ktor.http.contentType +import io.ktor.utils.io.readBuffer +import kotlinx.coroutines.runBlocking +import kotlinx.io.Buffer +import kotlinx.io.EOFException +import kotlinx.io.readByteArray +import kotlinx.io.readString +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.ByteArrayOutputStream +import java.io.StringReader +import javax.annotation.WillNotClose +import kotlin.math.min + +internal class HttpResponseInfo private constructor( + val statusCode: Int, + val requestExcerpt: String?, + val responseExcerpt: String?, + val errors: List +) { + + companion object { + + fun fromResponse(@WillNotClose response: HttpResponse): HttpResponseInfo { + + // extract request body if it's text + val request = response.request + val requestExcerptBuilder = StringBuilder( + "${request.method} ${request.url}" + ) + request.content.let { requestBody -> + requestBody.contentType?.let { contentType -> + if (contentType.isText()) { + val requestBodyBytes = when (requestBody) { + is TextContent -> requestBody.text.toByteArray(contentType.charset() ?: Charsets.UTF_8) + is ByteArrayContent -> requestBody.bytes() + else -> requestBody.toString().toByteArray(contentType.charset() ?: Charsets.UTF_8) // Fallback, may not be ideal + } + val buffer = Buffer() + buffer.write(requestBodyBytes) + + val bytesToRead = min(buffer.size, MAX_EXCERPT_SIZE.toLong()).toInt() + val excerptBytes = buffer.readByteArray(bytesToRead) + val baos = ByteArrayOutputStream() + baos.write(excerptBytes) + + requestExcerptBuilder + .append("\n\n") + .append(baos.toString()) + } else + requestExcerptBuilder.append("\n\n") + } + } + //requestExcerpt = requestExcerptBuilder.toString() + + // extract response body if it's text + val responseBody = response.contentType()?.let { mimeType -> + if (mimeType.isText()) + try { + runBlocking { + response + .bodyAsChannel() + .readBuffer(MAX_EXCERPT_SIZE) + .readString(mimeType.charset() ?: Charsets.UTF_8) + } + } catch (_: Exception) { + // response body not available anymore, probably already consumed / closed + null + } + else + null + } + + // get XML errors from request body excerpt + val errors: List = if (response.contentType()?.isXml() == true && responseBody != null) + extractErrors(responseBody) + else + emptyList() + + return HttpResponseInfo( + statusCode = response.status.value, + requestExcerpt = requestExcerptBuilder.toString(), + responseExcerpt = responseBody, + errors = errors + ) + } + + private fun extractErrors(xml: String): List { + try { + val parser = XmlUtils.newPullParser() + parser.setInput(StringReader(xml)) + + var eventType = parser.eventType + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG && parser.depth == 1) + if (parser.propertyName() == at.bitfire.dav4jvm.ktor.Error.NAME) + return at.bitfire.dav4jvm.ktor.Error.parseError(parser) + eventType = parser.next() + } + } catch (_: XmlPullParserException) { + // Couldn't parse XML, either invalid or maybe it wasn't even XML + } catch (_: EOFException) { + // Couldn't parse XML, either invalid or maybe it wasn't even XML + } + + return emptyList() + } + + + // extensions + + fun ContentType.isText() = + this.match(ContentType.Text.Any) + || this.match(ContentType.Application.Xml) + || (this.contentType == ContentType.Application.TYPE && this.contentSubtype == ContentType.Text.Html.contentSubtype) + + fun ContentType.isXml() = + this.match(ContentType.Text.Xml) + || this.match(ContentType.Application.Xml) + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/InvalidPropertyException.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/InvalidPropertyException.kt new file mode 100644 index 00000000..23490582 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/InvalidPropertyException.kt @@ -0,0 +1,18 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.exception + +/** + * Represents an invalid XML (WebDAV) property. This is for instance thrown + * when parsing something like `...` + * because a text value would be expected. + */ +class InvalidPropertyException(message: String): DavException(message) \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/NotFoundException.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/NotFoundException.kt new file mode 100644 index 00000000..8a038915 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/NotFoundException.kt @@ -0,0 +1,22 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.exception + +import io.ktor.client.statement.HttpResponse +import io.ktor.http.HttpStatusCode + +class NotFoundException: HttpException { + + constructor(response: HttpResponse) : super(response) { + if (response.status.value != HttpStatusCode.NotFound.value) + throw IllegalArgumentException("Status code must be 404") + } +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/PreconditionFailedException.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/PreconditionFailedException.kt new file mode 100644 index 00000000..c17f22df --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/PreconditionFailedException.kt @@ -0,0 +1,22 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.exception + +import io.ktor.client.statement.HttpResponse +import io.ktor.http.HttpStatusCode + +class PreconditionFailedException: HttpException { + + constructor(response: HttpResponse) : super(response) { + if (response.status.value != HttpStatusCode.PreconditionFailed.value) + throw IllegalArgumentException("Status code must be 412") + } +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableException.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableException.kt new file mode 100644 index 00000000..e48e2b70 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableException.kt @@ -0,0 +1,86 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.exception + +import at.bitfire.dav4jvm.ktor.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_DEFAULT +import at.bitfire.dav4jvm.ktor.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_MAX +import at.bitfire.dav4jvm.ktor.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_MIN +import io.ktor.client.statement.HttpResponse +import io.ktor.http.HttpStatusCode +import java.time.Instant +import java.util.logging.Level +import java.util.logging.Logger + + +class ServiceUnavailableException(response: HttpResponse) : HttpException(response) { + + private val logger + get() = Logger.getLogger(javaClass.name) + + val retryAfter: Instant? + + init { + if (response.status.value != HttpStatusCode.ServiceUnavailable.value) + throw IllegalArgumentException("Status code must be 503") + + // Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds ) + // HTTP-date = rfc1123-date | rfc850-date | asctime-date + + var retryAfterValue: Instant? = null + response.headers["Retry-After"]?.let { after -> + retryAfterValue = at.bitfire.dav4jvm.okhttp.HttpUtils.parseDate(after) ?: + // not a HTTP-date, must be delta-seconds + try { + val seconds = after.toLong() + Instant.now().plusSeconds(seconds) + } catch (e: NumberFormatException) { + logger.log(Level.WARNING, "Received Retry-After which was not a HTTP-date nor delta-seconds: $after", e) + null + } + } + + retryAfter = retryAfterValue + } + + + /** + * Returns appropriate sync retry delay in seconds, considering the servers suggestion + * in [retryAfter] ([DELAY_UNTIL_DEFAULT] if no server suggestion). + * + * Takes current time into account to calculate intervals. Interval + * will be restricted to values between [DELAY_UNTIL_MIN] and [DELAY_UNTIL_MAX]. + * + * @param start timestamp to calculate the delay from (default: now) + * + * @return until when to wait before sync can be retried + */ + fun getDelayUntil(start: Instant = Instant.now()): Instant { + if (retryAfter == null) + return start.plusSeconds(DELAY_UNTIL_DEFAULT) + + // take server suggestion, but restrict to plausible min/max values + return retryAfter.coerceIn( + minimumValue = start.plusSeconds(DELAY_UNTIL_MIN), + maximumValue = start.plusSeconds(DELAY_UNTIL_MAX) + ) + } + + + companion object { + + // default values for getDelayUntil + const val DELAY_UNTIL_DEFAULT = 15 * 60L // 15 min + const val DELAY_UNTIL_MIN = 1 * 60L // 1 min + const val DELAY_UNTIL_MAX = 2 * 60 * 60L // 2 hours + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/UnauthorizedException.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/UnauthorizedException.kt new file mode 100644 index 00000000..f76490e3 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/UnauthorizedException.kt @@ -0,0 +1,22 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.exception + +import io.ktor.client.statement.HttpResponse +import io.ktor.http.HttpStatusCode + +class UnauthorizedException: HttpException { + + constructor(response: HttpResponse) : super(response) { + if (response.status.value != HttpStatusCode.Unauthorized.value) + throw IllegalArgumentException("Status code must be 401") + } +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarColor.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarColor.kt new file mode 100644 index 00000000..616468be --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarColor.kt @@ -0,0 +1,70 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser +import java.util.logging.Level +import java.util.logging.Logger +import java.util.regex.Pattern + +data class CalendarColor( + val color: Int? +): Property { + + companion object { + @JvmField + val NAME = Property.Name(NS_APPLE_ICAL, "calendar-color") + + private val PATTERN = Pattern.compile("#?(\\p{XDigit}{6})(\\p{XDigit}{2})?")!! + + /** + * Converts a WebDAV color from one of these formats: + * #RRGGBB (alpha = 0xFF) + * RRGGBB (alpha = 0xFF) + * #RRGGBBAA + * RRGGBBAA + * to an [Int] with alpha. + */ + @Throws(IllegalArgumentException::class) + fun parseARGBColor(davColor: String): Int { + val m = PATTERN.matcher(davColor) + if (m.find()) { + val color_rgb = Integer.parseInt(m.group(1), 16) + val color_alpha = m.group(2)?.let { Integer.parseInt(m.group(2), 16) and 0xFF } ?: 0xFF + return (color_alpha shl 24) or color_rgb + } else + throw IllegalArgumentException("Couldn't parse color value: $davColor") + } + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): CalendarColor { + XmlReader(parser).readText()?.let { + try { + return CalendarColor(parseARGBColor(it)) + } catch (e: IllegalArgumentException) { + val logger = Logger.getLogger(javaClass.name) + logger.log(Level.WARNING, "Couldn't parse color, ignoring", e) + } + } + return CalendarColor(null) + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarData.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarData.kt new file mode 100644 index 00000000..e3170a15 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarData.kt @@ -0,0 +1,42 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class CalendarData( + val iCalendar: String? +): Property { + + companion object { + @JvmField + val NAME = Property.Name(NS_CALDAV, "calendar-data") + + // attributes + const val CONTENT_TYPE = "content-type" + const val VERSION = "version" + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = + // + CalendarData(XmlReader(parser).readText()) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarDescription.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarDescription.kt new file mode 100644 index 00000000..b77c4118 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarDescription.kt @@ -0,0 +1,38 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class CalendarDescription( + val description: String? +): Property { + + companion object { + @JvmField + val NAME = Property.Name(NS_CALDAV, "calendar-description") + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = + // + CalendarDescription(XmlReader(parser).readText()) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarHomeSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarHomeSet.kt new file mode 100644 index 00000000..5c3de6ed --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarHomeSet.kt @@ -0,0 +1,37 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import org.xmlpull.v1.XmlPullParser + +data class CalendarHomeSet( + override val hrefs: List = emptyList() +): HrefListProperty(hrefs) { + + companion object { + + @JvmField + val NAME = Property.Name(NS_CALDAV, "calendar-home-set") + + } + + + object Factory: HrefListProperty.Factory() { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = create(parser, ::CalendarHomeSet) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarProxyReadFor.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarProxyReadFor.kt new file mode 100644 index 00000000..e36df6ed --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarProxyReadFor.kt @@ -0,0 +1,35 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import org.xmlpull.v1.XmlPullParser + +data class CalendarProxyReadFor( + override val hrefs: List = emptyList() +): HrefListProperty(hrefs) { + + companion object { + @JvmField + val NAME = Property.Name(NS_CALENDARSERVER, "calendar-proxy-read-for") + } + + + object Factory: HrefListProperty.Factory() { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = create(parser, ::CalendarProxyReadFor) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarProxyWriteFor.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarProxyWriteFor.kt new file mode 100644 index 00000000..0f632ea9 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarProxyWriteFor.kt @@ -0,0 +1,35 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import org.xmlpull.v1.XmlPullParser + +data class CalendarProxyWriteFor( + override val hrefs: List = emptyList() +): HrefListProperty(hrefs) { + + companion object { + @JvmField + val NAME = Property.Name(NS_CALENDARSERVER, "calendar-proxy-write-for") + } + + + object Factory: HrefListProperty.Factory() { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = create(parser, ::CalendarProxyWriteFor) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarTimezone.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarTimezone.kt new file mode 100644 index 00000000..cea93b73 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarTimezone.kt @@ -0,0 +1,37 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class CalendarTimezone( + val vTimeZone: String? +): Property { + + companion object { + @JvmField + val NAME = Property.Name(NS_CALDAV, "calendar-timezone") + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = + // + CalendarTimezone(XmlReader(parser).readText()) + + } +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarTimezoneId.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarTimezoneId.kt new file mode 100644 index 00000000..f6a3240f --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarTimezoneId.kt @@ -0,0 +1,37 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class CalendarTimezoneId( + val identifier: String? +): Property { + + companion object { + @JvmField + val NAME = Property.Name(NS_CALDAV, "calendar-timezone-id") + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = + // + CalendarTimezoneId(XmlReader(parser).readText()) + + } +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarUserAddressSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarUserAddressSet.kt new file mode 100644 index 00000000..aec3284e --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarUserAddressSet.kt @@ -0,0 +1,35 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import org.xmlpull.v1.XmlPullParser + +data class CalendarUserAddressSet( + override val hrefs: List = emptyList() +): HrefListProperty(hrefs) { + + companion object { + @JvmField + val NAME = Property.Name(NS_CALDAV, "calendar-user-address-set") + } + + + object Factory: HrefListProperty.Factory() { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = create(parser, ::CalendarUserAddressSet) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/GetCTag.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/GetCTag.kt new file mode 100644 index 00000000..b99a0a23 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/GetCTag.kt @@ -0,0 +1,35 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class GetCTag( + val cTag: String? +): Property { + + companion object { + @JvmField + val NAME = Property.Name(NS_CALENDARSERVER, "getctag") + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = GetCTag(XmlReader(parser).readText()) + + } +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/MaxResourceSize.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/MaxResourceSize.kt new file mode 100644 index 00000000..1bfd997b --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/MaxResourceSize.kt @@ -0,0 +1,32 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class MaxResourceSize( + val maxSize: Long? +) : Property { + companion object { + @JvmField + val NAME = Property.Name(NS_CALDAV, "max-resource-size") + } + + object Factory: PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = + MaxResourceSize(XmlReader(parser).readLong()) + } +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/ScheduleTag.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/ScheduleTag.kt new file mode 100644 index 00000000..383b2cbc --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/ScheduleTag.kt @@ -0,0 +1,51 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.QuotedStringUtils +import at.bitfire.dav4jvm.ktor.XmlReader +import io.ktor.client.statement.HttpResponse +import io.ktor.http.HttpHeaders +import org.xmlpull.v1.XmlPullParser + +data class ScheduleTag( + val rawScheduleTag: String? +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_CALDAV, "schedule-tag") + + fun fromResponse(response: HttpResponse) = + response.headers[HttpHeaders.ScheduleTag]?.let { ScheduleTag(it) } + + } + + /* Value: opaque-tag + opaque-tag = quoted-string + */ + val scheduleTag: String? = rawScheduleTag?.let { QuotedStringUtils.decodeQuotedString(it) } + + override fun toString() = scheduleTag ?: "(null)" + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = ScheduleTag(XmlReader(parser).readText()) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/Source.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/Source.kt new file mode 100644 index 00000000..2dd548ac --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/Source.kt @@ -0,0 +1,37 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import org.xmlpull.v1.XmlPullParser + +class Source( + override val hrefs: List = emptyList() +): HrefListProperty(hrefs) { + + companion object { + + @JvmField + val NAME = Property.Name(NS_CALENDARSERVER, "source") + + } + + + object Factory: HrefListProperty.Factory() { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = create(parser, ::Source) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/SupportedCalendarComponentSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/SupportedCalendarComponentSet.kt new file mode 100644 index 00000000..8ba3e811 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/SupportedCalendarComponentSet.kt @@ -0,0 +1,76 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import org.xmlpull.v1.XmlPullParser + +data class SupportedCalendarComponentSet( + val supportsEvents: Boolean, + val supportsTasks: Boolean, + val supportsJournal: Boolean +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_CALDAV, "supported-calendar-component-set") + + val ALLCOMP = Property.Name(NS_CALDAV, "allcomp") + val COMP = Property.Name(NS_CALDAV, "comp") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): SupportedCalendarComponentSet { + /* + + + */ + var components = SupportedCalendarComponentSet( + supportsEvents = false, + supportsTasks = false, + supportsJournal = false + ) + + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { + when (parser.propertyName()) { + ALLCOMP -> { + components = SupportedCalendarComponentSet( + supportsEvents = true, + supportsTasks = true, + supportsJournal = true + ) + } + COMP -> + when (parser.getAttributeValue(null, "name")?.uppercase()) { + "VEVENT" -> components = components.copy(supportsEvents = true) + "VTODO" -> components = components.copy(supportsTasks = true) + "VJOURNAL" -> components = components.copy(supportsJournal = true) + } + } + } + eventType = parser.next() + } + + return components + } + } +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/SupportedCalendarData.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/SupportedCalendarData.kt new file mode 100644 index 00000000..56f17de6 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/SupportedCalendarData.kt @@ -0,0 +1,51 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import io.ktor.http.ContentType +import org.xmlpull.v1.XmlPullParser + +data class SupportedCalendarData( + val types: Set = emptySet() +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_CALDAV, "supported-calendar-data") + + val CALENDAR_DATA_TYPE = Property.Name(NS_CALDAV, "calendar-data") + const val CONTENT_TYPE = "content-type" + const val VERSION = "version" + + } + + fun hasJCal() = types.any { ContentType.Application.contains(it) && "calendar+json".equals(it.contentSubtype, true) } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): SupportedCalendarData { + val supportedTypes = mutableSetOf() + + XmlReader(parser).readContentTypes(CALENDAR_DATA_TYPE, supportedTypes::add) + + return SupportedCalendarData(supportedTypes) + } + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/namespace.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/namespace.kt new file mode 100644 index 00000000..369c35af --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/namespace.kt @@ -0,0 +1,19 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.caldav + +/** + * CalDAV XML namespace (defined in RFC 4791) + */ +const val NS_CALDAV = "urn:ietf:params:xml:ns:caldav" + +const val NS_APPLE_ICAL = "http://apple.com/ns/ical/" +const val NS_CALENDARSERVER = "http://calendarserver.org/ns/" diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressData.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressData.kt new file mode 100644 index 00000000..d3889b00 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressData.kt @@ -0,0 +1,44 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.carddav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class AddressData( + val card: String? +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_CARDDAV, "address-data") + + // attributes + const val CONTENT_TYPE = "content-type" + const val VERSION = "version" + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = + // + AddressData(XmlReader(parser).readText()) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressbookDescription.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressbookDescription.kt new file mode 100644 index 00000000..0a21bbf6 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressbookDescription.kt @@ -0,0 +1,37 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.carddav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class AddressbookDescription( + val description: String? = null +): Property { + + companion object { + @JvmField + val NAME = Property.Name(NS_CARDDAV, "addressbook-description") + } + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = + // + AddressbookDescription(XmlReader(parser).readText()) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressbookHomeSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressbookHomeSet.kt new file mode 100644 index 00000000..aa4903b4 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressbookHomeSet.kt @@ -0,0 +1,37 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.carddav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import org.xmlpull.v1.XmlPullParser + +class AddressbookHomeSet( + override val hrefs: List = emptyList() +): HrefListProperty(hrefs) { + + companion object { + + @JvmField + val NAME = Property.Name(NS_CARDDAV, "addressbook-home-set") + + } + + + object Factory: HrefListProperty.Factory() { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = create(parser, ::AddressbookHomeSet) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/MaxResourceSize.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/MaxResourceSize.kt new file mode 100644 index 00000000..e6dab87f --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/MaxResourceSize.kt @@ -0,0 +1,38 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.carddav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class MaxResourceSize( + val maxSize: Long? +) : Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_CARDDAV, "max-resource-size") + + } + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = + MaxResourceSize(XmlReader(parser).readLong()) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/SupportedAddressData.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/SupportedAddressData.kt new file mode 100644 index 00000000..1a4d6e0e --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/SupportedAddressData.kt @@ -0,0 +1,54 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.carddav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import io.ktor.http.ContentType +import org.xmlpull.v1.XmlPullParser + +class SupportedAddressData( + val types: Set = emptySet() +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_CARDDAV, "supported-address-data") + + val ADDRESS_DATA_TYPE = Property.Name(NS_CARDDAV, "address-data-type") + const val CONTENT_TYPE = "content-type" + const val VERSION = "version" + + } + + fun hasVCard4() = types.any { "text/vcard; version=4.0".equals(it.toString(), true) } + fun hasJCard() = types.any { ContentType.Application.contains(it) && "vcard+json".equals(it.contentSubtype, true) } + + override fun toString() = "[${types.joinToString(", ")}]" + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): SupportedAddressData { + val supportedTypes = mutableSetOf() + + XmlReader(parser).readContentTypes(ADDRESS_DATA_TYPE, supportedTypes::add) + + return SupportedAddressData(supportedTypes) + } + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/namespace.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/namespace.kt new file mode 100644 index 00000000..8328343c --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/namespace.kt @@ -0,0 +1,16 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.carddav + +/** + * CardDAV XML namespace (defined in RFC 6352) + */ +const val NS_CARDDAV = "urn:ietf:params:xml:ns:carddav" diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/common/HrefListProperty.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/common/HrefListProperty.kt new file mode 100644 index 00000000..ff5f194f --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/common/HrefListProperty.kt @@ -0,0 +1,48 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.common + +import at.bitfire.dav4jvm.ktor.DavResource +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a list of hrefs. + * + * Every [HrefListProperty] must be a data class. + */ +abstract class HrefListProperty( + open val hrefs: List +): Property { + + abstract class Factory : PropertyFactory { + + @Deprecated("hrefs is no longer mutable.", level = DeprecationLevel.ERROR) + fun create(parser: XmlPullParser, list: HrefListProperty): HrefListProperty { + val hrefs = list.hrefs.toMutableList() + XmlReader(parser).readTextPropertyList(DavResource.Companion.HREF, hrefs) + return list + } + + fun create( + parser: XmlPullParser, + constructor: (hrefs: List + ) -> PropertyType): PropertyType { + val hrefs = mutableListOf() + XmlReader(parser).readTextPropertyList(DavResource.Companion.HREF, hrefs) + return constructor(hrefs) + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/AuthSecret.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/AuthSecret.kt new file mode 100644 index 00000000..09496893 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/AuthSecret.kt @@ -0,0 +1,44 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:auth-secret` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class AuthSecret( + val secret: String? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "auth-secret") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): AuthSecret = + AuthSecret(XmlReader(parser).readText()) + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/ContentUpdate.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/ContentUpdate.kt new file mode 100644 index 00000000..d92aa70d --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/ContentUpdate.kt @@ -0,0 +1,67 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.ktor.property.webdav.Depth +import at.bitfire.dav4jvm.ktor.property.webdav.SyncLevel +import at.bitfire.dav4jvm.ktor.property.webdav.SyncToken +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:content-update` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class ContentUpdate( + val depth: Depth? = null, + val syncToken: SyncToken? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "content-update") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): ContentUpdate { + var contentUpdate = ContentUpdate() + + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { + when (parser.propertyName()) { + SyncLevel.NAME -> contentUpdate = contentUpdate.copy( + depth = Depth.Factory.create(parser) + ) + SyncToken.NAME -> contentUpdate = contentUpdate.copy( + syncToken = SyncToken.Factory.create(parser) + ) + } + } + eventType = parser.next() + } + + return contentUpdate + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PropertyUpdate.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PropertyUpdate.kt new file mode 100644 index 00000000..9a966405 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PropertyUpdate.kt @@ -0,0 +1,60 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.ktor.property.webdav.SyncLevel +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:property-update` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class PropertyUpdate( + val syncLevel: SyncLevel? = null, +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "property-update") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): PropertyUpdate { + var propertyUpdate = PropertyUpdate() + + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { + when (parser.propertyName()) { + SyncLevel.NAME -> propertyUpdate = propertyUpdate.copy( + syncLevel = SyncLevel.Factory.create(parser) + ) + } + } + eventType = parser.next() + } + return propertyUpdate + } + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushMessage.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushMessage.kt new file mode 100644 index 00000000..c1784edc --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushMessage.kt @@ -0,0 +1,68 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:push-message` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class PushMessage( + val topic: Topic? = null, + val contentUpdate: ContentUpdate? = null, + val propertyUpdate: PropertyUpdate? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "push-message") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): PushMessage { + var message = PushMessage() + + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { + when (parser.propertyName()) { + Topic.NAME -> message = message.copy( + topic = Topic.Factory.create(parser) + ) + ContentUpdate.NAME -> message = message.copy( + contentUpdate = ContentUpdate.Factory.create(parser) + ) + PropertyUpdate.NAME -> message = message.copy( + propertyUpdate = PropertyUpdate.Factory.create(parser) + ) + } + } + eventType = parser.next() + } + + return message + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushRegister.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushRegister.kt new file mode 100644 index 00000000..ce63c48b --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushRegister.kt @@ -0,0 +1,77 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.HttpUtils +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import org.xmlpull.v1.XmlPullParser +import java.time.Instant + +/** + * Represents a [NS_WEBDAV_PUSH]`:push-register` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class PushRegister( + val expires: Instant? = null, + val subscription: Subscription? = null, + val trigger: Trigger? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "push-register") + + val EXPIRES = Property.Name(NS_WEBDAV_PUSH, "expires") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): PushRegister { + var register = PushRegister() + + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) + when (parser.propertyName()) { + EXPIRES -> + register = register.copy( + expires = XmlReader(parser).readText()?.let { + HttpUtils.parseDate(it) + } + ) + Subscription.NAME -> + register = register.copy( + subscription = Subscription.Factory.create(parser) + ) + Trigger.NAME -> + register = register.copy( + trigger = Trigger.Factory.create(parser) + ) + } + eventType = parser.next() + } + + return register + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushResource.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushResource.kt new file mode 100644 index 00000000..f701dafc --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushResource.kt @@ -0,0 +1,54 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser +import java.net.URI +import java.net.URISyntaxException + +/** + * Represents a [NS_WEBDAV_PUSH]`:push-resource` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class PushResource( + val uri: URI? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "push-resource") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): PushResource = + PushResource( + uri = XmlReader(parser).readText()?.let { uri -> + try { + URI(uri) + } catch (_: URISyntaxException) { + null + } + } + ) + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushTransport.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushTransport.kt new file mode 100644 index 00000000..5d79fbee --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushTransport.kt @@ -0,0 +1,18 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.Property + +/** + * Identifies a property as a push transport. + */ +interface PushTransport: Property diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushTransports.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushTransports.kt new file mode 100644 index 00000000..de842a6c --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushTransports.kt @@ -0,0 +1,56 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:push-transports` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +class PushTransports private constructor( + val transports: Set +): Property { + + companion object { + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "transports") + } + + fun hasWebPush() = transports.any { it is WebPush } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): PushTransports { + val transports = mutableListOf() + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { + when (parser.propertyName()) { + WebPush.NAME -> transports += WebPush.Factory.create(parser) + } + } + eventType = parser.next() + } + return PushTransports(transports.toSet()) + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Subscription.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Subscription.kt new file mode 100644 index 00000000..5390a91a --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Subscription.kt @@ -0,0 +1,52 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:subscription` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class Subscription private constructor( + val webPushSubscription: WebPushSubscription? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "subscription") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Subscription { + // currently we only support WebPushSubscription + var webPushSubscription: WebPushSubscription? = null + + XmlReader(parser).processTag(WebPushSubscription.NAME) { + webPushSubscription = WebPushSubscription.Factory.create(parser) + } + + return Subscription(webPushSubscription) + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/SubscriptionPublicKey.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/SubscriptionPublicKey.kt new file mode 100644 index 00000000..46dce801 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/SubscriptionPublicKey.kt @@ -0,0 +1,49 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:subscription-public-key` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class SubscriptionPublicKey( + val type: String? = null, + val key: String? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "subscription-public-key") + + } + + + object Factory : PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): SubscriptionPublicKey { + return SubscriptionPublicKey( + type = parser.getAttributeValue(null, "type"), + key = XmlReader(parser).readText() + ) + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/SupportedTriggers.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/SupportedTriggers.kt new file mode 100644 index 00000000..bf88aa7d --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/SupportedTriggers.kt @@ -0,0 +1,64 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:content-update` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class SupportedTriggers( + val contentUpdate: ContentUpdate? = null, + val propertyUpdate: PropertyUpdate? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "supported-triggers") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): SupportedTriggers { + var supportedTriggers = SupportedTriggers() + + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { + when (parser.propertyName()) { + ContentUpdate.NAME -> supportedTriggers = supportedTriggers.copy( + contentUpdate = ContentUpdate.Factory.create(parser) + ) + PropertyUpdate.NAME -> supportedTriggers = supportedTriggers.copy( + propertyUpdate = PropertyUpdate.Factory.create(parser) + ) + } + } + eventType = parser.next() + } + + return supportedTriggers + } + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Topic.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Topic.kt new file mode 100644 index 00000000..4e568ad7 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Topic.kt @@ -0,0 +1,44 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:topic` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class Topic( + val topic: String? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "topic") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Topic = + Topic(XmlReader(parser).readText()) + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Trigger.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Trigger.kt new file mode 100644 index 00000000..77330fad --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Trigger.kt @@ -0,0 +1,59 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import org.xmlpull.v1.XmlPullParser + +data class Trigger( + val contentUpdate: ContentUpdate? = null, + val propertyUpdate: PropertyUpdate? = null +) : Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "trigger") + + } + + + object Factory : PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Trigger { + var trigger = Trigger() + + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { + when (parser.propertyName()) { + ContentUpdate.NAME -> trigger = trigger.copy( + contentUpdate = ContentUpdate.Factory.create(parser) + ) + PropertyUpdate.NAME -> trigger = trigger.copy( + propertyUpdate = PropertyUpdate.Factory.create(parser) + ) + } + } + eventType = parser.next() + } + + return trigger + } + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/VapidPublicKey.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/VapidPublicKey.kt new file mode 100644 index 00000000..abacb97b --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/VapidPublicKey.kt @@ -0,0 +1,49 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:vapid-public-key` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class VapidPublicKey( + val type: String? = null, + val key: String? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "vapid-public-key") + + } + + + object Factory : PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): VapidPublicKey { + return VapidPublicKey( + type = parser.getAttributeValue(null, "type"), + key = XmlReader(parser).readText() + ) + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPush.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPush.kt new file mode 100644 index 00000000..c9ee88d1 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPush.kt @@ -0,0 +1,53 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:web-push` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class WebPush( + val vapidPublicKey: VapidPublicKey? = null +) : PushTransport { + + companion object { + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "web-push") + } + + + object Factory : PropertyFactory { + + override fun getName(): Property.Name = NAME + + override fun create(parser: XmlPullParser): WebPush { + var vapidPublicKey: VapidPublicKey? = null + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.namespace == NS_WEBDAV_PUSH) { + when (parser.name) { + VapidPublicKey.NAME.name -> vapidPublicKey = VapidPublicKey.Factory.create(parser) + } + } + eventType = parser.next() + } + return WebPush(vapidPublicKey) + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPushSubscription.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPushSubscription.kt new file mode 100644 index 00000000..b7e17538 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPushSubscription.kt @@ -0,0 +1,62 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:web-push-subscription` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class WebPushSubscription( + val pushResource: PushResource? = null, + val subscriptionPublicKey: SubscriptionPublicKey? = null, + val authSecret: AuthSecret? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "web-push-subscription") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): WebPushSubscription { + var subscription = WebPushSubscription() + + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { + when (parser.propertyName()) { + PushResource.NAME -> subscription = subscription.copy(pushResource = PushResource.Factory.create(parser)) + SubscriptionPublicKey.NAME -> subscription = subscription.copy(subscriptionPublicKey = SubscriptionPublicKey.Factory.create(parser)) + AuthSecret.NAME -> subscription = subscription.copy(authSecret = AuthSecret.Factory.create(parser)) + } + } + eventType = parser.next() + } + + return subscription + } + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/namespace.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/namespace.kt new file mode 100644 index 00000000..2fdbc283 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/namespace.kt @@ -0,0 +1,19 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +/** + * XML namespace of WebDAV-Push (draft), see: + * https://github.com/bitfireAT/webdav-push/ + * + * Experimental! + */ +const val NS_WEBDAV_PUSH = "https://bitfire.at/webdav-push" diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/AddMember.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/AddMember.kt new file mode 100644 index 00000000..a9e2b838 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/AddMember.kt @@ -0,0 +1,41 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.DavResource +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +/** + * Defined in RFC 5995 3.2.1 DAV:add-member Property (Protected). + */ +data class AddMember( + val href: String? +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "add-member") + + } + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = AddMember(XmlReader(parser).readTextProperty(DavResource.HREF)) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CreationDate.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CreationDate.kt new file mode 100644 index 00000000..42e6037b --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CreationDate.kt @@ -0,0 +1,38 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class CreationDate( + var creationDate: String? +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "creationdate") + + } + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = + CreationDate(XmlReader(parser).readText()) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CurrentUserPrincipal.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CurrentUserPrincipal.kt new file mode 100644 index 00000000..829ca3d1 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CurrentUserPrincipal.kt @@ -0,0 +1,48 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.DavResource +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +// see RFC 5397: WebDAV Current Principal Extension + +data class CurrentUserPrincipal( + val href: String? +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "current-user-principal") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): CurrentUserPrincipal { + // + var href: String? = null + XmlReader(parser).processTag(DavResource.HREF) { + href = readText() + } + return CurrentUserPrincipal(href) + } + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CurrentUserPrivilegeSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CurrentUserPrivilegeSet.kt new file mode 100644 index 00000000..7fd81271 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CurrentUserPrivilegeSet.kt @@ -0,0 +1,98 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import org.xmlpull.v1.XmlPullParser + +data class CurrentUserPrivilegeSet( + // not all privileges from RFC 3744 are implemented by now + // feel free to add more if you need them for your project + val mayRead: Boolean = false, + val mayWriteProperties: Boolean = false, + val mayWriteContent: Boolean = false, + val mayBind: Boolean = false, + val mayUnbind: Boolean = false +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "current-user-privilege-set") + + val PRIVILEGE = Property.Name(NS_WEBDAV, "privilege") + val READ = Property.Name(NS_WEBDAV, "read") + val WRITE = Property.Name(NS_WEBDAV, "write") + val WRITE_PROPERTIES = Property.Name(NS_WEBDAV, "write-properties") + val WRITE_CONTENT = Property.Name(NS_WEBDAV, "write-content") + val BIND = Property.Name(NS_WEBDAV, "bind") + val UNBIND = Property.Name(NS_WEBDAV, "unbind") + val ALL = Property.Name(NS_WEBDAV, "all") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): CurrentUserPrivilegeSet { + // + // + var privs = CurrentUserPrivilegeSet() + + XmlReader(parser).processTag(PRIVILEGE) { + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) + when (parser.propertyName()) { + READ -> + privs = privs.copy(mayRead = true) + WRITE -> { + privs = privs.copy( + mayBind = true, + mayUnbind = true, + mayWriteProperties = true, + mayWriteContent = true + ) + } + WRITE_PROPERTIES -> + privs = privs.copy(mayWriteProperties = true) + WRITE_CONTENT -> + privs = privs.copy(mayWriteContent = true) + BIND -> + privs = privs.copy(mayBind = true) + UNBIND -> + privs = privs.copy(mayUnbind = true) + ALL -> { + privs = privs.copy( + mayRead = true, + mayBind = true, + mayUnbind = true, + mayWriteProperties = true, + mayWriteContent = true + ) + } + } + eventType = parser.next() + } + } + + return privs + } + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/Depth.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/Depth.kt new file mode 100644 index 00000000..f476c7ff --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/Depth.kt @@ -0,0 +1,51 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV]`:depth` property. + */ +data class Depth( + /** May be `0`, `1` or [Int.MAX_VALUE] (infinite). */ + val depth: Int? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "depth") + + const val INFINITY = Int.MAX_VALUE + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Depth { + val text = XmlReader(parser).readText() + val level = if (text.equals("infinity", true)) + INFINITY + else + text?.toIntOrNull() + return Depth(level) + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/DisplayName.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/DisplayName.kt new file mode 100644 index 00000000..08fd88d2 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/DisplayName.kt @@ -0,0 +1,40 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class DisplayName( + val displayName: String? +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "displayname") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = + // + DisplayName(XmlReader(parser).readText()) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetContentLength.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetContentLength.kt new file mode 100644 index 00000000..be38df56 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetContentLength.kt @@ -0,0 +1,38 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class GetContentLength( + val contentLength: Long? +) : Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "getcontentlength") + + } + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = + GetContentLength(XmlReader(parser).readLong()) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetContentType.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetContentType.kt new file mode 100644 index 00000000..68cddb4c --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetContentType.kt @@ -0,0 +1,48 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import io.ktor.http.BadContentTypeFormatException +import io.ktor.http.ContentType +import org.xmlpull.v1.XmlPullParser + +data class GetContentType( + val type: ContentType? +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "getcontenttype") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = + // + GetContentType(XmlReader(parser).readText()?.let { + try { + ContentType.parse(it) + } catch (_: BadContentTypeFormatException) { + null + } + }) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetETag.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetETag.kt new file mode 100644 index 00000000..9e787877 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetETag.kt @@ -0,0 +1,93 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.QuotedStringUtils +import at.bitfire.dav4jvm.ktor.XmlReader +import io.ktor.client.statement.HttpResponse +import io.ktor.http.HttpHeaders +import org.xmlpull.v1.XmlPullParser + +/** + * The GetETag property. + * + * Can also be used to parse ETags from HTTP responses – just pass the raw ETag + * header value to the constructor and then use [eTag] and [weak]. + */ +data class GetETag( + val rawETag: String? +): Property { + + companion object { + @JvmField + val NAME = Property.Name(NS_WEBDAV, "getetag") + + fun fromResponse(response: HttpResponse) = + response.headers[HttpHeaders.ETag]?.let { GetETag(it) } + } + + /** + * The parsed ETag value, excluding the weakness indicator and the quotes. + */ + val eTag: String? + + /** + * Whether the ETag is weak. + */ + var weak: Boolean + + init { + /* entity-tag = [ weak ] opaque-tag + weak = "W/" + opaque-tag = quoted-string + */ + + if (rawETag != null) { + val tag: String? + // remove trailing "W/" + if (rawETag.startsWith("W/")) { + // entity tag is weak + tag = rawETag.substring(2) + weak = true + } else { + tag = rawETag + weak = false + } + eTag = QuotedStringUtils.decodeQuotedString(tag) + } else { + eTag = null + weak = false + } + } + + override fun equals(other: Any?): Boolean { + if (other !is GetETag) + return false + return eTag == other.eTag && weak == other.weak + } + + override fun hashCode(): Int { + return eTag.hashCode() xor weak.hashCode() + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): GetETag = + GetETag(XmlReader(parser).readText()) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetLastModified.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetLastModified.kt new file mode 100644 index 00000000..e1c761c4 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetLastModified.kt @@ -0,0 +1,44 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser +import java.time.Instant + +data class GetLastModified( + val lastModified: Instant? +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "getlastmodified") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): GetLastModified { + // + return GetLastModified( + XmlReader(parser).readHttpDate() + ) + } + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GroupMembership.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GroupMembership.kt new file mode 100644 index 00000000..ec90db57 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GroupMembership.kt @@ -0,0 +1,37 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import org.xmlpull.v1.XmlPullParser + +class GroupMembership( + override val hrefs: List +): HrefListProperty(hrefs) { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "group-membership") + + } + + + object Factory: HrefListProperty.Factory() { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = create(parser, ::GroupMembership) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/Owner.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/Owner.kt new file mode 100644 index 00000000..56bd3499 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/Owner.kt @@ -0,0 +1,40 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.DavResource +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import org.xmlpull.v1.XmlPullParser + +data class Owner( + val href: String? +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "owner") + + } + + + object Factory: HrefListProperty.Factory() { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Owner = + Owner(XmlReader(parser).readTextProperty(DavResource.Companion.HREF)) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/QuotaAvailableBytes.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/QuotaAvailableBytes.kt new file mode 100644 index 00000000..de339625 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/QuotaAvailableBytes.kt @@ -0,0 +1,38 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class QuotaAvailableBytes( + val quotaAvailableBytes: Long? +) : Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "quota-available-bytes") + + } + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = + QuotaAvailableBytes(XmlReader(parser).readLong()) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/QuotaUsedBytes.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/QuotaUsedBytes.kt new file mode 100644 index 00000000..d209de32 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/QuotaUsedBytes.kt @@ -0,0 +1,38 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class QuotaUsedBytes( + val quotaUsedBytes: Long? +) : Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "quota-used-bytes") + + } + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = + QuotaUsedBytes(XmlReader(parser).readLong()) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/ResourceType.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/ResourceType.kt new file mode 100644 index 00000000..36de5918 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/ResourceType.kt @@ -0,0 +1,75 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.property.caldav.NS_CALDAV +import at.bitfire.dav4jvm.ktor.property.caldav.NS_CALENDARSERVER +import at.bitfire.dav4jvm.ktor.property.carddav.NS_CARDDAV +import org.xmlpull.v1.XmlPullParser + +class ResourceType( + val types: Set = emptySet() +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "resourcetype") + + val COLLECTION = Property.Name(NS_WEBDAV, "collection") // WebDAV + val PRINCIPAL = Property.Name(NS_WEBDAV, "principal") // WebDAV ACL + val ADDRESSBOOK = Property.Name(NS_CARDDAV, "addressbook") // CardDAV + val CALENDAR = Property.Name(NS_CALDAV, "calendar") // CalDAV + + // CalendarServer extensions + val CALENDAR_PROXY_READ = Property.Name(NS_CALENDARSERVER, "calendar-proxy-read") // CalDAV Proxy + val CALENDAR_PROXY_WRITE = Property.Name(NS_CALENDARSERVER, "calendar-proxy-write") // CalDAV Proxy + val SUBSCRIBED = Property.Name(NS_CALENDARSERVER, "subscribed") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): ResourceType { + val types = mutableSetOf() + + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { + // use static objects to allow types.contains() + var typeName = Property.Name(parser.namespace, parser.name) + when (typeName) { // if equals(), replace by our instance + COLLECTION -> typeName = COLLECTION + PRINCIPAL -> typeName = PRINCIPAL + ADDRESSBOOK -> typeName = ADDRESSBOOK + CALENDAR -> typeName = CALENDAR + CALENDAR_PROXY_READ -> typeName = CALENDAR_PROXY_READ + CALENDAR_PROXY_WRITE -> typeName = CALENDAR_PROXY_WRITE + SUBSCRIBED -> typeName = SUBSCRIBED + } + types.add(typeName) + } + eventType = parser.next() + } + assert(parser.depth == depth) + + return ResourceType(types) + } + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SupportedReportSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SupportedReportSet.kt new file mode 100644 index 00000000..f416ed84 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SupportedReportSet.kt @@ -0,0 +1,60 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class SupportedReportSet( + val reports: Set = emptySet() +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "supported-report-set") + + val SUPPORTED_REPORT = Property.Name(NS_WEBDAV, "supported-report") + val REPORT = Property.Name(NS_WEBDAV, "report") + + const val SYNC_COLLECTION = "DAV:sync-collection" // collection synchronization (RFC 6578) + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): SupportedReportSet { + /* + + + */ + + val reports = mutableSetOf() + XmlReader(parser).processTag(SUPPORTED_REPORT) { + processTag(REPORT) { + parser.nextTag() + if (parser.eventType == XmlPullParser.TEXT) + reports += parser.text + else if (parser.eventType == XmlPullParser.START_TAG) + reports += "${parser.namespace}${parser.name}" + } + } + return SupportedReportSet(reports) + } + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SyncLevel.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SyncLevel.kt new file mode 100644 index 00000000..feede891 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SyncLevel.kt @@ -0,0 +1,46 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV]`:sync-level` property. + */ +data class SyncLevel( + /** May be `0`, `1` or [Int.MAX_VALUE] (infinite). */ + val level: Int? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "sync-level") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): SyncLevel { + val text = XmlReader(parser).readText() + val level = if (text == "infinite") Int.MAX_VALUE else text?.toIntOrNull() + return SyncLevel(level) + } + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SyncToken.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SyncToken.kt new file mode 100644 index 00000000..87bfecc2 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SyncToken.kt @@ -0,0 +1,40 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.ktor.XmlReader +import org.xmlpull.v1.XmlPullParser + +data class SyncToken( + val token: String? +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV, "sync-token") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser) = + // + SyncToken(XmlReader(parser).readText()) + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/namespace.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/namespace.kt new file mode 100644 index 00000000..1b07f714 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/namespace.kt @@ -0,0 +1,16 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.webdav + +/** + * WebDAV XML namespace (defined in RFC 4918) + */ +const val NS_WEBDAV = "DAV:" diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavCalendarTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavCalendarTest.kt new file mode 100644 index 00000000..e6192926 --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavCalendarTest.kt @@ -0,0 +1,68 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.client.engine.mock.toByteArray +import io.ktor.http.ContentType +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.Url +import io.ktor.http.headersOf +import io.ktor.http.withCharset +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Test +import java.time.Instant + +class DavCalendarTest { + + + @Test + fun calendarQuery_formatStartEnd() { + val mockEngine = MockEngine { request -> + respond( + content = "", + status = HttpStatusCode.MultiStatus, // 207 + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Xml.withCharset(Charsets.UTF_8).toString()) + ) + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val cal = DavCalendar(httpClient, Url("/")) + + runBlocking { + cal.calendarQuery( + "VEVENT", + start = Instant.ofEpochSecond(784111777), + end = Instant.ofEpochSecond(1689324577) + ) { _, _ -> } + + assertEquals( + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "", + mockEngine.requestHistory.last().body.toByteArray().toString(Charsets.UTF_8) + ) + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavCollectionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavCollectionTest.kt new file mode 100644 index 00000000..2dd3e34c --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavCollectionTest.kt @@ -0,0 +1,291 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.exception.HttpException +import at.bitfire.dav4jvm.ktor.property.webdav.GetETag +import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.ktor.property.webdav.SyncToken +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.client.statement.request +import io.ktor.http.ContentType +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpMethod +import io.ktor.http.HttpStatusCode +import io.ktor.http.URLBuilder +import io.ktor.http.Url +import io.ktor.http.headersOf +import io.ktor.http.takeFrom +import io.ktor.http.withCharset +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Test + +class DavCollectionTest { + + private val sampleText = "SAMPLE RESPONSE" + private val sampleUrl = Url("http://127.0.0.1/dav/") + + + /** + * Test sample response for an initial sync-collection report from RFC 6578 3.8. + */ + @Test + fun testInitialSyncCollectionReport() { + + val mockEngine = MockEngine { request -> + respond( + content = + "\n" + + " \n" + + " \n" + + " ${sampleUrl}test.doc\n" + + " \n" + + " \n" + + " \"00001-abcd1\"\n" + + " \n" + + " Box type A\n" + + " \n" + + " \n" + + " HTTP/1.1 200 OK\n" + + " \n" + + " \n" + + " \n" + + " ${sampleUrl}vcard.vcf\n" + + " \n" + + " \n" + + " \"00002-abcd1\"\n" + + " \n" + + " HTTP/1.1 200 OK\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " HTTP/1.1 404 Not Found\n" + + " \n" + + " \n" + + " \n" + + " ${sampleUrl}calendar.ics\n" + + " \n" + + " \n" + + " \"00003-abcd1\"\n" + + " \n" + + " HTTP/1.1 200 OK\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " HTTP/1.1 404 Not Found\n" + + " \n" + + " \n" + + " http://example.com/ns/sync/1234\n" + + " ", + status = HttpStatusCode.MultiStatus, + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Xml.withCharset(Charsets.UTF_8).toString()) + ) + } + val httpClient = HttpClient(mockEngine) + + val url = sampleUrl + val collection = DavCollection(httpClient, url) + + runBlocking { + + var nrCalled = 0 + val result = collection.reportChanges(null, false, null, GetETag.NAME) { response, relation -> + when (response.href) { + URLBuilder(url).takeFrom("/dav/test.doc").build() -> { + assertTrue(response.isSuccess()) + assertEquals(Response.HrefRelation.MEMBER, relation) + val eTag = response[GetETag::class.java] + assertEquals("00001-abcd1", eTag!!.eTag) + assertFalse(eTag.weak) + nrCalled++ + } + URLBuilder(url).takeFrom("/dav/vcard.vcf").build() -> { + assertTrue(response.isSuccess()) + assertEquals(Response.HrefRelation.MEMBER, relation) + val eTag = response[GetETag::class.java] + assertEquals("00002-abcd1", eTag!!.eTag) + assertFalse(eTag.weak) + nrCalled++ + } + URLBuilder(url).takeFrom("/dav/calendar.ics").build() -> { + assertTrue(response.isSuccess()) + assertEquals(Response.HrefRelation.MEMBER, relation) + val eTag = response[GetETag::class.java] + assertEquals("00003-abcd1", eTag!!.eTag) + assertFalse(eTag.weak) + nrCalled++ + } + } + } + assertEquals(3, nrCalled) + assertEquals("http://example.com/ns/sync/1234", result.filterIsInstance().first().token) + } + } + + /** + * Test sample response for an initial sync-collection report with truncation from RFC 6578 3.10. + */ + @Test + fun testInitialSyncCollectionReportWithTruncation() { + + val mockEngine = MockEngine { request -> + respond( + content = "\n" + + " \n" + + " \n" + + " ${sampleUrl}test.doc\n" + + " \n" + + " \n" + + " \"00001-abcd1\"\n" + + " \n" + + " HTTP/1.1 200 OK\n" + + " \n" + + " \n" + + " \n" + + " ${sampleUrl}vcard.vcf\n" + + " \n" + + " \n" + + " \"00002-abcd1\"\n" + + " \n" + + " HTTP/1.1 200 OK\n" + + " \n" + + " \n" + + " \n" + + " ${sampleUrl}removed.txt\n" + + " HTTP/1.1 404 Not Found\n" + + " " + + " \n" + + " ${sampleUrl}\n" + + " HTTP/1.1 507 Insufficient Storage\n" + + " \n" + + " " + + " http://example.com/ns/sync/1233\n" + + " ", + status = HttpStatusCode.MultiStatus, // 207 + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Xml.withCharset(Charsets.UTF_8).toString()) + ) + } + val httpClient = HttpClient(mockEngine) + val collection = DavCollection(httpClient, sampleUrl) + + var nrCalled = 0 + + runBlocking { + val result = collection.reportChanges(null, false, null, GetETag.NAME) { response, relation -> + when (response.href) { + URLBuilder(sampleUrl).takeFrom("/dav/test.doc").build() -> { + assertTrue(response.isSuccess()) + assertEquals(Response.HrefRelation.MEMBER, relation) + val eTag = response[GetETag::class.java] + assertEquals("00001-abcd1", eTag?.eTag) + assertTrue(eTag?.weak == false) + nrCalled++ + } + + URLBuilder(sampleUrl).takeFrom("/dav/vcard.vcf").build() -> { + assertTrue(response.isSuccess()) + assertEquals(Response.HrefRelation.MEMBER, relation) + val eTag = response[GetETag::class.java] + assertEquals("00002-abcd1", eTag?.eTag) + assertTrue(eTag?.weak == false) + nrCalled++ + } + + URLBuilder(sampleUrl).takeFrom("/dav/removed.txt").build() -> { + assertFalse(response.isSuccess()) + assertEquals(404, response.status?.value) + assertEquals(Response.HrefRelation.MEMBER, relation) + nrCalled++ + } + + URLBuilder(sampleUrl).takeFrom("/dav/").build() -> { + assertFalse(response.isSuccess()) + assertEquals(507, response.status?.value) + assertEquals(Response.HrefRelation.SELF, relation) + nrCalled++ + } + } + } + assertEquals("http://example.com/ns/sync/1233", result.filterIsInstance().first().token) + assertEquals(4, nrCalled) + } + } + + /** + * Test sample response for a sync-collection report with unsupported limit from RFC 6578 3.12. + */ + @Test + fun testSyncCollectionReportWithUnsupportedLimit() { + + val mockEngine = MockEngine { request -> + respond( + content = "\n" + + " \n" + + " \n" + + " ", + status = HttpStatusCode.InsufficientStorage, // 507 @Ricki, does 507 really make sense here? + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Xml.withCharset(Charsets.UTF_8).toString()) + ) + } + val httpClient = HttpClient(mockEngine) + val collection = DavCollection(httpClient, sampleUrl) + + runBlocking { + try { + collection.reportChanges("http://example.com/ns/sync/1232", false, 100, GetETag.NAME) { _, _ -> } + fail("Expected HttpException") + } catch (e: HttpException) { + assertEquals(HttpStatusCode.InsufficientStorage.value, e.statusCode) + assertTrue(e.errors.any { it.name == Property.Name(NS_WEBDAV, "number-of-matches-within-limits") }) + assertEquals(1, e.errors.size) + } + } + } + + @Test + fun testPost() { + + val mockEngine = MockEngine { request -> + respond( + content = sampleText, + status = HttpStatusCode.Created, // 201 Created + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString()) + ) + } + val httpClient = HttpClient(mockEngine) + val dav = DavCollection(httpClient, sampleUrl) + + var called = false + runBlocking { + dav.post(sampleText) { response -> + assertEquals(HttpMethod.Post, response.request.method) + assertEquals(HttpStatusCode.Created, response.status) + assertEquals(response.request.url, dav.location) + called = true + } + assertTrue(called) + } + } + +} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavResourceTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavResourceTest.kt new file mode 100644 index 00000000..f4a905d3 --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavResourceTest.kt @@ -0,0 +1,1393 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.exception.DavException +import at.bitfire.dav4jvm.ktor.exception.HttpException +import at.bitfire.dav4jvm.ktor.exception.PreconditionFailedException +import at.bitfire.dav4jvm.ktor.property.webdav.DisplayName +import at.bitfire.dav4jvm.ktor.property.webdav.GetETag +import at.bitfire.dav4jvm.ktor.property.webdav.ResourceType +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.client.engine.mock.respondError +import io.ktor.client.engine.mock.respondOk +import io.ktor.client.engine.mock.respondRedirect +import io.ktor.client.request.get +import io.ktor.client.request.prepareRequest +import io.ktor.client.statement.bodyAsText +import io.ktor.client.statement.request +import io.ktor.http.ContentType +import io.ktor.http.HeadersBuilder +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpMethod +import io.ktor.http.HttpStatusCode +import io.ktor.http.URLBuilder +import io.ktor.http.Url +import io.ktor.http.content.TextContent +import io.ktor.http.contentType +import io.ktor.http.fullPath +import io.ktor.http.headersOf +import io.ktor.http.takeFrom +import io.ktor.http.withCharset +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Test + +class DavResourceTest { + + private val sampleText = "SAMPLE RESPONSE" + val sampleUrl = Url("https://127.0.0.1/dav/") + val sampleDestination = URLBuilder(sampleUrl).takeFrom("test").build() + + + @Test + fun `Copy POSITIVE no preconditions, 201 Created, resulted in the creation of a new resource`() { + val mockEngine = MockEngine { request -> + respond( + content = sampleText, + status = HttpStatusCode.Created, // 201 Created + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString()) + ) + } + val httpClient = HttpClient(mockEngine) + var called = false + + runBlocking { + DavResource(httpClient, sampleUrl).let { dav -> + dav.copy(sampleDestination, false) { + called = true + } + assertTrue(called) + } + + val rq = mockEngine.requestHistory.last() + assertEquals(HttpMethod.parse("COPY"), rq.method) + assertEquals(sampleUrl.encodedPath, rq.url.encodedPath) + assertEquals(sampleDestination.toString(), rq.headers[HttpHeaders.Destination]) + assertEquals("F", rq.headers[HttpHeaders.Overwrite]) + } + } + + @Test + fun `Copy POSITIVE no preconditions, 204 No content, resource successfully copied to a preexisting destination resource`() { + val mockEngine = MockEngine { request -> + respond( + content = sampleText, + status = HttpStatusCode.NoContent, // 204 No content + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString()) + ) + } + val httpClient = HttpClient(mockEngine) + + runBlocking { + var called = false + DavResource(httpClient, sampleUrl).let { dav -> + dav.copy(sampleDestination, true) { + called = true + } + assertTrue(called) + } + + val rq = mockEngine.requestHistory.last() + assertEquals(HttpMethod.parse("COPY"), rq.method) + assertEquals(sampleUrl.encodedPath, rq.url.encodedPath) + assertEquals(sampleDestination.toString(), rq.headers[HttpHeaders.Destination]) + assertNull(rq.headers[HttpHeaders.Overwrite]) + } + } + + @Test + fun `Copy NEGATIVE 207 multi-status eg errors on some of resources affected by the COPY prevented the operation from taking place`() { + val mockEngine = MockEngine { request -> + respond( + content = sampleText, + status = HttpStatusCode.MultiStatus, // 207 multi-status + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString()) + ) + } + val httpClient = HttpClient(mockEngine) + var called = false + + runBlocking { + try { + called = false + DavResource(httpClient, sampleUrl).let { dav -> + dav.copy(sampleDestination, false) { called = true } + fail("Expected HttpException") + } + } catch (_: HttpException) { + assertFalse(called) + } + } + } + + @Test + fun `Delete only eTag POSITIVE TEST CASES precondition If-Match 200 OK`() { + + val mockEngine = MockEngine { request -> + respond(sampleText, HttpStatusCode.NoContent) // 204 No Content + } + val httpClient = HttpClient(mockEngine) + val dav = DavResource(httpClient, sampleUrl) + var called = false + + runBlocking { + dav.delete { called = true } + assertTrue(called) + val rq = mockEngine.requestHistory.last() + assertEquals(HttpMethod.Delete, rq.method) + assertEquals(sampleUrl.encodedPath, rq.url.encodedPath) + assertNull(rq.headers[HttpHeaders.IfMatch]) + + } + } + + @Test + fun `Delete eTag and schedule Tag POSITIVE TEST CASES precondition If-Match 200 OK`() { + + val mockEngine = MockEngine { request -> + respondOk(content = sampleText) + } + val httpClient = HttpClient(mockEngine) + val dav = DavResource(httpClient, sampleUrl) + var called = false + + runBlocking { + dav.delete("DeleteOnlyThisETag", "DeleteOnlyThisScheduleTag") { called = true } + assertTrue(called) + + val rq = mockEngine.requestHistory.last() + assertEquals("\"DeleteOnlyThisETag\"", rq.headers[HttpHeaders.IfMatch]) + assertEquals("\"DeleteOnlyThisScheduleTag\"", rq.headers[HttpHeaders.IfScheduleTagMatch]) + } + } + + + @Test + fun `Delete POSITIVE TEST CASES precondition If-Match 302 Moved Temporarily`() { + + var numResponses = 0 + val mockEngine = MockEngine { request -> + numResponses+=1 + when(numResponses) { + 1 -> respondRedirect("/new-location") //307 TemporaryRedirect + else -> respondOk() + } + } + val httpClient = HttpClient(mockEngine) + val dav = DavResource(httpClient, sampleUrl) + var called = false + + runBlocking { + dav.delete(null) { + called = true + } + assertTrue(called) + } + } + + + @Test + fun `Delete NEGATIVE TEST CASES precondition If-Match 207 multi-status`() { + + val mockEngine = MockEngine { request -> + respondError(HttpStatusCode.MultiStatus) + } + val httpClient = HttpClient(mockEngine) + val dav = DavResource(httpClient, sampleUrl) + var called = false + + runBlocking { + try { + dav.delete(null) { called = true } + fail("Expected HttpException") + } catch(_: HttpException) { + assertFalse(called) + } + } + } + + + @Test + fun `followRedirects 302 Found`() { + var numResponses = 0 + val mockEngine = MockEngine { request -> + numResponses+=1 + when(numResponses) { + 1 -> respond("New location!", HttpStatusCode.Found, headersOf(HttpHeaders.Location, "https://to.com/")) + else -> respond("", HttpStatusCode.NoContent, headersOf(HttpHeaders.Location, "https://to.com/")) + } + } + val httpClient = HttpClient(mockEngine) { + followRedirects = false + } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + dav.followRedirects { + httpClient.get("https://from.com/") + }.let { response -> + assertEquals(HttpStatusCode.NoContent, response.status) + assertEquals(Url("https://to.com/"), dav.location) + } + } + } + + @Test(expected = DavException::class) + fun `followRedirects Https To Http`() { + val mockEngine = MockEngine { request -> + respond("New location!", HttpStatusCode.Found, headersOf(HttpHeaders.Location, "http://to.com/")) + } + val httpClient = HttpClient(mockEngine) { + followRedirects = false + } + + val dav = DavResource(httpClient, Url("https://from.com")) + runBlocking { + dav.followRedirects { + httpClient.get("https://from.com/") + } + } + } + + @Test + fun `Get POSITIVE TEST CASES 200 OK`() { + val mockEngine = MockEngine { request -> + respond( + content = sampleText, + status = HttpStatusCode.OK, // 200 OK + headers = HeadersBuilder().apply { + append(HttpHeaders.ETag, "W/\"My Weak ETag\"") + append(HttpHeaders.ContentType, "application/x-test-result") + }.build() + ) + } + val httpClient = HttpClient(mockEngine) + val dav = DavResource(httpClient, sampleUrl) + var called = false + + runBlocking { + dav.get(ContentType.Any.toString(), null) { response -> + called = true + runBlocking { assertEquals(sampleText, response.bodyAsText()) } + + val eTag = GetETag.fromResponse(response) + assertEquals("My Weak ETag", eTag!!.eTag) + assertTrue(eTag.weak) + assertEquals(ContentType.parse("application/x-test-result"), response.contentType()) + } + assertTrue(called) + + val rq = mockEngine.requestHistory.last() + assertEquals(HttpMethod.Get, rq.method) + assertEquals(sampleUrl.encodedPath, rq.url.encodedPath) + assertEquals(ContentType.Any.toString(), rq.headers[HttpHeaders.Accept]) + } + } + + + @Test + fun `Get POSITIVE TEST CASES 302 Moved Temporarily + 200 OK`() { + var numResponses = 0 + val mockEngine = MockEngine { request -> + numResponses+=1 + when(numResponses) { + 1 -> respond("This resource was moved.", HttpStatusCode.TemporaryRedirect, headersOf(HttpHeaders.Location,"/target")) + else -> respond(sampleText, HttpStatusCode.OK, headersOf(HttpHeaders.ETag,"\"StrongETag\"")) + } + } + val httpClient = HttpClient(mockEngine) + val dav = DavResource(httpClient, sampleUrl) + var called = false + + runBlocking { + + dav.get(ContentType.Any.toString(), null) { response -> + called = true + runBlocking { assertEquals(sampleText, response.bodyAsText()) } + val eTag = GetETag(response.headers[HttpHeaders.ETag]) + assertEquals("StrongETag", eTag.eTag) + assertFalse(eTag.weak) + } + assertTrue(called) + + val rq = mockEngine.requestHistory.last() + assertEquals(HttpMethod.Get, rq.method) + assertEquals("/target", rq.url.fullPath) + } + } + + + @Test + fun `Get POSITIVE TEST CASES 200 OK without ETag in response`() { + val mockEngine = MockEngine { request -> + respond(sampleText, HttpStatusCode.OK) + } + val httpClient = HttpClient(mockEngine) + val dav = DavResource(httpClient, sampleUrl) + var called = false + + runBlocking { + dav.get(ContentType.Any.toString(), null) { response -> + called = true + assertNull(response.headers[HttpHeaders.ETag]) + } + assertTrue(called) + } + } + + + @Test + fun `GetRange Ok`() { + val mockEngine = MockEngine { request -> + respond("",HttpStatusCode.PartialContent) // 206 + } + val httpClient = HttpClient(mockEngine) + val dav = DavResource(httpClient, sampleUrl) + + var called = false + runBlocking { + dav.getRange(ContentType.Any.toString(), 100, 342) { response -> + assertEquals("bytes=100-441", response.request.headers[HttpHeaders.Range]) + called = true + } + assertTrue(called) + } + } + + @Test + fun `Post POSITIVE TEST CASES 200 OK`() { + val mockEngine = MockEngine { request -> + respond( + content = sampleText, + status = HttpStatusCode.OK, // 200 OK + headers = HeadersBuilder().apply { + append(HttpHeaders.ContentType, "application/x-test-result") + append(HttpHeaders.ETag, "W/\"My Weak ETag\"") + }.build() + ) + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + /* POSITIVE TEST CASES */ + + // 200 OK + runBlocking { + var called = false + dav.post( + body = "body", + headers = HeadersBuilder().apply { append(HttpHeaders.ContentType, "application/x-test-result") }.build() + ) { response -> + called = true + runBlocking { assertEquals(sampleText, response.bodyAsText()) } + + val eTag = GetETag.fromResponse(response) + assertEquals("My Weak ETag", eTag!!.eTag) + assertTrue(eTag.weak) + assertEquals(ContentType.parse("application/x-test-result"), response.contentType()) + } + assertTrue(called) + + val rq = mockEngine.requestHistory.last() + assertEquals(HttpMethod.Post, rq.method) + assertEquals(sampleUrl.encodedPath, rq.url.encodedPath) + assertEquals(ContentType.parse("application/x-test-result"), rq.body.contentType) // TODO: Originally there was a check for the header, not the content type in the body, what is correct here? + assertEquals("body", (rq.body as TextContent).text) + + /* + // 302 Moved Temporarily + 200 OK + called = false + dav.post( + body = "body", + headers = HeadersBuilder().apply { append(HttpHeaders.ContentType, ContentType.parse("application/x-test-result").toString()) }.build() + ) { response -> + called = true + runBlocking { assertEquals(sampleText, response.bodyAsText()) } + val eTag = GetETag(response.headers[HttpHeaders.ETag]) + assertEquals("StrongETag", eTag.eTag) + assertFalse(eTag.weak) + } + assertTrue(called) + + rq = mockEngine.requestHistory.last() + assertEquals(HttpMethod.Post, rq.method) + assertEquals("/target", rq.url.encodedPath) + + // 200 OK without ETag in response + called = false + dav.post( + body = "body", + ) { response -> + called = true + assertNull(response.headers[HttpHeaders.ETag]) + } + assertTrue(called) + + */ + } + } + + @Test + fun `Post POSITIVE TEST CASES 302 Moved Temporarily + 200 OK`() { + var numResponses = 0 + val mockEngine = MockEngine { request -> + numResponses+=1 + when(numResponses) { + 1 -> respond("This resource was moved.", HttpStatusCode.TemporaryRedirect, headersOf(HttpHeaders.Location,"/target")) + else -> respond(sampleText, HttpStatusCode.OK, headersOf(HttpHeaders.ETag,"\"StrongETag\"")) + } + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + var called = false + dav.post( + body = "body", + headers = HeadersBuilder().apply { append(HttpHeaders.ContentType, ContentType.parse("application/x-test-result").toString()) }.build() + ) { response -> + called = true + runBlocking { assertEquals(sampleText, response.bodyAsText()) } + val eTag = GetETag(response.headers[HttpHeaders.ETag]) + assertEquals("StrongETag", eTag.eTag) + assertFalse(eTag.weak) + } + assertTrue(called) + + val rq = mockEngine.requestHistory.last() + assertEquals(HttpMethod.Post, rq.method) + assertEquals("/target", rq.url.encodedPath) + } + } + + @Test + fun `Post POSITIVE TEST CASES 200 OK without ETag in response`() { + val mockEngine = MockEngine { request -> + respond(sampleText, HttpStatusCode.OK) + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + var called = false + dav.post( + body = "body", + ) { response -> + called = true + assertNull(response.headers[HttpHeaders.ETag]) + } + assertTrue(called) + } + } + + @Test + fun `Move POSITIVE TEST CASES no preconditions, 201 Created, new URL mapping at the destination`() { + val mockEngine = MockEngine { request -> + respond("",HttpStatusCode.Created) // 201 Created + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val destination = URLBuilder(sampleUrl).takeFrom("test").build() + + runBlocking { + // no preconditions, 201 Created, new URL mapping at the destination + var called = false + DavResource(httpClient, sampleUrl).let { dav -> + dav.move(destination, false) { + called = true + } + assertTrue(called) + assertEquals(destination, dav.location) + } + + val rq = mockEngine.requestHistory.last() + assertEquals(HttpMethod.parse("MOVE"), rq.method) + assertEquals(sampleUrl.encodedPath, rq.url.encodedPath) + assertEquals(destination.toString(), rq.headers[HttpHeaders.Destination]) + assertEquals("F", rq.headers[HttpHeaders.Overwrite]) + } + } + + @Test + fun `Move POSITIVE TEST CASES no preconditions, 204 No content, URL already mapped, overwrite`() { + val mockEngine = MockEngine { request -> + respond("",HttpStatusCode.NoContent) // 204 No content + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val destination = URLBuilder(sampleUrl).takeFrom("test").build() + + runBlocking { + var called = false + DavResource(httpClient, sampleUrl).let { dav -> + dav.move(destination, true) { + called = true + } + assertTrue(called) + assertEquals(destination, dav.location) + } + + val rq = mockEngine.requestHistory.last() + assertEquals(HttpMethod.parse("MOVE"), rq.method) + assertEquals(sampleUrl.encodedPath, rq.url.encodedPath) + assertEquals(destination.toString(), rq.headers[HttpHeaders.Destination]) + assertNull(rq.headers[HttpHeaders.Overwrite]) + } + } + + @Test + fun `Move NEGATIVE TEST CASES no preconditions, 207 multi-status`() { + val mockEngine = MockEngine { request -> + respond("",HttpStatusCode.MultiStatus) // 207 Multi-Status + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val destination = URLBuilder(sampleUrl).takeFrom("test").build() + + runBlocking { + // 207 multi-status (e.g. errors on some of resources affected by + // the MOVE prevented the operation from taking place) + var called = false + try { + DavResource(httpClient, sampleUrl).let { dav -> + dav.move(destination, false) { called = true } + fail("Expected HttpException") + } + } catch (_: HttpException) { + assertFalse(called) + } + } + } + + @Test + fun `Options with capabilities`() { + val mockEngine = MockEngine { request -> + respond("",HttpStatusCode.OK, HeadersBuilder().apply { append("DAV", " 1, 2 ,3,hyperactive-access")}.build()) // 200 Ok + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + var called = false + dav.options { davCapabilities, _ -> + called = true + assertTrue(davCapabilities.any { it.contains("1") }) + assertTrue(davCapabilities.any { it.contains("2") }) + assertTrue(davCapabilities.any { it.contains("3") }) + assertTrue(davCapabilities.any { it.contains("hyperactive-access") }) + } + assertTrue(called) + } + } + + + @Test + fun `Options without capabilities`() { + val mockEngine = MockEngine { request -> + respondOk() // 200 OK + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + var called = false + dav.options { davCapabilities, _ -> + called = true + assertTrue(davCapabilities.isEmpty()) + } + assertTrue(called) + } + } + + @Test + fun `NEGATIVE TEST CASES Propfind And MultiStatus 500 Internal Server Error`() { + val mockEngine = MockEngine { request -> + respondError(HttpStatusCode.InternalServerError) // 500 + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + var called = false + try { + dav.propfind(0, ResourceType.NAME) { _, _ -> called = true } + fail("Expected HttpException") + } catch (e: HttpException) { + assertFalse(called) + } + + } + } + + @Test + fun `NEGATIVE TEST CASES Propfind And MultiStatus 200 OK (instead of 207 Multi-Status)`() { + val mockEngine = MockEngine { request -> respondOk() } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + // * 200 OK (instead of 207 Multi-Status) + var called = false + try { + dav.propfind(0, ResourceType.NAME) { _, _ -> called = true } + fail("Expected DavException") + } catch (_: DavException) { + assertFalse(called) + } + } + } + + @Test + fun `NEGATIVE TEST CASES Propfind And MultiStatus non-XML response`() { + val mockEngine = MockEngine { request -> + respond("", HttpStatusCode.MultiStatus, headersOf(HttpHeaders.ContentType, ContentType.Text.Html.toString())) // non-XML response + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + // * non-XML response + var called = false + try { + dav.propfind(0, ResourceType.NAME) { _, _ -> called = true } + fail("Expected DavException") + } catch (e: DavException) { + assertFalse(called) + } + } + } + + @Test + fun `NEGATIVE TEST CASES Propfind And MultiStatus malformed XML response`() { + val mockEngine = MockEngine { request -> + respond("", HttpStatusCode.MultiStatus, headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.withCharset(Charsets.UTF_8).toString())) // * malformed XML response + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + // * malformed XML response + var called = false + try { + dav.propfind(0, ResourceType.NAME) { _, _ -> called = true } + fail("Expected DavException") + } catch (_: DavException) { + assertFalse(called) + } + } + } + + + @Test + fun `NEGATIVE TEST CASES Propfind And MultiStatus response without multistatus root element`() { + val mockEngine = MockEngine { request -> + respond("", HttpStatusCode.MultiStatus, headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.withCharset(Charsets.UTF_8).toString())) // * response without root element + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + // * response without root element + var called = false + try { + dav.propfind(0, ResourceType.NAME) { _, _ -> called = true } + fail("Expected DavException") + } catch (_: DavException) { + assertFalse(called) + } + } + } + + + @Test + fun `NEGATIVE TEST CASES Propfind And MultiStatus multi-status response with invalid status in response`() { + val mockEngine = MockEngine { request -> + respond("" + + " " + + " /dav" + + " Invalid Status Line" + + " " + + "", + HttpStatusCode.MultiStatus, + headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.withCharset(Charsets.UTF_8).toString()) + ) // * multi-status response with invalid in + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + // * multi-status response with invalid in + var called = false + dav.propfind(0, ResourceType.NAME) { response, relation -> + assertEquals(Response.HrefRelation.SELF, relation) + assertEquals(HttpStatusCode.InternalServerError, response.status) + called = true + } + assertTrue(called) + } + } + + @Test + fun `NEGATIVE TEST CASES Propfind And MultiStatus multi-status response with response-status element indicating failure`() { + val mockEngine = MockEngine { request -> + respond("" + + " " + + " /dav" + + " HTTP/1.1 403 Forbidden" + + " " + + "", + HttpStatusCode.MultiStatus, + headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.withCharset(Charsets.UTF_8).toString()) + ) // * multi-status response with / element indicating failure + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + // * multi-status response with / element indicating failure + var called = false + dav.propfind(0, ResourceType.NAME) { response, relation -> + assertEquals(Response.HrefRelation.SELF, relation) + assertEquals(HttpStatusCode.Forbidden, response.status) + called = true + } + assertTrue(called) + } + } + + @Test + fun `NEGATIVE TEST CASES Propfind And MultiStatus multi-status response with invalid status in propstat`() { + val mockEngine = MockEngine { request -> + respond("" + + " " + + " /dav" + + " " + + " " + + " " + + " " + + " Invalid Status Line" + + " " + + " " + + "", + HttpStatusCode.MultiStatus, + headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.withCharset(Charsets.UTF_8).toString()) + ) // * multi-status response with invalid in + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + // * multi-status response with invalid in + var called = false + dav.propfind(0, ResourceType.NAME) { response, relation -> + called = true + assertEquals(Response.HrefRelation.SELF, relation) + assertTrue(response.properties.filterIsInstance().isEmpty()) + } + assertTrue(called) + + } + } + + + @Test + fun `NEGATIVE TEST CASES Propfind And MultiStatus multi-status response without response elements`() { + val mockEngine = MockEngine { request -> + respond("", + HttpStatusCode.MultiStatus, + headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.withCharset(Charsets.UTF_8).toString()) + ) // multi-status response without elements + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + // multi-status response without elements + dav.propfind(0, ResourceType.NAME) { _, _ -> + fail("Shouldn't be called") + } + } + } + + @Test + fun `POSITIVE TEST CASES Propfind And MultiStatus multi-status response with response-status element indicating success`() { + val mockEngine = MockEngine { request -> + respond( "" + + " " + + " /dav" + + " HTTP/1.1 200 OK" + + " " + + "", + HttpStatusCode.MultiStatus, + headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.withCharset(Charsets.UTF_8).toString()) + ) // multi-status response with / element indicating success + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + var called = false + // multi-status response with / element indicating success + dav.propfind(0, ResourceType.NAME) { response, relation -> + called = true + assertTrue(response.isSuccess()) + assertEquals(Response.HrefRelation.SELF, relation) + assertEquals(0, response.properties.size) + } + assertTrue(called) + } + } + + + @Test + fun `POSITIVE TEST CASES Propfind And MultiStatus multi-status response with response-propstat element`() { + val mockEngine = MockEngine { request -> + respond( "" + + " " + + " /dav" + + " " + + " " + + " " + + " My DAV Collection" + + " " + + " HTTP/1.1 200 OK" + + " " + + " " + + "", + HttpStatusCode.MultiStatus, + headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.withCharset(Charsets.UTF_8).toString()) + ) // multi-status response with / element + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + var called = false + dav.propfind(0, ResourceType.NAME, DisplayName.NAME) { response, relation -> + called = true + assertTrue(response.isSuccess()) + assertEquals(Response.HrefRelation.SELF, relation) + assertEquals("My DAV Collection", response[DisplayName::class.java]?.displayName) + } + assertTrue(called) + } + } + + @Test + fun `POSITIVE TEST CASES Propfind And MultiStatus SPECIAL CASES multi-status response for collection with several members, incomplete (not all resourcetypes listed)`() { + val mockEngine = MockEngine { request -> + respond( "" + + " " + + " " + sampleUrl.toString() + "" + + " " + + " " + + " " + + " My DAV Collection" + + " " + + " HTTP/1.1 200 OK" + + " " + + " " + + " " + + " /dav/subcollection" + + " " + + " " + + " " + + " A Subfolder" + + " " + + " HTTP/1.1 200 OK" + + " " + + " " + + " " + + " /dav/uid@host:file" + + " " + + " " + + " Absolute path with @ and :" + + " " + + " HTTP/1.1 200 OK" + + " " + + " " + + " " + + " relative-uid@host.file" + + " " + + " " + + " Relative path with @" + + " " + + " HTTP/1.1 200 OK" + + " " + + " " + + " " + + " relative:colon.vcf" + + " " + + " " + + " Relative path with colon" + + " " + + " HTTP/1.1 200 OK" + + " " + + " " + + " " + + " /something-very/else" + + " " + + " " + + " Not requested" + + " " + + " HTTP/1.1 200 OK" + + " " + + " " + + "", + HttpStatusCode.MultiStatus, + headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.withCharset(Charsets.UTF_8).toString()) + ) // multi-status response for collection with several members; incomplete (not all s listed) + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + // multi-status response for collection with several members; incomplete (not all s listed) + var nrCalled = 0 + dav.propfind(1, ResourceType.NAME, DisplayName.NAME) { response, relation -> + when (response.href) { + URLBuilder(sampleUrl).takeFrom("/dav/").build() -> { + assertTrue(response.isSuccess()) + assertEquals(Response.HrefRelation.SELF, relation) + assertTrue(response[ResourceType::class.java]!!.types.contains(ResourceType.COLLECTION)) + assertEquals("My DAV Collection", response[DisplayName::class.java]?.displayName) + nrCalled++ + } + + URLBuilder(sampleUrl).takeFrom("/dav/subcollection/").build() -> { + assertTrue(response.isSuccess()) + assertEquals(Response.HrefRelation.MEMBER, relation) + assertTrue(response[ResourceType::class.java]!!.types.contains(ResourceType.COLLECTION)) + assertEquals("A Subfolder", response[DisplayName::class.java]?.displayName) + nrCalled++ + } + + URLBuilder(sampleUrl).takeFrom("/dav/uid@host:file").build() -> { + assertTrue(response.isSuccess()) + assertEquals(Response.HrefRelation.MEMBER, relation) + assertEquals("Absolute path with @ and :", response[DisplayName::class.java]?.displayName) + nrCalled++ + } + + URLBuilder(sampleUrl).takeFrom("/dav/relative-uid@host.file").build() -> { + assertTrue(response.isSuccess()) + assertEquals(Response.HrefRelation.MEMBER, relation) + assertEquals("Relative path with @", response[DisplayName::class.java]?.displayName) + nrCalled++ + } + + URLBuilder(sampleUrl).takeFrom("/dav/relative:colon.vcf").build() -> { + assertTrue(response.isSuccess()) + assertEquals(Response.HrefRelation.MEMBER, relation) + assertEquals("Relative path with colon", response[DisplayName::class.java]?.displayName) + nrCalled++ + } + } + } + assertEquals(4, nrCalled) + } + } + + @Test + fun `POSITIVE TEST CASES Propfind And MultiStatus SPECIAL CASES same property is sent as 200 OK and 404 Not Found in same response (seen in iCloud)`() { + val mockEngine = MockEngine { request -> + respond( "" + + " " + + " " + sampleUrl.toString() + "" + + " " + + " " + + " " + + " My DAV Collection" + + " " + + " HTTP/1.1 200 OK" + + " " + + " " + + " " + + " " + + " " + + " HTTP/1.1 404 Not Found" + + " " + + " " + + "", + HttpStatusCode.MultiStatus, + headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.withCharset(Charsets.UTF_8).toString()) + ) // same property is sent as 200 OK and 404 Not Found in same (seen in iCloud) + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + var called = false + dav.propfind(0, ResourceType.NAME, DisplayName.NAME) { response, relation -> + called = true + assertTrue(response.isSuccess()) + assertEquals(Response.HrefRelation.SELF, relation) + assertEquals(URLBuilder(sampleUrl).takeFrom("/dav/").build(), response.href) + assertTrue(response[ResourceType::class.java]!!.types.contains(ResourceType.COLLECTION)) + assertEquals("My DAV Collection", response[DisplayName::class.java]?.displayName) + } + assertTrue(called) + } + } + + + @Test + fun `POSITIVE TEST CASES Propfind And MultiStatus SPECIAL CASES multi-status response with propstat that doesn't contain status, assume 200 OK`() { + val mockEngine = MockEngine { request -> + respond( "" + + " " + + " /dav" + + " " + + " " + + " Without Status" + + " " + + " " + + " " + + "", + HttpStatusCode.MultiStatus, + headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.withCharset(Charsets.UTF_8).toString()) + ) // multi-status response with that doesn't contain (=> assume 200 OK) + } + val httpClient = HttpClient(mockEngine) { followRedirects = false } + val dav = DavResource(httpClient, sampleUrl) + + runBlocking { + /*** SPECIAL CASES ***/ + + // multi-status response with that doesn't contain (=> assume 200 OK) + var called = false + dav.propfind(0, DisplayName.NAME) { response, _ -> + called = true + assertEquals(200, response.propstat.first().status.value) + assertEquals("Without Status", response[DisplayName::class.java]?.displayName) + } + assertTrue(called) + } + } + + @Test + fun `Proppatch`() { + // multi-status response with / elements + val mockEngine = MockEngine { request -> + respond( + content = "" + + " " + + " /dav" + + " " + + " " + + " Some Value" + + " " + + " HTTP/1.1 200 OK" + + " " + + " " + + " " + + " " + + " " + + " HTTP/1.1 404 Not Found" + + " " + + " " + + "", + status = HttpStatusCode.MultiStatus, // 207 + headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.withCharset(Charsets.UTF_8).toString()) + ) + } + val httpClient = HttpClient(mockEngine) + val dav = DavResource(httpClient, sampleUrl) + + var called = false + runBlocking { + dav.proppatch( + setProperties = mapOf(Pair(Property.Name("sample", "setThis"), "Some Value")), + removeProperties = listOf(Property.Name("sample", "removeThis")) + ) { _, hrefRelation -> + called = true + assertEquals(Response.HrefRelation.SELF, hrefRelation) + } + assertTrue(called) + } + } + + + @Test + fun `Proppatch createProppatchXml`() { + val xml = DavResource.createProppatchXml( + setProperties = mapOf(Pair(Property.Name("sample", "setThis"), "Some Value")), + removeProperties = listOf(Property.Name("sample", "removeThis")) + ) + assertEquals("" + + "" + + "Some Value" + + "" + + "", xml) + } + + @Test + fun `Put POSITIVE TEST CASES no preconditions, 201 Created`() { + val mockEngine = MockEngine { request -> + respond( + content = " ", + status = HttpStatusCode.Created, // 201 Created + headers = headersOf(HttpHeaders.ETag, "W/\"Weak PUT ETag\"") + ) + } + + val httpClient = HttpClient(mockEngine) + val dav = DavResource(httpClient, sampleUrl) + + var called = false + runBlocking { + dav.put( + body = sampleText, + headers = HeadersBuilder().apply { append(HttpHeaders.ContentType, "text/plain") }.build() + ) { response -> + called = true + val eTag = GetETag.fromResponse(response)!! + assertEquals("Weak PUT ETag", eTag.eTag) + assertTrue(eTag.weak) + assertEquals(response.request.url, dav.location) + } + assertTrue(called) + + val rq = mockEngine.requestHistory.last() + assertEquals(HttpMethod.Put, rq.method) + assertEquals(sampleUrl.encodedPath, rq.url.encodedPath) + assertNull(rq.headers[HttpHeaders.IfMatch]) + assertNull(rq.headers[HttpHeaders.IfNoneMatch]) + } + } + + @Test + fun `Put POSITIVE TEST CASES precondition If-None-Match, 301 Moved Permanently + 204 No Content, no ETag in response`() { + var numberOfResponse = 0 + val mockEngine = MockEngine { request -> + numberOfResponse+=1 + when(numberOfResponse) { + 1 -> respond( + content = "", + status = HttpStatusCode.MovedPermanently, // 301 Moved Permanently + headers = headersOf(HttpHeaders.Location, "/target") + ) + else -> respond("", HttpStatusCode.NoContent) + } + } + + val httpClient = HttpClient(mockEngine) + val dav = DavResource(httpClient, sampleUrl) + + var called = false + runBlocking { + dav.put(sampleText, headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString()), ifNoneMatch = true) { response -> + called = true + assertEquals(URLBuilder(sampleUrl).takeFrom("/target").build(), response.request.url) + val eTag = GetETag.fromResponse(response) + assertNull("Weak PUT ETag", eTag?.eTag) + assertNull(eTag?.weak) + } + assertTrue(called) + + val rq = mockEngine.requestHistory.last() + assertEquals(HttpMethod.Put, rq.method) + assertEquals("*", rq.headers[HttpHeaders.IfNoneMatch]) + } + } + + @Test + fun `Put NEGATIVE TEST CASES precondition If-Match, 412 Precondition Failed`() { + val mockEngine = MockEngine { request -> + respond("", HttpStatusCode.PreconditionFailed) + } + + val httpClient = HttpClient(mockEngine) + val dav = DavResource(httpClient, sampleUrl) + + var called = false + runBlocking { + try { + dav.put(sampleText, headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString()), "ExistingETag") { + called = true + } + fail("Expected PreconditionFailedException") + } catch(_: PreconditionFailedException) {} + assertFalse(called) + val rq = mockEngine.requestHistory.last() + assertEquals("\"ExistingETag\"", rq.headers[HttpHeaders.IfMatch]) + assertNull(rq.headers[HttpHeaders.IfNoneMatch]) + } + } + + @Test + fun `Search`() { + val mockEngine = MockEngine { request -> + respond( + content = "" + + " " + + " /dav" + + " " + + " " + + " Found something" + + " " + + " " + + " " + + "", + status = HttpStatusCode.MultiStatus, // 207 Multi-Status + headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.withCharset(Charsets.UTF_8).toString()) + ) + } + val httpClient = HttpClient(mockEngine) + val dav = DavResource(httpClient, sampleUrl) + var called = false + + runBlocking { + dav.search("") { response, hrefRelation -> + assertEquals(Response.HrefRelation.SELF, hrefRelation) + assertEquals("Found something", response[DisplayName::class.java]?.displayName) + called = true + } + assertTrue(called) + + val rq = mockEngine.requestHistory.last() + val requestBodyText = rq.body as TextContent + + assertEquals(HttpMethod.parse("SEARCH"), rq.method) + assertEquals(sampleUrl.encodedPath, rq.url.encodedPath) + assertEquals("", requestBodyText.text) + } + } + + + /** test helpers **/ + + @Test + fun `AssertMultiStatus EmptyBody NoXML`() { + val mockEngine = MockEngine { request -> + respond( + content = "", + status = HttpStatusCode.MultiStatus // 207 Multi-Status + ) + } + val httpClient = HttpClient(mockEngine) + + runBlocking { + val dav = DavResource(httpClient, Url("https://from.com")) + dav.assertMultiStatus(httpClient.prepareRequest(dav.location).execute()) + } + } + + @Test + fun `AssertMultiStatus EmptyBody XML`() { + val mockEngine = MockEngine { request -> + respond( + content = "", + status = HttpStatusCode.MultiStatus, // 207 Multi-Status + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Xml.toString()) + ) + } + val httpClient = HttpClient(mockEngine) + + runBlocking { + val dav = DavResource(httpClient, Url("https://from.com")) + dav.assertMultiStatus(httpClient.prepareRequest(dav.location).execute()) + } + } + + @Test + fun `AssertMultiStatus NonXML ButContentIsXML`() { + val mockEngine = MockEngine { request -> + respond( + content = "", + status = HttpStatusCode.MultiStatus, // 207 Multi-Status + headers = headersOf(HttpHeaders.ContentType, ContentType.Application.OctetStream.toString()) + ) + } + val httpClient = HttpClient(mockEngine) + + runBlocking { + val dav = DavResource(httpClient, Url("https://from.com")) + dav.assertMultiStatus(httpClient.prepareRequest(dav.location).execute()) + } + } + + @Test(expected = DavException::class) + fun `AssertMultiStatus NonXML Really Not XML`() { + val mockEngine = MockEngine { request -> + respond( + content = "Some error occurred", + status = HttpStatusCode.MultiStatus, // 207 Multi-Status + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString()) + ) + } + val httpClient = HttpClient(mockEngine) + + runBlocking { + val dav = DavResource(httpClient, Url("https://from.com")) + dav.assertMultiStatus(httpClient.prepareRequest(dav.location).execute()) + } + + } + + @Test(expected = DavException::class) + fun `AssertMultiStatus Not 207`() { + + val mockEngine = MockEngine { request -> + respond( + content = "", + status = HttpStatusCode(403, "Multi-Status"), // 207 Multi-Status + headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.toString()) + ) + } + val httpClient = HttpClient(mockEngine) + + runBlocking { + val dav = DavResource(httpClient, Url("https://from.com")) + dav.assertMultiStatus(httpClient.prepareRequest(dav.location).execute()) + } + + } + + @Test + fun `AssertMultiStatus Ok ApplicationXml`() { + + val mockEngine = MockEngine { request -> + respond( + content = "", + status = HttpStatusCode.MultiStatus, // 207 Multi-Status + headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.toString()) + ) + } + val httpClient = HttpClient(mockEngine) + + runBlocking { + val dav = DavResource(httpClient, Url("https://from.com")) + dav.assertMultiStatus(httpClient.prepareRequest(dav.location).execute()) + } + } + + @Test + fun `AssertMultiStatus Ok TextXml`() { + + val mockEngine = MockEngine { request -> + respond( + content = "", + status = HttpStatusCode.MultiStatus, // 207 Multi-Status + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Xml.toString()) + ) + } + val httpClient = HttpClient(mockEngine) + val dav = DavResource(httpClient, Url("https://from.com")) + runBlocking { + dav.assertMultiStatus(httpClient.prepareRequest(dav.location).execute()) + } + } +} diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/ErrorTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/ErrorTest.kt new file mode 100644 index 00000000..34ae57e5 --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/ErrorTest.kt @@ -0,0 +1,24 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import org.junit.Assert.assertTrue +import org.junit.Test + +class ErrorTest { + + @Test + fun testEquals() { + val errors = listOf(Error(Property.Name("DAV:", "valid-sync-token"))) + assertTrue(errors.contains(Error.VALID_SYNC_TOKEN)) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/HttpUtilsTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/HttpUtilsTest.kt new file mode 100644 index 00000000..4762e007 --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/HttpUtilsTest.kt @@ -0,0 +1,324 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.HttpUtils.INVALID_STATUS +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.client.request.get +import io.ktor.http.HeadersBuilder +import io.ktor.http.HttpStatusCode +import io.ktor.http.Url +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test +import java.time.Instant +import java.time.LocalDate +import java.time.LocalTime +import java.time.ZoneOffset +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.util.Calendar +import java.util.Locale +import java.util.TimeZone +import java.util.logging.Logger + +class HttpUtilsTest { + + val sampleUrl = Url("http://127.0.0.1") + private val multipleHeaderValues = " 1, 2 ,3,hyperactive-access" + private val singleHeaderValue = "other" + + private fun getMockEngineWithDAVHeaderValues(vararg headerValues: String): HttpClient { + val mockEngine = MockEngine { request -> + respond("",HttpStatusCode.OK, HeadersBuilder().apply { + headerValues.forEach { headerValue -> + append("DAV", headerValue) + } + }.build()) + } + return HttpClient(mockEngine) + } + + + @Test + fun fileName() { + assertEquals("", HttpUtils.fileName(Url("https://example.com"))) + assertEquals("", HttpUtils.fileName(Url("https://example.com/"))) + assertEquals("file1", HttpUtils.fileName(Url("https://example.com/file1"))) + assertEquals("dir1", HttpUtils.fileName(Url("https://example.com/dir1/"))) + assertEquals("file2", HttpUtils.fileName(Url("https://example.com/dir1/file2"))) + assertEquals("dir2", HttpUtils.fileName(Url("https://example.com/dir1/dir2/"))) + } + + @Test + fun formatDate() { + val cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")) + cal.set(2023, 4, 11, 17, 26, 35) + cal.timeZone = TimeZone.getTimeZone("UTC") + assertEquals("Sun, 06 Nov 1994 08:49:37 GMT", HttpUtils.formatDate( + ZonedDateTime.of( + LocalDate.of(1994, 11, 6), + LocalTime.of(8, 49, 37), + ZoneOffset.UTC + ).toInstant() + )) + } + + + @Test + fun parseDate_IMF_FixDate() { + // RFC 7231 IMF-fixdate (preferred format) + assertEquals(Instant.ofEpochSecond(784111777), HttpUtils.parseDate("Sun, 06 Nov 1994 08:49:37 GMT")) + } + + @Test + fun parseDate_RFC850_1994() { + // obsolete RFC 850 format – fails when run after year 2000 because 06 Nov 2094 (!) is not a Sunday + assertNull(HttpUtils.parseDate("Sun, 06-Nov-94 08:49:37 GMT")) + } + + @Test + fun parseDate_RFC850_2004_CEST() { + // obsolete RFC 850 format with European time zone + assertEquals(Instant.ofEpochSecond(1689317377), HttpUtils.parseDate("Friday, 14-Jul-23 08:49:37 CEST")) + } + + @Test + fun parseDate_RFC850_2004_GMT() { + // obsolete RFC 850 format + assertEquals(Instant.ofEpochSecond(1689324577), HttpUtils.parseDate("Friday, 14-Jul-23 08:49:37 GMT")) + } + + @Test + fun parseDate_ANSI_C() { + // ANSI C's asctime() format + val logger = Logger.getLogger(javaClass.name) + logger.info("Expected date: " + DateTimeFormatter.ofPattern("EEE MMM ppd HH:mm:ss yyyy", Locale.US).format(ZonedDateTime.now())) + + assertEquals(Instant.ofEpochSecond(784111777), HttpUtils.parseDate("Sun Nov 6 08:49:37 1994")) + } + + + @Test + fun `listHeader with a single header value`() { + // Verify that when a header name has a single value, it's returned as a single-element array. + runBlocking { + val httpClient = getMockEngineWithDAVHeaderValues(singleHeaderValue) + val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") + assertEquals(singleHeaderValue, headersArray[0]) + assertEquals(1, headersArray.size) + } + } + + @Test + fun `listHeader with multiple comma separated header values`() { + // Verify that when a header name has multiple comma-separated values, they are correctly split into an array. + runBlocking { + val httpClient = getMockEngineWithDAVHeaderValues(multipleHeaderValues) + val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") + assertEquals(4, headersArray.size) + assertEquals("1", headersArray[0]) + assertEquals("2", headersArray[1]) + assertEquals("3", headersArray[2]) + assertEquals("hyperactive-access", headersArray[3]) + } + } + + @Test + fun `listHeader with multiple distinct header entries for the same name`() { + // Verify that if the same header name appears multiple times (e.g., 'Set-Cookie'), all values are joined by a comma and then split correctly. + runBlocking { + val httpClient = getMockEngineWithDAVHeaderValues(multipleHeaderValues, singleHeaderValue) + val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") + assertEquals(5, headersArray.size) + assertEquals("1", headersArray[0]) + assertEquals("2", headersArray[1]) + assertEquals("3", headersArray[2]) + assertEquals("hyperactive-access", headersArray[3]) + assertEquals("other", headersArray[4]) + } + } + + @Test + fun `listHeader with a header name that does not exist`() { + // Verify that when a requested header name is not present in the response, an empty array is returned. + runBlocking { + val httpClient = getMockEngineWithDAVHeaderValues(multipleHeaderValues, singleHeaderValue) + val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "other") + assertEquals(0, headersArray.size) + } + } + + @Test + fun `listHeader with an empty header value`() { + // Verify that if a header exists but its value is an empty string, an empty array is returned (due to filter { it.isNotEmpty() }). + runBlocking { + val httpClient = getMockEngineWithDAVHeaderValues("", "", "") + val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") + assertEquals(0, headersArray.size) + } + } + + @Test + fun `listHeader with a header value containing only commas`() { + // Verify that if a header value consists only of commas (e.g., ',,,') an empty array is returned. + runBlocking { + val httpClient = getMockEngineWithDAVHeaderValues(",", ",", ",") + val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") + assertEquals(0, headersArray.size) + } + } + + + @Test + fun `listHeader with header values that are themselves empty after splitting`() { + // Verify that if a header value is like 'value1,,value2', the empty string between commas is filtered out, resulting in ['value1', 'value2']. + runBlocking { + val httpClient = getMockEngineWithDAVHeaderValues(" ", singleHeaderValue, ", ") + val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") + assertEquals(1, headersArray.size) + assertEquals(singleHeaderValue, headersArray[0]) + } + } + + @Test + fun `listHeader with a case insensitive header name`() { + // HTTP header names are case-insensitive. Verify that `response.headers.getAll(name)` correctly retrieves the header regardless of the casing used for `name` (e.g., 'Content-Type' vs 'content-type'). + runBlocking { + val httpClient = getMockEngineWithDAVHeaderValues(singleHeaderValue) + val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "dav") + assertEquals(singleHeaderValue, headersArray[0]) + assertEquals(1, headersArray.size) + } + } + + @Test + fun `listHeader with an empty string as header name`() { + // Test what happens if an empty string is passed as the header name. This depends on how `response.headers.getAll` handles empty keys. + runBlocking { + val httpClient = getMockEngineWithDAVHeaderValues(singleHeaderValue) + val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "") + assertEquals(0, headersArray.size) + } + } + + + @Test + fun `Full status line with HTTP 1 1 200 code and description`() = + assertEquals(HttpStatusCode.OK, HttpUtils.parseStatusLine("HTTP/1.1 200 OK")) + + @Test + fun `Full status line with HTTP 1 0 404 code and description`() = + assertEquals(HttpStatusCode.NotFound, HttpUtils.parseStatusLine("HTTP/1.0 404 Not Found")) + + @Test + fun `Full status line with HTTP 2 503 code and multi word description`() = + assertEquals(HttpStatusCode.ServiceUnavailable, HttpUtils.parseStatusLine("HTTP/2 503 Service Unavailable")) + + @Test + fun `Full status line with HTTP 1 1 200 code and no description`() = + assertEquals(HttpStatusCode.OK, HttpUtils.parseStatusLine("HTTP/1.1 200")) + + @Test + fun `Partial status line with code and description`() = + assertEquals(HttpStatusCode.OK, HttpUtils.parseStatusLine("HTTP/1.1 200 OK")) + + @Test + fun `Partial status line with code and multi word description`() = + assertEquals(HttpStatusCode.NotFound, HttpUtils.parseStatusLine("404 Not Found")) + + @Test + fun `Partial status line with only code 200`() = + assertEquals(HttpStatusCode.OK, HttpUtils.parseStatusLine("200")) + + + @Test + fun `Partial status line with only code 404`() = + assertEquals(HttpStatusCode.NotFound, HttpUtils.parseStatusLine("404")) + + + @Test + fun `Partial status line with only a known code not having a default description`() = + assertEquals(HttpStatusCode(303, ""), HttpUtils.parseStatusLine("303")) + + @Test + fun `Partial status line with only an unknown code`() = + assertEquals(HttpStatusCode(999, ""), HttpUtils.parseStatusLine("999")) + + @Test + fun `Invalid status line empty string`() = + assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("")) + + + @Test + fun `Invalid status line just HTTP version`() = + assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("HTTP/1.1")) + + @Test + fun `Invalid status line HTTP version and non numeric code`() = + assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("HTTP/1.1 ABC OK")) + + @Test + fun `Invalid status line partial with non numeric code`() = + assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("ABC OK")) + + @Test + fun `Invalid status line just non numeric text`() = + assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("Invalid")) + + + @Test + fun `Invalid status line HTTP version malformed`() = + assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("HTTP1.1 200 OK")) + // Test a status line with a malformed HTTP version: 'HTTP1.1 200 OK'. Expects INVALID_STATUS (as it will be treated as parts.size == 3 but parts[0] doesn't start with 'HTTP/'). + + @Test + fun `Invalid status line status code with leading trailing spaces`() = + assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("HTTP/1.1 200 OK")) + + @Test + fun `Invalid status line partial code with leading trailing spaces`() = + assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine(" 200 ")) + + @Test + fun `Status line with extra spaces between code and description`() = + assertEquals(HttpStatusCode.OK, HttpUtils.parseStatusLine("HTTP/1.1 200 OK")) + + + @Test + fun `Partial status line with extra spaces between code and description`() = + assertEquals(HttpStatusCode.OK, HttpUtils.parseStatusLine("200 OK")) + + @Test + fun `Full status line with negative status code`() = + assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("HTTP/1.1 -100 Error")) + + + @Test + fun `Partial status line with negative status code`() = + assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("-100")) + + @Test + fun `Status line with only spaces`() = + assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine(" ")) + + + @Test + fun `Status line with special characters in description`() = + assertEquals(HttpStatusCode(200, "Description with !@#\$%^&*()"), HttpUtils.parseStatusLine("HTTP/1.1 200 Description with !@#\$%^&*()")) + + @Test + fun `Status line with numeric description only partial case `() = + assertEquals(HttpStatusCode(200, "404"), HttpUtils.parseStatusLine("HTTP/1.1 200 404")) +} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/PropertyTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/PropertyTest.kt new file mode 100644 index 00000000..df96e954 --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/PropertyTest.kt @@ -0,0 +1,69 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import at.bitfire.dav4jvm.ktor.property.webdav.GetETag +import org.junit.Assert.assertEquals +import org.junit.Test +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserFactory +import java.io.StringReader + +class PropertyTest { + + @Test + fun testParse_InvalidProperty() { + val parser = XmlPullParserFactory.newInstance().apply { + isNamespaceAware = true + }.newPullParser() + parser.setInput(StringReader("")) + do { + parser.next() + } while (parser.eventType != XmlPullParser.START_TAG && parser.name != "multistatus") + + // we're now at the start of + assertEquals(XmlPullParser.START_TAG, parser.eventType) + assertEquals("multistatus", parser.name) + + // parse invalid DAV:getetag + Property.parse(parser).let { + assertEquals(1, it.size) + assertEquals(GetETag(null), it[0]) + } + + // we're now at the end of + assertEquals(XmlPullParser.END_TAG, parser.eventType) + assertEquals("multistatus", parser.name) + } + + @Test + fun testParse_ValidProperty() { + val parser = XmlPullParserFactory.newInstance().apply { + isNamespaceAware = true + }.newPullParser() + parser.setInput(StringReader("12345")) + do { + parser.next() + } while (parser.eventType != XmlPullParser.START_TAG && parser.name != "multistatus") + + // we're now at the start of + assertEquals(XmlPullParser.START_TAG, parser.eventType) + assertEquals("multistatus", parser.name) + + val etag = Property.parse(parser).first() + assertEquals(GetETag("12345"), etag) + + // we're now at the end of + assertEquals(XmlPullParser.END_TAG, parser.eventType) + assertEquals("multistatus", parser.name) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/QuotedStringUtilsTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/QuotedStringUtilsTest.kt new file mode 100644 index 00000000..b48471ae --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/QuotedStringUtilsTest.kt @@ -0,0 +1,39 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import org.junit.Assert.assertEquals +import org.junit.Test + +class QuotedStringUtilsTest { + + @Test + fun testAsQuotedString() { + assertEquals("\"\"", QuotedStringUtils.asQuotedString("")) + assertEquals("\"\\\"\"", QuotedStringUtils.asQuotedString("\"")) + assertEquals("\"\\\\\"", QuotedStringUtils.asQuotedString("\\")) + } + + @Test + fun testDecodeQuotedString() { + assertEquals("\"", QuotedStringUtils.decodeQuotedString("\"")) + assertEquals("\\", QuotedStringUtils.decodeQuotedString("\"\\\"")) + assertEquals("\"test", QuotedStringUtils.decodeQuotedString("\"test")) + assertEquals("test", QuotedStringUtils.decodeQuotedString("test")) + assertEquals("", QuotedStringUtils.decodeQuotedString("\"\"")) + assertEquals("test", QuotedStringUtils.decodeQuotedString("\"test\"")) + assertEquals("test\\", QuotedStringUtils.decodeQuotedString("\"test\\\"")) + assertEquals("test", QuotedStringUtils.decodeQuotedString("\"t\\e\\st\"")) + assertEquals("12\"34", QuotedStringUtils.decodeQuotedString("\"12\\\"34\"")) + assertEquals("1234\"", QuotedStringUtils.decodeQuotedString("\"1234\\\"\"")) + } + +} diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/UrlUtilsTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/UrlUtilsTest.kt new file mode 100644 index 00000000..505864a7 --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/UrlUtilsTest.kt @@ -0,0 +1,67 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import io.ktor.http.Url +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test + +class UrlUtilsTest { + + @Test + fun testHostToDomain() { + assertNull(UrlUtils.hostToDomain(null)) + assertEquals("", UrlUtils.hostToDomain(".")) + assertEquals("com", UrlUtils.hostToDomain("com")) + assertEquals("com", UrlUtils.hostToDomain("com.")) + assertEquals("example.com", UrlUtils.hostToDomain("example.com")) + assertEquals("example.com", UrlUtils.hostToDomain("example.com.")) + assertEquals("example.com", UrlUtils.hostToDomain(".example.com")) + assertEquals("example.com", UrlUtils.hostToDomain(".example.com.")) + assertEquals("example.com", UrlUtils.hostToDomain("host.example.com")) + assertEquals("example.com", UrlUtils.hostToDomain("host.example.com.")) + assertEquals("example.com", UrlUtils.hostToDomain("sub.host.example.com")) + assertEquals("example.com", UrlUtils.hostToDomain("sub.host.example.com.")) + } + + @Test + fun testOmitTrailingSlash() { + assertEquals(Url("http://host/resource"), UrlUtils.omitTrailingSlash(Url("http://host/resource"))) + assertEquals(Url("http://host/resource"), UrlUtils.omitTrailingSlash(Url("http://host/resource/"))) + } + + @Test + fun testWithTrailingSlash() { + assertEquals(Url("http://host/resource/"), UrlUtils.withTrailingSlash(Url("http://host/resource"))) + assertEquals(Url("http://host/resource/"), UrlUtils.withTrailingSlash(Url("http://host/resource/"))) + } + + + @Test + fun testHttpUrl_EqualsForWebDAV() { + assertTrue(Url("http://host/resource").equalsForWebDAV(Url("http://host/resource"))) + assertTrue(Url("http://host:80/resource").equalsForWebDAV(Url("http://host/resource"))) + assertTrue(Url("https://HOST:443/resource").equalsForWebDAV(Url("https://host/resource"))) + assertTrue(Url("https://host:443/my@dav/").equalsForWebDAV(Url("https://host/my%40dav/"))) + assertTrue(Url("http://host/resource").equalsForWebDAV(Url("http://host/resource#frag1"))) + + assertFalse(Url("http://host/resource").equalsForWebDAV(Url("http://host/resource/"))) + assertFalse(Url("http://host/resource").equalsForWebDAV(Url("http://host:81/resource"))) + + assertTrue(Url("https://www.example.com/folder/[X]Y!.txt").equalsForWebDAV(Url("https://www.example.com/folder/[X]Y!.txt"))) + assertTrue(Url("https://www.example.com/folder/%5BX%5DY!.txt").equalsForWebDAV(Url("https://www.example.com/folder/[X]Y!.txt"))) + assertTrue(Url("https://www.example.com/folder/%5bX%5dY%21.txt").equalsForWebDAV(Url("https://www.example.com/folder/[X]Y!.txt"))) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/XmlReaderTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/XmlReaderTest.kt new file mode 100644 index 00000000..b3abeba1 --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/XmlReaderTest.kt @@ -0,0 +1,164 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor + +import io.ktor.http.ContentType +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test +import org.xmlpull.v1.XmlPullParser +import java.io.StringReader +import java.time.Instant + +class XmlReaderTest { + + @Test + fun testProcessTag_Root() { + val parser = XmlUtils.newPullParser() + parser.setInput(StringReader("")) + // now on START_DOCUMENT [0] + + var processed = false + XmlReader(parser).processTag(Property.Name("", "test")) { + processed = true + } + assertTrue(processed) + } + + @Test + fun testProcessTag_Depth1() { + val parser = XmlUtils.newPullParser() + parser.setInput(StringReader("")) + parser.next() // now on START_TAG + + var processed = false + XmlReader(parser).processTag(Property.Name("", "test")) { + processed = true + } + assertTrue(processed) + } + + + @Test + fun testReadText() { + val parser = XmlUtils.newPullParser() + parser.setInput(StringReader("Test 1Test 2")) + parser.next() + parser.next() // now on START_TAG + val reader = XmlReader(parser) + + assertEquals("Test 1", reader.readText()) + assertEquals(XmlPullParser.END_TAG, parser.eventType) + parser.next() + + assertEquals("Test 2", reader.readText()) + assertEquals(XmlPullParser.END_TAG, parser.eventType) + } + + @Test + fun testReadText_CDATA() { + val parser = XmlUtils.newPullParser() + parser.setInput(StringReader("Test 2]]>")) + parser.next() // now on START_TAG + + assertEquals("Test 1Test 2", XmlReader(parser).readText()) + assertEquals(XmlPullParser.END_TAG, parser.eventType) + } + + @Test + fun testReadText_PropertyRoot() { + val parser = XmlUtils.newPullParser() + parser.setInput(StringReader("Test 1Test 2")) + parser.next() // now on START_TAG + + val entries = mutableListOf() + XmlReader(parser).readTextPropertyList(Property.Name("", "entry"), entries) + assertEquals("Test 1", entries[0]) + assertEquals("Test 2", entries[1]) + + parser.next() // END_TAG + assertEquals(XmlPullParser.END_DOCUMENT, parser.eventType) + } + + + @Test + fun testReadTextPropertyList_Depth1() { + val parser = XmlUtils.newPullParser() + parser.setInput(StringReader("Test 1Test 2")) + parser.next() // now on START_TAG [1] + + val entries = mutableListOf() + XmlReader(parser).readTextPropertyList(Property.Name("", "entry"), entries) + assertEquals("Test 1", entries[0]) + assertEquals("Test 2", entries[1]) + assertEquals(XmlPullParser.END_TAG, parser.eventType) + assertEquals("test", parser.name) + } + + + @Test + fun testReadLong() { + val parser = XmlUtils.newPullParser() + parser.setInput(StringReader("12a")) + parser.next() + parser.next() // now on START_TAG + val reader = XmlReader(parser) + + assertEquals(1L, reader.readLong()) + assertEquals(XmlPullParser.END_TAG, parser.eventType) + parser.next() + + assertEquals(2L, reader.readLong()) + assertEquals(XmlPullParser.END_TAG, parser.eventType) + parser.next() + + assertNull(reader.readLong()) + assertEquals(XmlPullParser.END_TAG, parser.eventType) + } + + + @Test + fun testReadHttpDate() { + val parser = XmlUtils.newPullParser() + parser.setInput(StringReader("Sun, 06 Nov 1994 08:49:37 GMTSun, 06 Nov 1994 08:49:37 GMTinvalid")) + parser.next() + parser.next() // now on START_TAG + val reader = XmlReader(parser) + + assertEquals(Instant.ofEpochSecond(784111777), reader.readHttpDate()) + assertEquals(XmlPullParser.END_TAG, parser.eventType) + parser.next() + + assertEquals(Instant.ofEpochSecond(784111777), reader.readHttpDate()) + assertEquals(XmlPullParser.END_TAG, parser.eventType) + parser.next() + + assertNull(reader.readHttpDate()) + assertEquals(XmlPullParser.END_TAG, parser.eventType) + } + + + @Test + fun testReadContentTypes() { + val parser = XmlUtils.newPullParser() + parser.setInput(StringReader("text{}")) + parser.next() + val reader = XmlReader(parser) + + val types = mutableListOf() + reader.readContentTypes(Property.Name("", "test"), types::add) + assertEquals(2, types.size) + assertEquals(ContentType.Text.Plain, types[0]) + assertEquals(ContentType.Application.Json, types[1]) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/XmlUtilsTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/XmlUtilsTest.kt new file mode 100644 index 00000000..97b9529e --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/XmlUtilsTest.kt @@ -0,0 +1,29 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm + +import at.bitfire.dav4jvm.okhttp.XmlUtils +import org.junit.Assert.assertNotNull +import org.junit.Test + +class XmlUtilsTest { + + @Test + fun newPullParser() { + assertNotNull(XmlUtils.newPullParser()) + } + + @Test + fun newSerializer() { + assertNotNull(XmlUtils.newSerializer()) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/DavExceptionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/DavExceptionTest.kt new file mode 100644 index 00000000..b906a6b1 --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/DavExceptionTest.kt @@ -0,0 +1,111 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.exception + +import at.bitfire.dav4jvm.ktor.Error +import at.bitfire.dav4jvm.ktor.Property +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.client.engine.mock.respondOk +import io.ktor.client.request.get +import io.ktor.http.ContentType +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.Url +import io.ktor.http.headersOf +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.FileNotFoundException +import java.io.ObjectInputStream +import java.io.ObjectOutputStream + +class DavExceptionTest { + + val sampleUrl = Url("https://127.0.0.1/dav/") + + + @Test + fun fromResponse() { + + val mockEngine = MockEngine { request -> + respond( + status = HttpStatusCode.OK, + content = "Your Information", + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString()) + ) + } + val httpClient = HttpClient(mockEngine) { + followRedirects = false + } + val cause = Exception() + + + runBlocking { + val response = httpClient.get(sampleUrl) + val result = DavException("Message", cause, response) + + assertEquals("Message", result.message) + assertEquals(cause, result.cause) + assertEquals(200, result.statusCode) + assertEquals("GET $sampleUrl", result.requestExcerpt) + assertEquals("Your Information", result.responseExcerpt) + assertTrue(result.errors.isEmpty()) + } + } + + @Test + fun `is Java-serializable`() { + val ex = DavException( + message = "Some Error", + statusCode = 500, + requestExcerpt = "Request Body", + responseExcerpt = "Response Body", + errors = listOf( + Error(Property.Name("Serialized", "Name")) + ), + cause = FileNotFoundException() + ) + + // serialize (Java-style as in Serializable interface, not Kotlin serialization) + val blob = ByteArrayOutputStream().use { baos -> + ObjectOutputStream(baos).use { oos -> + oos.writeObject(ex) + } + baos.toByteArray() + } + + // deserialize + ByteArrayInputStream(blob).use { bais -> + ObjectInputStream(bais).use { ois -> + val actual = ois.readObject() as DavException + assertEquals(ex.message, actual.message) + assertEquals(ex.statusCode, actual.statusCode) + assertEquals(ex.requestExcerpt, actual.requestExcerpt) + assertEquals(ex.responseExcerpt, actual.responseExcerpt) + assertEquals(ex.errors, actual.errors) + assertTrue(actual.cause is FileNotFoundException) + } + } + } + + + + +} + + + + diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpExceptionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpExceptionTest.kt new file mode 100644 index 00000000..6e34ccd5 --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpExceptionTest.kt @@ -0,0 +1,145 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.exception + +import at.bitfire.dav4jvm.ktor.Error +import at.bitfire.dav4jvm.ktor.Property +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.client.request.get +import io.ktor.http.ContentType +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.headersOf +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.FileNotFoundException +import java.io.ObjectInputStream +import java.io.ObjectOutputStream + +class HttpExceptionTest { + + private val sampleUrl = "https://example.com" + + @Test + fun isRedirect() { + + val mockEngine = MockEngine { request -> + respond( + status = HttpStatusCode.Found, + content = "Your Information", + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString()) + ) + } + val httpClient = HttpClient(mockEngine) { + followRedirects = false + } + + runBlocking { + val response = httpClient.get(sampleUrl) + val result = HttpException(response, "Message") + + assertTrue(result.isRedirect) + assertFalse(result.isClientError) + assertFalse(result.isServerError) + } + } + + @Test + fun isClientError() { + + val mockEngine = MockEngine { request -> + respond( + status = HttpStatusCode.NotFound, + content = "Your Information", + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString()) + ) + } + val httpClient = HttpClient(mockEngine) { + followRedirects = false + } + + runBlocking { + val response = httpClient.get(sampleUrl) + val result = HttpException(response, "Message") + + assertFalse(result.isRedirect) + assertTrue(result.isClientError) + assertFalse(result.isServerError) + } + } + + @Test + fun isServerError() { + + val mockEngine = MockEngine { request -> + respond( + status = HttpStatusCode.InternalServerError, + content = "Your Information", + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString()) + ) + } + val httpClient = HttpClient(mockEngine) { + followRedirects = false + } + + runBlocking { + val response = httpClient.get(sampleUrl) + val result = HttpException(response, "Message") + + assertFalse(result.isRedirect) + assertFalse(result.isClientError) + assertTrue(result.isServerError) + } + } + + @Test + fun `is Java-serializable`() { + val ex = HttpException( + message = "Some Error", + statusCode = 500, + requestExcerpt = "Request Body", + responseExcerpt = "Response Body", + errors = listOf( + Error(Property.Name("Serialized", "Name")) + ), + cause = FileNotFoundException() + ) + + // serialize (Java-style as in Serializable interface, not Kotlin serialization) + val blob = ByteArrayOutputStream().use { baos -> + ObjectOutputStream(baos).use { oos -> + oos.writeObject(ex) + } + baos.toByteArray() + } + + // deserialize + ByteArrayInputStream(blob).use { bais -> + ObjectInputStream(bais).use { ois -> + val actual = ois.readObject() as HttpException + assertEquals(ex.message, actual.message) + assertEquals(ex.statusCode, actual.statusCode) + assertEquals(ex.requestExcerpt, actual.requestExcerpt) + assertEquals(ex.responseExcerpt, actual.responseExcerpt) + assertEquals(ex.errors, actual.errors) + assertTrue(actual.cause is FileNotFoundException) + } + } + } + +} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfoTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfoTest.kt new file mode 100644 index 00000000..ad34e49a --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfoTest.kt @@ -0,0 +1,216 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.exception + +import at.bitfire.dav4jvm.ktor.Error +import at.bitfire.dav4jvm.ktor.Property +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.client.engine.mock.respondError +import io.ktor.client.engine.mock.respondOk +import io.ktor.client.request.get +import io.ktor.client.request.post +import io.ktor.client.request.setBody +import io.ktor.http.ContentType +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.contentType +import io.ktor.http.headersOf +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test + +class HttpResponseInfoTest { + + private val sampleUrl = "https://example.com" + + // requestExcerpt + + @Test + fun `requestExcerpt (binary blob)`() { + + val mockEngine = MockEngine { request -> + respondError( + status = HttpStatusCode.NoContent, + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString()) + ) + } + val httpClient = HttpClient(mockEngine) { + followRedirects = false + } + + runBlocking { + val response = httpClient.post(sampleUrl) { + setBody("Sample") + contentType(ContentType.parse("application/test")) + } + val result = HttpException(response, "Message") + assertEquals("POST $sampleUrl\n\n", result.requestExcerpt) + } + } + + @Test + fun `requestExcerpt (large CSS text)`() { + + val mockEngine = MockEngine { request -> + respondError( + status = HttpStatusCode.NoContent, + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString()) + ) + } + val httpClient = HttpClient(mockEngine) { + followRedirects = false + } + + runBlocking { + val result = httpClient.post(sampleUrl) { + setBody("*".repeat(DavException.Companion.MAX_EXCERPT_SIZE * 2)) + contentType(ContentType.Text.CSS) + }.let { response -> + HttpResponseInfo.fromResponse(response) + } + val truncatedText = "*".repeat(DavException.Companion.MAX_EXCERPT_SIZE) + assertEquals("POST $sampleUrl\n\n$truncatedText", result.requestExcerpt) + } + } + + + // responseExcerpt + + @Test + fun `responseExcerpt (binary blob)`() { + + val mockEngine = MockEngine { request -> + respondError( + status = HttpStatusCode.NotFound, + content = "Evil binary data", + headers = headersOf(HttpHeaders.ContentType, ContentType.Application.OctetStream.toString()) + ) + } + val httpClient = HttpClient(mockEngine) { + followRedirects = false + } + + runBlocking { + val result = httpClient.get(sampleUrl).let { response -> + HttpResponseInfo.Companion.fromResponse(response) + } + assertNull(result.responseExcerpt) + } + } + + @Test + fun `responseExcerpt (HTML)`() { + + val mockEngine = MockEngine { request -> + respondError( + status = HttpStatusCode.NotFound, + content = "Interesting details about error", + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Html.toString()) + ) + } + val httpClient = HttpClient(mockEngine) { + followRedirects = false + } + + runBlocking { + val result = httpClient.get(sampleUrl).let { response -> + HttpResponseInfo.Companion.fromResponse(response) + } + assertEquals("Interesting details about error", result.responseExcerpt) + } + } + + @Test + fun `responseExcerpt (large HTML)`() { + + val mockEngine = MockEngine { request -> + respondError( + status = HttpStatusCode.NotFound, + content = "0123456789".repeat(3 * 1024), + headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Html.toString()) + ) + } + val httpClient = HttpClient(mockEngine) { + followRedirects = false + } + + runBlocking { + val result = httpClient.get(sampleUrl).let { response -> + HttpResponseInfo.Companion.fromResponse(response) + } + assertEquals( + "0123456789".repeat(2 * 1024), // limited to 20 kB + result.responseExcerpt + ) + } + } + + + @Test + fun `responseExcerpt (no MIME type)`() { + + val mockEngine = MockEngine { request -> + respondOk( + content = "Maybe evil binary data", + ) + } + val httpClient = HttpClient(mockEngine) { + followRedirects = false + } + + runBlocking { + val result = httpClient.get(sampleUrl).let { response -> + HttpResponseInfo.Companion.fromResponse(response) + } + assertNull(result.responseExcerpt) + } + } + + @Test + fun `responseExcerpt (XML with error elements)`() { + + val xml = """ + + + + /locked/ + + + """.trimIndent() + + val mockEngine = MockEngine { request -> + respond( + status = HttpStatusCode.OK, + headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Xml.toString()), + content = xml + ) + } + val httpClient = HttpClient(mockEngine) { + followRedirects = false + } + + runBlocking { + val result = httpClient.get(sampleUrl).let { response -> + HttpResponseInfo.Companion.fromResponse(response) + } + assertEquals(xml, result.responseExcerpt) + assertEquals( + listOf( + Error(Property.Name("DAV:", "lock-token-submitted")) + ), + result.errors + ) + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableExceptionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableExceptionTest.kt new file mode 100644 index 00000000..8359c8af --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableExceptionTest.kt @@ -0,0 +1,99 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.exception + +import at.bitfire.dav4jvm.ktor.HttpUtils +import at.bitfire.dav4jvm.ktor.exception.ServiceUnavailableException +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.client.engine.mock.respondError +import io.ktor.client.request.get +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.Url +import io.ktor.http.headersOf +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test +import java.time.Instant + +class ServiceUnavailableExceptionTest { + + val mockUrl = Url("http://www.example.com") + + @Test + fun testRetryAfter_NoTime() { + + val mockEngine = MockEngine { request -> + respondError(HttpStatusCode.ServiceUnavailable) // 503 + } + val httpClient = HttpClient(mockEngine) + + runBlocking { + val e = ServiceUnavailableException(httpClient.get(mockUrl)) + assertNull(e.retryAfter) + } + } + + @Test + fun testRetryAfter_Seconds() { + val mockEngine = MockEngine { request -> + respond( + content = "", + status = HttpStatusCode.ServiceUnavailable, // 503 + headers = headersOf(HttpHeaders.RetryAfter, "120") + ) + } + val httpClient = HttpClient(mockEngine) + + runBlocking { + val response = httpClient.get(mockUrl) + val e = ServiceUnavailableException(response) + assertNotNull(e.retryAfter) + assertTrue(withinTimeRange(e.retryAfter!!, 120)) + } + } + + @Test + fun testRetryAfter_Date() { + + val after30min = Instant.now().plusSeconds(30*60) + val mockEngine = MockEngine { request -> + respondError( + status = HttpStatusCode.ServiceUnavailable, // 503 + headers = headersOf(HttpHeaders.RetryAfter, HttpUtils.formatDate(after30min)) + ) + } + val httpClient = HttpClient(mockEngine) + + runBlocking { + val response = httpClient.get(mockUrl) + val e = ServiceUnavailableException(response) + assertNotNull(e.retryAfter) + assertTrue(withinTimeRange(e.retryAfter!!, 30*60)) + } + } + + + private fun withinTimeRange(d: Instant, seconds: Long) = + d.isBefore( + Instant.now() + .plusSeconds(seconds) + .plusSeconds(5) // tolerance for test running + ) + +} + + + diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/CalendarDescriptionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/CalendarDescriptionTest.kt new file mode 100644 index 00000000..bfa92015 --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/CalendarDescriptionTest.kt @@ -0,0 +1,26 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property + +import at.bitfire.dav4jvm.ktor.property.caldav.CalendarDescription +import org.junit.Assert.assertEquals +import org.junit.Test + +class CalendarDescriptionTest: PropertyTest() { + + @Test + fun testCalendarDescription() { + val results = parseProperty("My Calendar") + val result = results.first() as CalendarDescription + assertEquals("My Calendar", result.description) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/GetETagTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/GetETagTest.kt new file mode 100644 index 00000000..67e827a3 --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/GetETagTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property + +import at.bitfire.dav4jvm.ktor.property.webdav.GetETag +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class GetETagTest: PropertyTest() { + + @Test + fun testGetETag_Strong() { + val results = parseProperty("\"Correct strong ETag\"") + val getETag = results.first() as GetETag + assertEquals("Correct strong ETag", getETag.eTag) + assertFalse(getETag.weak) + } + + @Test + fun testGetETag_Strong_NoQuotes() { + val results = parseProperty("Strong ETag without quotes") + val getETag = results.first() as GetETag + assertEquals("Strong ETag without quotes", getETag.eTag) + assertFalse(getETag.weak) + } + + @Test + fun testGetETag_Weak() { + val results = parseProperty("W/\"Correct weak ETag\"") + val getETag = results.first() as GetETag + assertEquals("Correct weak ETag", getETag.eTag) + assertTrue(getETag.weak) + } + + @Test + fun testGetETag_Weak_Empty() { + val results = parseProperty("W/") + val getETag = results.first() as GetETag + assertEquals("", getETag.eTag) + assertTrue(getETag.weak) + } + + @Test + fun testGetETag_Weak_NoQuotes() { + val results = parseProperty("W/Weak ETag without quotes") + val getETag = results.first() as GetETag + assertEquals("Weak ETag without quotes", getETag.eTag) + assertTrue(getETag.weak) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/OwnerTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/OwnerTest.kt new file mode 100644 index 00000000..d2648cdc --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/OwnerTest.kt @@ -0,0 +1,64 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property + +import at.bitfire.dav4jvm.ktor.property.webdav.Owner +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test + +class OwnerTest: PropertyTest() { + + @Test + fun testOwner_Empty() { + val results = parseProperty("") + assertTrue(results.isEmpty()) + } + + @Test + fun testOwner_PlainText() { + val results = parseProperty("https://example.com") + val owner = results.first() as Owner + assertNull(owner.href) + } + + @Test + fun testOwner_PlainTextAndHref() { + val results = parseProperty("Principal Name. mailto:owner@example.com (test)") + val owner = results.first() as Owner + assertEquals("mailto:owner@example.com", owner.href) + } + + @Test + fun testOwner_Href() { + val results = parseProperty("https://example.com") + val owner = results.first() as Owner + assertEquals("https://example.com", owner.href) + } + + @Test + fun testOwner_TwoHrefs() { + val results = parseProperty("" + + "https://example.com/owner1" + + "https://example.com/owner2" + + "") + val owner = results.first() as Owner + assertEquals("https://example.com/owner1", owner.href) + } + + @Test + fun testOwner_WithoutHref() { + val results = parseProperty("invalid") + assertTrue(results.isEmpty()) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/PropertyTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/PropertyTest.kt new file mode 100644 index 00000000..71c38866 --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/PropertyTest.kt @@ -0,0 +1,30 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property + +import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.ktor.XmlUtils +import java.io.StringReader + +open class PropertyTest { + + companion object { + + fun parseProperty(s: String): List { + val parser = XmlUtils.newPullParser() + parser.setInput(StringReader("$s")) + parser.nextTag() // move into + return Property.parse(parser) + } + + } + +} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPushTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPushTest.kt new file mode 100644 index 00000000..57d287a1 --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPushTest.kt @@ -0,0 +1,101 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.ktor.property.push + +import at.bitfire.dav4jvm.ktor.property.PropertyTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Test +import java.time.Instant + +class WebPushTest: PropertyTest() { + + @Test + fun testPushRegister() { + val results = parseProperty( + "" + + " " + + " \n" + + " https://up.example.net/yohd4yai5Phiz1wi\n" + + " BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4\n" + + " BTBZMqHH6r4Tts7J_aSIgg" + + " \n" + + " " + + " Wed, 20 Dec 2023 10:03:31 GMT" + + "") + val result = results.first() as PushRegister + assertEquals(Instant.ofEpochSecond(1703066611), result.expires) + val subscription = result.subscription?.webPushSubscription + assertEquals("https://up.example.net/yohd4yai5Phiz1wi", subscription?.pushResource?.uri?.toString()) + assertEquals("BTBZMqHH6r4Tts7J_aSIgg", subscription?.authSecret?.secret) + + val publicKey = subscription?.subscriptionPublicKey + assertEquals("p256dh", publicKey?.type) + assertEquals("BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4", publicKey?.key) + } + + @Test + fun testServiceDetection() { + val results = parseProperty( + "" + + " " + + " " + + " BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4" + + " " + + "" + + "SomeTopic") + val result = results.first() as PushTransports + + assertEquals(setOf( + // something else is ignored because it's not a recognized transport + WebPush( + VapidPublicKey( + type = "p256dh", + key = "BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4" + ) + ) + ), result.transports) + assertTrue(result.hasWebPush()) + + assertEquals("SomeTopic", (results[1] as Topic).topic) + } + + @Test + fun testMessage() { + val results = parseProperty( + """ + + O7M1nQ7cKkKTKsoS_j6Z3w + + http://example.com/sync/10 + + + + """.trimIndent() + ) + val message = results.first() as PushMessage + + val topic = message.topic + assertNotNull(topic) + assertEquals("O7M1nQ7cKkKTKsoS_j6Z3w", topic?.topic) + + val contentUpdate = message.contentUpdate + assertNotNull(contentUpdate) + val syncToken = contentUpdate?.syncToken + assertNotNull(syncToken) + assertEquals("http://example.com/sync/10", syncToken?.token) + + val propertyUpdate = message.propertyUpdate + assertNotNull(propertyUpdate) + } + +} \ No newline at end of file From 6a0c74ab9bcc845e42b612f5c7490421ade7d43c Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Sun, 21 Sep 2025 12:34:08 -0400 Subject: [PATCH 02/11] Moved Properties a level up to be shared between ktor and okhttp --- .../at/bitfire/dav4jvm/{ktor => }/Error.kt | 7 +- .../bitfire/dav4jvm/{okhttp => }/HttpUtils.kt | 51 +---- .../bitfire/dav4jvm/{okhttp => }/Property.kt | 2 +- .../dav4jvm/{okhttp => }/PropertyFactory.kt | 2 +- .../dav4jvm/{ktor => }/PropertyRegistry.kt | 87 +++++---- .../dav4jvm/{ktor => }/QuotedStringUtils.kt | 2 +- .../bitfire/dav4jvm/{okhttp => }/XmlReader.kt | 16 +- .../at/bitfire/dav4jvm/{ktor => }/XmlUtils.kt | 3 +- .../at/bitfire/dav4jvm/ktor/DavAddressBook.kt | 14 +- .../at/bitfire/dav4jvm/ktor/DavCalendar.kt | 16 +- .../at/bitfire/dav4jvm/ktor/DavCollection.kt | 8 +- .../at/bitfire/dav4jvm/ktor/DavResource.kt | 26 +-- .../ktor/{HttpUtils.kt => KtorHttpUtils.kt} | 58 +----- .../at/bitfire/dav4jvm/ktor/PropStat.kt | 8 +- .../at/bitfire/dav4jvm/ktor/Property.kt | 75 -------- .../bitfire/dav4jvm/ktor/PropertyFactory.kt | 35 ---- .../at/bitfire/dav4jvm/ktor/Response.kt | 12 +- .../at/bitfire/dav4jvm/ktor/XmlReader.kt | 182 ------------------ .../dav4jvm/ktor/exception/DavException.kt | 2 +- .../dav4jvm/ktor/exception/HttpException.kt | 4 +- .../ktor/exception/HttpResponseInfo.kt | 10 +- .../exception/ServiceUnavailableException.kt | 3 +- .../bitfire/dav4jvm/okhttp/DavAddressBook.kt | 14 +- .../at/bitfire/dav4jvm/okhttp/DavCalendar.kt | 16 +- .../bitfire/dav4jvm/okhttp/DavCollection.kt | 8 +- .../at/bitfire/dav4jvm/okhttp/DavResource.kt | 22 ++- .../kotlin/at/bitfire/dav4jvm/okhttp/Error.kt | 3 +- .../at/bitfire/dav4jvm/okhttp/OkHttpUtils.kt | 75 ++++++++ .../at/bitfire/dav4jvm/okhttp/PropStat.kt | 11 +- .../dav4jvm/okhttp/PropertyRegistry.kt | 149 -------------- .../dav4jvm/okhttp/QuotedStringUtils.kt | 40 ---- .../at/bitfire/dav4jvm/okhttp/Response.kt | 9 +- .../at/bitfire/dav4jvm/okhttp/XmlUtils.kt | 76 -------- .../okhttp/exception/HttpResponseInfo.kt | 4 +- .../exception/ServiceUnavailableException.kt | 2 +- .../okhttp/property/caldav/CalendarColor.kt | 70 ------- .../okhttp/property/caldav/CalendarData.kt | 42 ---- .../property/caldav/CalendarDescription.kt | 38 ---- .../okhttp/property/caldav/CalendarHomeSet.kt | 37 ---- .../property/caldav/CalendarProxyReadFor.kt | 35 ---- .../property/caldav/CalendarProxyWriteFor.kt | 35 ---- .../property/caldav/CalendarTimezone.kt | 37 ---- .../property/caldav/CalendarTimezoneId.kt | 37 ---- .../property/caldav/CalendarUserAddressSet.kt | 35 ---- .../dav4jvm/okhttp/property/caldav/GetCTag.kt | 35 ---- .../okhttp/property/caldav/MaxResourceSize.kt | 32 --- .../okhttp/property/caldav/ScheduleTag.kt | 50 ----- .../dav4jvm/okhttp/property/caldav/Source.kt | 37 ---- .../caldav/SupportedCalendarComponentSet.kt | 76 -------- .../property/caldav/SupportedCalendarData.kt | 51 ----- .../okhttp/property/caldav/namespace.kt | 19 -- .../okhttp/property/carddav/AddressData.kt | 44 ----- .../carddav/AddressbookDescription.kt | 37 ---- .../property/carddav/AddressbookHomeSet.kt | 37 ---- .../property/carddav/MaxResourceSize.kt | 38 ---- .../property/carddav/SupportedAddressData.kt | 54 ------ .../okhttp/property/carddav/namespace.kt | 16 -- .../property/common/HrefListProperty.kt | 48 ----- .../okhttp/property/push/AuthSecret.kt | 44 ----- .../okhttp/property/push/ContentUpdate.kt | 67 ------- .../okhttp/property/push/PropertyUpdate.kt | 60 ------ .../okhttp/property/push/PushMessage.kt | 68 ------- .../okhttp/property/push/PushRegister.kt | 77 -------- .../okhttp/property/push/PushResource.kt | 54 ------ .../okhttp/property/push/PushTransport.kt | 18 -- .../okhttp/property/push/PushTransports.kt | 56 ------ .../okhttp/property/push/Subscription.kt | 52 ----- .../property/push/SubscriptionPublicKey.kt | 49 ----- .../okhttp/property/push/SupportedTriggers.kt | 64 ------ .../dav4jvm/okhttp/property/push/Topic.kt | 44 ----- .../dav4jvm/okhttp/property/push/Trigger.kt | 59 ------ .../okhttp/property/push/VapidPublicKey.kt | 49 ----- .../dav4jvm/okhttp/property/push/WebPush.kt | 53 ----- .../property/push/WebPushSubscription.kt | 62 ------ .../dav4jvm/okhttp/property/push/namespace.kt | 19 -- .../okhttp/property/webdav/AddMember.kt | 41 ---- .../okhttp/property/webdav/CreationDate.kt | 38 ---- .../property/webdav/CurrentUserPrincipal.kt | 48 ----- .../webdav/CurrentUserPrivilegeSet.kt | 98 ---------- .../dav4jvm/okhttp/property/webdav/Depth.kt | 51 ----- .../okhttp/property/webdav/DisplayName.kt | 40 ---- .../property/webdav/GetContentLength.kt | 38 ---- .../okhttp/property/webdav/GetContentType.kt | 42 ---- .../dav4jvm/okhttp/property/webdav/GetETag.kt | 92 --------- .../okhttp/property/webdav/GetLastModified.kt | 44 ----- .../okhttp/property/webdav/GroupMembership.kt | 37 ---- .../dav4jvm/okhttp/property/webdav/Owner.kt | 40 ---- .../property/webdav/QuotaAvailableBytes.kt | 38 ---- .../okhttp/property/webdav/QuotaUsedBytes.kt | 38 ---- .../okhttp/property/webdav/ResourceType.kt | 75 -------- .../property/webdav/SupportedReportSet.kt | 60 ------ .../okhttp/property/webdav/SyncLevel.kt | 46 ----- .../okhttp/property/webdav/SyncToken.kt | 40 ---- .../okhttp/property/webdav/namespace.kt | 16 -- .../property/caldav/CalendarColor.kt | 8 +- .../property/caldav/CalendarData.kt | 8 +- .../property/caldav/CalendarDescription.kt | 8 +- .../property/caldav/CalendarHomeSet.kt | 6 +- .../property/caldav/CalendarProxyReadFor.kt | 6 +- .../property/caldav/CalendarProxyWriteFor.kt | 6 +- .../property/caldav/CalendarTimezone.kt | 8 +- .../property/caldav/CalendarTimezoneId.kt | 8 +- .../property/caldav/CalendarUserAddressSet.kt | 6 +- .../{ktor => }/property/caldav/GetCTag.kt | 8 +- .../property/caldav/MaxResourceSize.kt | 8 +- .../{ktor => }/property/caldav/ScheduleTag.kt | 10 +- .../{ktor => }/property/caldav/Source.kt | 6 +- .../caldav/SupportedCalendarComponentSet.kt | 8 +- .../property/caldav/SupportedCalendarData.kt | 16 +- .../{ktor => }/property/caldav/namespace.kt | 2 +- .../property/carddav/AddressData.kt | 8 +- .../carddav/AddressbookDescription.kt | 8 +- .../property/carddav/AddressbookHomeSet.kt | 6 +- .../property/carddav/MaxResourceSize.kt | 8 +- .../property/carddav/SupportedAddressData.kt | 20 +- .../{ktor => }/property/carddav/namespace.kt | 2 +- .../property/common/HrefListProperty.kt | 8 +- .../{ktor => }/property/push/AuthSecret.kt | 8 +- .../{ktor => }/property/push/ContentUpdate.kt | 14 +- .../property/push/PropertyUpdate.kt | 10 +- .../{ktor => }/property/push/PushMessage.kt | 8 +- .../{ktor => }/property/push/PushRegister.kt | 12 +- .../{ktor => }/property/push/PushResource.kt | 8 +- .../{ktor => }/property/push/PushTransport.kt | 4 +- .../property/push/PushTransports.kt | 8 +- .../{ktor => }/property/push/Subscription.kt | 8 +- .../property/push/SubscriptionPublicKey.kt | 8 +- .../property/push/SupportedTriggers.kt | 8 +- .../dav4jvm/{ktor => }/property/push/Topic.kt | 8 +- .../{ktor => }/property/push/Trigger.kt | 8 +- .../property/push/VapidPublicKey.kt | 8 +- .../{ktor => }/property/push/WebPush.kt | 6 +- .../property/push/WebPushSubscription.kt | 8 +- .../{ktor => }/property/push/namespace.kt | 2 +- .../{ktor => }/property/webdav/AddMember.kt | 8 +- .../property/webdav/CreationDate.kt | 8 +- .../property/webdav/CurrentUserPrincipal.kt | 8 +- .../webdav/CurrentUserPrivilegeSet.kt | 10 +- .../{ktor => }/property/webdav/Depth.kt | 8 +- .../{ktor => }/property/webdav/DisplayName.kt | 8 +- .../property/webdav/GetContentLength.kt | 8 +- .../property/webdav/GetContentType.kt | 18 +- .../{ktor => }/property/webdav/GetETag.kt | 16 +- .../property/webdav/GetLastModified.kt | 8 +- .../property/webdav/GroupMembership.kt | 6 +- .../{ktor => }/property/webdav/Owner.kt | 8 +- .../property/webdav/QuotaAvailableBytes.kt | 8 +- .../property/webdav/QuotaUsedBytes.kt | 8 +- .../property/webdav/ResourceType.kt | 12 +- .../property/webdav/SupportedReportSet.kt | 8 +- .../{ktor => }/property/webdav/SyncLevel.kt | 8 +- .../{ktor => }/property/webdav/SyncToken.kt | 8 +- .../{ktor => }/property/webdav/namespace.kt | 2 +- .../dav4jvm/{okhttp => }/HttpUtilsTest.kt | 13 +- .../dav4jvm/{okhttp => }/PropertyTest.kt | 4 +- .../dav4jvm/{okhttp => }/XmlReaderTest.kt | 8 +- .../dav4jvm/{ktor => }/XmlUtilsTest.kt | 1 - .../bitfire/dav4jvm/ktor/DavCollectionTest.kt | 7 +- .../bitfire/dav4jvm/ktor/DavResourceTest.kt | 15 +- .../at/bitfire/dav4jvm/ktor/ErrorTest.kt | 2 + ...{HttpUtilsTest.kt => KtorHttpUtilsTest.kt} | 145 +++++--------- .../at/bitfire/dav4jvm/ktor/PropertyTest.kt | 3 +- .../dav4jvm/ktor/QuotedStringUtilsTest.kt | 39 ---- .../at/bitfire/dav4jvm/ktor/XmlReaderTest.kt | 164 ---------------- .../ktor/exception/DavExceptionTest.kt | 5 +- .../ktor/exception/HttpExceptionTest.kt | 4 +- .../ktor/exception/HttpResponseInfoTest.kt | 4 +- .../ServiceUnavailableExceptionTest.kt | 3 +- .../dav4jvm/okhttp/DavCollectionTest.kt | 7 +- .../bitfire/dav4jvm/okhttp/DavResourceTest.kt | 21 +- .../at/bitfire/dav4jvm/okhttp/ErrorTest.kt | 1 + .../bitfire/dav4jvm/okhttp/OkHttpUtilsTest.kt | 28 +++ .../dav4jvm/okhttp/QuotedStringUtilsTest.kt | 1 + .../at/bitfire/dav4jvm/okhttp/XmlUtilsTest.kt | 29 --- .../okhttp/exception/DavExceptionTest.kt | 2 +- .../okhttp/exception/HttpExceptionTest.kt | 2 +- .../okhttp/exception/HttpResponseInfoTest.kt | 2 +- .../ServiceUnavailableExceptionTest.kt | 2 +- .../property/CalendarDescriptionTest.kt | 2 +- .../dav4jvm/okhttp/property/GetETagTest.kt | 2 +- .../dav4jvm/okhttp/property/OwnerTest.kt | 2 +- .../dav4jvm/okhttp/property/PropertyTest.kt | 4 +- .../okhttp/property/push/WebPushTest.kt | 7 + .../property/CalendarDescriptionTest.kt | 4 +- .../{ktor => }/property/GetETagTest.kt | 4 +- .../dav4jvm/{ktor => }/property/OwnerTest.kt | 4 +- .../{ktor => }/property/PropertyTest.kt | 6 +- .../{ktor => }/property/push/WebPushTest.kt | 4 +- 188 files changed, 638 insertions(+), 4208 deletions(-) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/Error.kt (92%) rename src/main/kotlin/at/bitfire/dav4jvm/{okhttp => }/HttpUtils.kt (63%) rename src/main/kotlin/at/bitfire/dav4jvm/{okhttp => }/Property.kt (98%) rename src/main/kotlin/at/bitfire/dav4jvm/{okhttp => }/PropertyFactory.kt (96%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/PropertyRegistry.kt (55%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/QuotedStringUtils.kt (97%) rename src/main/kotlin/at/bitfire/dav4jvm/{okhttp => }/XmlReader.kt (92%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/XmlUtils.kt (96%) rename src/main/kotlin/at/bitfire/dav4jvm/ktor/{HttpUtils.kt => KtorHttpUtils.kt} (66%) delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/Property.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/PropertyFactory.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/XmlReader.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/OkHttpUtils.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/PropertyRegistry.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/QuotedStringUtils.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/XmlUtils.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarColor.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarData.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarDescription.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarHomeSet.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarProxyReadFor.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarProxyWriteFor.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarTimezone.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarTimezoneId.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarUserAddressSet.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/GetCTag.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/MaxResourceSize.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/ScheduleTag.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/Source.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/SupportedCalendarComponentSet.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/SupportedCalendarData.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/namespace.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/AddressData.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/AddressbookDescription.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/AddressbookHomeSet.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/MaxResourceSize.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/SupportedAddressData.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/namespace.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/common/HrefListProperty.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/AuthSecret.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/ContentUpdate.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PropertyUpdate.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushMessage.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushRegister.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushResource.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushTransport.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushTransports.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/Subscription.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/SubscriptionPublicKey.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/SupportedTriggers.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/Topic.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/Trigger.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/VapidPublicKey.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/WebPush.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/WebPushSubscription.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/namespace.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/AddMember.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/CreationDate.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/CurrentUserPrincipal.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/CurrentUserPrivilegeSet.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/Depth.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/DisplayName.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetContentLength.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetContentType.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetETag.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetLastModified.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GroupMembership.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/Owner.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/QuotaAvailableBytes.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/QuotaUsedBytes.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/ResourceType.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/SupportedReportSet.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/SyncLevel.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/SyncToken.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/namespace.kt rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/CalendarColor.kt (92%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/CalendarData.kt (83%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/CalendarDescription.kt (82%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/CalendarHomeSet.kt (83%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/CalendarProxyReadFor.kt (84%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/CalendarProxyWriteFor.kt (84%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/CalendarTimezone.kt (82%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/CalendarTimezoneId.kt (82%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/CalendarUserAddressSet.kt (84%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/GetCTag.kt (80%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/MaxResourceSize.kt (81%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/ScheduleTag.kt (83%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/Source.kt (83%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/SupportedCalendarComponentSet.kt (93%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/SupportedCalendarData.kt (71%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/caldav/namespace.kt (92%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/carddav/AddressData.kt (83%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/carddav/AddressbookDescription.kt (82%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/carddav/AddressbookHomeSet.kt (83%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/carddav/MaxResourceSize.kt (81%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/carddav/SupportedAddressData.kt (63%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/carddav/namespace.kt (90%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/common/HrefListProperty.kt (88%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/AuthSecret.kt (83%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/ContentUpdate.kt (83%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/PropertyUpdate.kt (86%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/PushMessage.kt (91%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/PushRegister.kt (89%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/PushResource.kt (87%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/PushTransport.kt (83%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/PushTransports.kt (89%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/Subscription.kt (87%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/SubscriptionPublicKey.kt (86%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/SupportedTriggers.kt (90%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/Topic.kt (83%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/Trigger.kt (89%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/VapidPublicKey.kt (86%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/WebPush.kt (91%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/WebPushSubscription.kt (91%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/namespace.kt (91%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/AddMember.kt (83%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/CreationDate.kt (81%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/CurrentUserPrincipal.kt (86%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/CurrentUserPrivilegeSet.kt (93%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/Depth.kt (86%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/DisplayName.kt (82%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/GetContentLength.kt (81%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/GetContentType.kt (71%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/GetETag.kt (84%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/GetLastModified.kt (83%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/GroupMembership.kt (83%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/Owner.kt (81%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/QuotaAvailableBytes.kt (81%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/QuotaUsedBytes.kt (81%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/ResourceType.kt (89%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/SupportedReportSet.kt (90%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/SyncLevel.kt (85%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/SyncToken.kt (81%) rename src/main/kotlin/at/bitfire/dav4jvm/{ktor => }/property/webdav/namespace.kt (89%) rename src/test/kotlin/at/bitfire/dav4jvm/{okhttp => }/HttpUtilsTest.kt (79%) rename src/test/kotlin/at/bitfire/dav4jvm/{okhttp => }/PropertyTest.kt (96%) rename src/test/kotlin/at/bitfire/dav4jvm/{okhttp => }/XmlReaderTest.kt (96%) rename src/test/kotlin/at/bitfire/dav4jvm/{ktor => }/XmlUtilsTest.kt (93%) rename src/test/kotlin/at/bitfire/dav4jvm/ktor/{HttpUtilsTest.kt => KtorHttpUtilsTest.kt} (57%) delete mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/QuotedStringUtilsTest.kt delete mode 100644 src/test/kotlin/at/bitfire/dav4jvm/ktor/XmlReaderTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/okhttp/OkHttpUtilsTest.kt delete mode 100644 src/test/kotlin/at/bitfire/dav4jvm/okhttp/XmlUtilsTest.kt rename src/test/kotlin/at/bitfire/dav4jvm/{ktor => }/property/CalendarDescriptionTest.kt (87%) rename src/test/kotlin/at/bitfire/dav4jvm/{ktor => }/property/GetETagTest.kt (95%) rename src/test/kotlin/at/bitfire/dav4jvm/{ktor => }/property/OwnerTest.kt (95%) rename src/test/kotlin/at/bitfire/dav4jvm/{ktor => }/property/PropertyTest.kt (85%) rename src/test/kotlin/at/bitfire/dav4jvm/{ktor => }/property/push/WebPushTest.kt (97%) diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/Error.kt b/src/main/kotlin/at/bitfire/dav4jvm/Error.kt similarity index 92% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/Error.kt rename to src/main/kotlin/at/bitfire/dav4jvm/Error.kt index feef3824..f8408369 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/Error.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/Error.kt @@ -8,12 +8,13 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor +package at.bitfire.dav4jvm - -import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV import org.xmlpull.v1.XmlPullParser import java.io.Serializable +import kotlin.collections.plusAssign /** * Represents an XML precondition/postcondition error. Every error has a name, which is the XML element diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/HttpUtils.kt b/src/main/kotlin/at/bitfire/dav4jvm/HttpUtils.kt similarity index 63% rename from src/main/kotlin/at/bitfire/dav4jvm/okhttp/HttpUtils.kt rename to src/main/kotlin/at/bitfire/dav4jvm/HttpUtils.kt index b89a6072..78d06512 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/HttpUtils.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/HttpUtils.kt @@ -8,11 +8,8 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.okhttp +package at.bitfire.dav4jvm -import at.bitfire.dav4jvm.okhttp.HttpUtils.httpDateFormat -import okhttp3.HttpUrl -import okhttp3.Response import java.time.Instant import java.time.LocalDateTime import java.time.ZoneOffset @@ -33,52 +30,6 @@ object HttpUtils { private val logger get() = Logger.getLogger(javaClass.name) - - /** - * Gets the resource name (the last segment of the path) from an URL. - * Empty if the resource is the base directory. - * - * * `dir` for `https://example.com/dir/` - * * `file` for `https://example.com/file` - * * `` for `https://example.com` or `https://example.com/` - * - * @return resource name - */ - fun fileName(url: HttpUrl): String { - val pathSegments = url.pathSegments.dropLastWhile { it == "" } - return pathSegments.lastOrNull() ?: "" - } - - /** - * Gets all values of a header that is defined as a list [RFC 9110 5.6.1], - * regardless of they're sent as one line or as multiple lines. - * - * For instance, regardless of whether a server sends: - * - * ``` - * DAV: 1 - * DAV: 2 - * ``` - * - * or - * - * ``` - * DAV: 1, 2 - * ``` - * - * this method would return `arrayOf("1","2")` for the `DAV` header. - * - * @param response the HTTP response to evaluate - * @param name header name (for instance: `DAV`) - * - * @return all values for the given header name - */ - fun listHeader(response: Response, name: String): Array { - val value = response.headers(name).joinToString(",") - return value.split(',').filter { it.isNotEmpty() }.toTypedArray() - } - - /** * Formats a date for use in HTTP headers using [httpDateFormat]. * diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Property.kt b/src/main/kotlin/at/bitfire/dav4jvm/Property.kt similarity index 98% rename from src/main/kotlin/at/bitfire/dav4jvm/okhttp/Property.kt rename to src/main/kotlin/at/bitfire/dav4jvm/Property.kt index 5fe81435..29d60cf4 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Property.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/Property.kt @@ -8,7 +8,7 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.okhttp +package at.bitfire.dav4jvm import at.bitfire.dav4jvm.okhttp.exception.InvalidPropertyException import org.xmlpull.v1.XmlPullParser diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/PropertyFactory.kt b/src/main/kotlin/at/bitfire/dav4jvm/PropertyFactory.kt similarity index 96% rename from src/main/kotlin/at/bitfire/dav4jvm/okhttp/PropertyFactory.kt rename to src/main/kotlin/at/bitfire/dav4jvm/PropertyFactory.kt index 789d7217..b17617d7 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/PropertyFactory.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/PropertyFactory.kt @@ -8,7 +8,7 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.okhttp +package at.bitfire.dav4jvm import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParserException diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropertyRegistry.kt b/src/main/kotlin/at/bitfire/dav4jvm/PropertyRegistry.kt similarity index 55% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/PropertyRegistry.kt rename to src/main/kotlin/at/bitfire/dav4jvm/PropertyRegistry.kt index 2afcf367..cae12799 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropertyRegistry.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/PropertyRegistry.kt @@ -8,49 +8,48 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor +package at.bitfire.dav4jvm -import at.bitfire.dav4jvm.ktor.property.caldav.CalendarColor -import at.bitfire.dav4jvm.ktor.property.caldav.CalendarData -import at.bitfire.dav4jvm.ktor.property.caldav.CalendarDescription -import at.bitfire.dav4jvm.ktor.property.caldav.CalendarHomeSet -import at.bitfire.dav4jvm.ktor.property.caldav.CalendarProxyReadFor -import at.bitfire.dav4jvm.ktor.property.caldav.CalendarProxyWriteFor -import at.bitfire.dav4jvm.ktor.property.caldav.CalendarTimezone -import at.bitfire.dav4jvm.ktor.property.caldav.CalendarTimezoneId -import at.bitfire.dav4jvm.ktor.property.caldav.CalendarUserAddressSet -import at.bitfire.dav4jvm.ktor.property.caldav.GetCTag -import at.bitfire.dav4jvm.ktor.property.caldav.MaxResourceSize -import at.bitfire.dav4jvm.ktor.property.caldav.ScheduleTag -import at.bitfire.dav4jvm.ktor.property.caldav.Source -import at.bitfire.dav4jvm.ktor.property.caldav.SupportedCalendarComponentSet -import at.bitfire.dav4jvm.ktor.property.caldav.SupportedCalendarData -import at.bitfire.dav4jvm.ktor.property.carddav.AddressData -import at.bitfire.dav4jvm.ktor.property.carddav.AddressbookDescription -import at.bitfire.dav4jvm.ktor.property.carddav.AddressbookHomeSet -import at.bitfire.dav4jvm.ktor.property.carddav.SupportedAddressData -import at.bitfire.dav4jvm.ktor.property.push.PushMessage -import at.bitfire.dav4jvm.ktor.property.push.PushRegister -import at.bitfire.dav4jvm.ktor.property.push.PushTransports -import at.bitfire.dav4jvm.ktor.property.push.Subscription -import at.bitfire.dav4jvm.ktor.property.push.Topic -import at.bitfire.dav4jvm.ktor.property.push.WebPushSubscription -import at.bitfire.dav4jvm.ktor.property.webdav.AddMember -import at.bitfire.dav4jvm.ktor.property.webdav.CreationDate -import at.bitfire.dav4jvm.ktor.property.webdav.CurrentUserPrincipal -import at.bitfire.dav4jvm.ktor.property.webdav.CurrentUserPrivilegeSet -import at.bitfire.dav4jvm.ktor.property.webdav.DisplayName -import at.bitfire.dav4jvm.ktor.property.webdav.GetContentLength -import at.bitfire.dav4jvm.ktor.property.webdav.GetContentType -import at.bitfire.dav4jvm.ktor.property.webdav.GetETag -import at.bitfire.dav4jvm.ktor.property.webdav.GetLastModified -import at.bitfire.dav4jvm.ktor.property.webdav.GroupMembership -import at.bitfire.dav4jvm.ktor.property.webdav.Owner -import at.bitfire.dav4jvm.ktor.property.webdav.QuotaAvailableBytes -import at.bitfire.dav4jvm.ktor.property.webdav.QuotaUsedBytes -import at.bitfire.dav4jvm.ktor.property.webdav.ResourceType -import at.bitfire.dav4jvm.ktor.property.webdav.SupportedReportSet -import at.bitfire.dav4jvm.ktor.property.webdav.SyncToken +import at.bitfire.dav4jvm.property.caldav.CalendarColor +import at.bitfire.dav4jvm.property.caldav.CalendarData +import at.bitfire.dav4jvm.property.caldav.CalendarDescription +import at.bitfire.dav4jvm.property.caldav.CalendarHomeSet +import at.bitfire.dav4jvm.property.caldav.CalendarProxyReadFor +import at.bitfire.dav4jvm.property.caldav.CalendarProxyWriteFor +import at.bitfire.dav4jvm.property.caldav.CalendarTimezone +import at.bitfire.dav4jvm.property.caldav.CalendarTimezoneId +import at.bitfire.dav4jvm.property.caldav.CalendarUserAddressSet +import at.bitfire.dav4jvm.property.caldav.GetCTag +import at.bitfire.dav4jvm.property.caldav.ScheduleTag +import at.bitfire.dav4jvm.property.caldav.Source +import at.bitfire.dav4jvm.property.caldav.SupportedCalendarComponentSet +import at.bitfire.dav4jvm.property.caldav.SupportedCalendarData +import at.bitfire.dav4jvm.property.carddav.AddressData +import at.bitfire.dav4jvm.property.carddav.AddressbookDescription +import at.bitfire.dav4jvm.property.carddav.AddressbookHomeSet +import at.bitfire.dav4jvm.property.carddav.SupportedAddressData +import at.bitfire.dav4jvm.property.push.PushMessage +import at.bitfire.dav4jvm.property.push.PushRegister +import at.bitfire.dav4jvm.property.push.PushTransports +import at.bitfire.dav4jvm.property.push.Subscription +import at.bitfire.dav4jvm.property.push.Topic +import at.bitfire.dav4jvm.property.push.WebPushSubscription +import at.bitfire.dav4jvm.property.webdav.AddMember +import at.bitfire.dav4jvm.property.webdav.CreationDate +import at.bitfire.dav4jvm.property.webdav.CurrentUserPrincipal +import at.bitfire.dav4jvm.property.webdav.CurrentUserPrivilegeSet +import at.bitfire.dav4jvm.property.webdav.DisplayName +import at.bitfire.dav4jvm.property.webdav.GetContentLength +import at.bitfire.dav4jvm.property.webdav.GetContentType +import at.bitfire.dav4jvm.property.webdav.GetETag +import at.bitfire.dav4jvm.property.webdav.GetLastModified +import at.bitfire.dav4jvm.property.webdav.GroupMembership +import at.bitfire.dav4jvm.property.webdav.Owner +import at.bitfire.dav4jvm.property.webdav.QuotaAvailableBytes +import at.bitfire.dav4jvm.property.webdav.QuotaUsedBytes +import at.bitfire.dav4jvm.property.webdav.ResourceType +import at.bitfire.dav4jvm.property.webdav.SupportedReportSet +import at.bitfire.dav4jvm.property.webdav.SyncToken import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParserException import java.util.logging.Level @@ -93,8 +92,8 @@ object PropertyRegistry { GetETag.Factory, GetLastModified.Factory, GroupMembership.Factory, - MaxResourceSize.Factory, - at.bitfire.dav4jvm.ktor.property.carddav.MaxResourceSize.Factory, + at.bitfire.dav4jvm.property.caldav.MaxResourceSize.Factory, + at.bitfire.dav4jvm.property.carddav.MaxResourceSize.Factory, Owner.Factory, PushMessage.Factory, PushRegister.Factory, diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/QuotedStringUtils.kt b/src/main/kotlin/at/bitfire/dav4jvm/QuotedStringUtils.kt similarity index 97% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/QuotedStringUtils.kt rename to src/main/kotlin/at/bitfire/dav4jvm/QuotedStringUtils.kt index b3385cf2..e95514c7 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/QuotedStringUtils.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/QuotedStringUtils.kt @@ -8,7 +8,7 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor +package at.bitfire.dav4jvm object QuotedStringUtils { diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/XmlReader.kt b/src/main/kotlin/at/bitfire/dav4jvm/XmlReader.kt similarity index 92% rename from src/main/kotlin/at/bitfire/dav4jvm/okhttp/XmlReader.kt rename to src/main/kotlin/at/bitfire/dav4jvm/XmlReader.kt index f206abd4..13babe4a 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/XmlReader.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/XmlReader.kt @@ -8,16 +8,18 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.okhttp +package at.bitfire.dav4jvm -import at.bitfire.dav4jvm.okhttp.XmlUtils.propertyName -import at.bitfire.dav4jvm.okhttp.property.caldav.SupportedCalendarData.Companion.CONTENT_TYPE -import at.bitfire.dav4jvm.okhttp.property.caldav.SupportedCalendarData.Companion.VERSION +import at.bitfire.dav4jvm.XmlUtils.propertyName +import at.bitfire.dav4jvm.property.caldav.SupportedCalendarData.Companion.CONTENT_TYPE +import at.bitfire.dav4jvm.property.caldav.SupportedCalendarData.Companion.VERSION +import io.ktor.http.ContentType import okhttp3.MediaType import okhttp3.MediaType.Companion.toMediaTypeOrNull import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParserException import java.io.IOException +import java.lang.Exception import java.time.Instant import java.util.logging.Level import java.util.logging.Logger @@ -162,13 +164,15 @@ class XmlReader( * @param tagName The name of the tag that contains the [CONTENT_TYPE] attribute value. * @param onNewType Called every time a new [MediaType] is found. */ - fun readContentTypes(tagName: Property.Name, onNewType: (MediaType) -> Unit) { + fun readContentTypes(tagName: Property.Name, onNewType: (String) -> Unit) { try { processTag(tagName) { parser.getAttributeValue(null, CONTENT_TYPE)?.let { contentType -> var type = contentType parser.getAttributeValue(null, VERSION)?.let { version -> type += "; version=$version" } - type.toMediaTypeOrNull()?.let(onNewType) + try { + onNewType(ContentType.parse(type).toString()) + } catch (_: Exception) { } } } } catch(e: XmlPullParserException) { diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/XmlUtils.kt b/src/main/kotlin/at/bitfire/dav4jvm/XmlUtils.kt similarity index 96% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/XmlUtils.kt rename to src/main/kotlin/at/bitfire/dav4jvm/XmlUtils.kt index 63ace56f..1fd1ead2 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/XmlUtils.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/XmlUtils.kt @@ -8,9 +8,8 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor +package at.bitfire.dav4jvm -import at.bitfire.dav4jvm.ktor.XmlUtils.FEATURE_RELAXED import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParserException import org.xmlpull.v1.XmlPullParserFactory diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavAddressBook.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavAddressBook.kt index 875ce564..1621a672 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavAddressBook.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavAddressBook.kt @@ -10,12 +10,14 @@ package at.bitfire.dav4jvm.ktor -import at.bitfire.dav4jvm.ktor.XmlUtils.insertTag -import at.bitfire.dav4jvm.ktor.property.carddav.AddressData -import at.bitfire.dav4jvm.ktor.property.carddav.NS_CARDDAV -import at.bitfire.dav4jvm.ktor.property.webdav.GetContentType -import at.bitfire.dav4jvm.ktor.property.webdav.GetETag -import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.XmlUtils +import at.bitfire.dav4jvm.XmlUtils.insertTag +import at.bitfire.dav4jvm.property.carddav.AddressData +import at.bitfire.dav4jvm.property.carddav.NS_CARDDAV +import at.bitfire.dav4jvm.property.webdav.GetContentType +import at.bitfire.dav4jvm.property.webdav.GetETag +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV import io.ktor.client.HttpClient import io.ktor.client.request.prepareRequest import io.ktor.client.request.setBody diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCalendar.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCalendar.kt index aea672b5..f23a9169 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCalendar.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCalendar.kt @@ -10,13 +10,15 @@ package at.bitfire.dav4jvm.ktor -import at.bitfire.dav4jvm.ktor.XmlUtils.insertTag -import at.bitfire.dav4jvm.ktor.property.caldav.CalendarData -import at.bitfire.dav4jvm.ktor.property.caldav.NS_CALDAV -import at.bitfire.dav4jvm.ktor.property.caldav.ScheduleTag -import at.bitfire.dav4jvm.ktor.property.webdav.GetContentType -import at.bitfire.dav4jvm.ktor.property.webdav.GetETag -import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.XmlUtils +import at.bitfire.dav4jvm.XmlUtils.insertTag +import at.bitfire.dav4jvm.property.caldav.CalendarData +import at.bitfire.dav4jvm.property.caldav.NS_CALDAV +import at.bitfire.dav4jvm.property.caldav.ScheduleTag +import at.bitfire.dav4jvm.property.webdav.GetContentType +import at.bitfire.dav4jvm.property.webdav.GetETag +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV import io.ktor.client.HttpClient import io.ktor.client.request.prepareRequest import io.ktor.client.request.setBody diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCollection.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCollection.kt index 905e811c..673a70d1 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCollection.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCollection.kt @@ -10,9 +10,11 @@ package at.bitfire.dav4jvm.ktor -import at.bitfire.dav4jvm.ktor.XmlUtils.insertTag -import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV -import at.bitfire.dav4jvm.ktor.property.webdav.SyncToken +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.XmlUtils +import at.bitfire.dav4jvm.XmlUtils.insertTag +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.property.webdav.SyncToken import io.ktor.client.HttpClient import io.ktor.client.request.header import io.ktor.client.request.prepareRequest diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavResource.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavResource.kt index 98e532fa..a256ea44 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavResource.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavResource.kt @@ -10,10 +10,14 @@ package at.bitfire.dav4jvm.ktor +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.QuotedStringUtils +import at.bitfire.dav4jvm.XmlReader +import at.bitfire.dav4jvm.XmlUtils import at.bitfire.dav4jvm.ktor.Response.Companion.MULTISTATUS import at.bitfire.dav4jvm.ktor.Response.Companion.RESPONSE -import at.bitfire.dav4jvm.ktor.XmlUtils.insertTag -import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.XmlUtils.insertTag +import at.bitfire.dav4jvm.XmlUtils.propertyName import at.bitfire.dav4jvm.ktor.exception.ConflictException import at.bitfire.dav4jvm.ktor.exception.DavException import at.bitfire.dav4jvm.ktor.exception.ForbiddenException @@ -23,10 +27,10 @@ import at.bitfire.dav4jvm.ktor.exception.NotFoundException import at.bitfire.dav4jvm.ktor.exception.PreconditionFailedException import at.bitfire.dav4jvm.ktor.exception.ServiceUnavailableException import at.bitfire.dav4jvm.ktor.exception.UnauthorizedException -import at.bitfire.dav4jvm.ktor.property.caldav.NS_CALDAV -import at.bitfire.dav4jvm.ktor.property.carddav.NS_CARDDAV -import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV -import at.bitfire.dav4jvm.ktor.property.webdav.SyncToken +import at.bitfire.dav4jvm.property.caldav.NS_CALDAV +import at.bitfire.dav4jvm.property.carddav.NS_CARDDAV +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.property.webdav.SyncToken import io.ktor.client.HttpClient import io.ktor.client.request.header import io.ktor.client.request.prepareRequest @@ -161,9 +165,9 @@ open class DavResource @JvmOverloads constructor( /** - * Gets the file name of this resource. See [HttpUtils.fileName] for details. + * Gets the file name of this resource. See [KtorHttpUtils.fileName] for details. */ - fun fileName() = HttpUtils.fileName(location) + fun fileName() = KtorHttpUtils.fileName(location) /** @@ -192,7 +196,7 @@ open class DavResource @JvmOverloads constructor( checkStatus(response) callback.onCapabilities( - HttpUtils.listHeader(response, "DAV").map { it.trim() }.toSet(), + KtorHttpUtils.listHeader(response, "DAV").map { it.trim() }.toSet(), response ) @@ -278,7 +282,7 @@ open class DavResource @JvmOverloads constructor( * * @param xmlBody optional request body (used for MKCALENDAR or Extended MKCOL) * @param method HTTP MKCOL method (`MKCOL` by default, may for instance be `MKCALENDAR`) - * @param headers additional headers to send with the request + * @param headersOptional additional headers to send with the request * @param callback called for the response * * @throws IOException on I/O error @@ -737,7 +741,7 @@ open class DavResource @JvmOverloads constructor( /** * Validates a 207 Multi-Status response. * - * @param response will be checked for Multi-Status response + * @param httpResponse will be checked for Multi-Status response * * @throws DavException if the response is not a Multi-Status response */ diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/HttpUtils.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtils.kt similarity index 66% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/HttpUtils.kt rename to src/main/kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtils.kt index 760272c0..cf6fbda3 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/HttpUtils.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtils.kt @@ -10,20 +10,14 @@ package at.bitfire.dav4jvm.ktor -import at.bitfire.dav4jvm.ktor.HttpUtils.httpDateFormat import io.ktor.client.statement.HttpResponse import io.ktor.http.HttpStatusCode import io.ktor.http.Url -import java.time.Instant -import java.time.LocalDateTime -import java.time.ZoneOffset -import java.time.ZonedDateTime import java.time.format.DateTimeFormatter -import java.time.format.DateTimeParseException import java.util.Locale import java.util.logging.Logger -object HttpUtils { +object KtorHttpUtils { /** * Preferred HTTP date/time format, see RFC 7231 7.1.1.1 IMF-fixdate @@ -80,56 +74,6 @@ object HttpUtils { } - /** - * Formats a date for use in HTTP headers using [httpDateFormat]. - * - * @param date date to be formatted - * - * @return date in HTTP-date format - */ - fun formatDate(date: Instant): String = - ZonedDateTime.ofInstant(date, ZoneOffset.UTC).format(httpDateFormat) - - /** - * Parses a HTTP-date according to RFC 7231 section 7.1.1.1. - * - * @param dateStr date-time formatted in one of the three accepted formats: - * - * - preferred format (`IMF-fixdate`) - * - obsolete RFC 850 format - * - ANSI C's `asctime()` format - * - * @return date-time, or null if date could not be parsed - */ - fun parseDate(dateStr: String): Instant? { - val zonedFormats = arrayOf( - // preferred format - httpDateFormat, - - // obsolete RFC 850 format - DateTimeFormatter.ofPattern("EEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US), - ) - - // try the two formats with zone info - for (format in zonedFormats) - try { - return ZonedDateTime.parse(dateStr, format).toInstant() - } catch (ignored: DateTimeParseException) { - } - - // try ANSI C's asctime() format - try { - val formatC = DateTimeFormatter.ofPattern("EEE MMM ppd HH:mm:ss yyyy", Locale.US) - val local = LocalDateTime.parse(dateStr, formatC) - return local.atZone(ZoneOffset.UTC).toInstant() - } catch (ignored: DateTimeParseException) { - } - - // no success in parsing - logger.warning("Couldn't parse HTTP date: $dateStr, ignoring") - return null - } - /** * Parses an HTTP status line. * diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropStat.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropStat.kt index 3fad6a3d..4d2c1830 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropStat.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropStat.kt @@ -10,9 +10,11 @@ package at.bitfire.dav4jvm.ktor +import at.bitfire.dav4jvm.Error +import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.ktor.Response.Companion.STATUS -import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName -import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.XmlUtils.propertyName +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV import io.ktor.http.HttpStatusCode import org.xmlpull.v1.XmlPullParser import java.util.LinkedList @@ -47,7 +49,7 @@ data class PropStat( when (parser.propertyName()) { DavResource.Companion.PROP -> prop.addAll(Property.parse(parser)) - STATUS -> status = HttpUtils.parseStatusLine(parser.nextText()) } + STATUS -> status = KtorHttpUtils.parseStatusLine(parser.nextText()) } eventType = parser.next() } diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/Property.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/Property.kt deleted file mode 100644 index 74769398..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/Property.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.ktor - -import at.bitfire.dav4jvm.ktor.exception.InvalidPropertyException -import org.slf4j.LoggerFactory -import org.xmlpull.v1.XmlPullParser -import java.io.Serializable -import java.util.LinkedList - -/** - * Represents a WebDAV property. - * - * Every [Property] must define a static field (use `@JvmStatic`) called `NAME` of type [Property.Name], - * which will be accessed by reflection. - * - * Every [Property] should be a data class in order to be able to compare it against others, and convert to a useful - * string for debugging. - */ -interface Property { - - data class Name( - val namespace: String, - val name: String - ): Serializable { - - override fun toString() = "$namespace:$name" - - } - - companion object { - - fun parse(parser: XmlPullParser): List { - val logger = LoggerFactory.getLogger(Property::javaClass.name) - - // - val depth = parser.depth - val properties = LinkedList() - - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { - val depthBeforeParsing = parser.depth - val name = Name(parser.namespace, parser.name) - - try { - val property = PropertyRegistry.create(name, parser) - assert(parser.depth == depthBeforeParsing) - - if (property != null) { - properties.add(property) - } else - logger.info("Ignoring unknown property $name") - } catch (e: InvalidPropertyException) { - logger.warn("Ignoring invalid property", e) - } - } - - eventType = parser.next() - } - - return properties - } - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropertyFactory.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropertyFactory.kt deleted file mode 100644 index d8eaf459..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropertyFactory.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.ktor - -import org.xmlpull.v1.XmlPullParser -import org.xmlpull.v1.XmlPullParserException - -interface PropertyFactory { - - /** - * Name of the Property the factory creates, - * e.g. `Property.Name("DAV:", "displayname")` if the factory creates - * [at.bitfire.dav4jvm.ktor.property.webdav.DisplayName] objects) - */ - fun getName(): Property.Name - - /** - * Parses XML of a property and returns its data class. - * - * Implementations shouldn't make assumptions on which sub-properties are available - * or not and in doubt return an empty [Property]. - * - * @throws XmlPullParserException in case of parsing errors - */ - fun create(parser: XmlPullParser): Property - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/Response.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/Response.kt index 891cbf6f..668015dc 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/Response.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/Response.kt @@ -10,9 +10,11 @@ package at.bitfire.dav4jvm.ktor -import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName -import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV -import at.bitfire.dav4jvm.ktor.property.webdav.ResourceType +import at.bitfire.dav4jvm.Error +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.XmlUtils.propertyName +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.property.webdav.ResourceType import io.ktor.http.HttpStatusCode import io.ktor.http.URLBuilder import io.ktor.http.Url @@ -93,7 +95,7 @@ data class Response( /** * Returns the name (last path segment) of the resource. */ - fun hrefName() = HttpUtils.fileName(href) + fun hrefName() = KtorHttpUtils.fileName(href) companion object { @@ -163,7 +165,7 @@ data class Response( } } STATUS -> - status = HttpUtils.parseStatusLine(parser.nextText()) + status = KtorHttpUtils.parseStatusLine(parser.nextText()) PropStat.Companion.NAME -> PropStat.Companion.parse(parser).let { propStat += it } Error.NAME -> diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/XmlReader.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/XmlReader.kt deleted file mode 100644 index 6b08672f..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/XmlReader.kt +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.ktor - -import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName -import at.bitfire.dav4jvm.ktor.property.caldav.SupportedCalendarData -import io.ktor.http.BadContentTypeFormatException -import io.ktor.http.ContentType -import org.xmlpull.v1.XmlPullParser -import org.xmlpull.v1.XmlPullParserException -import java.io.IOException -import java.time.Instant -import java.util.logging.Level -import java.util.logging.Logger - -/** - * Reads/processes XML tags which are used for WebDAV. - * - * @param parser The parser to read from. - */ -class XmlReader( - private val parser: XmlPullParser -) { - - // base processing - - /** - * Reads child elements of the current element. Whenever a direct child with the given name is found, - * [processor] is called for each one. - */ - @Throws(IOException::class, XmlPullParserException::class) - fun processTag(name: Property.Name, processor: XmlReader.() -> Unit) { - val depth = parser.depth - var eventType = parser.eventType - while (!((eventType == XmlPullParser.END_TAG || eventType == XmlPullParser.END_DOCUMENT) && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1 && parser.propertyName() == name) - processor() - eventType = parser.next() - } - } - - /** - * Reads the inline text of the current element. - * - * For instance, if the parser is at the beginning of this XML: - * - * ``` - * text - * ``` - * - * this function will return "text". - * - * @return text or `null` if no text is found - */ - @Throws(IOException::class, XmlPullParserException::class) - fun readText(): String? { - var text: String? = null - - val depth = parser.depth - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.TEXT && parser.depth == depth) - text = parser.text - eventType = parser.next() - } - - return text - } - - /** - * Reads child elements of the current element. When a direct child with the given name is found, - * its text is returned. - * - * @param name The name of the tag to read. - * @return The text inside the tag, or `null` if the tag is not found. - */ - @Throws(IOException::class, XmlPullParserException::class) - fun readTextProperty(name: Property.Name): String? { - var result: String? = null - - val depth = parser.depth - var eventType = parser.eventType - while (!((eventType == XmlPullParser.END_TAG || eventType == XmlPullParser.END_DOCUMENT) && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1 && parser.propertyName() == name && result == null) - result = parser.nextText() - eventType = parser.next() - } - return result - } - - /** - * Reads child elements of the current element. Whenever a direct child with the given name is - * found, its text is added to the given list. - * - * @param name The name of the tag to read. - * @param list The list to add the text to. - */ - @Throws(IOException::class, XmlPullParserException::class) - fun readTextPropertyList(name: Property.Name, list: MutableCollection) { - val depth = parser.depth - var eventType = parser.eventType - while (!((eventType == XmlPullParser.END_TAG || eventType == XmlPullParser.END_DOCUMENT) && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1 && parser.propertyName() == name) - list.add(parser.nextText()) - eventType = parser.next() - } - } - - - // extended processing (uses readText etc.) - - /** - * Uses [readText] to read the tag's value (which is expected to be in _HTTP-date_ format), and converts - * it into an [Instant] using [HttpUtils.parseDate]. - * - * If the conversion fails for any reason, null is returned, and a message is displayed in log. - */ - fun readHttpDate(): Instant? { - return readText()?.let { rawDate -> - val date = HttpUtils.parseDate(rawDate) - if (date != null) - date - else { - val logger = Logger.getLogger(javaClass.name) - logger.warning("Couldn't parse HTTP-date") - null - } - } - } - - /** - * Uses [readText] to read the tag's value (which is expected to be a number), and converts it - * into a [Long] with [String.toLong]. - * - * If the conversion fails for any reason, null is returned, and a message is displayed in log. - */ - fun readLong(): Long? { - return readText()?.let { valueStr -> - try { - valueStr.toLong() - } catch(e: NumberFormatException) { - val logger = Logger.getLogger(javaClass.name) - logger.log(Level.WARNING, "Couldn't parse as Long: $valueStr", e) - null - } - } - } - - /** - * Processes all the tags named [tagName], and sends every tag that has the [at.bitfire.dav4jvm.ktor.property.caldav.SupportedCalendarData.Companion.CONTENT_TYPE] - * attribute with [onNewType]. - * - * @param tagName The name of the tag that contains the [at.bitfire.dav4jvm.ktor.property.caldav.SupportedCalendarData.Companion.CONTENT_TYPE] attribute value. - * @param onNewType Called every time a new [ContentType] is found. - */ - fun readContentTypes(tagName: Property.Name, onNewType: (ContentType) -> Unit) { - try { - processTag(tagName) { - parser.getAttributeValue(null, SupportedCalendarData.Companion.CONTENT_TYPE)?.let { contentType -> - var type = contentType - parser.getAttributeValue(null, SupportedCalendarData.Companion.VERSION)?.let { version -> type += "; version=$version" } - ContentType.parse(type).let(onNewType) - } - } - } catch(e: XmlPullParserException) { - val logger = Logger.getLogger(javaClass.name) - logger.log(Level.SEVERE, "Couldn't parse content types", e) - } catch (e: BadContentTypeFormatException) { - val logger = Logger.getLogger(javaClass.name) - logger.log(Level.SEVERE, "Couldn't parse content types", e) - } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/DavException.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/DavException.kt index cde80a83..62bc0b77 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/DavException.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/DavException.kt @@ -10,7 +10,7 @@ package at.bitfire.dav4jvm.ktor.exception -import at.bitfire.dav4jvm.ktor.Error +import at.bitfire.dav4jvm.Error import io.ktor.client.statement.HttpResponse import javax.annotation.WillNotClose diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpException.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpException.kt index 1b1bdc3c..2db6e2f3 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpException.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpException.kt @@ -10,9 +10,7 @@ package at.bitfire.dav4jvm.ktor.exception -import at.bitfire.dav4jvm.ktor.Error -import at.bitfire.dav4jvm.ktor.exception.DavException -import at.bitfire.dav4jvm.ktor.exception.HttpResponseInfo +import at.bitfire.dav4jvm.Error import io.ktor.client.statement.HttpResponse import javax.annotation.WillNotClose diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfo.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfo.kt index 2063b8ff..65f4ad8d 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfo.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfo.kt @@ -10,9 +10,9 @@ package at.bitfire.dav4jvm.ktor.exception -import at.bitfire.dav4jvm.ktor.Error -import at.bitfire.dav4jvm.ktor.XmlUtils -import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.Error +import at.bitfire.dav4jvm.XmlUtils +import at.bitfire.dav4jvm.XmlUtils.propertyName import at.bitfire.dav4jvm.ktor.exception.DavException.Companion.MAX_EXCERPT_SIZE import io.ktor.client.statement.HttpResponse import io.ktor.client.statement.bodyAsChannel @@ -116,8 +116,8 @@ internal class HttpResponseInfo private constructor( var eventType = parser.eventType while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG && parser.depth == 1) - if (parser.propertyName() == at.bitfire.dav4jvm.ktor.Error.NAME) - return at.bitfire.dav4jvm.ktor.Error.parseError(parser) + if (parser.propertyName() == Error.NAME) + return Error.parseError(parser) eventType = parser.next() } } catch (_: XmlPullParserException) { diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableException.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableException.kt index e48e2b70..69ea46fa 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableException.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableException.kt @@ -10,6 +10,7 @@ package at.bitfire.dav4jvm.ktor.exception +import at.bitfire.dav4jvm.HttpUtils import at.bitfire.dav4jvm.ktor.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_DEFAULT import at.bitfire.dav4jvm.ktor.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_MAX import at.bitfire.dav4jvm.ktor.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_MIN @@ -36,7 +37,7 @@ class ServiceUnavailableException(response: HttpResponse) : HttpException(respon var retryAfterValue: Instant? = null response.headers["Retry-After"]?.let { after -> - retryAfterValue = at.bitfire.dav4jvm.okhttp.HttpUtils.parseDate(after) ?: + retryAfterValue = HttpUtils.parseDate(after) ?: // not a HTTP-date, must be delta-seconds try { val seconds = after.toLong() diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavAddressBook.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavAddressBook.kt index bbb628e0..1e0028f1 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavAddressBook.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavAddressBook.kt @@ -10,12 +10,14 @@ package at.bitfire.dav4jvm.okhttp -import at.bitfire.dav4jvm.okhttp.XmlUtils.insertTag -import at.bitfire.dav4jvm.okhttp.property.carddav.AddressData -import at.bitfire.dav4jvm.okhttp.property.carddav.NS_CARDDAV -import at.bitfire.dav4jvm.okhttp.property.webdav.GetContentType -import at.bitfire.dav4jvm.okhttp.property.webdav.GetETag -import at.bitfire.dav4jvm.okhttp.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.XmlUtils +import at.bitfire.dav4jvm.XmlUtils.insertTag +import at.bitfire.dav4jvm.property.carddav.AddressData +import at.bitfire.dav4jvm.property.carddav.NS_CARDDAV +import at.bitfire.dav4jvm.property.webdav.GetContentType +import at.bitfire.dav4jvm.property.webdav.GetETag +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV import okhttp3.HttpUrl import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavCalendar.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavCalendar.kt index 45246fd2..8ea9bdc4 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavCalendar.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavCalendar.kt @@ -10,15 +10,17 @@ package at.bitfire.dav4jvm.okhttp -import at.bitfire.dav4jvm.okhttp.XmlUtils.insertTag +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.XmlUtils +import at.bitfire.dav4jvm.XmlUtils.insertTag import at.bitfire.dav4jvm.okhttp.exception.DavException import at.bitfire.dav4jvm.okhttp.exception.HttpException -import at.bitfire.dav4jvm.okhttp.property.caldav.CalendarData -import at.bitfire.dav4jvm.okhttp.property.caldav.NS_CALDAV -import at.bitfire.dav4jvm.okhttp.property.caldav.ScheduleTag -import at.bitfire.dav4jvm.okhttp.property.webdav.GetContentType -import at.bitfire.dav4jvm.okhttp.property.webdav.GetETag -import at.bitfire.dav4jvm.okhttp.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.property.caldav.CalendarData +import at.bitfire.dav4jvm.property.caldav.NS_CALDAV +import at.bitfire.dav4jvm.property.caldav.ScheduleTag +import at.bitfire.dav4jvm.property.webdav.GetContentType +import at.bitfire.dav4jvm.property.webdav.GetETag +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV import okhttp3.HttpUrl import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavCollection.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavCollection.kt index 541e8555..94a5e408 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavCollection.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavCollection.kt @@ -10,11 +10,13 @@ package at.bitfire.dav4jvm.okhttp -import at.bitfire.dav4jvm.okhttp.XmlUtils.insertTag +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.XmlUtils +import at.bitfire.dav4jvm.XmlUtils.insertTag import at.bitfire.dav4jvm.okhttp.exception.DavException import at.bitfire.dav4jvm.okhttp.exception.HttpException -import at.bitfire.dav4jvm.okhttp.property.webdav.NS_WEBDAV -import at.bitfire.dav4jvm.okhttp.property.webdav.SyncToken +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.property.webdav.SyncToken import okhttp3.HttpUrl import okhttp3.OkHttpClient import okhttp3.Request diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavResource.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavResource.kt index 019d7721..5966eda6 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavResource.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavResource.kt @@ -10,9 +10,13 @@ package at.bitfire.dav4jvm.okhttp +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.QuotedStringUtils +import at.bitfire.dav4jvm.XmlReader +import at.bitfire.dav4jvm.XmlUtils import at.bitfire.dav4jvm.okhttp.DavResource.Companion.MAX_REDIRECTS -import at.bitfire.dav4jvm.okhttp.XmlUtils.insertTag -import at.bitfire.dav4jvm.okhttp.XmlUtils.propertyName +import at.bitfire.dav4jvm.XmlUtils.insertTag +import at.bitfire.dav4jvm.XmlUtils.propertyName import at.bitfire.dav4jvm.okhttp.exception.ConflictException import at.bitfire.dav4jvm.okhttp.exception.DavException import at.bitfire.dav4jvm.okhttp.exception.ForbiddenException @@ -22,10 +26,10 @@ import at.bitfire.dav4jvm.okhttp.exception.NotFoundException import at.bitfire.dav4jvm.okhttp.exception.PreconditionFailedException import at.bitfire.dav4jvm.okhttp.exception.ServiceUnavailableException import at.bitfire.dav4jvm.okhttp.exception.UnauthorizedException -import at.bitfire.dav4jvm.okhttp.property.caldav.NS_CALDAV -import at.bitfire.dav4jvm.okhttp.property.carddav.NS_CARDDAV -import at.bitfire.dav4jvm.okhttp.property.webdav.NS_WEBDAV -import at.bitfire.dav4jvm.okhttp.property.webdav.SyncToken +import at.bitfire.dav4jvm.property.caldav.NS_CALDAV +import at.bitfire.dav4jvm.property.carddav.NS_CARDDAV +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.property.webdav.SyncToken import okhttp3.Headers import okhttp3.HttpUrl import okhttp3.MediaType.Companion.toMediaType @@ -145,9 +149,9 @@ open class DavResource @JvmOverloads constructor( /** - * Gets the file name of this resource. See [HttpUtils.fileName] for details. + * Gets the file name of this resource. See [at.bitfire.dav4jvm.HttpUtils.fileName] for details. */ - fun fileName() = HttpUtils.fileName(location) + fun fileName() = OkHttpUtils.fileName(location) /** @@ -178,7 +182,7 @@ open class DavResource @JvmOverloads constructor( response.use { checkStatus(response) callback.onCapabilities( - HttpUtils.listHeader(response, "DAV").map { it.trim() }.toSet(), + OkHttpUtils.listHeader(response, "DAV").map { it.trim() }.toSet(), response ) } diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Error.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Error.kt index 8c0caa0a..62a20d1e 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Error.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Error.kt @@ -10,7 +10,8 @@ package at.bitfire.dav4jvm.okhttp -import at.bitfire.dav4jvm.okhttp.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV import org.xmlpull.v1.XmlPullParser import java.io.Serializable diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/OkHttpUtils.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/OkHttpUtils.kt new file mode 100644 index 00000000..51ce5941 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/OkHttpUtils.kt @@ -0,0 +1,75 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.okhttp + +import okhttp3.HttpUrl +import okhttp3.Response +import java.time.format.DateTimeFormatter +import java.util.Locale +import java.util.logging.Logger + +object OkHttpUtils { + + /** + * Preferred HTTP date/time format, see RFC 7231 7.1.1.1 IMF-fixdate + */ + private const val httpDateFormatStr = "EEE, dd MMM yyyy HH:mm:ss ZZZZ" + private val httpDateFormat = DateTimeFormatter.ofPattern(httpDateFormatStr, Locale.US) + + private val logger + get() = Logger.getLogger(javaClass.name) + + + /** + * Gets the resource name (the last segment of the path) from an URL. + * Empty if the resource is the base directory. + * + * * `dir` for `https://example.com/dir/` + * * `file` for `https://example.com/file` + * * `` for `https://example.com` or `https://example.com/` + * + * @return resource name + */ + fun fileName(url: HttpUrl): String { + val pathSegments = url.pathSegments.dropLastWhile { it == "" } + return pathSegments.lastOrNull() ?: "" + } + + /** + * Gets all values of a header that is defined as a list [RFC 9110 5.6.1], + * regardless of they're sent as one line or as multiple lines. + * + * For instance, regardless of whether a server sends: + * + * ``` + * DAV: 1 + * DAV: 2 + * ``` + * + * or + * + * ``` + * DAV: 1, 2 + * ``` + * + * this method would return `arrayOf("1","2")` for the `DAV` header. + * + * @param response the HTTP response to evaluate + * @param name header name (for instance: `DAV`) + * + * @return all values for the given header name + */ + fun listHeader(response: Response, name: String): Array { + val value = response.headers(name).joinToString(",") + return value.split(',').filter { it.isNotEmpty() }.toTypedArray() + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/PropStat.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/PropStat.kt index a178588f..3d60f81b 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/PropStat.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/PropStat.kt @@ -10,8 +10,9 @@ package at.bitfire.dav4jvm.okhttp -import at.bitfire.dav4jvm.okhttp.XmlUtils.propertyName -import at.bitfire.dav4jvm.okhttp.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.XmlUtils.propertyName +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV import okhttp3.Protocol import okhttp3.internal.http.StatusLine import org.xmlpull.v1.XmlPullParser @@ -24,9 +25,9 @@ import java.util.LinkedList * */ data class PropStat( - val properties: List, - val status: StatusLine, - val error: List? = null + val properties: List, + val status: StatusLine, + val error: List? = null ) { companion object { diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/PropertyRegistry.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/PropertyRegistry.kt deleted file mode 100644 index 92752f4f..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/PropertyRegistry.kt +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp - -import at.bitfire.dav4jvm.okhttp.property.caldav.CalendarColor -import at.bitfire.dav4jvm.okhttp.property.caldav.CalendarData -import at.bitfire.dav4jvm.okhttp.property.caldav.CalendarDescription -import at.bitfire.dav4jvm.okhttp.property.caldav.CalendarHomeSet -import at.bitfire.dav4jvm.okhttp.property.caldav.CalendarProxyReadFor -import at.bitfire.dav4jvm.okhttp.property.caldav.CalendarProxyWriteFor -import at.bitfire.dav4jvm.okhttp.property.caldav.CalendarTimezone -import at.bitfire.dav4jvm.okhttp.property.caldav.CalendarTimezoneId -import at.bitfire.dav4jvm.okhttp.property.caldav.CalendarUserAddressSet -import at.bitfire.dav4jvm.okhttp.property.caldav.GetCTag -import at.bitfire.dav4jvm.okhttp.property.caldav.ScheduleTag -import at.bitfire.dav4jvm.okhttp.property.caldav.Source -import at.bitfire.dav4jvm.okhttp.property.caldav.SupportedCalendarComponentSet -import at.bitfire.dav4jvm.okhttp.property.caldav.SupportedCalendarData -import at.bitfire.dav4jvm.okhttp.property.carddav.AddressData -import at.bitfire.dav4jvm.okhttp.property.carddav.AddressbookDescription -import at.bitfire.dav4jvm.okhttp.property.carddav.AddressbookHomeSet -import at.bitfire.dav4jvm.okhttp.property.carddav.SupportedAddressData -import at.bitfire.dav4jvm.okhttp.property.push.PushMessage -import at.bitfire.dav4jvm.okhttp.property.push.PushRegister -import at.bitfire.dav4jvm.okhttp.property.push.PushTransports -import at.bitfire.dav4jvm.okhttp.property.push.Subscription -import at.bitfire.dav4jvm.okhttp.property.push.Topic -import at.bitfire.dav4jvm.okhttp.property.push.WebPushSubscription -import at.bitfire.dav4jvm.okhttp.property.webdav.AddMember -import at.bitfire.dav4jvm.okhttp.property.webdav.CreationDate -import at.bitfire.dav4jvm.okhttp.property.webdav.CurrentUserPrincipal -import at.bitfire.dav4jvm.okhttp.property.webdav.CurrentUserPrivilegeSet -import at.bitfire.dav4jvm.okhttp.property.webdav.DisplayName -import at.bitfire.dav4jvm.okhttp.property.webdav.GetContentLength -import at.bitfire.dav4jvm.okhttp.property.webdav.GetContentType -import at.bitfire.dav4jvm.okhttp.property.webdav.GetETag -import at.bitfire.dav4jvm.okhttp.property.webdav.GetLastModified -import at.bitfire.dav4jvm.okhttp.property.webdav.GroupMembership -import at.bitfire.dav4jvm.okhttp.property.webdav.Owner -import at.bitfire.dav4jvm.okhttp.property.webdav.QuotaAvailableBytes -import at.bitfire.dav4jvm.okhttp.property.webdav.QuotaUsedBytes -import at.bitfire.dav4jvm.okhttp.property.webdav.ResourceType -import at.bitfire.dav4jvm.okhttp.property.webdav.SupportedReportSet -import at.bitfire.dav4jvm.okhttp.property.webdav.SyncToken -import org.xmlpull.v1.XmlPullParser -import org.xmlpull.v1.XmlPullParserException -import java.util.logging.Level -import java.util.logging.Logger - -object PropertyRegistry { - - private val factories = mutableMapOf() - private val logger - get() = Logger.getLogger(javaClass.name) - - - init { - logger.info("Registering DAV property factories") - registerDefaultFactories() - } - - private fun registerDefaultFactories() { - register(listOf( - AddMember.Factory, - AddressbookDescription.Factory, - AddressbookHomeSet.Factory, - AddressData.Factory, - CalendarColor.Factory, - CalendarData.Factory, - CalendarDescription.Factory, - CalendarHomeSet.Factory, - CalendarProxyReadFor.Factory, - CalendarProxyWriteFor.Factory, - CalendarTimezone.Factory, - CalendarTimezoneId.Factory, - CalendarUserAddressSet.Factory, - CreationDate.Factory, - CurrentUserPrincipal.Factory, - CurrentUserPrivilegeSet.Factory, - DisplayName.Factory, - GetContentLength.Factory, - GetContentType.Factory, - GetCTag.Factory, - GetETag.Factory, - GetLastModified.Factory, - GroupMembership.Factory, - at.bitfire.dav4jvm.okhttp.property.caldav.MaxResourceSize.Factory, - at.bitfire.dav4jvm.okhttp.property.carddav.MaxResourceSize.Factory, - Owner.Factory, - PushMessage.Factory, - PushRegister.Factory, - PushTransports.Factory, - QuotaAvailableBytes.Factory, - QuotaUsedBytes.Factory, - ResourceType.Factory, - ScheduleTag.Factory, - Source.Factory, - Subscription.Factory, - SupportedAddressData.Factory, - SupportedCalendarComponentSet.Factory, - SupportedCalendarData.Factory, - SupportedReportSet.Factory, - SyncToken.Factory, - Topic.Factory, - WebPushSubscription.Factory - )) - } - - - /** - * Registers a property factory, so that objects for all WebDAV properties which are handled - * by this factory can be created. - * - * @param factory property factory to be registered - */ - fun register(factory: PropertyFactory) { - logger.fine("Registering ${factory::class.java.name} for ${factory.getName()}") - factories[factory.getName()] = factory - } - - /** - * Registers some property factories, so that objects for all WebDAV properties which are handled - * by these factories can be created. - - * @param factories property factories to be registered - */ - fun register(factories: Iterable) { - factories.forEach { - register(it) - } - } - - fun create(name: Property.Name, parser: XmlPullParser) = - try { - factories[name]?.create(parser) - } catch (e: XmlPullParserException) { - logger.log(Level.WARNING, "Couldn't parse $name", e) - null - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/QuotedStringUtils.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/QuotedStringUtils.kt deleted file mode 100644 index 15cf4b2e..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/QuotedStringUtils.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp - -object QuotedStringUtils { - - fun asQuotedString(raw: String) = - "\"" + raw.replace("\\" ,"\\\\").replace("\"", "\\\"") + "\"" - - fun decodeQuotedString(quoted: String): String { - /* quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) - qdtext = > - quoted-pair = "\" CHAR - */ - - val len = quoted.length - if (len >= 2 && quoted[0] == '"' && quoted[len-1] == '"') { - val result = StringBuffer(len) - var pos = 1 - while (pos < len-1) { - var c = quoted[pos] - if (c == '\\' && pos != len-2) - c = quoted[++pos] - result.append(c) - pos++ - } - return result.toString() - } else - return quoted - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Response.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Response.kt index 1cb5e542..a3302f02 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Response.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Response.kt @@ -10,9 +10,10 @@ package at.bitfire.dav4jvm.okhttp -import at.bitfire.dav4jvm.okhttp.XmlUtils.propertyName -import at.bitfire.dav4jvm.okhttp.property.webdav.NS_WEBDAV -import at.bitfire.dav4jvm.okhttp.property.webdav.ResourceType +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.XmlUtils.propertyName +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.property.webdav.ResourceType import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.Protocol @@ -93,7 +94,7 @@ data class Response( /** * Returns the name (last path segment) of the resource. */ - fun hrefName() = HttpUtils.fileName(href) + fun hrefName() = OkHttpUtils.fileName(href) companion object { diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/XmlUtils.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/XmlUtils.kt deleted file mode 100644 index 55787c54..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/XmlUtils.kt +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp - -import at.bitfire.dav4jvm.okhttp.XmlUtils.FEATURE_RELAXED -import org.xmlpull.v1.XmlPullParser -import org.xmlpull.v1.XmlPullParserException -import org.xmlpull.v1.XmlPullParserFactory -import org.xmlpull.v1.XmlSerializer - -object XmlUtils { - - /** - * Requests the parser to be as lenient as possible when parsing invalid XML. - * - * See [https://www.xmlpull.org/](xmlpull.org) and specific implementations, for instance - * [Android XML](https://developer.android.com/reference/android/util/Xml#FEATURE_RELAXED) - */ - private const val FEATURE_RELAXED = "http://xmlpull.org/v1/doc/features.html#relaxed" - - /** [XmlPullParserFactory] that is namespace-aware and does relaxed parsing */ - private val relaxedFactory = - XmlPullParserFactory.newInstance().apply { - isNamespaceAware = true - setFeature(FEATURE_RELAXED, true) - } - - /** [XmlPullParserFactory] that is namespace-aware */ - private val standardFactory: XmlPullParserFactory = - XmlPullParserFactory.newInstance().apply { - isNamespaceAware = true - } - - /** - * Creates a new [XmlPullParser]. - * - * First tries to create a namespace-aware parser that supports [FEATURE_RELAXED]. If that - * fails, it falls back to a namespace-aware parser without relaxed parsing. - * - * @throws XmlPullParserException when no parser could be created - */ - fun newPullParser(): XmlPullParser = - try { - relaxedFactory.newPullParser() - } catch (_: XmlPullParserException) { - // FEATURE_RELAXED may not be supported, try without it - null - } - ?: standardFactory.newPullParser() - - fun newSerializer(): XmlSerializer = standardFactory.newSerializer() - - - fun XmlSerializer.insertTag(name: Property.Name, contentGenerator: XmlSerializer.() -> Unit = {}) { - startTag(name.namespace, name.name) - contentGenerator(this) - endTag(name.namespace, name.name) - } - - fun XmlPullParser.propertyName(): Property.Name { - val propNs = namespace - val propName = name - if (propNs == null || propName == null) - throw IllegalStateException("Current event must be START_TAG or END_TAG") - return Property.Name(propNs, propName) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfo.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfo.kt index 463d6983..d4746fea 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfo.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfo.kt @@ -11,8 +11,8 @@ package at.bitfire.dav4jvm.okhttp.exception import at.bitfire.dav4jvm.okhttp.Error -import at.bitfire.dav4jvm.okhttp.XmlUtils -import at.bitfire.dav4jvm.okhttp.XmlUtils.propertyName +import at.bitfire.dav4jvm.XmlUtils +import at.bitfire.dav4jvm.XmlUtils.propertyName import at.bitfire.dav4jvm.okhttp.exception.DavException.Companion.MAX_EXCERPT_SIZE import okhttp3.MediaType import okhttp3.Response diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/ServiceUnavailableException.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/ServiceUnavailableException.kt index 41c970e4..4dce87cc 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/ServiceUnavailableException.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/ServiceUnavailableException.kt @@ -10,7 +10,7 @@ package at.bitfire.dav4jvm.okhttp.exception -import at.bitfire.dav4jvm.okhttp.HttpUtils +import at.bitfire.dav4jvm.HttpUtils import at.bitfire.dav4jvm.okhttp.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_DEFAULT import at.bitfire.dav4jvm.okhttp.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_MAX import at.bitfire.dav4jvm.okhttp.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_MIN diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarColor.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarColor.kt deleted file mode 100644 index 0445d9c6..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarColor.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser -import java.util.logging.Level -import java.util.logging.Logger -import java.util.regex.Pattern - -data class CalendarColor( - val color: Int? -): Property { - - companion object { - @JvmField - val NAME = Property.Name(NS_APPLE_ICAL, "calendar-color") - - private val PATTERN = Pattern.compile("#?(\\p{XDigit}{6})(\\p{XDigit}{2})?")!! - - /** - * Converts a WebDAV color from one of these formats: - * #RRGGBB (alpha = 0xFF) - * RRGGBB (alpha = 0xFF) - * #RRGGBBAA - * RRGGBBAA - * to an [Int] with alpha. - */ - @Throws(IllegalArgumentException::class) - fun parseARGBColor(davColor: String): Int { - val m = PATTERN.matcher(davColor) - if (m.find()) { - val color_rgb = Integer.parseInt(m.group(1), 16) - val color_alpha = m.group(2)?.let { Integer.parseInt(m.group(2), 16) and 0xFF } ?: 0xFF - return (color_alpha shl 24) or color_rgb - } else - throw IllegalArgumentException("Couldn't parse color value: $davColor") - } - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): CalendarColor { - XmlReader(parser).readText()?.let { - try { - return CalendarColor(parseARGBColor(it)) - } catch (e: IllegalArgumentException) { - val logger = Logger.getLogger(javaClass.name) - logger.log(Level.WARNING, "Couldn't parse color, ignoring", e) - } - } - return CalendarColor(null) - } - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarData.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarData.kt deleted file mode 100644 index 537b8e07..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarData.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class CalendarData( - val iCalendar: String? -): Property { - - companion object { - @JvmField - val NAME = Property.Name(NS_CALDAV, "calendar-data") - - // attributes - const val CONTENT_TYPE = "content-type" - const val VERSION = "version" - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = - // - CalendarData(XmlReader(parser).readText()) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarDescription.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarDescription.kt deleted file mode 100644 index 89dc4723..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarDescription.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class CalendarDescription( - val description: String? -): Property { - - companion object { - @JvmField - val NAME = Property.Name(NS_CALDAV, "calendar-description") - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = - // - CalendarDescription(XmlReader(parser).readText()) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarHomeSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarHomeSet.kt deleted file mode 100644 index d93cf0d4..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarHomeSet.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.property.common.HrefListProperty -import org.xmlpull.v1.XmlPullParser - -data class CalendarHomeSet( - override val hrefs: List = emptyList() -): HrefListProperty(hrefs) { - - companion object { - - @JvmField - val NAME = Property.Name(NS_CALDAV, "calendar-home-set") - - } - - - object Factory: HrefListProperty.Factory() { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = create(parser, ::CalendarHomeSet) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarProxyReadFor.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarProxyReadFor.kt deleted file mode 100644 index d233b91c..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarProxyReadFor.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.property.common.HrefListProperty -import org.xmlpull.v1.XmlPullParser - -data class CalendarProxyReadFor( - override val hrefs: List = emptyList() -): HrefListProperty(hrefs) { - - companion object { - @JvmField - val NAME = Property.Name(NS_CALENDARSERVER, "calendar-proxy-read-for") - } - - - object Factory: HrefListProperty.Factory() { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = create(parser, ::CalendarProxyReadFor) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarProxyWriteFor.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarProxyWriteFor.kt deleted file mode 100644 index 2ba98ca2..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarProxyWriteFor.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.property.common.HrefListProperty -import org.xmlpull.v1.XmlPullParser - -data class CalendarProxyWriteFor( - override val hrefs: List = emptyList() -): HrefListProperty(hrefs) { - - companion object { - @JvmField - val NAME = Property.Name(NS_CALENDARSERVER, "calendar-proxy-write-for") - } - - - object Factory: HrefListProperty.Factory() { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = create(parser, ::CalendarProxyWriteFor) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarTimezone.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarTimezone.kt deleted file mode 100644 index bf6bf095..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarTimezone.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class CalendarTimezone( - val vTimeZone: String? -): Property { - - companion object { - @JvmField - val NAME = Property.Name(NS_CALDAV, "calendar-timezone") - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = - // - CalendarTimezone(XmlReader(parser).readText()) - - } -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarTimezoneId.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarTimezoneId.kt deleted file mode 100644 index c827230d..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarTimezoneId.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class CalendarTimezoneId( - val identifier: String? -): Property { - - companion object { - @JvmField - val NAME = Property.Name(NS_CALDAV, "calendar-timezone-id") - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = - // - CalendarTimezoneId(XmlReader(parser).readText()) - - } -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarUserAddressSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarUserAddressSet.kt deleted file mode 100644 index e316f3be..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/CalendarUserAddressSet.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.property.common.HrefListProperty -import org.xmlpull.v1.XmlPullParser - -data class CalendarUserAddressSet( - override val hrefs: List = emptyList() -): HrefListProperty(hrefs) { - - companion object { - @JvmField - val NAME = Property.Name(NS_CALDAV, "calendar-user-address-set") - } - - - object Factory: HrefListProperty.Factory() { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = create(parser, ::CalendarUserAddressSet) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/GetCTag.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/GetCTag.kt deleted file mode 100644 index 329950ca..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/GetCTag.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class GetCTag( - val cTag: String? -): Property { - - companion object { - @JvmField - val NAME = Property.Name(NS_CALENDARSERVER, "getctag") - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = GetCTag(XmlReader(parser).readText()) - - } -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/MaxResourceSize.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/MaxResourceSize.kt deleted file mode 100644 index 1fe86a91..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/MaxResourceSize.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class MaxResourceSize( - val maxSize: Long? -) : Property { - companion object { - @JvmField - val NAME = Property.Name(NS_CALDAV, "max-resource-size") - } - - object Factory: PropertyFactory { - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = - MaxResourceSize(XmlReader(parser).readLong()) - } -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/ScheduleTag.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/ScheduleTag.kt deleted file mode 100644 index 30b990bd..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/ScheduleTag.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.QuotedStringUtils -import at.bitfire.dav4jvm.okhttp.XmlReader -import okhttp3.Response -import org.xmlpull.v1.XmlPullParser - -data class ScheduleTag( - val rawScheduleTag: String? -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_CALDAV, "schedule-tag") - - fun fromResponse(response: Response) = - response.header("Schedule-Tag")?.let { ScheduleTag(it) } - - } - - /* Value: opaque-tag - opaque-tag = quoted-string - */ - val scheduleTag: String? = rawScheduleTag?.let { QuotedStringUtils.decodeQuotedString(it) } - - override fun toString() = scheduleTag ?: "(null)" - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = ScheduleTag(XmlReader(parser).readText()) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/Source.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/Source.kt deleted file mode 100644 index d3e9c66a..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/Source.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.property.common.HrefListProperty -import org.xmlpull.v1.XmlPullParser - -class Source( - override val hrefs: List = emptyList() -): HrefListProperty(hrefs) { - - companion object { - - @JvmField - val NAME = Property.Name(NS_CALENDARSERVER, "source") - - } - - - object Factory: HrefListProperty.Factory() { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = create(parser, ::Source) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/SupportedCalendarComponentSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/SupportedCalendarComponentSet.kt deleted file mode 100644 index 57d5bfdb..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/SupportedCalendarComponentSet.kt +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlUtils.propertyName -import org.xmlpull.v1.XmlPullParser - -data class SupportedCalendarComponentSet( - val supportsEvents: Boolean, - val supportsTasks: Boolean, - val supportsJournal: Boolean -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_CALDAV, "supported-calendar-component-set") - - val ALLCOMP = Property.Name(NS_CALDAV, "allcomp") - val COMP = Property.Name(NS_CALDAV, "comp") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): SupportedCalendarComponentSet { - /* - - - */ - var components = SupportedCalendarComponentSet( - supportsEvents = false, - supportsTasks = false, - supportsJournal = false - ) - - val depth = parser.depth - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { - when (parser.propertyName()) { - ALLCOMP -> { - components = SupportedCalendarComponentSet( - supportsEvents = true, - supportsTasks = true, - supportsJournal = true - ) - } - COMP -> - when (parser.getAttributeValue(null, "name")?.uppercase()) { - "VEVENT" -> components = components.copy(supportsEvents = true) - "VTODO" -> components = components.copy(supportsTasks = true) - "VJOURNAL" -> components = components.copy(supportsJournal = true) - } - } - } - eventType = parser.next() - } - - return components - } - } -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/SupportedCalendarData.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/SupportedCalendarData.kt deleted file mode 100644 index 49d98c07..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/SupportedCalendarData.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import okhttp3.MediaType -import org.xmlpull.v1.XmlPullParser - -data class SupportedCalendarData( - val types: Set = emptySet() -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_CALDAV, "supported-calendar-data") - - val CALENDAR_DATA_TYPE = Property.Name(NS_CALDAV, "calendar-data") - const val CONTENT_TYPE = "content-type" - const val VERSION = "version" - - } - - fun hasJCal() = types.any { "application".equals(it.type, true) && "calendar+json".equals(it.subtype, true) } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): SupportedCalendarData { - val supportedTypes = mutableSetOf() - - XmlReader(parser).readContentTypes(CALENDAR_DATA_TYPE, supportedTypes::add) - - return SupportedCalendarData(supportedTypes) - } - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/namespace.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/namespace.kt deleted file mode 100644 index 719224c5..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/caldav/namespace.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.caldav - -/** - * CalDAV XML namespace (defined in RFC 4791) - */ -const val NS_CALDAV = "urn:ietf:params:xml:ns:caldav" - -const val NS_APPLE_ICAL = "http://apple.com/ns/ical/" -const val NS_CALENDARSERVER = "http://calendarserver.org/ns/" diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/AddressData.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/AddressData.kt deleted file mode 100644 index bef162f2..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/AddressData.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.carddav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class AddressData( - val card: String? -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_CARDDAV, "address-data") - - // attributes - const val CONTENT_TYPE = "content-type" - const val VERSION = "version" - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = - // - AddressData(XmlReader(parser).readText()) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/AddressbookDescription.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/AddressbookDescription.kt deleted file mode 100644 index 0ee8d3f5..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/AddressbookDescription.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.carddav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class AddressbookDescription( - val description: String? = null -): Property { - - companion object { - @JvmField - val NAME = Property.Name(NS_CARDDAV, "addressbook-description") - } - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = - // - AddressbookDescription(XmlReader(parser).readText()) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/AddressbookHomeSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/AddressbookHomeSet.kt deleted file mode 100644 index 56a5b3d3..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/AddressbookHomeSet.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.carddav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.property.common.HrefListProperty -import org.xmlpull.v1.XmlPullParser - -class AddressbookHomeSet( - override val hrefs: List = emptyList() -): HrefListProperty(hrefs) { - - companion object { - - @JvmField - val NAME = Property.Name(NS_CARDDAV, "addressbook-home-set") - - } - - - object Factory: HrefListProperty.Factory() { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = create(parser, ::AddressbookHomeSet) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/MaxResourceSize.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/MaxResourceSize.kt deleted file mode 100644 index 252a66dd..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/MaxResourceSize.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.carddav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class MaxResourceSize( - val maxSize: Long? -) : Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_CARDDAV, "max-resource-size") - - } - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = - MaxResourceSize(XmlReader(parser).readLong()) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/SupportedAddressData.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/SupportedAddressData.kt deleted file mode 100644 index d5faeef3..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/SupportedAddressData.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.carddav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import okhttp3.MediaType -import org.xmlpull.v1.XmlPullParser - -class SupportedAddressData( - val types: Set = emptySet() -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_CARDDAV, "supported-address-data") - - val ADDRESS_DATA_TYPE = Property.Name(NS_CARDDAV, "address-data-type") - const val CONTENT_TYPE = "content-type" - const val VERSION = "version" - - } - - fun hasVCard4() = types.any { "text/vcard; version=4.0".equals(it.toString(), true) } - fun hasJCard() = types.any { "application".equals(it.type, true) && "vcard+json".equals(it.subtype, true) } - - override fun toString() = "[${types.joinToString(", ")}]" - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): SupportedAddressData { - val supportedTypes = mutableSetOf() - - XmlReader(parser).readContentTypes(ADDRESS_DATA_TYPE, supportedTypes::add) - - return SupportedAddressData(supportedTypes) - } - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/namespace.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/namespace.kt deleted file mode 100644 index fd6e10aa..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/carddav/namespace.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.carddav - -/** - * CardDAV XML namespace (defined in RFC 6352) - */ -const val NS_CARDDAV = "urn:ietf:params:xml:ns:carddav" diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/common/HrefListProperty.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/common/HrefListProperty.kt deleted file mode 100644 index 3067a652..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/common/HrefListProperty.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.common - -import at.bitfire.dav4jvm.okhttp.DavResource -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -/** - * Represents a list of hrefs. - * - * Every [HrefListProperty] must be a data class. - */ -abstract class HrefListProperty( - open val hrefs: List -): Property { - - abstract class Factory : PropertyFactory { - - @Deprecated("hrefs is no longer mutable.", level = DeprecationLevel.ERROR) - fun create(parser: XmlPullParser, list: HrefListProperty): HrefListProperty { - val hrefs = list.hrefs.toMutableList() - XmlReader(parser).readTextPropertyList(DavResource.Companion.HREF, hrefs) - return list - } - - fun create( - parser: XmlPullParser, - constructor: (hrefs: List - ) -> PropertyType): PropertyType { - val hrefs = mutableListOf() - XmlReader(parser).readTextPropertyList(DavResource.Companion.HREF, hrefs) - return constructor(hrefs) - } - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/AuthSecret.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/AuthSecret.kt deleted file mode 100644 index 26c67850..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/AuthSecret.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -/** - * Represents a [NS_WEBDAV_PUSH]`:auth-secret` property. - * - * Experimental! See https://github.com/bitfireAT/webdav-push/ - */ -data class AuthSecret( - val secret: String? = null -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "auth-secret") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): AuthSecret = - AuthSecret(XmlReader(parser).readText()) - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/ContentUpdate.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/ContentUpdate.kt deleted file mode 100644 index 55cf00c7..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/ContentUpdate.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlUtils.propertyName -import at.bitfire.dav4jvm.okhttp.property.webdav.Depth -import at.bitfire.dav4jvm.okhttp.property.webdav.SyncLevel -import at.bitfire.dav4jvm.okhttp.property.webdav.SyncToken -import org.xmlpull.v1.XmlPullParser - -/** - * Represents a [NS_WEBDAV_PUSH]`:content-update` property. - * - * Experimental! See https://github.com/bitfireAT/webdav-push/ - */ -data class ContentUpdate( - val depth: Depth? = null, - val syncToken: SyncToken? = null -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "content-update") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): ContentUpdate { - var contentUpdate = ContentUpdate() - - val depth = parser.depth - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { - when (parser.propertyName()) { - SyncLevel.NAME -> contentUpdate = contentUpdate.copy( - depth = Depth.Factory.create(parser) - ) - SyncToken.NAME -> contentUpdate = contentUpdate.copy( - syncToken = SyncToken.Factory.create(parser) - ) - } - } - eventType = parser.next() - } - - return contentUpdate - } - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PropertyUpdate.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PropertyUpdate.kt deleted file mode 100644 index 81087a70..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PropertyUpdate.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlUtils.propertyName -import at.bitfire.dav4jvm.okhttp.property.webdav.SyncLevel -import org.xmlpull.v1.XmlPullParser - -/** - * Represents a [NS_WEBDAV_PUSH]`:property-update` property. - * - * Experimental! See https://github.com/bitfireAT/webdav-push/ - */ -data class PropertyUpdate( - val syncLevel: SyncLevel? = null, -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "property-update") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): PropertyUpdate { - var propertyUpdate = PropertyUpdate() - - val depth = parser.depth - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { - when (parser.propertyName()) { - SyncLevel.NAME -> propertyUpdate = propertyUpdate.copy( - syncLevel = SyncLevel.Factory.create(parser) - ) - } - } - eventType = parser.next() - } - return propertyUpdate - } - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushMessage.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushMessage.kt deleted file mode 100644 index 8b7bbf7e..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushMessage.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlUtils.propertyName -import org.xmlpull.v1.XmlPullParser - -/** - * Represents a [NS_WEBDAV_PUSH]`:push-message` property. - * - * Experimental! See https://github.com/bitfireAT/webdav-push/ - */ -data class PushMessage( - val topic: Topic? = null, - val contentUpdate: ContentUpdate? = null, - val propertyUpdate: PropertyUpdate? = null -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "push-message") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): PushMessage { - var message = PushMessage() - - val depth = parser.depth - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { - when (parser.propertyName()) { - Topic.NAME -> message = message.copy( - topic = Topic.Factory.create(parser) - ) - ContentUpdate.NAME -> message = message.copy( - contentUpdate = ContentUpdate.Factory.create(parser) - ) - PropertyUpdate.NAME -> message = message.copy( - propertyUpdate = PropertyUpdate.Factory.create(parser) - ) - } - } - eventType = parser.next() - } - - return message - } - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushRegister.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushRegister.kt deleted file mode 100644 index 50d3a583..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushRegister.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.HttpUtils -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import at.bitfire.dav4jvm.okhttp.XmlUtils.propertyName -import org.xmlpull.v1.XmlPullParser -import java.time.Instant - -/** - * Represents a [NS_WEBDAV_PUSH]`:push-register` property. - * - * Experimental! See https://github.com/bitfireAT/webdav-push/ - */ -data class PushRegister( - val expires: Instant? = null, - val subscription: Subscription? = null, - val trigger: Trigger? = null -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "push-register") - - val EXPIRES = Property.Name(NS_WEBDAV_PUSH, "expires") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): PushRegister { - var register = PushRegister() - - val depth = parser.depth - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) - when (parser.propertyName()) { - EXPIRES -> - register = register.copy( - expires = XmlReader(parser).readText()?.let { - HttpUtils.parseDate(it) - } - ) - Subscription.NAME -> - register = register.copy( - subscription = Subscription.Factory.create(parser) - ) - Trigger.NAME -> - register = register.copy( - trigger = Trigger.Factory.create(parser) - ) - } - eventType = parser.next() - } - - return register - } - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushResource.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushResource.kt deleted file mode 100644 index 9b8e18cb..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushResource.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser -import java.net.URI -import java.net.URISyntaxException - -/** - * Represents a [NS_WEBDAV_PUSH]`:push-resource` property. - * - * Experimental! See https://github.com/bitfireAT/webdav-push/ - */ -data class PushResource( - val uri: URI? = null -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "push-resource") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): PushResource = - PushResource( - uri = XmlReader(parser).readText()?.let { uri -> - try { - URI(uri) - } catch (_: URISyntaxException) { - null - } - } - ) - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushTransport.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushTransport.kt deleted file mode 100644 index c11c09bc..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushTransport.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.Property - -/** - * Identifies a property as a push transport. - */ -interface PushTransport: Property diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushTransports.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushTransports.kt deleted file mode 100644 index beec7fae..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/PushTransports.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlUtils.propertyName -import org.xmlpull.v1.XmlPullParser - -/** - * Represents a [NS_WEBDAV_PUSH]`:push-transports` property. - * - * Experimental! See https://github.com/bitfireAT/webdav-push/ - */ -class PushTransports private constructor( - val transports: Set -): Property { - - companion object { - @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "transports") - } - - fun hasWebPush() = transports.any { it is WebPush } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): PushTransports { - val transports = mutableListOf() - val depth = parser.depth - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { - when (parser.propertyName()) { - WebPush.NAME -> transports += WebPush.Factory.create(parser) - } - } - eventType = parser.next() - } - return PushTransports(transports.toSet()) - } - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/Subscription.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/Subscription.kt deleted file mode 100644 index 99d647de..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/Subscription.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -/** - * Represents a [NS_WEBDAV_PUSH]`:subscription` property. - * - * Experimental! See https://github.com/bitfireAT/webdav-push/ - */ -data class Subscription private constructor( - val webPushSubscription: WebPushSubscription? = null -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "subscription") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): Subscription { - // currently we only support WebPushSubscription - var webPushSubscription: WebPushSubscription? = null - - XmlReader(parser).processTag(WebPushSubscription.NAME) { - webPushSubscription = WebPushSubscription.Factory.create(parser) - } - - return Subscription(webPushSubscription) - } - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/SubscriptionPublicKey.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/SubscriptionPublicKey.kt deleted file mode 100644 index f5cb25d0..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/SubscriptionPublicKey.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -/** - * Represents a [NS_WEBDAV_PUSH]`:subscription-public-key` property. - * - * Experimental! See https://github.com/bitfireAT/webdav-push/ - */ -data class SubscriptionPublicKey( - val type: String? = null, - val key: String? = null -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "subscription-public-key") - - } - - - object Factory : PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): SubscriptionPublicKey { - return SubscriptionPublicKey( - type = parser.getAttributeValue(null, "type"), - key = XmlReader(parser).readText() - ) - } - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/SupportedTriggers.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/SupportedTriggers.kt deleted file mode 100644 index c365ed7e..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/SupportedTriggers.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlUtils.propertyName -import org.xmlpull.v1.XmlPullParser - -/** - * Represents a [NS_WEBDAV_PUSH]`:content-update` property. - * - * Experimental! See https://github.com/bitfireAT/webdav-push/ - */ -data class SupportedTriggers( - val contentUpdate: ContentUpdate? = null, - val propertyUpdate: PropertyUpdate? = null -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "supported-triggers") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): SupportedTriggers { - var supportedTriggers = SupportedTriggers() - - val depth = parser.depth - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { - when (parser.propertyName()) { - ContentUpdate.NAME -> supportedTriggers = supportedTriggers.copy( - contentUpdate = ContentUpdate.Factory.create(parser) - ) - PropertyUpdate.NAME -> supportedTriggers = supportedTriggers.copy( - propertyUpdate = PropertyUpdate.Factory.create(parser) - ) - } - } - eventType = parser.next() - } - - return supportedTriggers - } - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/Topic.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/Topic.kt deleted file mode 100644 index 25e2b8d3..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/Topic.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -/** - * Represents a [NS_WEBDAV_PUSH]`:topic` property. - * - * Experimental! See https://github.com/bitfireAT/webdav-push/ - */ -data class Topic( - val topic: String? = null -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "topic") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): Topic = - Topic(XmlReader(parser).readText()) - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/Trigger.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/Trigger.kt deleted file mode 100644 index 9ba65b08..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/Trigger.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlUtils.propertyName -import org.xmlpull.v1.XmlPullParser - -data class Trigger( - val contentUpdate: ContentUpdate? = null, - val propertyUpdate: PropertyUpdate? = null -) : Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "trigger") - - } - - - object Factory : PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): Trigger { - var trigger = Trigger() - - val depth = parser.depth - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { - when (parser.propertyName()) { - ContentUpdate.NAME -> trigger = trigger.copy( - contentUpdate = ContentUpdate.Factory.create(parser) - ) - PropertyUpdate.NAME -> trigger = trigger.copy( - propertyUpdate = PropertyUpdate.Factory.create(parser) - ) - } - } - eventType = parser.next() - } - - return trigger - } - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/VapidPublicKey.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/VapidPublicKey.kt deleted file mode 100644 index 3e174b17..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/VapidPublicKey.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -/** - * Represents a [NS_WEBDAV_PUSH]`:vapid-public-key` property. - * - * Experimental! See https://github.com/bitfireAT/webdav-push/ - */ -data class VapidPublicKey( - val type: String? = null, - val key: String? = null -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "vapid-public-key") - - } - - - object Factory : PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): VapidPublicKey { - return VapidPublicKey( - type = parser.getAttributeValue(null, "type"), - key = XmlReader(parser).readText() - ) - } - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/WebPush.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/WebPush.kt deleted file mode 100644 index 5000d2b0..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/WebPush.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import org.xmlpull.v1.XmlPullParser - -/** - * Represents a [NS_WEBDAV_PUSH]`:web-push` property. - * - * Experimental! See https://github.com/bitfireAT/webdav-push/ - */ -data class WebPush( - val vapidPublicKey: VapidPublicKey? = null -) : PushTransport { - - companion object { - @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "web-push") - } - - - object Factory : PropertyFactory { - - override fun getName(): Property.Name = NAME - - override fun create(parser: XmlPullParser): WebPush { - var vapidPublicKey: VapidPublicKey? = null - val depth = parser.depth - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.namespace == NS_WEBDAV_PUSH) { - when (parser.name) { - VapidPublicKey.NAME.name -> vapidPublicKey = VapidPublicKey.Factory.create(parser) - } - } - eventType = parser.next() - } - return WebPush(vapidPublicKey) - } - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/WebPushSubscription.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/WebPushSubscription.kt deleted file mode 100644 index 5f18fb64..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/WebPushSubscription.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlUtils.propertyName -import org.xmlpull.v1.XmlPullParser - -/** - * Represents a [NS_WEBDAV_PUSH]`:web-push-subscription` property. - * - * Experimental! See https://github.com/bitfireAT/webdav-push/ - */ -data class WebPushSubscription( - val pushResource: PushResource? = null, - val subscriptionPublicKey: SubscriptionPublicKey? = null, - val authSecret: AuthSecret? = null -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "web-push-subscription") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): WebPushSubscription { - var subscription = WebPushSubscription() - - val depth = parser.depth - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { - when (parser.propertyName()) { - PushResource.NAME -> subscription = subscription.copy(pushResource = PushResource.Factory.create(parser)) - SubscriptionPublicKey.NAME -> subscription = subscription.copy(subscriptionPublicKey = SubscriptionPublicKey.Factory.create(parser)) - AuthSecret.NAME -> subscription = subscription.copy(authSecret = AuthSecret.Factory.create(parser)) - } - } - eventType = parser.next() - } - - return subscription - } - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/namespace.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/namespace.kt deleted file mode 100644 index dae060d2..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/push/namespace.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.push - -/** - * XML namespace of WebDAV-Push (draft), see: - * https://github.com/bitfireAT/webdav-push/ - * - * Experimental! - */ -const val NS_WEBDAV_PUSH = "https://bitfire.at/webdav-push" diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/AddMember.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/AddMember.kt deleted file mode 100644 index 61b11b7c..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/AddMember.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.DavResource -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -/** - * Defined in RFC 5995 3.2.1 DAV:add-member Property (Protected). - */ -data class AddMember( - val href: String? -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "add-member") - - } - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = AddMember(XmlReader(parser).readTextProperty(DavResource.HREF)) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/CreationDate.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/CreationDate.kt deleted file mode 100644 index d6b232f5..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/CreationDate.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class CreationDate( - var creationDate: String? -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "creationdate") - - } - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = - CreationDate(XmlReader(parser).readText()) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/CurrentUserPrincipal.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/CurrentUserPrincipal.kt deleted file mode 100644 index 2d502b86..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/CurrentUserPrincipal.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.DavResource -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -// see RFC 5397: WebDAV Current Principal Extension - -data class CurrentUserPrincipal( - val href: String? -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "current-user-principal") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): CurrentUserPrincipal { - // - var href: String? = null - XmlReader(parser).processTag(DavResource.HREF) { - href = readText() - } - return CurrentUserPrincipal(href) - } - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/CurrentUserPrivilegeSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/CurrentUserPrivilegeSet.kt deleted file mode 100644 index 88e6a880..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/CurrentUserPrivilegeSet.kt +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import at.bitfire.dav4jvm.okhttp.XmlUtils.propertyName -import org.xmlpull.v1.XmlPullParser - -data class CurrentUserPrivilegeSet( - // not all privileges from RFC 3744 are implemented by now - // feel free to add more if you need them for your project - val mayRead: Boolean = false, - val mayWriteProperties: Boolean = false, - val mayWriteContent: Boolean = false, - val mayBind: Boolean = false, - val mayUnbind: Boolean = false -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "current-user-privilege-set") - - val PRIVILEGE = Property.Name(NS_WEBDAV, "privilege") - val READ = Property.Name(NS_WEBDAV, "read") - val WRITE = Property.Name(NS_WEBDAV, "write") - val WRITE_PROPERTIES = Property.Name(NS_WEBDAV, "write-properties") - val WRITE_CONTENT = Property.Name(NS_WEBDAV, "write-content") - val BIND = Property.Name(NS_WEBDAV, "bind") - val UNBIND = Property.Name(NS_WEBDAV, "unbind") - val ALL = Property.Name(NS_WEBDAV, "all") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): CurrentUserPrivilegeSet { - // - // - var privs = CurrentUserPrivilegeSet() - - XmlReader(parser).processTag(PRIVILEGE) { - val depth = parser.depth - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) - when (parser.propertyName()) { - READ -> - privs = privs.copy(mayRead = true) - WRITE -> { - privs = privs.copy( - mayBind = true, - mayUnbind = true, - mayWriteProperties = true, - mayWriteContent = true - ) - } - WRITE_PROPERTIES -> - privs = privs.copy(mayWriteProperties = true) - WRITE_CONTENT -> - privs = privs.copy(mayWriteContent = true) - BIND -> - privs = privs.copy(mayBind = true) - UNBIND -> - privs = privs.copy(mayUnbind = true) - ALL -> { - privs = privs.copy( - mayRead = true, - mayBind = true, - mayUnbind = true, - mayWriteProperties = true, - mayWriteContent = true - ) - } - } - eventType = parser.next() - } - } - - return privs - } - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/Depth.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/Depth.kt deleted file mode 100644 index b1af3047..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/Depth.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -/** - * Represents a [NS_WEBDAV]`:depth` property. - */ -data class Depth( - /** May be `0`, `1` or [Int.MAX_VALUE] (infinite). */ - val depth: Int? = null -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "depth") - - const val INFINITY = Int.MAX_VALUE - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): Depth { - val text = XmlReader(parser).readText() - val level = if (text.equals("infinity", true)) - INFINITY - else - text?.toIntOrNull() - return Depth(level) - } - - } - -} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/DisplayName.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/DisplayName.kt deleted file mode 100644 index 189c9a0d..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/DisplayName.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class DisplayName( - val displayName: String? -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "displayname") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = - // - DisplayName(XmlReader(parser).readText()) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetContentLength.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetContentLength.kt deleted file mode 100644 index 04e0165a..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetContentLength.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class GetContentLength( - val contentLength: Long? -) : Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "getcontentlength") - - } - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = - GetContentLength(XmlReader(parser).readLong()) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetContentType.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetContentType.kt deleted file mode 100644 index 3b100ad0..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetContentType.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import okhttp3.MediaType -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import org.xmlpull.v1.XmlPullParser - -data class GetContentType( - val type: MediaType? -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "getcontenttype") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = - // - GetContentType(XmlReader(parser).readText()?.toMediaTypeOrNull()) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetETag.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetETag.kt deleted file mode 100644 index eda6fb61..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetETag.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.QuotedStringUtils -import at.bitfire.dav4jvm.okhttp.XmlReader -import okhttp3.Response -import org.xmlpull.v1.XmlPullParser - -/** - * The GetETag property. - * - * Can also be used to parse ETags from HTTP responses – just pass the raw ETag - * header value to the constructor and then use [eTag] and [weak]. - */ -data class GetETag( - val rawETag: String? -): Property { - - companion object { - @JvmField - val NAME = Property.Name(NS_WEBDAV, "getetag") - - fun fromResponse(response: Response) = - response.header("ETag")?.let { GetETag(it) } - } - - /** - * The parsed ETag value, excluding the weakness indicator and the quotes. - */ - val eTag: String? - - /** - * Whether the ETag is weak. - */ - var weak: Boolean - - init { - /* entity-tag = [ weak ] opaque-tag - weak = "W/" - opaque-tag = quoted-string - */ - - if (rawETag != null) { - val tag: String? - // remove trailing "W/" - if (rawETag.startsWith("W/")) { - // entity tag is weak - tag = rawETag.substring(2) - weak = true - } else { - tag = rawETag - weak = false - } - eTag = QuotedStringUtils.decodeQuotedString(tag) - } else { - eTag = null - weak = false - } - } - - override fun equals(other: Any?): Boolean { - if (other !is GetETag) - return false - return eTag == other.eTag && weak == other.weak - } - - override fun hashCode(): Int { - return eTag.hashCode() xor weak.hashCode() - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): GetETag = - GetETag(XmlReader(parser).readText()) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetLastModified.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetLastModified.kt deleted file mode 100644 index d06cb85f..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GetLastModified.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser -import java.time.Instant - -data class GetLastModified( - val lastModified: Instant? -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "getlastmodified") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): GetLastModified { - // - return GetLastModified( - XmlReader(parser).readHttpDate() - ) - } - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GroupMembership.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GroupMembership.kt deleted file mode 100644 index 17fcae7d..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/GroupMembership.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.property.common.HrefListProperty -import org.xmlpull.v1.XmlPullParser - -class GroupMembership( - override val hrefs: List -): HrefListProperty(hrefs) { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "group-membership") - - } - - - object Factory: HrefListProperty.Factory() { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = create(parser, ::GroupMembership) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/Owner.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/Owner.kt deleted file mode 100644 index ee41c1a6..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/Owner.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.DavResource -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.XmlReader -import at.bitfire.dav4jvm.okhttp.property.common.HrefListProperty -import org.xmlpull.v1.XmlPullParser - -data class Owner( - val href: String? -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "owner") - - } - - - object Factory: HrefListProperty.Factory() { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): Owner = - Owner(XmlReader(parser).readTextProperty(DavResource.Companion.HREF)) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/QuotaAvailableBytes.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/QuotaAvailableBytes.kt deleted file mode 100644 index 3d4e1edd..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/QuotaAvailableBytes.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class QuotaAvailableBytes( - val quotaAvailableBytes: Long? -) : Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "quota-available-bytes") - - } - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = - QuotaAvailableBytes(XmlReader(parser).readLong()) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/QuotaUsedBytes.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/QuotaUsedBytes.kt deleted file mode 100644 index 67448630..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/QuotaUsedBytes.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class QuotaUsedBytes( - val quotaUsedBytes: Long? -) : Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "quota-used-bytes") - - } - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = - QuotaUsedBytes(XmlReader(parser).readLong()) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/ResourceType.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/ResourceType.kt deleted file mode 100644 index 3dad1cd8..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/ResourceType.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.property.caldav.NS_CALDAV -import at.bitfire.dav4jvm.okhttp.property.caldav.NS_CALENDARSERVER -import at.bitfire.dav4jvm.okhttp.property.carddav.NS_CARDDAV -import org.xmlpull.v1.XmlPullParser - -class ResourceType( - val types: Set = emptySet() -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "resourcetype") - - val COLLECTION = Property.Name(NS_WEBDAV, "collection") // WebDAV - val PRINCIPAL = Property.Name(NS_WEBDAV, "principal") // WebDAV ACL - val ADDRESSBOOK = Property.Name(NS_CARDDAV, "addressbook") // CardDAV - val CALENDAR = Property.Name(NS_CALDAV, "calendar") // CalDAV - - // CalendarServer extensions - val CALENDAR_PROXY_READ = Property.Name(NS_CALENDARSERVER, "calendar-proxy-read") // CalDAV Proxy - val CALENDAR_PROXY_WRITE = Property.Name(NS_CALENDARSERVER, "calendar-proxy-write") // CalDAV Proxy - val SUBSCRIBED = Property.Name(NS_CALENDARSERVER, "subscribed") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): ResourceType { - val types = mutableSetOf() - - val depth = parser.depth - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { - // use static objects to allow types.contains() - var typeName = Property.Name(parser.namespace, parser.name) - when (typeName) { // if equals(), replace by our instance - COLLECTION -> typeName = COLLECTION - PRINCIPAL -> typeName = PRINCIPAL - ADDRESSBOOK -> typeName = ADDRESSBOOK - CALENDAR -> typeName = CALENDAR - CALENDAR_PROXY_READ -> typeName = CALENDAR_PROXY_READ - CALENDAR_PROXY_WRITE -> typeName = CALENDAR_PROXY_WRITE - SUBSCRIBED -> typeName = SUBSCRIBED - } - types.add(typeName) - } - eventType = parser.next() - } - assert(parser.depth == depth) - - return ResourceType(types) - } - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/SupportedReportSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/SupportedReportSet.kt deleted file mode 100644 index c2960161..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/SupportedReportSet.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class SupportedReportSet( - val reports: Set = emptySet() -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "supported-report-set") - - val SUPPORTED_REPORT = Property.Name(NS_WEBDAV, "supported-report") - val REPORT = Property.Name(NS_WEBDAV, "report") - - const val SYNC_COLLECTION = "DAV:sync-collection" // collection synchronization (RFC 6578) - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): SupportedReportSet { - /* - - - */ - - val reports = mutableSetOf() - XmlReader(parser).processTag(SUPPORTED_REPORT) { - processTag(REPORT) { - parser.nextTag() - if (parser.eventType == XmlPullParser.TEXT) - reports += parser.text - else if (parser.eventType == XmlPullParser.START_TAG) - reports += "${parser.namespace}${parser.name}" - } - } - return SupportedReportSet(reports) - } - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/SyncLevel.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/SyncLevel.kt deleted file mode 100644 index 9a1edb8d..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/SyncLevel.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -/** - * Represents a [NS_WEBDAV]`:sync-level` property. - */ -data class SyncLevel( - /** May be `0`, `1` or [Int.MAX_VALUE] (infinite). */ - val level: Int? = null -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "sync-level") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser): SyncLevel { - val text = XmlReader(parser).readText() - val level = if (text == "infinite") Int.MAX_VALUE else text?.toIntOrNull() - return SyncLevel(level) - } - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/SyncToken.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/SyncToken.kt deleted file mode 100644 index bfa9c7c6..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/SyncToken.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.PropertyFactory -import at.bitfire.dav4jvm.okhttp.XmlReader -import org.xmlpull.v1.XmlPullParser - -data class SyncToken( - val token: String? -): Property { - - companion object { - - @JvmField - val NAME = Property.Name(NS_WEBDAV, "sync-token") - - } - - - object Factory: PropertyFactory { - - override fun getName() = NAME - - override fun create(parser: XmlPullParser) = - // - SyncToken(XmlReader(parser).readText()) - - } - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/namespace.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/namespace.kt deleted file mode 100644 index 7c0b20f2..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/property/webdav/namespace.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.property.webdav - -/** - * WebDAV XML namespace (defined in RFC 4918) - */ -const val NS_WEBDAV = "DAV:" diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarColor.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarColor.kt similarity index 92% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarColor.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarColor.kt index 616468be..996fc64c 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarColor.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarColor.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser import java.util.logging.Level import java.util.logging.Logger diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarData.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarData.kt similarity index 83% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarData.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarData.kt index e3170a15..55867f93 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarData.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarData.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class CalendarData( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarDescription.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarDescription.kt similarity index 82% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarDescription.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarDescription.kt index b77c4118..2d5cf96b 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarDescription.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarDescription.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class CalendarDescription( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarHomeSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarHomeSet.kt similarity index 83% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarHomeSet.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarHomeSet.kt index 5c3de6ed..5a975df7 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarHomeSet.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarHomeSet.kt @@ -8,10 +8,10 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.property.common.HrefListProperty import org.xmlpull.v1.XmlPullParser data class CalendarHomeSet( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarProxyReadFor.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarProxyReadFor.kt similarity index 84% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarProxyReadFor.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarProxyReadFor.kt index e36df6ed..7e674676 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarProxyReadFor.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarProxyReadFor.kt @@ -8,10 +8,10 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.property.common.HrefListProperty import org.xmlpull.v1.XmlPullParser data class CalendarProxyReadFor( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarProxyWriteFor.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarProxyWriteFor.kt similarity index 84% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarProxyWriteFor.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarProxyWriteFor.kt index 0f632ea9..d5debf2e 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarProxyWriteFor.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarProxyWriteFor.kt @@ -8,10 +8,10 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.property.common.HrefListProperty import org.xmlpull.v1.XmlPullParser data class CalendarProxyWriteFor( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarTimezone.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarTimezone.kt similarity index 82% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarTimezone.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarTimezone.kt index cea93b73..4475cbbb 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarTimezone.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarTimezone.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class CalendarTimezone( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarTimezoneId.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarTimezoneId.kt similarity index 82% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarTimezoneId.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarTimezoneId.kt index f6a3240f..d8a91b27 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarTimezoneId.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarTimezoneId.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class CalendarTimezoneId( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarUserAddressSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarUserAddressSet.kt similarity index 84% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarUserAddressSet.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarUserAddressSet.kt index aec3284e..acc1fd20 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/CalendarUserAddressSet.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarUserAddressSet.kt @@ -8,10 +8,10 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.property.common.HrefListProperty import org.xmlpull.v1.XmlPullParser data class CalendarUserAddressSet( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/GetCTag.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/GetCTag.kt similarity index 80% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/GetCTag.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/GetCTag.kt index b99a0a23..14d10673 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/GetCTag.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/GetCTag.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class GetCTag( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/MaxResourceSize.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/MaxResourceSize.kt similarity index 81% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/MaxResourceSize.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/MaxResourceSize.kt index 1bfd997b..334d4b33 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/MaxResourceSize.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/MaxResourceSize.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class MaxResourceSize( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/ScheduleTag.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt similarity index 83% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/ScheduleTag.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt index 383b2cbc..5f169d4f 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/ScheduleTag.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt @@ -8,12 +8,12 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.QuotedStringUtils -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.QuotedStringUtils +import at.bitfire.dav4jvm.XmlReader import io.ktor.client.statement.HttpResponse import io.ktor.http.HttpHeaders import org.xmlpull.v1.XmlPullParser diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/Source.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/Source.kt similarity index 83% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/Source.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/Source.kt index 2dd548ac..578d60a6 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/Source.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/Source.kt @@ -8,10 +8,10 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.property.common.HrefListProperty import org.xmlpull.v1.XmlPullParser class Source( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/SupportedCalendarComponentSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/SupportedCalendarComponentSet.kt similarity index 93% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/SupportedCalendarComponentSet.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/SupportedCalendarComponentSet.kt index 8ba3e811..eb977b42 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/SupportedCalendarComponentSet.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/SupportedCalendarComponentSet.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.propertyName import org.xmlpull.v1.XmlPullParser data class SupportedCalendarComponentSet( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/SupportedCalendarData.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/SupportedCalendarData.kt similarity index 71% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/SupportedCalendarData.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/SupportedCalendarData.kt index 56f17de6..b8cc67f9 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/SupportedCalendarData.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/SupportedCalendarData.kt @@ -8,16 +8,16 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import io.ktor.http.ContentType import org.xmlpull.v1.XmlPullParser data class SupportedCalendarData( - val types: Set = emptySet() + val types: Set = emptySet() ): Property { companion object { @@ -31,7 +31,9 @@ data class SupportedCalendarData( } - fun hasJCal() = types.any { ContentType.Application.contains(it) && "calendar+json".equals(it.contentSubtype, true) } + fun hasJCal() = types + .map { ContentType.parse(it) } + .any { ContentType.Application.contains(it) && "calendar+json".equals(it.contentSubtype, true) } object Factory: PropertyFactory { @@ -39,7 +41,7 @@ data class SupportedCalendarData( override fun getName() = NAME override fun create(parser: XmlPullParser): SupportedCalendarData { - val supportedTypes = mutableSetOf() + val supportedTypes = mutableSetOf() XmlReader(parser).readContentTypes(CALENDAR_DATA_TYPE, supportedTypes::add) diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/namespace.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/namespace.kt similarity index 92% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/namespace.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/caldav/namespace.kt index 369c35af..1f84362d 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/caldav/namespace.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/namespace.kt @@ -8,7 +8,7 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.caldav +package at.bitfire.dav4jvm.property.caldav /** * CalDAV XML namespace (defined in RFC 4791) diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressData.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/AddressData.kt similarity index 83% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressData.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/carddav/AddressData.kt index d3889b00..08260790 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressData.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/AddressData.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.carddav +package at.bitfire.dav4jvm.property.carddav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class AddressData( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressbookDescription.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/AddressbookDescription.kt similarity index 82% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressbookDescription.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/carddav/AddressbookDescription.kt index 0a21bbf6..09d4d83e 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressbookDescription.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/AddressbookDescription.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.carddav +package at.bitfire.dav4jvm.property.carddav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class AddressbookDescription( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressbookHomeSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/AddressbookHomeSet.kt similarity index 83% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressbookHomeSet.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/carddav/AddressbookHomeSet.kt index aa4903b4..ee851418 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/AddressbookHomeSet.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/AddressbookHomeSet.kt @@ -8,10 +8,10 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.carddav +package at.bitfire.dav4jvm.property.carddav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.property.common.HrefListProperty import org.xmlpull.v1.XmlPullParser class AddressbookHomeSet( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/MaxResourceSize.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/MaxResourceSize.kt similarity index 81% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/MaxResourceSize.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/carddav/MaxResourceSize.kt index e6dab87f..738902bf 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/MaxResourceSize.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/MaxResourceSize.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.carddav +package at.bitfire.dav4jvm.property.carddav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class MaxResourceSize( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/SupportedAddressData.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/SupportedAddressData.kt similarity index 63% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/SupportedAddressData.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/carddav/SupportedAddressData.kt index 1a4d6e0e..b860f81d 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/SupportedAddressData.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/SupportedAddressData.kt @@ -8,16 +8,16 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.carddav +package at.bitfire.dav4jvm.property.carddav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import io.ktor.http.ContentType import org.xmlpull.v1.XmlPullParser class SupportedAddressData( - val types: Set = emptySet() + val types: Set = emptySet() ): Property { companion object { @@ -31,8 +31,12 @@ class SupportedAddressData( } - fun hasVCard4() = types.any { "text/vcard; version=4.0".equals(it.toString(), true) } - fun hasJCard() = types.any { ContentType.Application.contains(it) && "vcard+json".equals(it.contentSubtype, true) } + fun hasVCard4() = types + .map { try { ContentType.parse(it) } catch (_: Exception) { ContentType.Any } } + .any { "text/vcard; version=4.0".equals(it.toString(), true) } + fun hasJCard() = types + .map { try { ContentType.parse(it) } catch (_: Exception) { ContentType.Any } } + .any { ContentType.Application.contains(it) && "vcard+json".equals(it.contentSubtype, true) } override fun toString() = "[${types.joinToString(", ")}]" @@ -42,7 +46,7 @@ class SupportedAddressData( override fun getName() = NAME override fun create(parser: XmlPullParser): SupportedAddressData { - val supportedTypes = mutableSetOf() + val supportedTypes = mutableSetOf() XmlReader(parser).readContentTypes(ADDRESS_DATA_TYPE, supportedTypes::add) diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/namespace.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/namespace.kt similarity index 90% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/namespace.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/carddav/namespace.kt index 8328343c..c5f98463 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/carddav/namespace.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/namespace.kt @@ -8,7 +8,7 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.carddav +package at.bitfire.dav4jvm.property.carddav /** * CardDAV XML namespace (defined in RFC 6352) diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/common/HrefListProperty.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/common/HrefListProperty.kt similarity index 88% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/common/HrefListProperty.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/common/HrefListProperty.kt index ff5f194f..6d16bc7c 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/common/HrefListProperty.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/common/HrefListProperty.kt @@ -8,12 +8,12 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.common +package at.bitfire.dav4jvm.property.common import at.bitfire.dav4jvm.ktor.DavResource -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/AuthSecret.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/AuthSecret.kt similarity index 83% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/AuthSecret.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/AuthSecret.kt index 09496893..aa925ea5 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/AuthSecret.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/AuthSecret.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/ContentUpdate.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/ContentUpdate.kt similarity index 83% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/ContentUpdate.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/ContentUpdate.kt index d92aa70d..0bafa8d2 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/ContentUpdate.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/ContentUpdate.kt @@ -8,14 +8,14 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName -import at.bitfire.dav4jvm.ktor.property.webdav.Depth -import at.bitfire.dav4jvm.ktor.property.webdav.SyncLevel -import at.bitfire.dav4jvm.ktor.property.webdav.SyncToken +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.propertyName +import at.bitfire.dav4jvm.property.webdav.Depth +import at.bitfire.dav4jvm.property.webdav.SyncLevel +import at.bitfire.dav4jvm.property.webdav.SyncToken import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PropertyUpdate.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PropertyUpdate.kt similarity index 86% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PropertyUpdate.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/PropertyUpdate.kt index 9a966405..ae2411c7 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PropertyUpdate.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PropertyUpdate.kt @@ -8,12 +8,12 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName -import at.bitfire.dav4jvm.ktor.property.webdav.SyncLevel +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.propertyName +import at.bitfire.dav4jvm.property.webdav.SyncLevel import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushMessage.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushMessage.kt similarity index 91% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushMessage.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/PushMessage.kt index c1784edc..baa03b00 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushMessage.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushMessage.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.propertyName import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushRegister.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushRegister.kt similarity index 89% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushRegister.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/PushRegister.kt index ce63c48b..0aa73c76 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushRegister.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushRegister.kt @@ -8,13 +8,13 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.HttpUtils -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader -import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.HttpUtils +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader +import at.bitfire.dav4jvm.XmlUtils.propertyName import org.xmlpull.v1.XmlPullParser import java.time.Instant diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushResource.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushResource.kt similarity index 87% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushResource.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/PushResource.kt index f701dafc..aa17f6d4 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushResource.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushResource.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser import java.net.URI import java.net.URISyntaxException diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushTransport.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransport.kt similarity index 83% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushTransport.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransport.kt index 5d79fbee..c0a774bb 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushTransport.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransport.kt @@ -8,9 +8,9 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.Property /** * Identifies a property as a push transport. diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushTransports.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransports.kt similarity index 89% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushTransports.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransports.kt index de842a6c..20579052 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/PushTransports.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransports.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.propertyName import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Subscription.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/Subscription.kt similarity index 87% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Subscription.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/Subscription.kt index 5390a91a..4a56cf69 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Subscription.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/Subscription.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/SubscriptionPublicKey.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/SubscriptionPublicKey.kt similarity index 86% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/SubscriptionPublicKey.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/SubscriptionPublicKey.kt index 46dce801..7a215f98 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/SubscriptionPublicKey.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/SubscriptionPublicKey.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/SupportedTriggers.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/SupportedTriggers.kt similarity index 90% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/SupportedTriggers.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/SupportedTriggers.kt index bf88aa7d..277c7b13 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/SupportedTriggers.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/SupportedTriggers.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.propertyName import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Topic.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/Topic.kt similarity index 83% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Topic.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/Topic.kt index 4e568ad7..440af99e 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Topic.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/Topic.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Trigger.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/Trigger.kt similarity index 89% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Trigger.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/Trigger.kt index 77330fad..30d8e51d 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/Trigger.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/Trigger.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.propertyName import org.xmlpull.v1.XmlPullParser data class Trigger( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/VapidPublicKey.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/VapidPublicKey.kt similarity index 86% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/VapidPublicKey.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/VapidPublicKey.kt index abacb97b..09da8692 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/VapidPublicKey.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/VapidPublicKey.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPush.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPush.kt similarity index 91% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPush.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPush.kt index c9ee88d1..0179a242 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPush.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPush.kt @@ -8,10 +8,10 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPushSubscription.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPushSubscription.kt similarity index 91% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPushSubscription.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPushSubscription.kt index b7e17538..b4e25867 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPushSubscription.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPushSubscription.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.propertyName import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/namespace.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/namespace.kt similarity index 91% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/namespace.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/namespace.kt index 2fdbc283..f4a3bd94 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/push/namespace.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/namespace.kt @@ -8,7 +8,7 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push /** * XML namespace of WebDAV-Push (draft), see: diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/AddMember.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/AddMember.kt similarity index 83% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/AddMember.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/AddMember.kt index a9e2b838..5b080678 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/AddMember.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/AddMember.kt @@ -8,12 +8,12 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav import at.bitfire.dav4jvm.ktor.DavResource -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CreationDate.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/CreationDate.kt similarity index 81% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CreationDate.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/CreationDate.kt index 42e6037b..14aaf5dd 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CreationDate.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/CreationDate.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class CreationDate( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CurrentUserPrincipal.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/CurrentUserPrincipal.kt similarity index 86% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CurrentUserPrincipal.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/CurrentUserPrincipal.kt index 829ca3d1..075650a6 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CurrentUserPrincipal.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/CurrentUserPrincipal.kt @@ -8,12 +8,12 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav import at.bitfire.dav4jvm.ktor.DavResource -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser // see RFC 5397: WebDAV Current Principal Extension diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CurrentUserPrivilegeSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/CurrentUserPrivilegeSet.kt similarity index 93% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CurrentUserPrivilegeSet.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/CurrentUserPrivilegeSet.kt index 7fd81271..d320db63 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/CurrentUserPrivilegeSet.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/CurrentUserPrivilegeSet.kt @@ -8,12 +8,12 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader -import at.bitfire.dav4jvm.ktor.XmlUtils.propertyName +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader +import at.bitfire.dav4jvm.XmlUtils.propertyName import org.xmlpull.v1.XmlPullParser data class CurrentUserPrivilegeSet( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/Depth.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/Depth.kt similarity index 86% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/Depth.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/Depth.kt index f476c7ff..b8e897e1 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/Depth.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/Depth.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/DisplayName.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/DisplayName.kt similarity index 82% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/DisplayName.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/DisplayName.kt index 08fd88d2..09875cfb 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/DisplayName.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/DisplayName.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class DisplayName( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetContentLength.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetContentLength.kt similarity index 81% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetContentLength.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetContentLength.kt index be38df56..dbc9a51c 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetContentLength.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetContentLength.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class GetContentLength( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetContentType.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetContentType.kt similarity index 71% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetContentType.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetContentType.kt index 68cddb4c..e7e8b9a8 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetContentType.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetContentType.kt @@ -8,17 +8,16 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader -import io.ktor.http.BadContentTypeFormatException +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import io.ktor.http.ContentType import org.xmlpull.v1.XmlPullParser data class GetContentType( - val type: ContentType? + val type: String? ): Property { companion object { @@ -28,7 +27,6 @@ data class GetContentType( } - object Factory: PropertyFactory { override fun getName() = NAME @@ -37,12 +35,10 @@ data class GetContentType( // GetContentType(XmlReader(parser).readText()?.let { try { - ContentType.parse(it) - } catch (_: BadContentTypeFormatException) { + ContentType.parse(it).toString() + } catch (_: Exception) { null } }) - } - } diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetETag.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetETag.kt similarity index 84% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetETag.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetETag.kt index 9e787877..5ac76d65 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetETag.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetETag.kt @@ -8,14 +8,15 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.QuotedStringUtils -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.QuotedStringUtils +import at.bitfire.dav4jvm.XmlReader import io.ktor.client.statement.HttpResponse import io.ktor.http.HttpHeaders +import okhttp3.Response import org.xmlpull.v1.XmlPullParser /** @@ -32,8 +33,11 @@ data class GetETag( @JvmField val NAME = Property.Name(NS_WEBDAV, "getetag") - fun fromResponse(response: HttpResponse) = + fun fromKtorResponse(response: HttpResponse) = response.headers[HttpHeaders.ETag]?.let { GetETag(it) } + + fun fromOkhttpResponse(response: Response) = + response.header("ETag")?.let { GetETag(it) } } /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetLastModified.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetLastModified.kt similarity index 83% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetLastModified.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetLastModified.kt index e1c761c4..2bdd3316 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GetLastModified.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetLastModified.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser import java.time.Instant diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GroupMembership.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GroupMembership.kt similarity index 83% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GroupMembership.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GroupMembership.kt index ec90db57..c0ef1efa 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/GroupMembership.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GroupMembership.kt @@ -8,10 +8,10 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.property.common.HrefListProperty import org.xmlpull.v1.XmlPullParser class GroupMembership( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/Owner.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/Owner.kt similarity index 81% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/Owner.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/Owner.kt index 56bd3499..f05f8dd3 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/Owner.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/Owner.kt @@ -8,12 +8,12 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav import at.bitfire.dav4jvm.ktor.DavResource -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.XmlReader -import at.bitfire.dav4jvm.ktor.property.common.HrefListProperty +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.XmlReader +import at.bitfire.dav4jvm.property.common.HrefListProperty import org.xmlpull.v1.XmlPullParser data class Owner( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/QuotaAvailableBytes.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/QuotaAvailableBytes.kt similarity index 81% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/QuotaAvailableBytes.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/QuotaAvailableBytes.kt index de339625..cf3ed2e6 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/QuotaAvailableBytes.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/QuotaAvailableBytes.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class QuotaAvailableBytes( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/QuotaUsedBytes.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/QuotaUsedBytes.kt similarity index 81% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/QuotaUsedBytes.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/QuotaUsedBytes.kt index d209de32..1e950769 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/QuotaUsedBytes.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/QuotaUsedBytes.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class QuotaUsedBytes( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/ResourceType.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/ResourceType.kt similarity index 89% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/ResourceType.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/ResourceType.kt index 36de5918..b2a2e9d8 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/ResourceType.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/ResourceType.kt @@ -8,13 +8,13 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.property.caldav.NS_CALDAV -import at.bitfire.dav4jvm.ktor.property.caldav.NS_CALENDARSERVER -import at.bitfire.dav4jvm.ktor.property.carddav.NS_CARDDAV +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.property.caldav.NS_CALDAV +import at.bitfire.dav4jvm.property.caldav.NS_CALENDARSERVER +import at.bitfire.dav4jvm.property.carddav.NS_CARDDAV import org.xmlpull.v1.XmlPullParser class ResourceType( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SupportedReportSet.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/SupportedReportSet.kt similarity index 90% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SupportedReportSet.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/SupportedReportSet.kt index f416ed84..2be1b84c 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SupportedReportSet.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/SupportedReportSet.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class SupportedReportSet( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SyncLevel.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/SyncLevel.kt similarity index 85% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SyncLevel.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/SyncLevel.kt index feede891..8fdbad09 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SyncLevel.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/SyncLevel.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser /** diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SyncToken.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/SyncToken.kt similarity index 81% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SyncToken.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/SyncToken.kt index 87bfecc2..4685ba0a 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/SyncToken.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/SyncToken.kt @@ -8,11 +8,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.PropertyFactory -import at.bitfire.dav4jvm.ktor.XmlReader +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser data class SyncToken( diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/namespace.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/namespace.kt similarity index 89% rename from src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/namespace.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/webdav/namespace.kt index 1b07f714..6659a63a 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/property/webdav/namespace.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/namespace.kt @@ -8,7 +8,7 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.webdav +package at.bitfire.dav4jvm.property.webdav /** * WebDAV XML namespace (defined in RFC 4918) diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/HttpUtilsTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/HttpUtilsTest.kt similarity index 79% rename from src/test/kotlin/at/bitfire/dav4jvm/okhttp/HttpUtilsTest.kt rename to src/test/kotlin/at/bitfire/dav4jvm/HttpUtilsTest.kt index 685ee5e2..d832c5bc 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/HttpUtilsTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/HttpUtilsTest.kt @@ -8,9 +8,8 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.okhttp +package at.bitfire.dav4jvm -import okhttp3.HttpUrl.Companion.toHttpUrl import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Test @@ -27,16 +26,6 @@ import java.util.logging.Logger class HttpUtilsTest { - @Test - fun fileName() { - assertEquals("", HttpUtils.fileName("https://example.com".toHttpUrl())) - assertEquals("", HttpUtils.fileName("https://example.com/".toHttpUrl())) - assertEquals("file1", HttpUtils.fileName("https://example.com/file1".toHttpUrl())) - assertEquals("dir1", HttpUtils.fileName("https://example.com/dir1/".toHttpUrl())) - assertEquals("file2", HttpUtils.fileName("https://example.com/dir1/file2".toHttpUrl())) - assertEquals("dir2", HttpUtils.fileName("https://example.com/dir1/dir2/".toHttpUrl())) - } - @Test fun formatDate() { val cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")) diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/PropertyTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/PropertyTest.kt similarity index 96% rename from src/test/kotlin/at/bitfire/dav4jvm/okhttp/PropertyTest.kt rename to src/test/kotlin/at/bitfire/dav4jvm/PropertyTest.kt index b31fcba1..040874f7 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/PropertyTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/PropertyTest.kt @@ -8,9 +8,9 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.okhttp +package at.bitfire.dav4jvm -import at.bitfire.dav4jvm.okhttp.property.webdav.GetETag +import at.bitfire.dav4jvm.property.webdav.GetETag import org.junit.Assert.assertEquals import org.junit.Test import org.xmlpull.v1.XmlPullParser diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/XmlReaderTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/XmlReaderTest.kt similarity index 96% rename from src/test/kotlin/at/bitfire/dav4jvm/okhttp/XmlReaderTest.kt rename to src/test/kotlin/at/bitfire/dav4jvm/XmlReaderTest.kt index 588e2d56..7710bb99 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/XmlReaderTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/XmlReaderTest.kt @@ -8,7 +8,7 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.okhttp +package at.bitfire.dav4jvm import okhttp3.MediaType import okhttp3.MediaType.Companion.toMediaType @@ -155,11 +155,11 @@ class XmlReaderTest { parser.next() val reader = XmlReader(parser) - val types = mutableListOf() + val types = mutableListOf() reader.readContentTypes(Property.Name("", "test"), types::add) assertEquals(2, types.size) - assertEquals("text/plain".toMediaType(), types[0]) - assertEquals("application/json".toMediaType(), types[1]) + assertEquals("text/plain", types[0]) + assertEquals("application/json", types[1]) } } \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/XmlUtilsTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/XmlUtilsTest.kt similarity index 93% rename from src/test/kotlin/at/bitfire/dav4jvm/ktor/XmlUtilsTest.kt rename to src/test/kotlin/at/bitfire/dav4jvm/XmlUtilsTest.kt index 97b9529e..c64f80d6 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/XmlUtilsTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/XmlUtilsTest.kt @@ -10,7 +10,6 @@ package at.bitfire.dav4jvm -import at.bitfire.dav4jvm.okhttp.XmlUtils import org.junit.Assert.assertNotNull import org.junit.Test diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavCollectionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavCollectionTest.kt index 2dd3e34c..be0db914 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavCollectionTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavCollectionTest.kt @@ -10,10 +10,11 @@ package at.bitfire.dav4jvm.ktor +import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.ktor.exception.HttpException -import at.bitfire.dav4jvm.ktor.property.webdav.GetETag -import at.bitfire.dav4jvm.ktor.property.webdav.NS_WEBDAV -import at.bitfire.dav4jvm.ktor.property.webdav.SyncToken +import at.bitfire.dav4jvm.property.webdav.GetETag +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.property.webdav.SyncToken import io.ktor.client.HttpClient import io.ktor.client.engine.mock.MockEngine import io.ktor.client.engine.mock.respond diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavResourceTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavResourceTest.kt index f4a905d3..aae46c1b 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavResourceTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavResourceTest.kt @@ -10,12 +10,13 @@ package at.bitfire.dav4jvm.ktor +import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.ktor.exception.DavException import at.bitfire.dav4jvm.ktor.exception.HttpException import at.bitfire.dav4jvm.ktor.exception.PreconditionFailedException -import at.bitfire.dav4jvm.ktor.property.webdav.DisplayName -import at.bitfire.dav4jvm.ktor.property.webdav.GetETag -import at.bitfire.dav4jvm.ktor.property.webdav.ResourceType +import at.bitfire.dav4jvm.property.webdav.DisplayName +import at.bitfire.dav4jvm.property.webdav.GetETag +import at.bitfire.dav4jvm.property.webdav.ResourceType import io.ktor.client.HttpClient import io.ktor.client.engine.mock.MockEngine import io.ktor.client.engine.mock.respond @@ -285,7 +286,7 @@ class DavResourceTest { called = true runBlocking { assertEquals(sampleText, response.bodyAsText()) } - val eTag = GetETag.fromResponse(response) + val eTag = GetETag.fromKtorResponse(response) assertEquals("My Weak ETag", eTag!!.eTag) assertTrue(eTag.weak) assertEquals(ContentType.parse("application/x-test-result"), response.contentType()) @@ -396,7 +397,7 @@ class DavResourceTest { called = true runBlocking { assertEquals(sampleText, response.bodyAsText()) } - val eTag = GetETag.fromResponse(response) + val eTag = GetETag.fromKtorResponse(response) assertEquals("My Weak ETag", eTag!!.eTag) assertTrue(eTag.weak) assertEquals(ContentType.parse("application/x-test-result"), response.contentType()) @@ -1153,7 +1154,7 @@ class DavResourceTest { headers = HeadersBuilder().apply { append(HttpHeaders.ContentType, "text/plain") }.build() ) { response -> called = true - val eTag = GetETag.fromResponse(response)!! + val eTag = GetETag.fromKtorResponse(response)!! assertEquals("Weak PUT ETag", eTag.eTag) assertTrue(eTag.weak) assertEquals(response.request.url, dav.location) @@ -1191,7 +1192,7 @@ class DavResourceTest { dav.put(sampleText, headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString()), ifNoneMatch = true) { response -> called = true assertEquals(URLBuilder(sampleUrl).takeFrom("/target").build(), response.request.url) - val eTag = GetETag.fromResponse(response) + val eTag = GetETag.fromKtorResponse(response) assertNull("Weak PUT ETag", eTag?.eTag) assertNull(eTag?.weak) } diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/ErrorTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/ErrorTest.kt index 34ae57e5..f18576e9 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/ErrorTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/ErrorTest.kt @@ -10,6 +10,8 @@ package at.bitfire.dav4jvm.ktor +import at.bitfire.dav4jvm.Error +import at.bitfire.dav4jvm.Property import org.junit.Assert.assertTrue import org.junit.Test diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/HttpUtilsTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtilsTest.kt similarity index 57% rename from src/test/kotlin/at/bitfire/dav4jvm/ktor/HttpUtilsTest.kt rename to src/test/kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtilsTest.kt index 4762e007..911ec179 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/HttpUtilsTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtilsTest.kt @@ -10,7 +10,7 @@ package at.bitfire.dav4jvm.ktor -import at.bitfire.dav4jvm.ktor.HttpUtils.INVALID_STATUS +import at.bitfire.dav4jvm.ktor.KtorHttpUtils.INVALID_STATUS import io.ktor.client.HttpClient import io.ktor.client.engine.mock.MockEngine import io.ktor.client.engine.mock.respond @@ -20,20 +20,9 @@ import io.ktor.http.HttpStatusCode import io.ktor.http.Url import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals -import org.junit.Assert.assertNull import org.junit.Test -import java.time.Instant -import java.time.LocalDate -import java.time.LocalTime -import java.time.ZoneOffset -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter -import java.util.Calendar -import java.util.Locale -import java.util.TimeZone -import java.util.logging.Logger - -class HttpUtilsTest { + +class KtorHttpUtilsTest { val sampleUrl = Url("http://127.0.0.1") private val multipleHeaderValues = " 1, 2 ,3,hyperactive-access" @@ -53,60 +42,12 @@ class HttpUtilsTest { @Test fun fileName() { - assertEquals("", HttpUtils.fileName(Url("https://example.com"))) - assertEquals("", HttpUtils.fileName(Url("https://example.com/"))) - assertEquals("file1", HttpUtils.fileName(Url("https://example.com/file1"))) - assertEquals("dir1", HttpUtils.fileName(Url("https://example.com/dir1/"))) - assertEquals("file2", HttpUtils.fileName(Url("https://example.com/dir1/file2"))) - assertEquals("dir2", HttpUtils.fileName(Url("https://example.com/dir1/dir2/"))) - } - - @Test - fun formatDate() { - val cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")) - cal.set(2023, 4, 11, 17, 26, 35) - cal.timeZone = TimeZone.getTimeZone("UTC") - assertEquals("Sun, 06 Nov 1994 08:49:37 GMT", HttpUtils.formatDate( - ZonedDateTime.of( - LocalDate.of(1994, 11, 6), - LocalTime.of(8, 49, 37), - ZoneOffset.UTC - ).toInstant() - )) - } - - - @Test - fun parseDate_IMF_FixDate() { - // RFC 7231 IMF-fixdate (preferred format) - assertEquals(Instant.ofEpochSecond(784111777), HttpUtils.parseDate("Sun, 06 Nov 1994 08:49:37 GMT")) - } - - @Test - fun parseDate_RFC850_1994() { - // obsolete RFC 850 format – fails when run after year 2000 because 06 Nov 2094 (!) is not a Sunday - assertNull(HttpUtils.parseDate("Sun, 06-Nov-94 08:49:37 GMT")) - } - - @Test - fun parseDate_RFC850_2004_CEST() { - // obsolete RFC 850 format with European time zone - assertEquals(Instant.ofEpochSecond(1689317377), HttpUtils.parseDate("Friday, 14-Jul-23 08:49:37 CEST")) - } - - @Test - fun parseDate_RFC850_2004_GMT() { - // obsolete RFC 850 format - assertEquals(Instant.ofEpochSecond(1689324577), HttpUtils.parseDate("Friday, 14-Jul-23 08:49:37 GMT")) - } - - @Test - fun parseDate_ANSI_C() { - // ANSI C's asctime() format - val logger = Logger.getLogger(javaClass.name) - logger.info("Expected date: " + DateTimeFormatter.ofPattern("EEE MMM ppd HH:mm:ss yyyy", Locale.US).format(ZonedDateTime.now())) - - assertEquals(Instant.ofEpochSecond(784111777), HttpUtils.parseDate("Sun Nov 6 08:49:37 1994")) + assertEquals("", KtorHttpUtils.fileName(Url("https://example.com"))) + assertEquals("", KtorHttpUtils.fileName(Url("https://example.com/"))) + assertEquals("file1", KtorHttpUtils.fileName(Url("https://example.com/file1"))) + assertEquals("dir1", KtorHttpUtils.fileName(Url("https://example.com/dir1/"))) + assertEquals("file2", KtorHttpUtils.fileName(Url("https://example.com/dir1/file2"))) + assertEquals("dir2", KtorHttpUtils.fileName(Url("https://example.com/dir1/dir2/"))) } @@ -115,7 +56,7 @@ class HttpUtilsTest { // Verify that when a header name has a single value, it's returned as a single-element array. runBlocking { val httpClient = getMockEngineWithDAVHeaderValues(singleHeaderValue) - val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") + val headersArray = KtorHttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") assertEquals(singleHeaderValue, headersArray[0]) assertEquals(1, headersArray.size) } @@ -126,7 +67,7 @@ class HttpUtilsTest { // Verify that when a header name has multiple comma-separated values, they are correctly split into an array. runBlocking { val httpClient = getMockEngineWithDAVHeaderValues(multipleHeaderValues) - val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") + val headersArray = KtorHttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") assertEquals(4, headersArray.size) assertEquals("1", headersArray[0]) assertEquals("2", headersArray[1]) @@ -140,7 +81,7 @@ class HttpUtilsTest { // Verify that if the same header name appears multiple times (e.g., 'Set-Cookie'), all values are joined by a comma and then split correctly. runBlocking { val httpClient = getMockEngineWithDAVHeaderValues(multipleHeaderValues, singleHeaderValue) - val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") + val headersArray = KtorHttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") assertEquals(5, headersArray.size) assertEquals("1", headersArray[0]) assertEquals("2", headersArray[1]) @@ -155,7 +96,7 @@ class HttpUtilsTest { // Verify that when a requested header name is not present in the response, an empty array is returned. runBlocking { val httpClient = getMockEngineWithDAVHeaderValues(multipleHeaderValues, singleHeaderValue) - val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "other") + val headersArray = KtorHttpUtils.listHeader(httpClient.get(sampleUrl), "other") assertEquals(0, headersArray.size) } } @@ -165,7 +106,7 @@ class HttpUtilsTest { // Verify that if a header exists but its value is an empty string, an empty array is returned (due to filter { it.isNotEmpty() }). runBlocking { val httpClient = getMockEngineWithDAVHeaderValues("", "", "") - val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") + val headersArray = KtorHttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") assertEquals(0, headersArray.size) } } @@ -175,7 +116,7 @@ class HttpUtilsTest { // Verify that if a header value consists only of commas (e.g., ',,,') an empty array is returned. runBlocking { val httpClient = getMockEngineWithDAVHeaderValues(",", ",", ",") - val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") + val headersArray = KtorHttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") assertEquals(0, headersArray.size) } } @@ -186,7 +127,7 @@ class HttpUtilsTest { // Verify that if a header value is like 'value1,,value2', the empty string between commas is filtered out, resulting in ['value1', 'value2']. runBlocking { val httpClient = getMockEngineWithDAVHeaderValues(" ", singleHeaderValue, ", ") - val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") + val headersArray = KtorHttpUtils.listHeader(httpClient.get(sampleUrl), "DAV") assertEquals(1, headersArray.size) assertEquals(singleHeaderValue, headersArray[0]) } @@ -197,7 +138,7 @@ class HttpUtilsTest { // HTTP header names are case-insensitive. Verify that `response.headers.getAll(name)` correctly retrieves the header regardless of the casing used for `name` (e.g., 'Content-Type' vs 'content-type'). runBlocking { val httpClient = getMockEngineWithDAVHeaderValues(singleHeaderValue) - val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "dav") + val headersArray = KtorHttpUtils.listHeader(httpClient.get(sampleUrl), "dav") assertEquals(singleHeaderValue, headersArray[0]) assertEquals(1, headersArray.size) } @@ -208,7 +149,7 @@ class HttpUtilsTest { // Test what happens if an empty string is passed as the header name. This depends on how `response.headers.getAll` handles empty keys. runBlocking { val httpClient = getMockEngineWithDAVHeaderValues(singleHeaderValue) - val headersArray = HttpUtils.listHeader(httpClient.get(sampleUrl), "") + val headersArray = KtorHttpUtils.listHeader(httpClient.get(sampleUrl), "") assertEquals(0, headersArray.size) } } @@ -216,109 +157,109 @@ class HttpUtilsTest { @Test fun `Full status line with HTTP 1 1 200 code and description`() = - assertEquals(HttpStatusCode.OK, HttpUtils.parseStatusLine("HTTP/1.1 200 OK")) + assertEquals(HttpStatusCode.OK, KtorHttpUtils.parseStatusLine("HTTP/1.1 200 OK")) @Test fun `Full status line with HTTP 1 0 404 code and description`() = - assertEquals(HttpStatusCode.NotFound, HttpUtils.parseStatusLine("HTTP/1.0 404 Not Found")) + assertEquals(HttpStatusCode.NotFound, KtorHttpUtils.parseStatusLine("HTTP/1.0 404 Not Found")) @Test fun `Full status line with HTTP 2 503 code and multi word description`() = - assertEquals(HttpStatusCode.ServiceUnavailable, HttpUtils.parseStatusLine("HTTP/2 503 Service Unavailable")) + assertEquals(HttpStatusCode.ServiceUnavailable, KtorHttpUtils.parseStatusLine("HTTP/2 503 Service Unavailable")) @Test fun `Full status line with HTTP 1 1 200 code and no description`() = - assertEquals(HttpStatusCode.OK, HttpUtils.parseStatusLine("HTTP/1.1 200")) + assertEquals(HttpStatusCode.OK, KtorHttpUtils.parseStatusLine("HTTP/1.1 200")) @Test fun `Partial status line with code and description`() = - assertEquals(HttpStatusCode.OK, HttpUtils.parseStatusLine("HTTP/1.1 200 OK")) + assertEquals(HttpStatusCode.OK, KtorHttpUtils.parseStatusLine("HTTP/1.1 200 OK")) @Test fun `Partial status line with code and multi word description`() = - assertEquals(HttpStatusCode.NotFound, HttpUtils.parseStatusLine("404 Not Found")) + assertEquals(HttpStatusCode.NotFound, KtorHttpUtils.parseStatusLine("404 Not Found")) @Test fun `Partial status line with only code 200`() = - assertEquals(HttpStatusCode.OK, HttpUtils.parseStatusLine("200")) + assertEquals(HttpStatusCode.OK, KtorHttpUtils.parseStatusLine("200")) @Test fun `Partial status line with only code 404`() = - assertEquals(HttpStatusCode.NotFound, HttpUtils.parseStatusLine("404")) + assertEquals(HttpStatusCode.NotFound, KtorHttpUtils.parseStatusLine("404")) @Test fun `Partial status line with only a known code not having a default description`() = - assertEquals(HttpStatusCode(303, ""), HttpUtils.parseStatusLine("303")) + assertEquals(HttpStatusCode(303, ""), KtorHttpUtils.parseStatusLine("303")) @Test fun `Partial status line with only an unknown code`() = - assertEquals(HttpStatusCode(999, ""), HttpUtils.parseStatusLine("999")) + assertEquals(HttpStatusCode(999, ""), KtorHttpUtils.parseStatusLine("999")) @Test fun `Invalid status line empty string`() = - assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("")) + assertEquals(INVALID_STATUS, KtorHttpUtils.parseStatusLine("")) @Test fun `Invalid status line just HTTP version`() = - assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("HTTP/1.1")) + assertEquals(INVALID_STATUS, KtorHttpUtils.parseStatusLine("HTTP/1.1")) @Test fun `Invalid status line HTTP version and non numeric code`() = - assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("HTTP/1.1 ABC OK")) + assertEquals(INVALID_STATUS, KtorHttpUtils.parseStatusLine("HTTP/1.1 ABC OK")) @Test fun `Invalid status line partial with non numeric code`() = - assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("ABC OK")) + assertEquals(INVALID_STATUS, KtorHttpUtils.parseStatusLine("ABC OK")) @Test fun `Invalid status line just non numeric text`() = - assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("Invalid")) + assertEquals(INVALID_STATUS, KtorHttpUtils.parseStatusLine("Invalid")) @Test fun `Invalid status line HTTP version malformed`() = - assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("HTTP1.1 200 OK")) + assertEquals(INVALID_STATUS, KtorHttpUtils.parseStatusLine("HTTP1.1 200 OK")) // Test a status line with a malformed HTTP version: 'HTTP1.1 200 OK'. Expects INVALID_STATUS (as it will be treated as parts.size == 3 but parts[0] doesn't start with 'HTTP/'). @Test fun `Invalid status line status code with leading trailing spaces`() = - assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("HTTP/1.1 200 OK")) + assertEquals(INVALID_STATUS, KtorHttpUtils.parseStatusLine("HTTP/1.1 200 OK")) @Test fun `Invalid status line partial code with leading trailing spaces`() = - assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine(" 200 ")) + assertEquals(INVALID_STATUS, KtorHttpUtils.parseStatusLine(" 200 ")) @Test fun `Status line with extra spaces between code and description`() = - assertEquals(HttpStatusCode.OK, HttpUtils.parseStatusLine("HTTP/1.1 200 OK")) + assertEquals(HttpStatusCode.OK, KtorHttpUtils.parseStatusLine("HTTP/1.1 200 OK")) @Test fun `Partial status line with extra spaces between code and description`() = - assertEquals(HttpStatusCode.OK, HttpUtils.parseStatusLine("200 OK")) + assertEquals(HttpStatusCode.OK, KtorHttpUtils.parseStatusLine("200 OK")) @Test fun `Full status line with negative status code`() = - assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("HTTP/1.1 -100 Error")) + assertEquals(INVALID_STATUS, KtorHttpUtils.parseStatusLine("HTTP/1.1 -100 Error")) @Test fun `Partial status line with negative status code`() = - assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine("-100")) + assertEquals(INVALID_STATUS, KtorHttpUtils.parseStatusLine("-100")) @Test fun `Status line with only spaces`() = - assertEquals(INVALID_STATUS, HttpUtils.parseStatusLine(" ")) + assertEquals(INVALID_STATUS, KtorHttpUtils.parseStatusLine(" ")) @Test fun `Status line with special characters in description`() = - assertEquals(HttpStatusCode(200, "Description with !@#\$%^&*()"), HttpUtils.parseStatusLine("HTTP/1.1 200 Description with !@#\$%^&*()")) + assertEquals(HttpStatusCode(200, "Description with !@#$%^&*()"), KtorHttpUtils.parseStatusLine("HTTP/1.1 200 Description with !@#$%^&*()")) @Test fun `Status line with numeric description only partial case `() = - assertEquals(HttpStatusCode(200, "404"), HttpUtils.parseStatusLine("HTTP/1.1 200 404")) + assertEquals(HttpStatusCode(200, "404"), KtorHttpUtils.parseStatusLine("HTTP/1.1 200 404")) } \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/PropertyTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/PropertyTest.kt index df96e954..30936867 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/PropertyTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/PropertyTest.kt @@ -10,7 +10,8 @@ package at.bitfire.dav4jvm.ktor -import at.bitfire.dav4jvm.ktor.property.webdav.GetETag +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.property.webdav.GetETag import org.junit.Assert.assertEquals import org.junit.Test import org.xmlpull.v1.XmlPullParser diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/QuotedStringUtilsTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/QuotedStringUtilsTest.kt deleted file mode 100644 index b48471ae..00000000 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/QuotedStringUtilsTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.ktor - -import org.junit.Assert.assertEquals -import org.junit.Test - -class QuotedStringUtilsTest { - - @Test - fun testAsQuotedString() { - assertEquals("\"\"", QuotedStringUtils.asQuotedString("")) - assertEquals("\"\\\"\"", QuotedStringUtils.asQuotedString("\"")) - assertEquals("\"\\\\\"", QuotedStringUtils.asQuotedString("\\")) - } - - @Test - fun testDecodeQuotedString() { - assertEquals("\"", QuotedStringUtils.decodeQuotedString("\"")) - assertEquals("\\", QuotedStringUtils.decodeQuotedString("\"\\\"")) - assertEquals("\"test", QuotedStringUtils.decodeQuotedString("\"test")) - assertEquals("test", QuotedStringUtils.decodeQuotedString("test")) - assertEquals("", QuotedStringUtils.decodeQuotedString("\"\"")) - assertEquals("test", QuotedStringUtils.decodeQuotedString("\"test\"")) - assertEquals("test\\", QuotedStringUtils.decodeQuotedString("\"test\\\"")) - assertEquals("test", QuotedStringUtils.decodeQuotedString("\"t\\e\\st\"")) - assertEquals("12\"34", QuotedStringUtils.decodeQuotedString("\"12\\\"34\"")) - assertEquals("1234\"", QuotedStringUtils.decodeQuotedString("\"1234\\\"\"")) - } - -} diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/XmlReaderTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/XmlReaderTest.kt deleted file mode 100644 index b3abeba1..00000000 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/XmlReaderTest.kt +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.ktor - -import io.ktor.http.ContentType -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNull -import org.junit.Assert.assertTrue -import org.junit.Test -import org.xmlpull.v1.XmlPullParser -import java.io.StringReader -import java.time.Instant - -class XmlReaderTest { - - @Test - fun testProcessTag_Root() { - val parser = XmlUtils.newPullParser() - parser.setInput(StringReader("")) - // now on START_DOCUMENT [0] - - var processed = false - XmlReader(parser).processTag(Property.Name("", "test")) { - processed = true - } - assertTrue(processed) - } - - @Test - fun testProcessTag_Depth1() { - val parser = XmlUtils.newPullParser() - parser.setInput(StringReader("")) - parser.next() // now on START_TAG - - var processed = false - XmlReader(parser).processTag(Property.Name("", "test")) { - processed = true - } - assertTrue(processed) - } - - - @Test - fun testReadText() { - val parser = XmlUtils.newPullParser() - parser.setInput(StringReader("Test 1Test 2")) - parser.next() - parser.next() // now on START_TAG - val reader = XmlReader(parser) - - assertEquals("Test 1", reader.readText()) - assertEquals(XmlPullParser.END_TAG, parser.eventType) - parser.next() - - assertEquals("Test 2", reader.readText()) - assertEquals(XmlPullParser.END_TAG, parser.eventType) - } - - @Test - fun testReadText_CDATA() { - val parser = XmlUtils.newPullParser() - parser.setInput(StringReader("Test 2]]>")) - parser.next() // now on START_TAG - - assertEquals("Test 1Test 2", XmlReader(parser).readText()) - assertEquals(XmlPullParser.END_TAG, parser.eventType) - } - - @Test - fun testReadText_PropertyRoot() { - val parser = XmlUtils.newPullParser() - parser.setInput(StringReader("Test 1Test 2")) - parser.next() // now on START_TAG - - val entries = mutableListOf() - XmlReader(parser).readTextPropertyList(Property.Name("", "entry"), entries) - assertEquals("Test 1", entries[0]) - assertEquals("Test 2", entries[1]) - - parser.next() // END_TAG - assertEquals(XmlPullParser.END_DOCUMENT, parser.eventType) - } - - - @Test - fun testReadTextPropertyList_Depth1() { - val parser = XmlUtils.newPullParser() - parser.setInput(StringReader("Test 1Test 2")) - parser.next() // now on START_TAG [1] - - val entries = mutableListOf() - XmlReader(parser).readTextPropertyList(Property.Name("", "entry"), entries) - assertEquals("Test 1", entries[0]) - assertEquals("Test 2", entries[1]) - assertEquals(XmlPullParser.END_TAG, parser.eventType) - assertEquals("test", parser.name) - } - - - @Test - fun testReadLong() { - val parser = XmlUtils.newPullParser() - parser.setInput(StringReader("12a")) - parser.next() - parser.next() // now on START_TAG - val reader = XmlReader(parser) - - assertEquals(1L, reader.readLong()) - assertEquals(XmlPullParser.END_TAG, parser.eventType) - parser.next() - - assertEquals(2L, reader.readLong()) - assertEquals(XmlPullParser.END_TAG, parser.eventType) - parser.next() - - assertNull(reader.readLong()) - assertEquals(XmlPullParser.END_TAG, parser.eventType) - } - - - @Test - fun testReadHttpDate() { - val parser = XmlUtils.newPullParser() - parser.setInput(StringReader("Sun, 06 Nov 1994 08:49:37 GMTSun, 06 Nov 1994 08:49:37 GMTinvalid")) - parser.next() - parser.next() // now on START_TAG - val reader = XmlReader(parser) - - assertEquals(Instant.ofEpochSecond(784111777), reader.readHttpDate()) - assertEquals(XmlPullParser.END_TAG, parser.eventType) - parser.next() - - assertEquals(Instant.ofEpochSecond(784111777), reader.readHttpDate()) - assertEquals(XmlPullParser.END_TAG, parser.eventType) - parser.next() - - assertNull(reader.readHttpDate()) - assertEquals(XmlPullParser.END_TAG, parser.eventType) - } - - - @Test - fun testReadContentTypes() { - val parser = XmlUtils.newPullParser() - parser.setInput(StringReader("text{}")) - parser.next() - val reader = XmlReader(parser) - - val types = mutableListOf() - reader.readContentTypes(Property.Name("", "test"), types::add) - assertEquals(2, types.size) - assertEquals(ContentType.Text.Plain, types[0]) - assertEquals(ContentType.Application.Json, types[1]) - } - -} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/DavExceptionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/DavExceptionTest.kt index b906a6b1..07d86d4e 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/DavExceptionTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/DavExceptionTest.kt @@ -10,12 +10,11 @@ package at.bitfire.dav4jvm.ktor.exception -import at.bitfire.dav4jvm.ktor.Error -import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.Error +import at.bitfire.dav4jvm.Property import io.ktor.client.HttpClient import io.ktor.client.engine.mock.MockEngine import io.ktor.client.engine.mock.respond -import io.ktor.client.engine.mock.respondOk import io.ktor.client.request.get import io.ktor.http.ContentType import io.ktor.http.HttpHeaders diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpExceptionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpExceptionTest.kt index 6e34ccd5..31da3d77 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpExceptionTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpExceptionTest.kt @@ -10,8 +10,8 @@ package at.bitfire.dav4jvm.ktor.exception -import at.bitfire.dav4jvm.ktor.Error -import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.Error +import at.bitfire.dav4jvm.Property import io.ktor.client.HttpClient import io.ktor.client.engine.mock.MockEngine import io.ktor.client.engine.mock.respond diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfoTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfoTest.kt index ad34e49a..33c2e4fd 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfoTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/HttpResponseInfoTest.kt @@ -10,8 +10,8 @@ package at.bitfire.dav4jvm.ktor.exception -import at.bitfire.dav4jvm.ktor.Error -import at.bitfire.dav4jvm.ktor.Property +import at.bitfire.dav4jvm.Error +import at.bitfire.dav4jvm.Property import io.ktor.client.HttpClient import io.ktor.client.engine.mock.MockEngine import io.ktor.client.engine.mock.respond diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableExceptionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableExceptionTest.kt index 8359c8af..992fa293 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableExceptionTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableExceptionTest.kt @@ -10,8 +10,7 @@ package at.bitfire.dav4jvm.ktor.exception -import at.bitfire.dav4jvm.ktor.HttpUtils -import at.bitfire.dav4jvm.ktor.exception.ServiceUnavailableException +import at.bitfire.dav4jvm.HttpUtils import io.ktor.client.HttpClient import io.ktor.client.engine.mock.MockEngine import io.ktor.client.engine.mock.respond diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/DavCollectionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/DavCollectionTest.kt index a36817cb..54aa9cd9 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/DavCollectionTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/DavCollectionTest.kt @@ -10,10 +10,11 @@ package at.bitfire.dav4jvm.okhttp +import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.okhttp.exception.HttpException -import at.bitfire.dav4jvm.okhttp.property.webdav.GetETag -import at.bitfire.dav4jvm.okhttp.property.webdav.NS_WEBDAV -import at.bitfire.dav4jvm.okhttp.property.webdav.SyncToken +import at.bitfire.dav4jvm.property.webdav.GetETag +import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV +import at.bitfire.dav4jvm.property.webdav.SyncToken import mockwebserver3.MockResponse import mockwebserver3.MockWebServer import okhttp3.MediaType.Companion.toMediaType diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/DavResourceTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/DavResourceTest.kt index 4cecde39..3376ed11 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/DavResourceTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/DavResourceTest.kt @@ -10,13 +10,14 @@ package at.bitfire.dav4jvm.okhttp +import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.okhttp.exception.DavException import at.bitfire.dav4jvm.okhttp.exception.HttpException import at.bitfire.dav4jvm.okhttp.exception.PreconditionFailedException -import at.bitfire.dav4jvm.okhttp.property.webdav.DisplayName -import at.bitfire.dav4jvm.okhttp.property.webdav.GetContentType -import at.bitfire.dav4jvm.okhttp.property.webdav.GetETag -import at.bitfire.dav4jvm.okhttp.property.webdav.ResourceType +import at.bitfire.dav4jvm.property.webdav.DisplayName +import at.bitfire.dav4jvm.property.webdav.GetContentType +import at.bitfire.dav4jvm.property.webdav.GetETag +import at.bitfire.dav4jvm.property.webdav.ResourceType import mockwebserver3.MockResponse import mockwebserver3.MockWebServer import okhttp3.HttpUrl.Companion.toHttpUrl @@ -261,10 +262,10 @@ class DavResourceTest { called = true assertEquals(sampleText, response.body.string()) - val eTag = GetETag.fromResponse(response) + val eTag = GetETag.fromOkhttpResponse(response) assertEquals("My Weak ETag", eTag!!.eTag) assertTrue(eTag.weak) - assertEquals("application/x-test-result".toMediaType(), GetContentType(response.body.contentType()!!).type) + assertEquals("application/x-test-result", GetContentType(response.body.contentType()?.toString()).type) } assertTrue(called) @@ -351,10 +352,10 @@ class DavResourceTest { called = true assertEquals(sampleText, response.body.string()) - val eTag = GetETag.fromResponse(response) + val eTag = GetETag.fromOkhttpResponse(response) assertEquals("My Weak ETag", eTag!!.eTag) assertTrue(eTag.weak) - assertEquals("application/x-test-result".toMediaType(), GetContentType(response.body.contentType()!!).type) + assertEquals("application/x-test-result", GetContentType(response.body.contentType()?.toString()).type) } assertTrue(called) @@ -988,7 +989,7 @@ class DavResourceTest { var called = false dav.put(sampleText.toRequestBody("text/plain".toMediaType())) { response -> called = true - val eTag = GetETag.fromResponse(response)!! + val eTag = GetETag.fromOkhttpResponse(response)!! assertEquals("Weak PUT ETag", eTag.eTag) assertTrue(eTag.weak) assertEquals(response.request.url, dav.location) @@ -1015,7 +1016,7 @@ class DavResourceTest { dav.put(sampleText.toRequestBody("text/plain".toMediaType()), ifNoneMatch = true) { response -> called = true assertEquals(url.resolve("/target"), response.request.url) - val eTag = GetETag.fromResponse(response) + val eTag = GetETag.fromOkhttpResponse(response) assertNull("Weak PUT ETag", eTag?.eTag) assertNull(eTag?.weak) } diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/ErrorTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/ErrorTest.kt index 58c81d3c..e62a59aa 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/ErrorTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/ErrorTest.kt @@ -10,6 +10,7 @@ package at.bitfire.dav4jvm.okhttp +import at.bitfire.dav4jvm.Property import org.junit.Assert.assertTrue import org.junit.Test diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/OkHttpUtilsTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/OkHttpUtilsTest.kt new file mode 100644 index 00000000..57aa1c29 --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/OkHttpUtilsTest.kt @@ -0,0 +1,28 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package at.bitfire.dav4jvm.okhttp + +import okhttp3.HttpUrl.Companion.toHttpUrl +import org.junit.Assert.assertEquals +import org.junit.Test + +class OkHttpUtilsTest { + + @Test + fun fileName() { + assertEquals("", OkHttpUtils.fileName("https://example.com".toHttpUrl())) + assertEquals("", OkHttpUtils.fileName("https://example.com/".toHttpUrl())) + assertEquals("file1", OkHttpUtils.fileName("https://example.com/file1".toHttpUrl())) + assertEquals("dir1", OkHttpUtils.fileName("https://example.com/dir1/".toHttpUrl())) + assertEquals("file2", OkHttpUtils.fileName("https://example.com/dir1/file2".toHttpUrl())) + assertEquals("dir2", OkHttpUtils.fileName("https://example.com/dir1/dir2/".toHttpUrl())) + } +} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/QuotedStringUtilsTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/QuotedStringUtilsTest.kt index e8ab4804..20459348 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/QuotedStringUtilsTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/QuotedStringUtilsTest.kt @@ -10,6 +10,7 @@ package at.bitfire.dav4jvm.okhttp +import at.bitfire.dav4jvm.QuotedStringUtils import org.junit.Assert.assertEquals import org.junit.Test diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/XmlUtilsTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/XmlUtilsTest.kt deleted file mode 100644 index 71bab6ff..00000000 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/XmlUtilsTest.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp - -import at.bitfire.dav4jvm.okhttp.XmlUtils -import org.junit.Assert.assertNotNull -import org.junit.Test - -class XmlUtilsTest { - - @Test - fun newPullParser() { - assertNotNull(XmlUtils.newPullParser()) - } - - @Test - fun newSerializer() { - assertNotNull(XmlUtils.newSerializer()) - } - -} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/DavExceptionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/DavExceptionTest.kt index 35731e81..fac7722c 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/DavExceptionTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/DavExceptionTest.kt @@ -11,7 +11,7 @@ package at.bitfire.dav4jvm.okhttp.exception import at.bitfire.dav4jvm.okhttp.Error -import at.bitfire.dav4jvm.okhttp.Property +import at.bitfire.dav4jvm.Property import okhttp3.MediaType.Companion.toMediaType import okhttp3.Protocol import okhttp3.Request diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpExceptionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpExceptionTest.kt index 36fc084f..bba95f24 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpExceptionTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpExceptionTest.kt @@ -11,7 +11,7 @@ package at.bitfire.dav4jvm.okhttp.exception import at.bitfire.dav4jvm.okhttp.Error -import at.bitfire.dav4jvm.okhttp.Property +import at.bitfire.dav4jvm.Property import okhttp3.Protocol import okhttp3.Request import okhttp3.Response diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfoTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfoTest.kt index 8d2d5949..e9d0a2b3 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfoTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfoTest.kt @@ -11,7 +11,7 @@ package at.bitfire.dav4jvm.okhttp.exception import at.bitfire.dav4jvm.okhttp.Error -import at.bitfire.dav4jvm.okhttp.Property +import at.bitfire.dav4jvm.Property import okhttp3.MediaType.Companion.toMediaType import okhttp3.Protocol import okhttp3.Request diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/ServiceUnavailableExceptionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/ServiceUnavailableExceptionTest.kt index 6571363f..760dbabf 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/ServiceUnavailableExceptionTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/ServiceUnavailableExceptionTest.kt @@ -10,7 +10,7 @@ package at.bitfire.dav4jvm.okhttp.exception -import at.bitfire.dav4jvm.okhttp.HttpUtils +import at.bitfire.dav4jvm.HttpUtils import okhttp3.Protocol import okhttp3.Request import okhttp3.Response diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/CalendarDescriptionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/CalendarDescriptionTest.kt index fa35069c..2431483a 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/CalendarDescriptionTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/CalendarDescriptionTest.kt @@ -10,7 +10,7 @@ package at.bitfire.dav4jvm.okhttp.property -import at.bitfire.dav4jvm.okhttp.property.caldav.CalendarDescription +import at.bitfire.dav4jvm.property.caldav.CalendarDescription import org.junit.Assert.assertEquals import org.junit.Test diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/GetETagTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/GetETagTest.kt index d4070091..b3549442 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/GetETagTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/GetETagTest.kt @@ -10,7 +10,7 @@ package at.bitfire.dav4jvm.okhttp.property -import at.bitfire.dav4jvm.okhttp.property.webdav.GetETag +import at.bitfire.dav4jvm.property.webdav.GetETag import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/OwnerTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/OwnerTest.kt index 72a30fbb..8870b58e 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/OwnerTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/OwnerTest.kt @@ -10,7 +10,7 @@ package at.bitfire.dav4jvm.okhttp.property -import at.bitfire.dav4jvm.okhttp.property.webdav.Owner +import at.bitfire.dav4jvm.property.webdav.Owner import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Assert.assertTrue diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/PropertyTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/PropertyTest.kt index 389aa0a6..ec1a1ff5 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/PropertyTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/PropertyTest.kt @@ -10,8 +10,8 @@ package at.bitfire.dav4jvm.okhttp.property -import at.bitfire.dav4jvm.okhttp.Property -import at.bitfire.dav4jvm.okhttp.XmlUtils +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.XmlUtils import java.io.StringReader open class PropertyTest { diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/push/WebPushTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/push/WebPushTest.kt index bdff1936..886b7000 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/push/WebPushTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/push/WebPushTest.kt @@ -11,6 +11,13 @@ package at.bitfire.dav4jvm.okhttp.property.push import at.bitfire.dav4jvm.okhttp.property.PropertyTest +import at.bitfire.dav4jvm.property.push.NS_WEBDAV_PUSH +import at.bitfire.dav4jvm.property.push.PushMessage +import at.bitfire.dav4jvm.property.push.PushRegister +import at.bitfire.dav4jvm.property.push.PushTransports +import at.bitfire.dav4jvm.property.push.Topic +import at.bitfire.dav4jvm.property.push.VapidPublicKey +import at.bitfire.dav4jvm.property.push.WebPush import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/CalendarDescriptionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/property/CalendarDescriptionTest.kt similarity index 87% rename from src/test/kotlin/at/bitfire/dav4jvm/ktor/property/CalendarDescriptionTest.kt rename to src/test/kotlin/at/bitfire/dav4jvm/property/CalendarDescriptionTest.kt index bfa92015..f3edb9cf 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/CalendarDescriptionTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/property/CalendarDescriptionTest.kt @@ -8,9 +8,9 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property +package at.bitfire.dav4jvm.property -import at.bitfire.dav4jvm.ktor.property.caldav.CalendarDescription +import at.bitfire.dav4jvm.property.caldav.CalendarDescription import org.junit.Assert.assertEquals import org.junit.Test diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/GetETagTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/property/GetETagTest.kt similarity index 95% rename from src/test/kotlin/at/bitfire/dav4jvm/ktor/property/GetETagTest.kt rename to src/test/kotlin/at/bitfire/dav4jvm/property/GetETagTest.kt index 67e827a3..4253ea65 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/GetETagTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/property/GetETagTest.kt @@ -8,9 +8,9 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property +package at.bitfire.dav4jvm.property -import at.bitfire.dav4jvm.ktor.property.webdav.GetETag +import at.bitfire.dav4jvm.property.webdav.GetETag import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/OwnerTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/property/OwnerTest.kt similarity index 95% rename from src/test/kotlin/at/bitfire/dav4jvm/ktor/property/OwnerTest.kt rename to src/test/kotlin/at/bitfire/dav4jvm/property/OwnerTest.kt index d2648cdc..90b78945 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/OwnerTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/property/OwnerTest.kt @@ -8,9 +8,9 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property +package at.bitfire.dav4jvm.property -import at.bitfire.dav4jvm.ktor.property.webdav.Owner +import at.bitfire.dav4jvm.property.webdav.Owner import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Assert.assertTrue diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/PropertyTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/property/PropertyTest.kt similarity index 85% rename from src/test/kotlin/at/bitfire/dav4jvm/ktor/property/PropertyTest.kt rename to src/test/kotlin/at/bitfire/dav4jvm/property/PropertyTest.kt index 71c38866..04427f85 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/PropertyTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/property/PropertyTest.kt @@ -8,10 +8,10 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property +package at.bitfire.dav4jvm.property -import at.bitfire.dav4jvm.ktor.Property -import at.bitfire.dav4jvm.ktor.XmlUtils +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.XmlUtils import java.io.StringReader open class PropertyTest { diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPushTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/property/push/WebPushTest.kt similarity index 97% rename from src/test/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPushTest.kt rename to src/test/kotlin/at/bitfire/dav4jvm/property/push/WebPushTest.kt index 57d287a1..83f2bdbf 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/property/push/WebPushTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/property/push/WebPushTest.kt @@ -8,9 +8,9 @@ * SPDX-License-Identifier: MPL-2.0 */ -package at.bitfire.dav4jvm.ktor.property.push +package at.bitfire.dav4jvm.property.push -import at.bitfire.dav4jvm.ktor.property.PropertyTest +import at.bitfire.dav4jvm.property.PropertyTest import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue From a24d86edcd5bf4f5846e1dba0ad69bc92a6ccfcf Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Sun, 21 Sep 2025 12:47:33 -0400 Subject: [PATCH 03/11] removed obsolete code --- src/main/kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtils.kt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtils.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtils.kt index cf6fbda3..55985d80 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtils.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtils.kt @@ -13,17 +13,10 @@ package at.bitfire.dav4jvm.ktor import io.ktor.client.statement.HttpResponse import io.ktor.http.HttpStatusCode import io.ktor.http.Url -import java.time.format.DateTimeFormatter -import java.util.Locale import java.util.logging.Logger object KtorHttpUtils { - /** - * Preferred HTTP date/time format, see RFC 7231 7.1.1.1 IMF-fixdate - */ - private const val httpDateFormatStr = "EEE, dd MMM yyyy HH:mm:ss ZZZZ" - private val httpDateFormat = DateTimeFormatter.ofPattern(httpDateFormatStr, Locale.US) val INVALID_STATUS = HttpStatusCode( 500, "Invalid status line") @@ -88,7 +81,6 @@ object KtorHttpUtils { */ fun parseStatusLine(statusText: String): HttpStatusCode { - val parts = statusText.split(" ", limit = 3) return if (parts.size >= 2 && parts[0].startsWith("HTTP/")) { // Full status line like "HTTP/1.1 200 OK" val statusCode = parts[1].toIntOrNull() From fe11265616fbd7f00d641d3158813b73dab90607 Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Sun, 21 Sep 2025 13:02:24 -0400 Subject: [PATCH 04/11] Cleanup double Error.kt --- src/main/kotlin/at/bitfire/dav4jvm/Error.kt | 2 - .../kotlin/at/bitfire/dav4jvm/okhttp/Error.kt | 61 ------------------- .../at/bitfire/dav4jvm/okhttp/Response.kt | 8 +-- .../dav4jvm/okhttp/exception/DavException.kt | 2 +- .../dav4jvm/okhttp/exception/HttpException.kt | 3 +- .../okhttp/exception/HttpResponseInfo.kt | 2 +- .../at/bitfire/dav4jvm/okhttp/ErrorTest.kt | 4 +- .../okhttp/exception/DavExceptionTest.kt | 2 +- .../okhttp/exception/HttpExceptionTest.kt | 2 +- .../okhttp/exception/HttpResponseInfoTest.kt | 2 +- 10 files changed, 12 insertions(+), 76 deletions(-) delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/Error.kt diff --git a/src/main/kotlin/at/bitfire/dav4jvm/Error.kt b/src/main/kotlin/at/bitfire/dav4jvm/Error.kt index f8408369..39996f08 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/Error.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/Error.kt @@ -10,11 +10,9 @@ package at.bitfire.dav4jvm -import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV import org.xmlpull.v1.XmlPullParser import java.io.Serializable -import kotlin.collections.plusAssign /** * Represents an XML precondition/postcondition error. Every error has a name, which is the XML element diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Error.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Error.kt deleted file mode 100644 index 62a20d1e..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Error.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp - -import at.bitfire.dav4jvm.Property -import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV -import org.xmlpull.v1.XmlPullParser -import java.io.Serializable - -/** - * Represents an XML precondition/postcondition error. Every error has a name, which is the XML element - * name. Subclassed errors may have more specific information available. - * - * At the moment, there is no logic for subclassing errors. - * - * @param name property name for the XML error element - */ -data class Error( - val name: Property.Name -): Serializable { - - companion object { - - val NAME = Property.Name(NS_WEBDAV, "error") - - fun parseError(parser: XmlPullParser): List { - val names = mutableSetOf() - - val depth = parser.depth - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) - names += Property.Name(parser.namespace, parser.name) - eventType = parser.next() - } - - return names.map { Error(it) } - } - - - // some pre-defined errors - - val NEED_PRIVILEGES = Error(Property.Name(NS_WEBDAV, "need-privileges")) - val VALID_SYNC_TOKEN = Error(Property.Name(NS_WEBDAV, "valid-sync-token")) - - } - - override fun equals(other: Any?) = - (other is Error) && other.name == name - - override fun hashCode() = name.hashCode() - -} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Response.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Response.kt index a3302f02..372ab3a9 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Response.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Response.kt @@ -55,7 +55,7 @@ data class Response( /** * list of precondition/postcondition elements (`error` XML elements) */ - val error: List? = null, + val error: List? = null, /** * new location of this response (`location` XML element), used for redirects @@ -123,7 +123,7 @@ data class Response( var hrefOrNull: HttpUrl? = null var status: StatusLine? = null val propStat = mutableListOf() - var error: List? = null + var error: List? = null var newLocation: HttpUrl? = null var eventType = parser.eventType @@ -163,8 +163,8 @@ data class Response( } PropStat.NAME -> PropStat.parse(parser).let { propStat += it } - Error.NAME -> - error = Error.parseError(parser) + at.bitfire.dav4jvm.Error.NAME -> + error = at.bitfire.dav4jvm.Error.parseError(parser) LOCATION -> newLocation = parser.nextText().toHttpUrlOrNull() } diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/DavException.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/DavException.kt index 747453fa..9205a8b8 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/DavException.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/DavException.kt @@ -10,7 +10,7 @@ package at.bitfire.dav4jvm.okhttp.exception -import at.bitfire.dav4jvm.okhttp.Error +import at.bitfire.dav4jvm.Error import okhttp3.Response import javax.annotation.WillNotClose diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpException.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpException.kt index 6c23123d..6e626e11 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpException.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpException.kt @@ -10,7 +10,6 @@ package at.bitfire.dav4jvm.okhttp.exception -import at.bitfire.dav4jvm.okhttp.Error import okhttp3.Response import javax.annotation.WillNotClose @@ -23,7 +22,7 @@ open class HttpException( override val statusCode: Int, requestExcerpt: String?, responseExcerpt: String?, - errors: List = emptyList() + errors: List = emptyList() ): DavException(message, cause, statusCode, requestExcerpt, responseExcerpt, errors) { // constructor from Response diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfo.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfo.kt index d4746fea..8edd0d2f 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfo.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfo.kt @@ -10,7 +10,7 @@ package at.bitfire.dav4jvm.okhttp.exception -import at.bitfire.dav4jvm.okhttp.Error +import at.bitfire.dav4jvm.Error import at.bitfire.dav4jvm.XmlUtils import at.bitfire.dav4jvm.XmlUtils.propertyName import at.bitfire.dav4jvm.okhttp.exception.DavException.Companion.MAX_EXCERPT_SIZE diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/ErrorTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/ErrorTest.kt index e62a59aa..9a99459b 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/ErrorTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/ErrorTest.kt @@ -18,8 +18,8 @@ class ErrorTest { @Test fun testEquals() { - val errors = listOf(Error(Property.Name("DAV:", "valid-sync-token"))) - assertTrue(errors.contains(Error.Companion.VALID_SYNC_TOKEN)) + val errors = listOf(at.bitfire.dav4jvm.Error(Property.Name("DAV:", "valid-sync-token"))) + assertTrue(errors.contains(at.bitfire.dav4jvm.Error.Companion.VALID_SYNC_TOKEN)) } } \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/DavExceptionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/DavExceptionTest.kt index fac7722c..92a23cdd 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/DavExceptionTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/DavExceptionTest.kt @@ -10,7 +10,7 @@ package at.bitfire.dav4jvm.okhttp.exception -import at.bitfire.dav4jvm.okhttp.Error +import at.bitfire.dav4jvm.Error import at.bitfire.dav4jvm.Property import okhttp3.MediaType.Companion.toMediaType import okhttp3.Protocol diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpExceptionTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpExceptionTest.kt index bba95f24..956f611c 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpExceptionTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpExceptionTest.kt @@ -10,7 +10,7 @@ package at.bitfire.dav4jvm.okhttp.exception -import at.bitfire.dav4jvm.okhttp.Error +import at.bitfire.dav4jvm.Error import at.bitfire.dav4jvm.Property import okhttp3.Protocol import okhttp3.Request diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfoTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfoTest.kt index e9d0a2b3..0052e4ed 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfoTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpResponseInfoTest.kt @@ -10,7 +10,7 @@ package at.bitfire.dav4jvm.okhttp.exception -import at.bitfire.dav4jvm.okhttp.Error +import at.bitfire.dav4jvm.Error import at.bitfire.dav4jvm.Property import okhttp3.MediaType.Companion.toMediaType import okhttp3.Protocol From b46e61bf09fd303f2453d6f8d6284bea0bab32d4 Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Tue, 23 Sep 2025 11:06:25 -0700 Subject: [PATCH 05/11] Removed InvalidPropertyException.kt and replaced by generic exception Created dedicated constructors in ScheduleTag.kt each for ktor and okhttp --- src/main/kotlin/at/bitfire/dav4jvm/Property.kt | 3 +-- .../kotlin/at/bitfire/dav4jvm/XmlReader.kt | 5 +---- .../at/bitfire/dav4jvm/ktor/DavResource.kt | 9 ++++----- .../ktor/exception/InvalidPropertyException.kt | 18 ------------------ .../exception/InvalidPropertyException.kt | 18 ------------------ .../dav4jvm/property/caldav/ScheduleTag.kt | 9 +++++++-- 6 files changed, 13 insertions(+), 49 deletions(-) delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/InvalidPropertyException.kt delete mode 100644 src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/InvalidPropertyException.kt diff --git a/src/main/kotlin/at/bitfire/dav4jvm/Property.kt b/src/main/kotlin/at/bitfire/dav4jvm/Property.kt index 29d60cf4..7aebc1a8 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/Property.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/Property.kt @@ -10,7 +10,6 @@ package at.bitfire.dav4jvm -import at.bitfire.dav4jvm.okhttp.exception.InvalidPropertyException import org.xmlpull.v1.XmlPullParser import java.io.Serializable import java.util.LinkedList @@ -60,7 +59,7 @@ interface Property { properties.add(property) } else logger.fine("Ignoring unknown property $name") - } catch (e: InvalidPropertyException) { + } catch (e: Exception) { // catching here generic exception in order to avoid dependency on specific okhttp or ktor Exception logger.log(Level.WARNING, "Ignoring invalid property", e) } } diff --git a/src/main/kotlin/at/bitfire/dav4jvm/XmlReader.kt b/src/main/kotlin/at/bitfire/dav4jvm/XmlReader.kt index 13babe4a..f0d9661d 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/XmlReader.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/XmlReader.kt @@ -14,12 +14,9 @@ import at.bitfire.dav4jvm.XmlUtils.propertyName import at.bitfire.dav4jvm.property.caldav.SupportedCalendarData.Companion.CONTENT_TYPE import at.bitfire.dav4jvm.property.caldav.SupportedCalendarData.Companion.VERSION import io.ktor.http.ContentType -import okhttp3.MediaType -import okhttp3.MediaType.Companion.toMediaTypeOrNull import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParserException import java.io.IOException -import java.lang.Exception import java.time.Instant import java.util.logging.Level import java.util.logging.Logger @@ -162,7 +159,7 @@ class XmlReader( * attribute with [onNewType]. * * @param tagName The name of the tag that contains the [CONTENT_TYPE] attribute value. - * @param onNewType Called every time a new [MediaType] is found. + * @param onNewType Called every time a new [ContentType] is found. */ fun readContentTypes(tagName: Property.Name, onNewType: (String) -> Unit) { try { diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavResource.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavResource.kt index a256ea44..b1fb2f72 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavResource.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavResource.kt @@ -14,10 +14,11 @@ import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.QuotedStringUtils import at.bitfire.dav4jvm.XmlReader import at.bitfire.dav4jvm.XmlUtils -import at.bitfire.dav4jvm.ktor.Response.Companion.MULTISTATUS -import at.bitfire.dav4jvm.ktor.Response.Companion.RESPONSE import at.bitfire.dav4jvm.XmlUtils.insertTag import at.bitfire.dav4jvm.XmlUtils.propertyName +import at.bitfire.dav4jvm.ktor.DavResource.Companion.MAX_REDIRECTS +import at.bitfire.dav4jvm.ktor.Response.Companion.MULTISTATUS +import at.bitfire.dav4jvm.ktor.Response.Companion.RESPONSE import at.bitfire.dav4jvm.ktor.exception.ConflictException import at.bitfire.dav4jvm.ktor.exception.DavException import at.bitfire.dav4jvm.ktor.exception.ForbiddenException @@ -64,8 +65,6 @@ import java.io.EOFException import java.io.IOException import java.io.Reader import java.io.StringWriter -import kotlin.collections.iterator -import kotlin.collections.plusAssign /** @@ -716,7 +715,7 @@ open class DavResource @JvmOverloads constructor( HttpStatusCode.MovedPermanently, HttpStatusCode.Found, HttpStatusCode.SeeOther) - ) //if is redirect, based on okhttp3/Response.kt: HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER + ) //if is redirect // handle 3xx Redirection response.let { val target = it.headers[HttpHeaders.Location]?.let { newLocation -> diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/InvalidPropertyException.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/InvalidPropertyException.kt deleted file mode 100644 index 23490582..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/InvalidPropertyException.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.ktor.exception - -/** - * Represents an invalid XML (WebDAV) property. This is for instance thrown - * when parsing something like `...` - * because a text value would be expected. - */ -class InvalidPropertyException(message: String): DavException(message) \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/InvalidPropertyException.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/InvalidPropertyException.kt deleted file mode 100644 index cbc8ce5b..00000000 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/InvalidPropertyException.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package at.bitfire.dav4jvm.okhttp.exception - -/** - * Represents an invalid XML (WebDAV) property. This is for instance thrown - * when parsing something like `...` - * because a text value would be expected. - */ -class InvalidPropertyException(message: String): DavException(message) \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt index 5f169d4f..d97bd9dc 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt @@ -14,8 +14,10 @@ import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.PropertyFactory import at.bitfire.dav4jvm.QuotedStringUtils import at.bitfire.dav4jvm.XmlReader +import at.bitfire.dav4jvm.property.webdav.GetETag import io.ktor.client.statement.HttpResponse import io.ktor.http.HttpHeaders +import okhttp3.Response import org.xmlpull.v1.XmlPullParser data class ScheduleTag( @@ -27,8 +29,11 @@ data class ScheduleTag( @JvmField val NAME = Property.Name(NS_CALDAV, "schedule-tag") - fun fromResponse(response: HttpResponse) = - response.headers[HttpHeaders.ScheduleTag]?.let { ScheduleTag(it) } + fun fromKtorResponse(response: HttpResponse) = + response.headers[HttpHeaders.ScheduleTag]?.let { ScheduleTag(it) } + + fun fromOkhttpResponse(response: Response) = + response.header(HttpHeaders.ScheduleTag)?.let { GetETag(it) } } From 311736228f4f91ab5fb9ea6a3c8b536380b6d9ef Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Tue, 23 Sep 2025 11:15:15 -0700 Subject: [PATCH 06/11] Cleanup --- .../kotlin/at/bitfire/dav4jvm/ktor/DavCollection.kt | 2 +- .../kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtils.kt | 6 ------ src/main/kotlin/at/bitfire/dav4jvm/ktor/Response.kt | 6 +++--- .../kotlin/at/bitfire/dav4jvm/okhttp/DavResource.kt | 5 ++--- .../kotlin/at/bitfire/dav4jvm/okhttp/OkHttpUtils.kt | 13 ------------- src/test/kotlin/at/bitfire/dav4jvm/XmlReaderTest.kt | 2 -- 6 files changed, 6 insertions(+), 28 deletions(-) diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCollection.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCollection.kt index 673a70d1..a3a85305 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCollection.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/DavCollection.kt @@ -53,7 +53,7 @@ open class DavCollection @JvmOverloads constructor( * @param callback called for every WebDAV response XML element in the result * * @return list of properties which have been received in the Multi-Status response, but - * are not part of response XML elements (like `sync-token` which is returned as [at.bitfire.dav4jvm.ktor.property.webdav.SyncToken]) + * are not part of response XML elements (like `sync-token` which is returned as [at.bitfire.dav4jvm.property.webdav.SyncToken]) * * @throws java.io.IOException on I/O error * @throws at.bitfire.dav4jvm.ktor.exception.HttpException on HTTP error diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtils.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtils.kt index 55985d80..d2f8f80c 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtils.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/KtorHttpUtils.kt @@ -13,18 +13,12 @@ package at.bitfire.dav4jvm.ktor import io.ktor.client.statement.HttpResponse import io.ktor.http.HttpStatusCode import io.ktor.http.Url -import java.util.logging.Logger object KtorHttpUtils { val INVALID_STATUS = HttpStatusCode( 500, "Invalid status line") - - private val logger - get() = Logger.getLogger(javaClass.name) - - /** * Gets the resource name (the last segment of the path) from an URL. * Empty if the resource is the base directory. diff --git a/src/main/kotlin/at/bitfire/dav4jvm/ktor/Response.kt b/src/main/kotlin/at/bitfire/dav4jvm/ktor/Response.kt index 668015dc..2408694f 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/ktor/Response.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/ktor/Response.kt @@ -109,12 +109,12 @@ data class Response( * Parses an XML response element and calls the [callback] for it (when it has a ``). * The arguments of the [MultiResponseCallback.onResponse] are set accordingly. * - * If the [at.bitfire.dav4jvm.ktor.property.webdav.ResourceType] of the queried resource is known (= was queried and returned by the server) - * and it contains [at.bitfire.dav4jvm.ktor.property.webdav.ResourceType.Companion.COLLECTION], the `href` property of the callback will automatically + * If the [at.bitfire.dav4jvm.property.webdav.ResourceType] of the queried resource is known (= was queried and returned by the server) + * and it contains [at.bitfire.dav4jvm.property.webdav.ResourceType.Companion.COLLECTION], the `href` property of the callback will automatically * have a trailing slash. * * So if you want PROPFIND results to have a trailing slash when they are collections, make sure - * that you query [at.bitfire.dav4jvm.ktor.property.webdav.ResourceType]. + * that you query [at.bitfire.dav4jvm.property.webdav.ResourceType]. */ fun parse(parser: XmlPullParser, location: Url, callback: MultiResponseCallback) { val logger = Logger.getLogger(Response::javaClass.name) diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavResource.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavResource.kt index 5966eda6..167e2159 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavResource.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/DavResource.kt @@ -14,9 +14,9 @@ import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.QuotedStringUtils import at.bitfire.dav4jvm.XmlReader import at.bitfire.dav4jvm.XmlUtils -import at.bitfire.dav4jvm.okhttp.DavResource.Companion.MAX_REDIRECTS import at.bitfire.dav4jvm.XmlUtils.insertTag import at.bitfire.dav4jvm.XmlUtils.propertyName +import at.bitfire.dav4jvm.okhttp.DavResource.Companion.MAX_REDIRECTS import at.bitfire.dav4jvm.okhttp.exception.ConflictException import at.bitfire.dav4jvm.okhttp.exception.DavException import at.bitfire.dav4jvm.okhttp.exception.ForbiddenException @@ -46,7 +46,6 @@ import java.io.Reader import java.io.StringWriter import java.util.logging.Level import java.util.logging.Logger -import kotlin.collections.iterator import at.bitfire.dav4jvm.okhttp.Response as DavResponse /** @@ -149,7 +148,7 @@ open class DavResource @JvmOverloads constructor( /** - * Gets the file name of this resource. See [at.bitfire.dav4jvm.HttpUtils.fileName] for details. + * Gets the file name of this resource. See [at.bitfire.dav4jvm.okhttp.OkHttpUtils.fileName] for details. */ fun fileName() = OkHttpUtils.fileName(location) diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/OkHttpUtils.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/OkHttpUtils.kt index 51ce5941..5192c80b 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/OkHttpUtils.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/OkHttpUtils.kt @@ -12,22 +12,9 @@ package at.bitfire.dav4jvm.okhttp import okhttp3.HttpUrl import okhttp3.Response -import java.time.format.DateTimeFormatter -import java.util.Locale -import java.util.logging.Logger object OkHttpUtils { - /** - * Preferred HTTP date/time format, see RFC 7231 7.1.1.1 IMF-fixdate - */ - private const val httpDateFormatStr = "EEE, dd MMM yyyy HH:mm:ss ZZZZ" - private val httpDateFormat = DateTimeFormatter.ofPattern(httpDateFormatStr, Locale.US) - - private val logger - get() = Logger.getLogger(javaClass.name) - - /** * Gets the resource name (the last segment of the path) from an URL. * Empty if the resource is the base directory. diff --git a/src/test/kotlin/at/bitfire/dav4jvm/XmlReaderTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/XmlReaderTest.kt index 7710bb99..247dada0 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/XmlReaderTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/XmlReaderTest.kt @@ -10,8 +10,6 @@ package at.bitfire.dav4jvm -import okhttp3.MediaType -import okhttp3.MediaType.Companion.toMediaType import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Assert.assertTrue From 415e9cc16dc9e15587faeea757a931aca6afa5fa Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:37:22 -0700 Subject: [PATCH 07/11] removed obsolete dependencies --- build.gradle.kts | 3 --- gradle/libs.versions.toml | 4 ---- 2 files changed, 7 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 5da048ae..55022724 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -59,9 +59,6 @@ dependencies { api(libs.xpp3) implementation(libs.ktor.client.core) - implementation(libs.ktor.client.logging) - implementation(libs.ktor.server.http.redirect) - implementation(libs.slf4j) testImplementation(libs.junit4) testImplementation(libs.ktor.client.mock) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fd32074a..0cbe2ee2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,17 +6,13 @@ okhttpVersion = "5.1.0" spotbugs = "4.9.6" xpp3Version = "1.1.6" ktor = "3.2.1" -slf4j = "1.7.36" [libraries] junit4 = { module = "junit:junit", version.ref = "junit4" } ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } -ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } -ktor-server-http-redirect = { module = "io.ktor:ktor-server-http-redirect", version.ref = "ktor" } ktor-client-mock = { module = "io.ktor:ktor-client-mock", version.ref = "ktor" } okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttpVersion" } okhttp-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver3", version.ref = "okhttpVersion" } -slf4j = { module = "org.slf4j:slf4j-android", version.ref = "slf4j" } spotbugs-annotations = { module = "com.github.spotbugs:spotbugs-annotations", version.ref = "spotbugs" } xpp3 = { module = "org.ogce:xpp3", version.ref = "xpp3Version" } From 3a91bfb8210f428f902055f9207eb6df6ba737d2 Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Sun, 28 Sep 2025 11:51:50 -0400 Subject: [PATCH 08/11] resolved open points in discussion imported Error file properly, adapted companion objects for eTag and ScheduleTag --- src/main/kotlin/at/bitfire/dav4jvm/okhttp/Response.kt | 9 +++++---- .../at/bitfire/dav4jvm/okhttp/exception/HttpException.kt | 3 ++- .../at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt | 4 ++-- .../kotlin/at/bitfire/dav4jvm/property/webdav/GetETag.kt | 4 ++-- src/test/kotlin/at/bitfire/dav4jvm/okhttp/ErrorTest.kt | 5 +++-- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Response.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Response.kt index 372ab3a9..f896f31b 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Response.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/Response.kt @@ -10,6 +10,7 @@ package at.bitfire.dav4jvm.okhttp +import at.bitfire.dav4jvm.Error import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.XmlUtils.propertyName import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV @@ -55,7 +56,7 @@ data class Response( /** * list of precondition/postcondition elements (`error` XML elements) */ - val error: List? = null, + val error: List? = null, /** * new location of this response (`location` XML element), used for redirects @@ -123,7 +124,7 @@ data class Response( var hrefOrNull: HttpUrl? = null var status: StatusLine? = null val propStat = mutableListOf() - var error: List? = null + var error: List? = null var newLocation: HttpUrl? = null var eventType = parser.eventType @@ -163,8 +164,8 @@ data class Response( } PropStat.NAME -> PropStat.parse(parser).let { propStat += it } - at.bitfire.dav4jvm.Error.NAME -> - error = at.bitfire.dav4jvm.Error.parseError(parser) + Error.NAME -> + error = Error.parseError(parser) LOCATION -> newLocation = parser.nextText().toHttpUrlOrNull() } diff --git a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpException.kt b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpException.kt index 6e626e11..6ce0bf02 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpException.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpException.kt @@ -10,6 +10,7 @@ package at.bitfire.dav4jvm.okhttp.exception +import at.bitfire.dav4jvm.Error import okhttp3.Response import javax.annotation.WillNotClose @@ -22,7 +23,7 @@ open class HttpException( override val statusCode: Int, requestExcerpt: String?, responseExcerpt: String?, - errors: List = emptyList() + errors: List = emptyList() ): DavException(message, cause, statusCode, requestExcerpt, responseExcerpt, errors) { // constructor from Response diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt index d97bd9dc..d6920ab8 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt @@ -29,10 +29,10 @@ data class ScheduleTag( @JvmField val NAME = Property.Name(NS_CALDAV, "schedule-tag") - fun fromKtorResponse(response: HttpResponse) = + fun fromHttpResponse(response: HttpResponse) = response.headers[HttpHeaders.ScheduleTag]?.let { ScheduleTag(it) } - fun fromOkhttpResponse(response: Response) = + fun fromResponse(response: Response) = response.header(HttpHeaders.ScheduleTag)?.let { GetETag(it) } } diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetETag.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetETag.kt index 5ac76d65..8fcb0821 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetETag.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetETag.kt @@ -33,10 +33,10 @@ data class GetETag( @JvmField val NAME = Property.Name(NS_WEBDAV, "getetag") - fun fromKtorResponse(response: HttpResponse) = + fun fromHttpResponse(response: HttpResponse) = response.headers[HttpHeaders.ETag]?.let { GetETag(it) } - fun fromOkhttpResponse(response: Response) = + fun fromResponse(response: Response) = response.header("ETag")?.let { GetETag(it) } } diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/ErrorTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/ErrorTest.kt index 9a99459b..1b78074d 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/ErrorTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/ErrorTest.kt @@ -10,6 +10,7 @@ package at.bitfire.dav4jvm.okhttp +import at.bitfire.dav4jvm.Error import at.bitfire.dav4jvm.Property import org.junit.Assert.assertTrue import org.junit.Test @@ -18,8 +19,8 @@ class ErrorTest { @Test fun testEquals() { - val errors = listOf(at.bitfire.dav4jvm.Error(Property.Name("DAV:", "valid-sync-token"))) - assertTrue(errors.contains(at.bitfire.dav4jvm.Error.Companion.VALID_SYNC_TOKEN)) + val errors = listOf(Error(Property.Name("DAV:", "valid-sync-token"))) + assertTrue(errors.contains(Error.Companion.VALID_SYNC_TOKEN)) } } \ No newline at end of file From 710a64fa29580fd2aa38ad143235afc1946c1750 Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Sun, 28 Sep 2025 11:54:38 -0400 Subject: [PATCH 09/11] Updated tests --- .../at/bitfire/dav4jvm/ktor/DavResourceTest.kt | 12 ++++++------ .../at/bitfire/dav4jvm/okhttp/DavResourceTest.kt | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavResourceTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavResourceTest.kt index aae46c1b..230ba005 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavResourceTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavResourceTest.kt @@ -286,7 +286,7 @@ class DavResourceTest { called = true runBlocking { assertEquals(sampleText, response.bodyAsText()) } - val eTag = GetETag.fromKtorResponse(response) + val eTag = GetETag.fromHttpResponse(response) assertEquals("My Weak ETag", eTag!!.eTag) assertTrue(eTag.weak) assertEquals(ContentType.parse("application/x-test-result"), response.contentType()) @@ -397,7 +397,7 @@ class DavResourceTest { called = true runBlocking { assertEquals(sampleText, response.bodyAsText()) } - val eTag = GetETag.fromKtorResponse(response) + val eTag = GetETag.fromHttpResponse(response) assertEquals("My Weak ETag", eTag!!.eTag) assertTrue(eTag.weak) assertEquals(ContentType.parse("application/x-test-result"), response.contentType()) @@ -625,7 +625,7 @@ class DavResourceTest { try { dav.propfind(0, ResourceType.NAME) { _, _ -> called = true } fail("Expected HttpException") - } catch (e: HttpException) { + } catch (_: HttpException) { assertFalse(called) } @@ -664,7 +664,7 @@ class DavResourceTest { try { dav.propfind(0, ResourceType.NAME) { _, _ -> called = true } fail("Expected DavException") - } catch (e: DavException) { + } catch (_: DavException) { assertFalse(called) } } @@ -1154,7 +1154,7 @@ class DavResourceTest { headers = HeadersBuilder().apply { append(HttpHeaders.ContentType, "text/plain") }.build() ) { response -> called = true - val eTag = GetETag.fromKtorResponse(response)!! + val eTag = GetETag.fromHttpResponse(response)!! assertEquals("Weak PUT ETag", eTag.eTag) assertTrue(eTag.weak) assertEquals(response.request.url, dav.location) @@ -1192,7 +1192,7 @@ class DavResourceTest { dav.put(sampleText, headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString()), ifNoneMatch = true) { response -> called = true assertEquals(URLBuilder(sampleUrl).takeFrom("/target").build(), response.request.url) - val eTag = GetETag.fromKtorResponse(response) + val eTag = GetETag.fromHttpResponse(response) assertNull("Weak PUT ETag", eTag?.eTag) assertNull(eTag?.weak) } diff --git a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/DavResourceTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/DavResourceTest.kt index 3376ed11..80a1b278 100644 --- a/src/test/kotlin/at/bitfire/dav4jvm/okhttp/DavResourceTest.kt +++ b/src/test/kotlin/at/bitfire/dav4jvm/okhttp/DavResourceTest.kt @@ -262,7 +262,7 @@ class DavResourceTest { called = true assertEquals(sampleText, response.body.string()) - val eTag = GetETag.fromOkhttpResponse(response) + val eTag = GetETag.fromResponse(response) assertEquals("My Weak ETag", eTag!!.eTag) assertTrue(eTag.weak) assertEquals("application/x-test-result", GetContentType(response.body.contentType()?.toString()).type) @@ -352,7 +352,7 @@ class DavResourceTest { called = true assertEquals(sampleText, response.body.string()) - val eTag = GetETag.fromOkhttpResponse(response) + val eTag = GetETag.fromResponse(response) assertEquals("My Weak ETag", eTag!!.eTag) assertTrue(eTag.weak) assertEquals("application/x-test-result", GetContentType(response.body.contentType()?.toString()).type) @@ -989,7 +989,7 @@ class DavResourceTest { var called = false dav.put(sampleText.toRequestBody("text/plain".toMediaType())) { response -> called = true - val eTag = GetETag.fromOkhttpResponse(response)!! + val eTag = GetETag.fromResponse(response)!! assertEquals("Weak PUT ETag", eTag.eTag) assertTrue(eTag.weak) assertEquals(response.request.url, dav.location) @@ -1016,7 +1016,7 @@ class DavResourceTest { dav.put(sampleText.toRequestBody("text/plain".toMediaType()), ifNoneMatch = true) { response -> called = true assertEquals(url.resolve("/target"), response.request.url) - val eTag = GetETag.fromOkhttpResponse(response) + val eTag = GetETag.fromResponse(response) assertNull("Weak PUT ETag", eTag?.eTag) assertNull(eTag?.weak) } From f656c1de09abd83a639944f68b263b420ca037e6 Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Thu, 2 Oct 2025 07:57:35 -0700 Subject: [PATCH 10/11] Fixed fromResponse method returns GetETag instead of ScheduleTag --- .../kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt index d6920ab8..74f5e81b 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt @@ -33,7 +33,7 @@ data class ScheduleTag( response.headers[HttpHeaders.ScheduleTag]?.let { ScheduleTag(it) } fun fromResponse(response: Response) = - response.header(HttpHeaders.ScheduleTag)?.let { GetETag(it) } + response.header(HttpHeaders.ScheduleTag)?.let { ScheduleTag(it) } } From 7178f5547b666a40b293e970240951566e381161 Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Thu, 2 Oct 2025 08:07:02 -0700 Subject: [PATCH 11/11] Removed ktor dependency in GetContentType.kt --- .../at/bitfire/dav4jvm/property/webdav/GetContentType.kt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetContentType.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetContentType.kt index e7e8b9a8..7e8eebda 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetContentType.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetContentType.kt @@ -13,7 +13,6 @@ package at.bitfire.dav4jvm.property.webdav import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.PropertyFactory import at.bitfire.dav4jvm.XmlReader -import io.ktor.http.ContentType import org.xmlpull.v1.XmlPullParser data class GetContentType( @@ -33,12 +32,6 @@ data class GetContentType( override fun create(parser: XmlPullParser) = // - GetContentType(XmlReader(parser).readText()?.let { - try { - ContentType.parse(it).toString() - } catch (_: Exception) { - null - } - }) + GetContentType(XmlReader(parser).readText()) } }