├── .github
├── FUNDING.yml
├── workflows
│ ├── test.yml
│ └── build-kdoc.yml
└── dependabot.yml
├── gradle.properties
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── libs.versions.toml
├── CHANGELOG.md
├── AUTHORS
├── .idea
├── codeStyles
│ ├── codeStyleConfig.xml
│ └── Project.xml
└── copyright
│ ├── profiles_settings.xml
│ └── MPL.xml
├── SECURITY.md
├── src
├── main
│ └── kotlin
│ │ └── at
│ │ └── bitfire
│ │ └── dav4jvm
│ │ ├── property
│ │ ├── push
│ │ │ ├── PushTransport.kt
│ │ │ ├── Topic.kt
│ │ │ ├── AuthSecret.kt
│ │ │ ├── VapidPublicKey.kt
│ │ │ ├── SubscriptionPublicKey.kt
│ │ │ ├── PushResource.kt
│ │ │ ├── Subscription.kt
│ │ │ ├── WebPush.kt
│ │ │ ├── PushTransports.kt
│ │ │ ├── Trigger.kt
│ │ │ ├── WebDAVPush.kt
│ │ │ ├── PropertyUpdate.kt
│ │ │ ├── SupportedTriggers.kt
│ │ │ ├── ContentUpdate.kt
│ │ │ ├── PushMessage.kt
│ │ │ ├── WebPushSubscription.kt
│ │ │ └── PushRegister.kt
│ │ ├── caldav
│ │ │ ├── Source.kt
│ │ │ ├── GetCTag.kt
│ │ │ ├── CalendarHomeSet.kt
│ │ │ ├── CalendarProxyReadFor.kt
│ │ │ ├── MaxResourceSize.kt
│ │ │ ├── CalendarProxyWriteFor.kt
│ │ │ ├── CalendarUserAddressSet.kt
│ │ │ ├── CalendarTimezone.kt
│ │ │ ├── CalendarTimezoneId.kt
│ │ │ ├── CalendarDescription.kt
│ │ │ ├── CalendarData.kt
│ │ │ ├── SupportedCalendarData.kt
│ │ │ ├── ScheduleTag.kt
│ │ │ ├── CalendarColor.kt
│ │ │ ├── SupportedCalendarComponentSet.kt
│ │ │ └── CalDAV.kt
│ │ ├── webdav
│ │ │ ├── GroupMembership.kt
│ │ │ ├── CreationDate.kt
│ │ │ ├── QuotaUsedBytes.kt
│ │ │ ├── GetContentLength.kt
│ │ │ ├── Owner.kt
│ │ │ ├── SyncToken.kt
│ │ │ ├── QuotaAvailableBytes.kt
│ │ │ ├── GetContentType.kt
│ │ │ ├── DisplayName.kt
│ │ │ ├── AddMember.kt
│ │ │ ├── GetLastModified.kt
│ │ │ ├── SyncLevel.kt
│ │ │ ├── CurrentUserPrincipal.kt
│ │ │ ├── Depth.kt
│ │ │ ├── SupportedReportSet.kt
│ │ │ ├── ResourceType.kt
│ │ │ ├── GetETag.kt
│ │ │ ├── CurrentUserPrivilegeSet.kt
│ │ │ └── WebDAV.kt
│ │ ├── carddav
│ │ │ ├── AddressbookHomeSet.kt
│ │ │ ├── MaxResourceSize.kt
│ │ │ ├── AddressbookDescription.kt
│ │ │ ├── AddressData.kt
│ │ │ ├── CardDAV.kt
│ │ │ └── SupportedAddressData.kt
│ │ └── common
│ │ │ └── HrefListProperty.kt
│ │ ├── okhttp
│ │ ├── exception
│ │ │ ├── GoneException.kt
│ │ │ ├── ConflictException.kt
│ │ │ ├── ForbiddenException.kt
│ │ │ ├── NotFoundException.kt
│ │ │ ├── UnauthorizedException.kt
│ │ │ ├── PreconditionFailedException.kt
│ │ │ ├── HttpException.kt
│ │ │ ├── DavException.kt
│ │ │ └── ServiceUnavailableException.kt
│ │ ├── CallbackInterfaces.kt
│ │ ├── OkHttpUtils.kt
│ │ └── PropStat.kt
│ │ ├── ktor
│ │ ├── PropStat.kt
│ │ ├── exception
│ │ │ ├── GoneException.kt
│ │ │ ├── ConflictException.kt
│ │ │ ├── NotFoundException.kt
│ │ │ ├── ForbiddenException.kt
│ │ │ ├── UnauthorizedException.kt
│ │ │ ├── PreconditionFailedException.kt
│ │ │ ├── DavException.kt
│ │ │ └── ServiceUnavailableException.kt
│ │ ├── PropStatParser.kt
│ │ ├── CallbackInterfaces.kt
│ │ ├── MultiStatusParser.kt
│ │ └── Response.kt
│ │ ├── PropertyFactory.kt
│ │ ├── QuotedStringUtils.kt
│ │ ├── Error.kt
│ │ ├── Property.kt
│ │ ├── XmlUtils.kt
│ │ └── HttpUtils.kt
└── test
│ └── kotlin
│ └── at
│ └── bitfire
│ └── dav4jvm
│ ├── XmlUtilsTest.kt
│ ├── ktor
│ ├── ErrorTest.kt
│ ├── ResponseParserTest.kt
│ ├── DavCalendarTest.kt
│ ├── PropertyTest.kt
│ ├── exception
│ │ ├── ServiceUnavailableExceptionTest.kt
│ │ └── DavExceptionTest.kt
│ └── UrlUtilsTest.kt
│ ├── okhttp
│ ├── ErrorTest.kt
│ ├── property
│ │ ├── PropertyTest.kt
│ │ ├── CalendarDescriptionTest.kt
│ │ ├── OwnerTest.kt
│ │ └── GetETagTest.kt
│ ├── OkHttpUtilsTest.kt
│ ├── QuotedStringUtilsTest.kt
│ ├── DavCalendarTest.kt
│ ├── exception
│ │ ├── ServiceUnavailableExceptionTest.kt
│ │ └── DavExceptionTest.kt
│ └── UrlUtilsTest.kt
│ ├── property
│ ├── PropertyTest.kt
│ ├── CalendarDescriptionTest.kt
│ ├── OwnerTest.kt
│ └── GetETagTest.kt
│ ├── PropertyTest.kt
│ └── HttpUtilsTest.kt
├── .gitignore
└── gradlew.bat
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 |
2 | github: bitfireAT
3 | custom: https://www.davx5.com/donate
4 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Dokka
2 | org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitfireAT/dav4jvm/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | See https://github.com/bitfireAT/dav4jvm/compare/, for instance
3 | https://github.com/bitfireAT/dav4jvm/compare/2.1.2...2.1.3
4 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | You can view the list of people who have contributed to the code base in the version control history:
2 | https://github.com/bitfireAT/dav4jvm/graphs/contributors
3 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Reporting a Vulnerability
4 |
5 | Please report security vulnerabilities using the [DAVx⁵ support form](https://www.davx5.com/support) or via email to support@davx5.com.
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 | on: push
3 | jobs:
4 | test:
5 | name: Run tests
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@v6
9 | - uses: actions/setup-java@v5
10 | with:
11 | distribution: temurin
12 | java-version: 17
13 | - uses: gradle/actions/setup-gradle@v5
14 |
15 | - name: Check
16 | run: ./gradlew --no-daemon check
17 |
18 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/copyright/MPL.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransport.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 |
15 | /**
16 | * Identifies a property as a push transport.
17 | */
18 | interface PushTransport: Property
19 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/GoneException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp.exception
12 |
13 | import okhttp3.Response
14 |
15 | class GoneException: HttpException {
16 |
17 | constructor(response: Response) : super(response) {
18 | if (response.code != 410)
19 | throw IllegalArgumentException("Status code must be 410")
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/ConflictException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp.exception
12 |
13 | import okhttp3.Response
14 |
15 | class ConflictException: HttpException {
16 |
17 | constructor(response: Response) : super(response) {
18 | if (response.code != 409)
19 | throw IllegalArgumentException("Status code must be 409")
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/ForbiddenException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp.exception
12 |
13 | import okhttp3.Response
14 |
15 | class ForbiddenException: HttpException {
16 |
17 | constructor(response: Response) : super(response) {
18 | if (response.code != 403)
19 | throw IllegalArgumentException("Status code must be 403")
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/NotFoundException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp.exception
12 |
13 | import okhttp3.Response
14 |
15 | class NotFoundException : HttpException {
16 |
17 | constructor(response: Response) : super(response) {
18 | if (response.code != 404)
19 | throw IllegalArgumentException("Status code must be 404")
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/UnauthorizedException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp.exception
12 |
13 | import okhttp3.Response
14 |
15 | class UnauthorizedException: HttpException {
16 |
17 | constructor(response: Response) : super(response) {
18 | if (response.code != 401)
19 | throw IllegalArgumentException("Status code must be 401")
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/PreconditionFailedException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp.exception
12 |
13 | import okhttp3.Response
14 |
15 | class PreconditionFailedException: HttpException {
16 |
17 | constructor(response: Response) : super(response) {
18 | if (response.code != 412)
19 | throw IllegalArgumentException("Status code must be 412")
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/XmlUtilsTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm
12 |
13 | import org.junit.Assert.assertNotNull
14 | import org.junit.Test
15 |
16 | class XmlUtilsTest {
17 |
18 | @Test
19 | fun newPullParser() {
20 | assertNotNull(XmlUtils.newPullParser())
21 | }
22 |
23 | @Test
24 | fun newSerializer() {
25 | assertNotNull(XmlUtils.newSerializer())
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 |
9 | - package-ecosystem: "github-actions"
10 | directory: "/"
11 | schedule:
12 | interval: "weekly"
13 | commit-message:
14 | prefix: "[CI] "
15 | groups:
16 | ci-actions:
17 | patterns: ["*"]
18 |
19 | - package-ecosystem: "gradle"
20 | directory: "/"
21 | schedule:
22 | interval: "weekly"
23 | groups:
24 | lib-dependencies:
25 | patterns: ["*"]
26 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropStat.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor
12 |
13 | import at.bitfire.dav4jvm.Error
14 | import at.bitfire.dav4jvm.Property
15 | import io.ktor.http.HttpStatusCode
16 |
17 | /**
18 | * Represents a WebDAV propstat XML element.
19 | *
20 | *
21 | */
22 | data class PropStat(
23 | val properties: List,
24 | val status: HttpStatusCode,
25 | val error: List? = null
26 | )
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/ktor/ErrorTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor
12 |
13 | import at.bitfire.dav4jvm.Error
14 | import at.bitfire.dav4jvm.Property
15 | import at.bitfire.dav4jvm.property.webdav.WebDAV
16 | import org.junit.Assert.assertTrue
17 | import org.junit.Test
18 |
19 | class ErrorTest {
20 |
21 | @Test
22 | fun testEquals() {
23 | val errors = listOf(Error(Property.Name("DAV:", "valid-sync-token")))
24 | assertTrue(errors.contains(Error(WebDAV.ValidSyncToken)))
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/okhttp/ErrorTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp
12 |
13 | import at.bitfire.dav4jvm.Error
14 | import at.bitfire.dav4jvm.Property
15 | import at.bitfire.dav4jvm.property.webdav.WebDAV
16 | import org.junit.Assert.assertTrue
17 | import org.junit.Test
18 |
19 | class ErrorTest {
20 |
21 | @Test
22 | fun testEquals() {
23 | val errors = listOf(Error(Property.Name("DAV:", "valid-sync-token")))
24 | assertTrue(errors.contains(Error(WebDAV.ValidSyncToken)))
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/Source.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.property.common.HrefListProperty
14 | import org.xmlpull.v1.XmlPullParser
15 |
16 | class Source(
17 | override val hrefs: List = emptyList()
18 | ): HrefListProperty(hrefs) {
19 |
20 | object Factory: HrefListProperty.Factory() {
21 |
22 | override fun getName() = CalDAV.Source
23 |
24 | override fun create(parser: XmlPullParser) = create(parser, ::Source)
25 |
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/GetCTag.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class GetCTag(
19 | val cTag: String?
20 | ): Property {
21 |
22 | object Factory: PropertyFactory {
23 |
24 | override fun getName() = CalDAV.GetCTag
25 |
26 | override fun create(parser: XmlPullParser) = GetCTag(XmlReader(parser).readText())
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GroupMembership.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.property.common.HrefListProperty
14 | import org.xmlpull.v1.XmlPullParser
15 |
16 | class GroupMembership(
17 | override val hrefs: List
18 | ): HrefListProperty(hrefs) {
19 |
20 | object Factory: HrefListProperty.Factory() {
21 |
22 | override fun getName() = WebDAV.GroupMembership
23 |
24 | override fun create(parser: XmlPullParser) = create(parser, ::GroupMembership)
25 |
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarHomeSet.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.property.common.HrefListProperty
14 | import org.xmlpull.v1.XmlPullParser
15 |
16 | data class CalendarHomeSet(
17 | override val hrefs: List = emptyList()
18 | ): HrefListProperty(hrefs) {
19 |
20 | object Factory: HrefListProperty.Factory() {
21 |
22 | override fun getName() = CalDAV.CalendarHomeSet
23 |
24 | override fun create(parser: XmlPullParser) = create(parser, ::CalendarHomeSet)
25 |
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/AddressbookHomeSet.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.carddav
12 |
13 | import at.bitfire.dav4jvm.property.common.HrefListProperty
14 | import org.xmlpull.v1.XmlPullParser
15 |
16 | class AddressbookHomeSet(
17 | override val hrefs: List = emptyList()
18 | ): HrefListProperty(hrefs) {
19 |
20 | object Factory: HrefListProperty.Factory() {
21 |
22 | override fun getName() = CardDAV.AddressbookHomeSet
23 |
24 | override fun create(parser: XmlPullParser) = create(parser, ::AddressbookHomeSet)
25 |
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/property/PropertyTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.XmlUtils
15 | import java.io.StringReader
16 |
17 | open class PropertyTest {
18 |
19 | companion object {
20 |
21 | fun parseProperty(s: String): List {
22 | val parser = XmlUtils.newPullParser()
23 | parser.setInput(StringReader("$s"))
24 | parser.nextTag() // move into
25 | return Property.parse(parser)
26 | }
27 |
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarProxyReadFor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.property.common.HrefListProperty
14 | import org.xmlpull.v1.XmlPullParser
15 |
16 | data class CalendarProxyReadFor(
17 | override val hrefs: List = emptyList()
18 | ): HrefListProperty(hrefs) {
19 |
20 | object Factory: HrefListProperty.Factory() {
21 |
22 | override fun getName() = CalDAV.CalendarProxyReadFor
23 |
24 | override fun create(parser: XmlPullParser) = create(parser, ::CalendarProxyReadFor)
25 |
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/MaxResourceSize.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class MaxResourceSize(
19 | val maxSize: Long?
20 | ) : Property {
21 |
22 | object Factory: PropertyFactory {
23 | override fun getName() = CalDAV.MaxResourceSize
24 |
25 | override fun create(parser: XmlPullParser) =
26 | MaxResourceSize(XmlReader(parser).readLong())
27 | }
28 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarProxyWriteFor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.property.common.HrefListProperty
14 | import org.xmlpull.v1.XmlPullParser
15 |
16 | data class CalendarProxyWriteFor(
17 | override val hrefs: List = emptyList()
18 | ): HrefListProperty(hrefs) {
19 |
20 | object Factory: HrefListProperty.Factory() {
21 |
22 | override fun getName() = CalDAV.CalendarProxyWriteFor
23 |
24 | override fun create(parser: XmlPullParser) = create(parser, ::CalendarProxyWriteFor)
25 |
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/PropertyTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp.property
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.XmlUtils
15 | import java.io.StringReader
16 |
17 | open class PropertyTest {
18 |
19 | companion object {
20 |
21 | fun parseProperty(s: String): List {
22 | val parser = XmlUtils.newPullParser()
23 | parser.setInput(StringReader("$s"))
24 | parser.nextTag() // move into
25 | return Property.parse(parser)
26 | }
27 |
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarUserAddressSet.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.property.common.HrefListProperty
14 | import org.xmlpull.v1.XmlPullParser
15 |
16 | data class CalendarUserAddressSet(
17 | override val hrefs: List = emptyList()
18 | ): HrefListProperty(hrefs) {
19 |
20 | object Factory: HrefListProperty.Factory() {
21 |
22 | override fun getName() = CalDAV.CalendarUserAddressSet
23 |
24 | override fun create(parser: XmlPullParser) = create(parser, ::CalendarUserAddressSet)
25 |
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/CreationDate.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class CreationDate(
19 | var creationDate: String?
20 | ): Property {
21 |
22 | object Factory: PropertyFactory {
23 |
24 | override fun getName() = WebDAV.CreationDate
25 |
26 | override fun create(parser: XmlPullParser) =
27 | CreationDate(XmlReader(parser).readText())
28 |
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/MaxResourceSize.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.carddav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class MaxResourceSize(
19 | val maxSize: Long?
20 | ) : Property {
21 |
22 | object Factory: PropertyFactory {
23 |
24 | override fun getName() = CardDAV.MaxResourceSize
25 |
26 | override fun create(parser: XmlPullParser) =
27 | MaxResourceSize(XmlReader(parser).readLong())
28 |
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/QuotaUsedBytes.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class QuotaUsedBytes(
19 | val quotaUsedBytes: Long?
20 | ) : Property {
21 |
22 | object Factory: PropertyFactory {
23 |
24 | override fun getName() = WebDAV.QuotaUsedBytes
25 |
26 | override fun create(parser: XmlPullParser) =
27 | QuotaUsedBytes(XmlReader(parser).readLong())
28 |
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetContentLength.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class GetContentLength(
19 | val contentLength: Long?
20 | ) : Property {
21 |
22 | object Factory: PropertyFactory {
23 |
24 | override fun getName() = WebDAV.GetContentLength
25 |
26 | override fun create(parser: XmlPullParser) =
27 | GetContentLength(XmlReader(parser).readLong())
28 |
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/property/CalendarDescriptionTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property
12 |
13 | import at.bitfire.dav4jvm.property.caldav.CalendarDescription
14 | import org.junit.Assert.assertEquals
15 | import org.junit.Test
16 |
17 | class CalendarDescriptionTest: PropertyTest() {
18 |
19 | @Test
20 | fun testCalendarDescription() {
21 | val results = parseProperty("My Calendar")
22 | val result = results.first() as CalendarDescription
23 | assertEquals("My Calendar", result.description)
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/GoneException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor.exception
12 |
13 | import io.ktor.http.HttpStatusCode
14 |
15 | class GoneException internal constructor(
16 | responseInfo: HttpResponseInfo
17 | ): HttpException(
18 | status = responseInfo.status,
19 | requestExcerpt = responseInfo.requestExcerpt,
20 | responseExcerpt = responseInfo.responseExcerpt,
21 | errors = responseInfo.errors
22 | ) {
23 |
24 | init {
25 | if (responseInfo.status != HttpStatusCode.Gone)
26 | throw IllegalArgumentException("Status must be ${HttpStatusCode.Gone}")
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/Owner.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.XmlReader
15 | import at.bitfire.dav4jvm.property.common.HrefListProperty
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class Owner(
19 | val href: String?
20 | ): Property {
21 |
22 | object Factory: HrefListProperty.Factory() {
23 |
24 | override fun getName() = WebDAV.Owner
25 |
26 | override fun create(parser: XmlPullParser): Owner =
27 | Owner(XmlReader(parser).readTextProperty(WebDAV.Href))
28 |
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/SyncToken.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class SyncToken(
19 | val token: String?
20 | ): Property {
21 |
22 | object Factory: PropertyFactory {
23 |
24 | override fun getName() = WebDAV.SyncToken
25 |
26 | override fun create(parser: XmlPullParser) =
27 | //
28 | SyncToken(XmlReader(parser).readText())
29 |
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/CalendarDescriptionTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp.property
12 |
13 | import at.bitfire.dav4jvm.property.caldav.CalendarDescription
14 | import org.junit.Assert.assertEquals
15 | import org.junit.Test
16 |
17 | class CalendarDescriptionTest: PropertyTest() {
18 |
19 | @Test
20 | fun testCalendarDescription() {
21 | val results = parseProperty("My Calendar")
22 | val result = results.first() as CalendarDescription
23 | assertEquals("My Calendar", result.description)
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ConflictException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor.exception
12 |
13 | import io.ktor.http.HttpStatusCode
14 |
15 | class ConflictException internal constructor(
16 | responseInfo: HttpResponseInfo
17 | ): HttpException(
18 | status = responseInfo.status,
19 | requestExcerpt = responseInfo.requestExcerpt,
20 | responseExcerpt = responseInfo.responseExcerpt,
21 | errors = responseInfo.errors
22 | ) {
23 |
24 | init {
25 | if (responseInfo.status != HttpStatusCode.Conflict)
26 | throw IllegalArgumentException("Status must be ${HttpStatusCode.Conflict}")
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/NotFoundException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor.exception
12 |
13 | import io.ktor.http.HttpStatusCode
14 |
15 | class NotFoundException internal constructor(
16 | responseInfo: HttpResponseInfo
17 | ): HttpException(
18 | status = responseInfo.status,
19 | requestExcerpt = responseInfo.requestExcerpt,
20 | responseExcerpt = responseInfo.responseExcerpt,
21 | errors = responseInfo.errors
22 | ) {
23 |
24 | init {
25 | if (responseInfo.status != HttpStatusCode.NotFound)
26 | throw IllegalArgumentException("Status must be ${HttpStatusCode.NotFound}")
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/QuotaAvailableBytes.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class QuotaAvailableBytes(
19 | val quotaAvailableBytes: Long?
20 | ) : Property {
21 |
22 | object Factory: PropertyFactory {
23 |
24 | override fun getName() = WebDAV.QuotaAvailableBytes
25 |
26 | override fun create(parser: XmlPullParser) =
27 | QuotaAvailableBytes(XmlReader(parser).readLong())
28 |
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ForbiddenException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor.exception
12 |
13 | import io.ktor.http.HttpStatusCode
14 |
15 | class ForbiddenException internal constructor(
16 | responseInfo: HttpResponseInfo
17 | ): HttpException(
18 | status = responseInfo.status,
19 | requestExcerpt = responseInfo.requestExcerpt,
20 | responseExcerpt = responseInfo.responseExcerpt,
21 | errors = responseInfo.errors
22 | ) {
23 |
24 | init {
25 | if (responseInfo.status != HttpStatusCode.Forbidden)
26 | throw IllegalArgumentException("Status must be ${HttpStatusCode.Forbidden}")
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetContentType.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class GetContentType(
19 | val type: String?
20 | ): Property {
21 |
22 | object Factory: PropertyFactory {
23 |
24 | override fun getName() = WebDAV.GetContentType
25 |
26 | override fun create(parser: XmlPullParser) =
27 | //
28 | GetContentType(XmlReader(parser).readText())
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/UnauthorizedException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor.exception
12 |
13 | import io.ktor.http.HttpStatusCode
14 |
15 | class UnauthorizedException internal constructor(
16 | responseInfo: HttpResponseInfo
17 | ): HttpException(
18 | status = responseInfo.status,
19 | requestExcerpt = responseInfo.requestExcerpt,
20 | responseExcerpt = responseInfo.responseExcerpt,
21 | errors = responseInfo.errors
22 | ) {
23 |
24 | init {
25 | if (responseInfo.status != HttpStatusCode.Unauthorized)
26 | throw IllegalArgumentException("Status must be ${HttpStatusCode.Unauthorized}")
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/DisplayName.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class DisplayName(
19 | val displayName: String?
20 | ): Property {
21 |
22 | object Factory: PropertyFactory {
23 |
24 | override fun getName() = WebDAV.DisplayName
25 |
26 | override fun create(parser: XmlPullParser) =
27 | //
28 | DisplayName(XmlReader(parser).readText())
29 |
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarTimezone.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class CalendarTimezone(
19 | val vTimeZone: String?
20 | ): Property {
21 |
22 | object Factory: PropertyFactory {
23 |
24 | override fun getName() = CalDAV.CalendarTimezone
25 |
26 | override fun create(parser: XmlPullParser) =
27 | //
28 | CalendarTimezone(XmlReader(parser).readText())
29 |
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/AddMember.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | /**
19 | * Defined in RFC 5995 3.2.1 DAV:add-member Property (Protected).
20 | */
21 | data class AddMember(
22 | val href: String?
23 | ): Property {
24 |
25 | object Factory: PropertyFactory {
26 |
27 | override fun getName() = WebDAV.AddMember
28 |
29 | override fun create(parser: XmlPullParser) = AddMember(XmlReader(parser).readTextProperty(WebDAV.Href))
30 |
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/PreconditionFailedException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor.exception
12 |
13 | import io.ktor.http.HttpStatusCode
14 |
15 | class PreconditionFailedException internal constructor(
16 | responseInfo: HttpResponseInfo
17 | ): HttpException(
18 | status = responseInfo.status,
19 | requestExcerpt = responseInfo.requestExcerpt,
20 | responseExcerpt = responseInfo.responseExcerpt,
21 | errors = responseInfo.errors
22 | ) {
23 |
24 | init {
25 | if (responseInfo.status != HttpStatusCode.PreconditionFailed)
26 | throw IllegalArgumentException("Status must be ${HttpStatusCode.PreconditionFailed}")
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarTimezoneId.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class CalendarTimezoneId(
19 | val identifier: String?
20 | ): Property {
21 |
22 | object Factory: PropertyFactory {
23 |
24 | override fun getName() = CalDAV.CalendarTimezoneId
25 |
26 | override fun create(parser: XmlPullParser) =
27 | //
28 | CalendarTimezoneId(XmlReader(parser).readText())
29 |
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/.github/workflows/build-kdoc.yml:
--------------------------------------------------------------------------------
1 | name: Build KDoc
2 | on:
3 | push:
4 | branches: [main]
5 |
6 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
7 | permissions:
8 | contents: read
9 | pages: write
10 | id-token: write
11 |
12 | jobs:
13 | build:
14 | name: Build and publish KDoc
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v6
18 | - uses: actions/setup-java@v5
19 | with:
20 | distribution: temurin
21 | java-version: 17
22 | - uses: gradle/actions/setup-gradle@v5
23 |
24 | - name: Build KDoc
25 | run: ./gradlew --no-daemon dokkaGenerate
26 |
27 | - uses: actions/upload-pages-artifact@v4
28 | with:
29 | path: build/dokka/html
30 |
31 | deploy:
32 | environment:
33 | name: github-pages
34 | url: ${{ steps.deployment.outputs.page_url }}
35 | runs-on: ubuntu-latest
36 | needs: build
37 | steps:
38 | - name: Deploy to GitHub Pages
39 | id: deployment
40 | uses: actions/deploy-pages@v4
41 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarDescription.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class CalendarDescription(
19 | val description: String?
20 | ): Property {
21 |
22 | object Factory: PropertyFactory {
23 |
24 | override fun getName() = CalDAV.CalendarDescription
25 |
26 | override fun create(parser: XmlPullParser) =
27 | //
28 | CalendarDescription(XmlReader(parser).readText())
29 |
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/AddressbookDescription.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.carddav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class AddressbookDescription(
19 | val description: String? = null
20 | ): Property {
21 |
22 | object Factory: PropertyFactory {
23 |
24 | override fun getName() = CardDAV.AddressbookDescription
25 |
26 | override fun create(parser: XmlPullParser) =
27 | //
28 | AddressbookDescription(XmlReader(parser).readText())
29 |
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/Topic.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | /**
19 | * Represents a [NS_WEBDAV_PUSH]`:topic` property.
20 | *
21 | * Experimental! See https://github.com/bitfireAT/webdav-push/
22 | */
23 | data class Topic(
24 | val topic: String? = null
25 | ): Property {
26 |
27 | object Factory: PropertyFactory {
28 |
29 | override fun getName() = WebDAVPush.Topic
30 |
31 | override fun create(parser: XmlPullParser): Topic =
32 | Topic(XmlReader(parser).readText())
33 |
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/AuthSecret.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | /**
19 | * Represents an `auth-secret` property.
20 | *
21 | * Experimental! See https://github.com/bitfireAT/webdav-push/
22 | */
23 | data class AuthSecret(
24 | val secret: String? = null
25 | ): Property {
26 |
27 | object Factory: PropertyFactory {
28 |
29 | override fun getName() = WebDAVPush.AuthSecret
30 |
31 | override fun create(parser: XmlPullParser): AuthSecret =
32 | AuthSecret(XmlReader(parser).readText())
33 |
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetLastModified.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 | import java.time.Instant
18 |
19 | data class GetLastModified(
20 | val lastModified: Instant?
21 | ): Property {
22 |
23 | object Factory: PropertyFactory {
24 |
25 | override fun getName() = WebDAV.GetLastModified
26 |
27 | override fun create(parser: XmlPullParser): GetLastModified {
28 | //
29 | return GetLastModified(
30 | XmlReader(parser).readHttpDate()
31 | )
32 | }
33 |
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | dokka = "2.1.0"
3 | junit4 = "4.13.2"
4 | kotlin = "2.3.0"
5 | kotlin-coroutines = "1.10.2"
6 | okhttpVersion = "5.3.2"
7 | spotbugs = "4.9.8"
8 | xpp3Version = "1.1.6"
9 | ktor = "3.3.3"
10 |
11 | [libraries]
12 | junit4 = { module = "junit:junit", version.ref = "junit4" }
13 | kotlin-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlin-coroutines" }
14 | ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
15 | ktor-client-encoding = { module = "io.ktor:ktor-client-encoding", version.ref = "ktor" }
16 | ktor-client-mock = { module = "io.ktor:ktor-client-mock", version.ref = "ktor" }
17 | okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttpVersion" }
18 | okhttp-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver3", version.ref = "okhttpVersion" }
19 | spotbugs-annotations = { module = "com.github.spotbugs:spotbugs-annotations", version.ref = "spotbugs" }
20 | xpp3 = { module = "org.ogce:xpp3", version.ref = "xpp3Version" }
21 |
22 | [plugins]
23 | dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
24 | kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
25 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarData.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class CalendarData(
19 | val iCalendar: String?
20 | ): Property {
21 |
22 | companion object {
23 | // attributes
24 | const val CONTENT_TYPE = "content-type"
25 | const val VERSION = "version"
26 | }
27 |
28 |
29 | object Factory: PropertyFactory {
30 |
31 | override fun getName() = CalDAV.CalendarData
32 |
33 | override fun create(parser: XmlPullParser) =
34 | //
35 | CalendarData(XmlReader(parser).readText())
36 |
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/AddressData.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.carddav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class AddressData(
19 | val card: String?
20 | ): Property {
21 |
22 | companion object {
23 |
24 | // attributes
25 | const val CONTENT_TYPE = "content-type"
26 | const val VERSION = "version"
27 |
28 | }
29 |
30 |
31 | object Factory: PropertyFactory {
32 |
33 | override fun getName() = CardDAV.AddressData
34 |
35 | override fun create(parser: XmlPullParser) =
36 | //
37 | AddressData(XmlReader(parser).readText())
38 |
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/okhttp/OkHttpUtilsTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp
12 |
13 | import okhttp3.HttpUrl.Companion.toHttpUrl
14 | import org.junit.Assert.assertEquals
15 | import org.junit.Test
16 |
17 | class OkHttpUtilsTest {
18 |
19 | @Test
20 | fun fileName() {
21 | assertEquals("", OkHttpUtils.fileName("https://example.com".toHttpUrl()))
22 | assertEquals("", OkHttpUtils.fileName("https://example.com/".toHttpUrl()))
23 | assertEquals("file1", OkHttpUtils.fileName("https://example.com/file1".toHttpUrl()))
24 | assertEquals("dir1", OkHttpUtils.fileName("https://example.com/dir1/".toHttpUrl()))
25 | assertEquals("file2", OkHttpUtils.fileName("https://example.com/dir1/file2".toHttpUrl()))
26 | assertEquals("dir2", OkHttpUtils.fileName("https://example.com/dir1/dir2/".toHttpUrl()))
27 | }
28 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/PropertyFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm
12 |
13 | import org.xmlpull.v1.XmlPullParser
14 | import org.xmlpull.v1.XmlPullParserException
15 |
16 | interface PropertyFactory {
17 |
18 | /**
19 | * Name of the Property the factory creates,
20 | * e.g. `Property.Name("DAV:", "displayname")` if the factory creates
21 | * [at.bitfire.dav4jvm.property.webdav.DisplayName] objects)
22 | */
23 | fun getName(): Property.Name
24 |
25 | /**
26 | * Parses XML of a property and returns its data class.
27 | *
28 | * Implementations shouldn't make assumptions on which sub-properties are available
29 | * or not and in doubt return an empty [Property].
30 | *
31 | * @throws XmlPullParserException in case of parsing errors
32 | */
33 | fun create(parser: XmlPullParser): Property
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/SyncLevel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | /**
19 | * Represents a [NS_WEBDAV]`:sync-level` property.
20 | */
21 | data class SyncLevel(
22 | /** May be `0`, `1` or [Int.MAX_VALUE] (infinite). */
23 | val level: Int? = null
24 | ): Property {
25 |
26 | object Factory: PropertyFactory {
27 |
28 | override fun getName() = WebDAV.SyncLevel
29 |
30 | override fun create(parser: XmlPullParser): SyncLevel {
31 | val text = XmlReader(parser).readText()
32 | val level = if (text == "infinite") Int.MAX_VALUE else text?.toIntOrNull()
33 | return SyncLevel(level)
34 | }
35 |
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/VapidPublicKey.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | /**
19 | * Represents a [NS_WEBDAV_PUSH]`:vapid-public-key` property.
20 | *
21 | * Experimental! See https://github.com/bitfireAT/webdav-push/
22 | */
23 | data class VapidPublicKey(
24 | val type: String? = null,
25 | val key: String? = null
26 | ): Property {
27 |
28 | object Factory : PropertyFactory {
29 |
30 | override fun getName() = WebDAVPush.VapidPublicKey
31 |
32 | override fun create(parser: XmlPullParser): VapidPublicKey {
33 | return VapidPublicKey(
34 | type = parser.getAttributeValue(null, "type"),
35 | key = XmlReader(parser).readText()
36 | )
37 | }
38 |
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/CurrentUserPrincipal.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | // see RFC 5397: WebDAV Current Principal Extension
19 |
20 | data class CurrentUserPrincipal(
21 | val href: String?
22 | ): Property {
23 |
24 | object Factory: PropertyFactory {
25 |
26 | override fun getName() = WebDAV.CurrentUserPrincipal
27 |
28 | override fun create(parser: XmlPullParser): CurrentUserPrincipal {
29 | //
30 | var href: String? = null
31 | XmlReader(parser).processTag(WebDAV.Href) {
32 | href = readText()
33 | }
34 | return CurrentUserPrincipal(href)
35 | }
36 |
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/SubscriptionPublicKey.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | /**
19 | * Represents a [NS_WEBDAV_PUSH]`:subscription-public-key` property.
20 | *
21 | * Experimental! See https://github.com/bitfireAT/webdav-push/
22 | */
23 | data class SubscriptionPublicKey(
24 | val type: String? = null,
25 | val key: String? = null
26 | ): Property {
27 |
28 | object Factory : PropertyFactory {
29 |
30 | override fun getName() = WebDAVPush.SubscriptionPublicKey
31 |
32 | override fun create(parser: XmlPullParser): SubscriptionPublicKey {
33 | return SubscriptionPublicKey(
34 | type = parser.getAttributeValue(null, "type"),
35 | key = XmlReader(parser).readText()
36 | )
37 | }
38 |
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/Depth.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | /**
19 | * Represents a [NS_WEBDAV]`:depth` property.
20 | */
21 | data class Depth(
22 | /** May be `0`, `1` or [Int.MAX_VALUE] (infinite). */
23 | val depth: Int? = null
24 | ): Property {
25 |
26 | companion object {
27 | const val INFINITY = Int.MAX_VALUE
28 | }
29 |
30 |
31 | object Factory: PropertyFactory {
32 |
33 | override fun getName() = WebDAV.Depth
34 |
35 | override fun create(parser: XmlPullParser): Depth {
36 | val text = XmlReader(parser).readText()
37 | val level = if (text.equals("infinity", true))
38 | INFINITY
39 | else
40 | text?.toIntOrNull()
41 | return Depth(level)
42 | }
43 |
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/QuotedStringUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm
12 |
13 | object QuotedStringUtils {
14 |
15 | fun asQuotedString(raw: String) =
16 | "\"" + raw.replace("\\" ,"\\\\").replace("\"", "\\\"") + "\""
17 |
18 | fun decodeQuotedString(quoted: String): String {
19 | /* quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
20 | qdtext = >
21 | quoted-pair = "\" CHAR
22 | */
23 |
24 | val len = quoted.length
25 | if (len >= 2 && quoted[0] == '"' && quoted[len-1] == '"') {
26 | val result = StringBuffer(len)
27 | var pos = 1
28 | while (pos < len-1) {
29 | var c = quoted[pos]
30 | if (c == '\\' && pos != len-2)
31 | c = quoted[++pos]
32 | result.append(c)
33 | pos++
34 | }
35 | return result.toString()
36 | } else
37 | return quoted
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/CardDAV.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.carddav
12 |
13 | import at.bitfire.dav4jvm.Property
14 |
15 | object CardDAV {
16 |
17 | // CardDAV (RFC 6352)
18 |
19 | const val NS_CARDDAV = "urn:ietf:params:xml:ns:carddav"
20 |
21 | val Addressbook = Property.Name(NS_CARDDAV, "addressbook") // CardDAV
22 | val AddressData = Property.Name(NS_CARDDAV, "address-data")
23 | val AddressDataType = Property.Name(NS_CARDDAV, "address-data-type")
24 | val AddressbookDescription = Property.Name(NS_CARDDAV, "addressbook-description")
25 | val AddressbookHomeSet = Property.Name(NS_CARDDAV, "addressbook-home-set")
26 | val AddressbookMultiget = Property.Name(NS_CARDDAV, "addressbook-multiget")
27 | val AddressbookQuery = Property.Name(NS_CARDDAV, "addressbook-query")
28 | val Filter = Property.Name(NS_CARDDAV, "filter")
29 | val MaxResourceSize = Property.Name(NS_CARDDAV, "max-resource-size")
30 | val SupportedAddressData = Property.Name(NS_CARDDAV, "supported-address-data")
31 |
32 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushResource.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 | import java.net.URI
18 | import java.net.URISyntaxException
19 |
20 | /**
21 | * Represents a [NS_WEBDAV_PUSH]`:push-resource` property.
22 | *
23 | * Experimental! See https://github.com/bitfireAT/webdav-push/
24 | */
25 | data class PushResource(
26 | val uri: URI? = null
27 | ): Property {
28 |
29 | object Factory: PropertyFactory {
30 |
31 | override fun getName() = WebDAVPush.PushResource
32 |
33 | override fun create(parser: XmlPullParser): PushResource =
34 | PushResource(
35 | uri = XmlReader(parser).readText()?.let { uri ->
36 | try {
37 | URI(uri)
38 | } catch (_: URISyntaxException) {
39 | null
40 | }
41 | }
42 | )
43 |
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/Subscription.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | /**
19 | * Represents a [NS_WEBDAV_PUSH]`:subscription` property.
20 | *
21 | * Experimental! See https://github.com/bitfireAT/webdav-push/
22 | */
23 | data class Subscription private constructor(
24 | val webPushSubscription: WebPushSubscription? = null
25 | ): Property {
26 |
27 | object Factory: PropertyFactory {
28 |
29 | override fun getName() = WebDAVPush.Subscription
30 |
31 | override fun create(parser: XmlPullParser): Subscription {
32 | // currently we only support WebPushSubscription
33 | var webPushSubscription: WebPushSubscription? = null
34 |
35 | XmlReader(parser).processTag(WebDAVPush.WebPushSubscription) {
36 | webPushSubscription = WebPushSubscription.Factory.create(parser)
37 | }
38 |
39 | return Subscription(webPushSubscription)
40 | }
41 |
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/SupportedCalendarData.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import io.ktor.http.ContentType
17 | import org.xmlpull.v1.XmlPullParser
18 |
19 | data class SupportedCalendarData(
20 | val types: Set = emptySet()
21 | ): Property {
22 |
23 | companion object {
24 |
25 | const val CONTENT_TYPE = "content-type"
26 | const val VERSION = "version"
27 |
28 | }
29 |
30 | fun hasJCal() = types
31 | .map { ContentType.parse(it) }
32 | .any { ContentType.Application.contains(it) && "calendar+json".equals(it.contentSubtype, true) }
33 |
34 |
35 | object Factory: PropertyFactory {
36 |
37 | override fun getName() = CalDAV.SupportedCalendarData
38 |
39 | override fun create(parser: XmlPullParser): SupportedCalendarData {
40 | val supportedTypes = mutableSetOf()
41 |
42 | XmlReader(parser).readContentTypes(CalDAV.CalendarData, supportedTypes::add)
43 |
44 | return SupportedCalendarData(supportedTypes)
45 | }
46 |
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/SupportedReportSet.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class SupportedReportSet(
19 | val reports: Set = emptySet()
20 | ): Property {
21 |
22 | object Factory: PropertyFactory {
23 |
24 | override fun getName() = WebDAV.SupportedReportSet
25 |
26 | override fun create(parser: XmlPullParser): SupportedReportSet {
27 | /*
28 |
29 |
30 | */
31 |
32 | val reports = mutableSetOf()
33 |
34 | XmlReader(parser).processTag(WebDAV.SupportedReport) {
35 | processTag(WebDAV.Report) {
36 | parser.nextTag()
37 | if (parser.eventType == XmlPullParser.START_TAG)
38 | reports += Property.Name(parser.namespace, parser.name)
39 | }
40 | }
41 | return SupportedReportSet(reports)
42 | }
43 |
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/ktor/PropStatParser.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.XmlUtils.propertyName
15 | import at.bitfire.dav4jvm.property.webdav.WebDAV
16 | import io.ktor.http.HttpStatusCode
17 | import org.xmlpull.v1.XmlPullParser
18 | import java.util.LinkedList
19 |
20 | object PropStatParser {
21 |
22 | private val ASSUMING_OK = HttpStatusCode(200, "Assuming OK")
23 |
24 | fun parse(parser: XmlPullParser): PropStat {
25 | val depth = parser.depth
26 |
27 | var status: HttpStatusCode? = null
28 | val prop = LinkedList()
29 |
30 | var eventType = parser.eventType
31 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
32 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1)
33 | when (parser.propertyName()) {
34 | WebDAV.Prop ->
35 | prop.addAll(Property.parse(parser))
36 | WebDAV.Status ->
37 | status = KtorHttpUtils.parseStatusLine(parser.nextText())
38 | }
39 | eventType = parser.next()
40 | }
41 |
42 | return PropStat(prop, status ?: ASSUMING_OK)
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io
2 |
3 | ### Android ###
4 | # Built application files
5 | *.apk
6 | *.ap_
7 |
8 | # Files for the Dalvik VM
9 | *.dex
10 |
11 | # Java/Kotlin files
12 | *.class
13 | .kotlin/
14 |
15 | # Generated files
16 | bin/
17 | gen/
18 |
19 | # Gradle files
20 | .gradle/
21 | build/
22 |
23 | # Local configuration file (sdk path, etc)
24 | local.properties
25 |
26 | # Proguard folder generated by Eclipse
27 | proguard/
28 |
29 | # Log Files
30 | *.log
31 |
32 |
33 | ### Intellij ###
34 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
35 |
36 | *.iml
37 |
38 | ## Directory-based project format:
39 | .idea/
40 | # if you remove the above rule, at least ignore the following:
41 |
42 | # User-specific stuff:
43 | # .idea/workspace.xml
44 | # .idea/tasks.xml
45 | # .idea/dictionaries
46 |
47 | # Sensitive or high-churn files:
48 | # .idea/dataSources.ids
49 | # .idea/dataSources.xml
50 | # .idea/sqlDataSources.xml
51 | # .idea/dynamic.xml
52 | # .idea/uiDesigner.xml
53 |
54 | # Gradle:
55 | # .idea/gradle.xml
56 | # .idea/libraries
57 |
58 | # Mongo Explorer plugin:
59 | # .idea/mongoSettings.xml
60 |
61 | ## File-based project format:
62 | *.ipr
63 | *.iws
64 |
65 | ## Plugin-specific files:
66 |
67 | # IntelliJ
68 | out/
69 |
70 | # mpeltonen/sbt-idea plugin
71 | .idea_modules/
72 |
73 | # JIRA plugin
74 | atlassian-ide-plugin.xml
75 |
76 | # Crashlytics plugin (for Android Studio and IntelliJ)
77 | com_crashlytics_export_strings.xml
78 | crashlytics.properties
79 | crashlytics-build.properties
80 |
81 |
82 | ### Gradle ###
83 | .gradle
84 | build/
85 |
86 | # Ignore Gradle GUI config
87 | gradle-app.setting
88 |
89 |
90 | ### external libs ###
91 | .svn
92 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/common/HrefListProperty.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.common
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import at.bitfire.dav4jvm.property.webdav.WebDAV
17 | import org.xmlpull.v1.XmlPullParser
18 |
19 | /**
20 | * Represents a list of hrefs.
21 | *
22 | * Every [HrefListProperty] must be a data class.
23 | */
24 | abstract class HrefListProperty(
25 | open val hrefs: List
26 | ): Property {
27 |
28 | abstract class Factory : PropertyFactory {
29 |
30 | @Deprecated("hrefs is no longer mutable.", level = DeprecationLevel.ERROR)
31 | fun create(parser: XmlPullParser, list: HrefListProperty): HrefListProperty {
32 | val hrefs = list.hrefs.toMutableList()
33 | XmlReader(parser).readTextPropertyList(WebDAV.Href, hrefs)
34 | return list
35 | }
36 |
37 | fun create(
38 | parser: XmlPullParser,
39 | constructor: (hrefs: List
40 | ) -> PropertyType): PropertyType {
41 | val hrefs = mutableListOf()
42 | XmlReader(parser).readTextPropertyList(WebDAV.Href, hrefs)
43 | return constructor(hrefs)
44 | }
45 |
46 | }
47 |
48 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/ScheduleTag.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.QuotedStringUtils
16 | import at.bitfire.dav4jvm.XmlReader
17 | import io.ktor.client.statement.HttpResponse
18 | import io.ktor.http.HttpHeaders
19 | import okhttp3.Response
20 | import org.xmlpull.v1.XmlPullParser
21 |
22 | data class ScheduleTag(
23 | val rawScheduleTag: String?
24 | ): Property {
25 |
26 | companion object {
27 |
28 | fun fromHttpResponse(response: HttpResponse) =
29 | response.headers[HttpHeaders.ScheduleTag]?.let { ScheduleTag(it) }
30 |
31 | fun fromResponse(response: Response) =
32 | response.header(HttpHeaders.ScheduleTag)?.let { ScheduleTag(it) }
33 |
34 | }
35 |
36 | /* Value: opaque-tag
37 | opaque-tag = quoted-string
38 | */
39 | val scheduleTag: String? = rawScheduleTag?.let { QuotedStringUtils.decodeQuotedString(it) }
40 |
41 | override fun toString() = scheduleTag ?: "(null)"
42 |
43 |
44 | object Factory: PropertyFactory {
45 |
46 | override fun getName() = CalDAV.ScheduleTag
47 |
48 | override fun create(parser: XmlPullParser) = ScheduleTag(XmlReader(parser).readText())
49 |
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/ktor/ResponseParserTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor
12 |
13 | import io.ktor.http.Url
14 | import org.junit.Assert.assertEquals
15 | import org.junit.Test
16 |
17 | class ResponseParserTest {
18 |
19 | val baseUrl = Url("https://example.com/collection/")
20 | val parser = ResponseParser(baseUrl, callback = { _, _ ->
21 | // no-op
22 | })
23 |
24 | @Test
25 | fun `resolveHref with absolute URL`() {
26 | assertEquals(
27 | Url("https://example.com/collection/member"),
28 | parser.resolveHref("https://example.com/collection/member")
29 | )
30 | }
31 |
32 | @Test
33 | fun `resolveHref with absolute path`() {
34 | assertEquals(
35 | Url("https://example.com/collection/member"),
36 | parser.resolveHref("/collection/member")
37 | )
38 | }
39 |
40 | @Test
41 | fun `resolveHref with relative path`() {
42 | assertEquals(
43 | Url("https://example.com/collection/member"),
44 | parser.resolveHref("member")
45 | )
46 | }
47 |
48 | @Test
49 | fun `resolveHref with relative path with colon`() {
50 | assertEquals(
51 | Url("https://example.com/collection/mem:ber"),
52 | parser.resolveHref("mem:ber")
53 | )
54 | }
55 |
56 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/Error.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm
12 |
13 | import org.xmlpull.v1.XmlPullParser
14 | import java.io.Serializable
15 |
16 | /**
17 | * Represents an XML precondition/postcondition error. Every error has a name, which is the XML element
18 | * name. Subclassed errors may have more specific information available.
19 | *
20 | * At the moment, there is no logic for subclassing errors.
21 | *
22 | * @param name property name for the XML error element
23 | */
24 | data class Error(
25 | val name: Property.Name
26 | ): Serializable {
27 |
28 | companion object {
29 |
30 | fun parseError(parser: XmlPullParser): List {
31 | val names = mutableSetOf()
32 |
33 | val depth = parser.depth
34 | var eventType = parser.eventType
35 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
36 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1)
37 | names += Property.Name(parser.namespace, parser.name)
38 | eventType = parser.next()
39 | }
40 |
41 | return names.map { Error(it) }
42 | }
43 |
44 | }
45 |
46 | override fun equals(other: Any?) =
47 | (other is Error) && other.name == name
48 |
49 | override fun hashCode() = name.hashCode()
50 |
51 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPush.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlUtils.propertyName
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | /**
19 | * Represents a [NS_WEBDAV_PUSH]`:web-push` property.
20 | *
21 | * Experimental! See https://github.com/bitfireAT/webdav-push/
22 | */
23 | data class WebPush(
24 | val vapidPublicKey: VapidPublicKey? = null
25 | ) : PushTransport {
26 |
27 | object Factory : PropertyFactory {
28 |
29 | override fun getName(): Property.Name = WebDAVPush.WebPush
30 |
31 | override fun create(parser: XmlPullParser): WebPush {
32 | var vapidPublicKey: VapidPublicKey? = null
33 | val depth = parser.depth
34 | var eventType = parser.eventType
35 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
36 | if (eventType == XmlPullParser.START_TAG) {
37 | when (parser.propertyName()) {
38 | WebDAVPush.VapidPublicKey -> vapidPublicKey = VapidPublicKey.Factory.create(parser)
39 | }
40 | }
41 | eventType = parser.next()
42 | }
43 | return WebPush(vapidPublicKey)
44 | }
45 |
46 | }
47 |
48 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/okhttp/QuotedStringUtilsTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp
12 |
13 | import at.bitfire.dav4jvm.QuotedStringUtils
14 | import org.junit.Assert.assertEquals
15 | import org.junit.Test
16 |
17 | class QuotedStringUtilsTest {
18 |
19 | @Test
20 | fun testAsQuotedString() {
21 | assertEquals("\"\"", QuotedStringUtils.asQuotedString(""))
22 | assertEquals("\"\\\"\"", QuotedStringUtils.asQuotedString("\""))
23 | assertEquals("\"\\\\\"", QuotedStringUtils.asQuotedString("\\"))
24 | }
25 |
26 | @Test
27 | fun testDecodeQuotedString() {
28 | assertEquals("\"", QuotedStringUtils.decodeQuotedString("\""))
29 | assertEquals("\\", QuotedStringUtils.decodeQuotedString("\"\\\""))
30 | assertEquals("\"test", QuotedStringUtils.decodeQuotedString("\"test"))
31 | assertEquals("test", QuotedStringUtils.decodeQuotedString("test"))
32 | assertEquals("", QuotedStringUtils.decodeQuotedString("\"\""))
33 | assertEquals("test", QuotedStringUtils.decodeQuotedString("\"test\""))
34 | assertEquals("test\\", QuotedStringUtils.decodeQuotedString("\"test\\\""))
35 | assertEquals("test", QuotedStringUtils.decodeQuotedString("\"t\\e\\st\""))
36 | assertEquals("12\"34", QuotedStringUtils.decodeQuotedString("\"12\\\"34\""))
37 | assertEquals("1234\"", QuotedStringUtils.decodeQuotedString("\"1234\\\"\""))
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/okhttp/CallbackInterfaces.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp
12 |
13 | import okhttp3.Response
14 |
15 | /**
16 | * Callback for the OPTIONS request.
17 | */
18 | fun interface CapabilitiesCallback {
19 | fun onCapabilities(davCapabilities: Set, response: Response)
20 | }
21 |
22 | /**
23 | * Callback for 207 Multi-Status responses.
24 | */
25 | fun interface MultiResponseCallback {
26 | /**
27 | * Called for every `` element in the `` body. For instance,
28 | * in response to a `PROPFIND` request, this callback will be called once for every found
29 | * member resource.
30 | *
31 | * Known collections have [response] `href` with trailing slash, see [at.bitfire.dav4jvm.okhttp.Response.parse] for details.
32 | *
33 | * @param response the parsed response (including URL)
34 | * @param relation relation of the response to the called resource
35 | */
36 | fun onResponse(response: at.bitfire.dav4jvm.okhttp.Response, relation: at.bitfire.dav4jvm.okhttp.Response.HrefRelation)
37 | }
38 |
39 | /**
40 | * Callback for HTTP responses.
41 | */
42 | fun interface ResponseCallback {
43 | /**
44 | * Called for a HTTP response. Typically this is only called for successful/redirect
45 | * responses because HTTP errors throw an exception before this callback is called.
46 | */
47 | fun onResponse(response: Response)
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransports.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlUtils.propertyName
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | /**
19 | * Represents a [NS_WEBDAV_PUSH]`:push-transports` property.
20 | *
21 | * Experimental! See https://github.com/bitfireAT/webdav-push/
22 | */
23 | class PushTransports private constructor(
24 | val transports: Set
25 | ): Property {
26 |
27 | fun hasWebPush() = transports.any { it is WebPush }
28 |
29 |
30 | object Factory: PropertyFactory {
31 |
32 | override fun getName() = WebDAVPush.Transports
33 |
34 | override fun create(parser: XmlPullParser): PushTransports {
35 | val transports = mutableListOf()
36 | val depth = parser.depth
37 | var eventType = parser.eventType
38 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
39 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
40 | when (parser.propertyName()) {
41 | WebDAVPush.WebPush ->
42 | transports += WebPush.Factory.create(parser)
43 | }
44 | }
45 | eventType = parser.next()
46 | }
47 | return PushTransports(transports.toSet())
48 | }
49 |
50 | }
51 |
52 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/SupportedAddressData.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.carddav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import io.ktor.http.ContentType
17 | import org.xmlpull.v1.XmlPullParser
18 |
19 | class SupportedAddressData(
20 | val types: Set = emptySet()
21 | ): Property {
22 |
23 | companion object {
24 |
25 | const val CONTENT_TYPE = "content-type"
26 | const val VERSION = "version"
27 |
28 | }
29 |
30 | fun hasVCard4() = types
31 | .map { try { ContentType.parse(it) } catch (_: Exception) { ContentType.Any } }
32 | .any { "text/vcard; version=4.0".equals(it.toString(), true) }
33 | fun hasJCard() = types
34 | .map { try { ContentType.parse(it) } catch (_: Exception) { ContentType.Any } }
35 | .any { ContentType.Application.contains(it) && "vcard+json".equals(it.contentSubtype, true) }
36 |
37 | override fun toString() = "[${types.joinToString(", ")}]"
38 |
39 |
40 | object Factory: PropertyFactory {
41 |
42 | override fun getName() = CardDAV.SupportedAddressData
43 |
44 | override fun create(parser: XmlPullParser): SupportedAddressData {
45 | val supportedTypes = mutableSetOf()
46 |
47 | XmlReader(parser).readContentTypes(CardDAV.AddressDataType, supportedTypes::add)
48 |
49 | return SupportedAddressData(supportedTypes)
50 | }
51 |
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/Trigger.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlUtils.propertyName
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class Trigger(
19 | val contentUpdate: ContentUpdate? = null,
20 | val propertyUpdate: PropertyUpdate? = null
21 | ) : Property {
22 |
23 | object Factory : PropertyFactory {
24 |
25 | override fun getName() = WebDAVPush.Trigger
26 |
27 | override fun create(parser: XmlPullParser): Trigger {
28 | var trigger = Trigger()
29 |
30 | val depth = parser.depth
31 | var eventType = parser.eventType
32 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
33 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
34 | when (parser.propertyName()) {
35 | WebDAVPush.ContentUpdate -> trigger = trigger.copy(
36 | contentUpdate = ContentUpdate.Factory.create(parser)
37 | )
38 | WebDAVPush.PropertyUpdate -> trigger = trigger.copy(
39 | propertyUpdate = PropertyUpdate.Factory.create(parser)
40 | )
41 | }
42 | }
43 | eventType = parser.next()
44 | }
45 |
46 | return trigger
47 | }
48 |
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/WebDAVPush.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 |
15 | object WebDAVPush {
16 |
17 | /**
18 | * XML namespace of WebDAV-Push (draft), see:
19 | * https://github.com/bitfireAT/webdav-push/
20 | *
21 | * Experimental!
22 | */
23 | const val NS_WEBDAV_PUSH = "https://bitfire.at/webdav-push"
24 |
25 | val AuthSecret = Property.Name(NS_WEBDAV_PUSH, "auth-secret")
26 | val ContentUpdate = Property.Name(NS_WEBDAV_PUSH, "content-update")
27 | val Expires = Property.Name(NS_WEBDAV_PUSH, "expires")
28 | val PropertyUpdate = Property.Name(NS_WEBDAV_PUSH, "property-update")
29 | val PushMessage = Property.Name(NS_WEBDAV_PUSH, "push-message")
30 | val PushRegister = Property.Name(NS_WEBDAV_PUSH, "push-register")
31 | val PushResource = Property.Name(NS_WEBDAV_PUSH, "push-resource")
32 | val Subscription = Property.Name(NS_WEBDAV_PUSH, "subscription")
33 | val SubscriptionPublicKey = Property.Name(NS_WEBDAV_PUSH, "subscription-public-key")
34 | val SupportedTriggers = Property.Name(NS_WEBDAV_PUSH, "supported-triggers")
35 | val Topic = Property.Name(NS_WEBDAV_PUSH, "topic")
36 | val Transports = Property.Name(NS_WEBDAV_PUSH, "transports")
37 | val Trigger = Property.Name(NS_WEBDAV_PUSH, "trigger")
38 | val VapidPublicKey = Property.Name(NS_WEBDAV_PUSH, "vapid-public-key")
39 | val WebPush = Property.Name(NS_WEBDAV_PUSH, "web-push")
40 | val WebPushSubscription = Property.Name(NS_WEBDAV_PUSH, "web-push-subscription")
41 |
42 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/PropertyUpdate.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlUtils.propertyName
16 | import at.bitfire.dav4jvm.property.webdav.SyncLevel
17 | import at.bitfire.dav4jvm.property.webdav.WebDAV
18 | import org.xmlpull.v1.XmlPullParser
19 |
20 | /**
21 | * Represents a [NS_WEBDAV_PUSH]`:property-update` property.
22 | *
23 | * Experimental! See https://github.com/bitfireAT/webdav-push/
24 | */
25 | data class PropertyUpdate(
26 | val syncLevel: SyncLevel? = null,
27 | ): Property {
28 |
29 | object Factory: PropertyFactory {
30 |
31 | override fun getName() = WebDAVPush.PropertyUpdate
32 |
33 | override fun create(parser: XmlPullParser): PropertyUpdate {
34 | var propertyUpdate = PropertyUpdate()
35 |
36 | val depth = parser.depth
37 | var eventType = parser.eventType
38 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
39 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
40 | when (parser.propertyName()) {
41 | WebDAV.SyncLevel -> propertyUpdate = propertyUpdate.copy(
42 | syncLevel = SyncLevel.Factory.create(parser)
43 | )
44 | }
45 | }
46 | eventType = parser.next()
47 | }
48 | return propertyUpdate
49 | }
50 |
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/ktor/CallbackInterfaces.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor
12 |
13 | import io.ktor.client.statement.HttpResponse
14 |
15 | /**
16 | * Callback for the OPTIONS request.
17 | */
18 | fun interface CapabilitiesCallback {
19 | suspend fun onCapabilities(davCapabilities: Set, response: HttpResponse)
20 | }
21 |
22 | /**
23 | * Callback for 207 Multi-Status responses.
24 | */
25 | fun interface MultiResponseCallback {
26 | /**
27 | * Called for every `` element in the `` body. For instance,
28 | * in response to a `PROPFIND` request, this callback will be called once for every found
29 | * member resource.
30 | *
31 | * Known collections have [response] `href` with trailing slash, see [Response.parse] for details.
32 | *
33 | * @param response the parsed response (including URL)
34 | * @param relation relation of the response to the called resource
35 | */
36 | suspend fun onResponse(response: Response, relation: Response.HrefRelation)
37 | }
38 |
39 | /**
40 | * Callback for HTTP responses.
41 | */
42 | fun interface ResponseCallback {
43 | /**
44 | * Called for a HTTP response. Typically this is only called for successful/redirect
45 | * responses because HTTP errors throw an exception before this callback is called.
46 | *
47 | * @param response scoped response that can be used to access the body
48 | * in a streaming way (**body won't be accessible anymore when
49 | * callback returns!**)
50 | */
51 | suspend fun onResponse(response: HttpResponse)
52 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/okhttp/OkHttpUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp
12 |
13 | import okhttp3.HttpUrl
14 | import okhttp3.Response
15 |
16 | object OkHttpUtils {
17 |
18 | /**
19 | * Gets the resource name (the last segment of the path) from an URL.
20 | * Empty if the resource is the base directory.
21 | *
22 | * * `dir` for `https://example.com/dir/`
23 | * * `file` for `https://example.com/file`
24 | * * `` for `https://example.com` or `https://example.com/`
25 | *
26 | * @return resource name
27 | */
28 | fun fileName(url: HttpUrl): String {
29 | val pathSegments = url.pathSegments.dropLastWhile { it == "" }
30 | return pathSegments.lastOrNull() ?: ""
31 | }
32 |
33 | /**
34 | * Gets all values of a header that is defined as a list [RFC 9110 5.6.1],
35 | * regardless of they're sent as one line or as multiple lines.
36 | *
37 | * For instance, regardless of whether a server sends:
38 | *
39 | * ```
40 | * DAV: 1
41 | * DAV: 2
42 | * ```
43 | *
44 | * or
45 | *
46 | * ```
47 | * DAV: 1, 2
48 | * ```
49 | *
50 | * this method would return `arrayOf("1","2")` for the `DAV` header.
51 | *
52 | * @param response the HTTP response to evaluate
53 | * @param name header name (for instance: `DAV`)
54 | *
55 | * @return all values for the given header name
56 | */
57 | fun listHeader(response: Response, name: String): Array {
58 | val value = response.headers(name).joinToString(",")
59 | return value.split(',').filter { it.isNotEmpty() }.toTypedArray()
60 | }
61 |
62 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/ktor/MultiStatusParser.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.XmlReader
15 | import at.bitfire.dav4jvm.XmlUtils.propertyName
16 | import at.bitfire.dav4jvm.property.webdav.SyncToken
17 | import at.bitfire.dav4jvm.property.webdav.WebDAV
18 | import io.ktor.http.Url
19 | import org.xmlpull.v1.XmlPullParser
20 |
21 | /**
22 | * Parses a WebDAV `` XML response.
23 | *
24 | * @param location location of the request (used to resolve possible relative `` in responses)
25 | */
26 | class MultiStatusParser(
27 | private val location: Url,
28 | private val callback: MultiResponseCallback
29 | ) {
30 |
31 | suspend fun parseResponse(parser: XmlPullParser): List {
32 | val responseProperties = mutableListOf()
33 | val responseParser = ResponseParser(location, callback)
34 |
35 | //
37 | val depth = parser.depth
38 | var eventType = parser.eventType
39 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
40 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1)
41 | when (parser.propertyName()) {
42 | WebDAV.Response ->
43 | responseParser.parseResponse(parser)
44 | WebDAV.SyncToken ->
45 | XmlReader(parser).readText()?.let {
46 | responseProperties += SyncToken(it)
47 | }
48 | }
49 | eventType = parser.next()
50 | }
51 |
52 | return responseProperties
53 | }
54 |
55 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/SupportedTriggers.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlUtils.propertyName
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | /**
19 | * Represents a [NS_WEBDAV_PUSH]`:content-update` property.
20 | *
21 | * Experimental! See https://github.com/bitfireAT/webdav-push/
22 | */
23 | data class SupportedTriggers(
24 | val contentUpdate: ContentUpdate? = null,
25 | val propertyUpdate: PropertyUpdate? = null
26 | ): Property {
27 |
28 | object Factory: PropertyFactory {
29 |
30 | override fun getName() = WebDAVPush.SupportedTriggers
31 |
32 | override fun create(parser: XmlPullParser): SupportedTriggers {
33 | var supportedTriggers = SupportedTriggers()
34 |
35 | val depth = parser.depth
36 | var eventType = parser.eventType
37 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
38 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
39 | when (parser.propertyName()) {
40 | WebDAVPush.ContentUpdate -> supportedTriggers = supportedTriggers.copy(
41 | contentUpdate = ContentUpdate.Factory.create(parser)
42 | )
43 | WebDAVPush.PropertyUpdate -> supportedTriggers = supportedTriggers.copy(
44 | propertyUpdate = PropertyUpdate.Factory.create(parser)
45 | )
46 | }
47 | }
48 | eventType = parser.next()
49 | }
50 |
51 | return supportedTriggers
52 | }
53 |
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/ContentUpdate.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlUtils.propertyName
16 | import at.bitfire.dav4jvm.property.webdav.Depth
17 | import at.bitfire.dav4jvm.property.webdav.SyncToken
18 | import at.bitfire.dav4jvm.property.webdav.WebDAV
19 | import org.xmlpull.v1.XmlPullParser
20 |
21 | /**
22 | * Represents a [NS_WEBDAV_PUSH]`:content-update` property.
23 | *
24 | * Experimental! See https://github.com/bitfireAT/webdav-push/
25 | */
26 | data class ContentUpdate(
27 | val depth: Depth? = null,
28 | val syncToken: SyncToken? = null
29 | ): Property {
30 |
31 | object Factory: PropertyFactory {
32 |
33 | override fun getName() = WebDAVPush.ContentUpdate
34 |
35 | override fun create(parser: XmlPullParser): ContentUpdate {
36 | var contentUpdate = ContentUpdate()
37 |
38 | val depth = parser.depth
39 | var eventType = parser.eventType
40 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
41 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
42 | when (parser.propertyName()) {
43 | WebDAV.SyncLevel -> contentUpdate = contentUpdate.copy(
44 | depth = Depth.Factory.create(parser)
45 | )
46 | WebDAV.SyncToken -> contentUpdate = contentUpdate.copy(
47 | syncToken = SyncToken.Factory.create(parser)
48 | )
49 | }
50 | }
51 | eventType = parser.next()
52 | }
53 |
54 | return contentUpdate
55 | }
56 |
57 | }
58 |
59 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/property/OwnerTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property
12 |
13 | import at.bitfire.dav4jvm.property.webdav.Owner
14 | import org.junit.Assert.assertEquals
15 | import org.junit.Assert.assertNull
16 | import org.junit.Assert.assertTrue
17 | import org.junit.Test
18 |
19 | class OwnerTest: PropertyTest() {
20 |
21 | @Test
22 | fun testOwner_Empty() {
23 | val results = parseProperty("")
24 | assertTrue(results.isEmpty())
25 | }
26 |
27 | @Test
28 | fun testOwner_PlainText() {
29 | val results = parseProperty("https://example.com")
30 | val owner = results.first() as Owner
31 | assertNull(owner.href)
32 | }
33 |
34 | @Test
35 | fun testOwner_PlainTextAndHref() {
36 | val results = parseProperty("Principal Name. mailto:owner@example.com (test)")
37 | val owner = results.first() as Owner
38 | assertEquals("mailto:owner@example.com", owner.href)
39 | }
40 |
41 | @Test
42 | fun testOwner_Href() {
43 | val results = parseProperty("https://example.com")
44 | val owner = results.first() as Owner
45 | assertEquals("https://example.com", owner.href)
46 | }
47 |
48 | @Test
49 | fun testOwner_TwoHrefs() {
50 | val results = parseProperty("" +
51 | "https://example.com/owner1" +
52 | "https://example.com/owner2" +
53 | "")
54 | val owner = results.first() as Owner
55 | assertEquals("https://example.com/owner1", owner.href)
56 | }
57 |
58 | @Test
59 | fun testOwner_WithoutHref() {
60 | val results = parseProperty("invalid")
61 | assertTrue(results.isEmpty())
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushMessage.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlUtils.propertyName
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | /**
19 | * Represents a [NS_WEBDAV_PUSH]`:push-message` property.
20 | *
21 | * Experimental! See https://github.com/bitfireAT/webdav-push/
22 | */
23 | data class PushMessage(
24 | val topic: Topic? = null,
25 | val contentUpdate: ContentUpdate? = null,
26 | val propertyUpdate: PropertyUpdate? = null
27 | ): Property {
28 |
29 | object Factory: PropertyFactory {
30 |
31 | override fun getName() = WebDAVPush.PushMessage
32 |
33 | override fun create(parser: XmlPullParser): PushMessage {
34 | var message = PushMessage()
35 |
36 | val depth = parser.depth
37 | var eventType = parser.eventType
38 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
39 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
40 | when (parser.propertyName()) {
41 | WebDAVPush.Topic -> message = message.copy(
42 | topic = Topic.Factory.create(parser)
43 | )
44 | WebDAVPush.ContentUpdate -> message = message.copy(
45 | contentUpdate = ContentUpdate.Factory.create(parser)
46 | )
47 | WebDAVPush.PropertyUpdate -> message = message.copy(
48 | propertyUpdate = PropertyUpdate.Factory.create(parser)
49 | )
50 | }
51 | }
52 | eventType = parser.next()
53 | }
54 |
55 | return message
56 | }
57 |
58 | }
59 |
60 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/property/GetETagTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property
12 |
13 | import at.bitfire.dav4jvm.property.webdav.GetETag
14 | import org.junit.Assert.assertEquals
15 | import org.junit.Assert.assertFalse
16 | import org.junit.Assert.assertTrue
17 | import org.junit.Test
18 |
19 | class GetETagTest: PropertyTest() {
20 |
21 | @Test
22 | fun testGetETag_Strong() {
23 | val results = parseProperty("\"Correct strong ETag\"")
24 | val getETag = results.first() as GetETag
25 | assertEquals("Correct strong ETag", getETag.eTag)
26 | assertFalse(getETag.weak)
27 | }
28 |
29 | @Test
30 | fun testGetETag_Strong_NoQuotes() {
31 | val results = parseProperty("Strong ETag without quotes")
32 | val getETag = results.first() as GetETag
33 | assertEquals("Strong ETag without quotes", getETag.eTag)
34 | assertFalse(getETag.weak)
35 | }
36 |
37 | @Test
38 | fun testGetETag_Weak() {
39 | val results = parseProperty("W/\"Correct weak ETag\"")
40 | val getETag = results.first() as GetETag
41 | assertEquals("Correct weak ETag", getETag.eTag)
42 | assertTrue(getETag.weak)
43 | }
44 |
45 | @Test
46 | fun testGetETag_Weak_Empty() {
47 | val results = parseProperty("W/")
48 | val getETag = results.first() as GetETag
49 | assertEquals("", getETag.eTag)
50 | assertTrue(getETag.weak)
51 | }
52 |
53 | @Test
54 | fun testGetETag_Weak_NoQuotes() {
55 | val results = parseProperty("W/Weak ETag without quotes")
56 | val getETag = results.first() as GetETag
57 | assertEquals("Weak ETag without quotes", getETag.eTag)
58 | assertTrue(getETag.weak)
59 | }
60 |
61 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/OwnerTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp.property
12 |
13 | import at.bitfire.dav4jvm.property.webdav.Owner
14 | import org.junit.Assert.assertEquals
15 | import org.junit.Assert.assertNull
16 | import org.junit.Assert.assertTrue
17 | import org.junit.Test
18 |
19 | class OwnerTest: PropertyTest() {
20 |
21 | @Test
22 | fun testOwner_Empty() {
23 | val results = parseProperty("")
24 | assertTrue(results.isEmpty())
25 | }
26 |
27 | @Test
28 | fun testOwner_PlainText() {
29 | val results = parseProperty("https://example.com")
30 | val owner = results.first() as Owner
31 | assertNull(owner.href)
32 | }
33 |
34 | @Test
35 | fun testOwner_PlainTextAndHref() {
36 | val results = parseProperty("Principal Name. mailto:owner@example.com (test)")
37 | val owner = results.first() as Owner
38 | assertEquals("mailto:owner@example.com", owner.href)
39 | }
40 |
41 | @Test
42 | fun testOwner_Href() {
43 | val results = parseProperty("https://example.com")
44 | val owner = results.first() as Owner
45 | assertEquals("https://example.com", owner.href)
46 | }
47 |
48 | @Test
49 | fun testOwner_TwoHrefs() {
50 | val results = parseProperty("" +
51 | "https://example.com/owner1" +
52 | "https://example.com/owner2" +
53 | "")
54 | val owner = results.first() as Owner
55 | assertEquals("https://example.com/owner1", owner.href)
56 | }
57 |
58 | @Test
59 | fun testOwner_WithoutHref() {
60 | val results = parseProperty("invalid")
61 | assertTrue(results.isEmpty())
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/okhttp/property/GetETagTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp.property
12 |
13 | import at.bitfire.dav4jvm.property.webdav.GetETag
14 | import org.junit.Assert.assertEquals
15 | import org.junit.Assert.assertFalse
16 | import org.junit.Assert.assertTrue
17 | import org.junit.Test
18 |
19 | class GetETagTest: PropertyTest() {
20 |
21 | @Test
22 | fun testGetETag_Strong() {
23 | val results = parseProperty("\"Correct strong ETag\"")
24 | val getETag = results.first() as GetETag
25 | assertEquals("Correct strong ETag", getETag.eTag)
26 | assertFalse(getETag.weak)
27 | }
28 |
29 | @Test
30 | fun testGetETag_Strong_NoQuotes() {
31 | val results = parseProperty("Strong ETag without quotes")
32 | val getETag = results.first() as GetETag
33 | assertEquals("Strong ETag without quotes", getETag.eTag)
34 | assertFalse(getETag.weak)
35 | }
36 |
37 | @Test
38 | fun testGetETag_Weak() {
39 | val results = parseProperty("W/\"Correct weak ETag\"")
40 | val getETag = results.first() as GetETag
41 | assertEquals("Correct weak ETag", getETag.eTag)
42 | assertTrue(getETag.weak)
43 | }
44 |
45 | @Test
46 | fun testGetETag_Weak_Empty() {
47 | val results = parseProperty("W/")
48 | val getETag = results.first() as GetETag
49 | assertEquals("", getETag.eTag)
50 | assertTrue(getETag.weak)
51 | }
52 |
53 | @Test
54 | fun testGetETag_Weak_NoQuotes() {
55 | val results = parseProperty("W/Weak ETag without quotes")
56 | val getETag = results.first() as GetETag
57 | assertEquals("Weak ETag without quotes", getETag.eTag)
58 | assertTrue(getETag.weak)
59 | }
60 |
61 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPushSubscription.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlUtils.propertyName
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | /**
19 | * Represents a [NS_WEBDAV_PUSH]`:web-push-subscription` property.
20 | *
21 | * Experimental! See https://github.com/bitfireAT/webdav-push/
22 | */
23 | data class WebPushSubscription(
24 | val pushResource: PushResource? = null,
25 | val subscriptionPublicKey: SubscriptionPublicKey? = null,
26 | val authSecret: AuthSecret? = null
27 | ): Property {
28 |
29 | object Factory: PropertyFactory {
30 |
31 | override fun getName() = WebDAVPush.WebPushSubscription
32 |
33 | override fun create(parser: XmlPullParser): WebPushSubscription {
34 | var subscription = WebPushSubscription()
35 |
36 | val depth = parser.depth
37 | var eventType = parser.eventType
38 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
39 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
40 | when (parser.propertyName()) {
41 | WebDAVPush.PushResource ->
42 | subscription = subscription.copy(pushResource = PushResource.Factory.create(parser))
43 | WebDAVPush.SubscriptionPublicKey ->
44 | subscription = subscription.copy(subscriptionPublicKey = SubscriptionPublicKey.Factory.create(parser))
45 | WebDAVPush.AuthSecret ->
46 | subscription = subscription.copy(authSecret = AuthSecret.Factory.create(parser))
47 | }
48 | }
49 | eventType = parser.next()
50 | }
51 |
52 | return subscription
53 | }
54 |
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/okhttp/DavCalendarTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp
12 |
13 | import mockwebserver3.MockResponse
14 | import mockwebserver3.MockWebServer
15 | import okhttp3.OkHttpClient
16 | import org.junit.After
17 | import org.junit.Assert.assertEquals
18 | import org.junit.Before
19 | import org.junit.Test
20 | import java.time.Instant
21 |
22 | class DavCalendarTest {
23 |
24 | private val httpClient = OkHttpClient.Builder()
25 | .followRedirects(false)
26 | .build()
27 | private val mockServer = MockWebServer()
28 |
29 | @Before
30 | fun startServer() {
31 | mockServer.start()
32 | }
33 |
34 | @After
35 | fun stopServer() {
36 | mockServer.close()
37 | }
38 |
39 |
40 | @Test
41 | fun calendarQuery_formatStartEnd() {
42 | val cal = DavCalendar(httpClient, mockServer.url("/"))
43 | mockServer.enqueue(MockResponse.Builder().code(207).body("").build())
44 | cal.calendarQuery("VEVENT",
45 | start = Instant.ofEpochSecond(784111777),
46 | end = Instant.ofEpochSecond(1689324577)) { _, _ -> }
47 | val rq = mockServer.takeRequest()
48 | assertEquals("" +
49 | "" +
50 | "" +
51 | "" +
52 | "" +
53 | "" +
54 | "" +
55 | "" +
56 | "" +
57 | "" +
58 | "" +
59 | "" +
60 | "", rq.body?.utf8())
61 | }
62 |
63 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/ServiceUnavailableExceptionTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp.exception
12 |
13 | import at.bitfire.dav4jvm.HttpUtils
14 | import okhttp3.Protocol
15 | import okhttp3.Request
16 | import okhttp3.Response
17 | import org.junit.Assert.assertNotNull
18 | import org.junit.Assert.assertNull
19 | import org.junit.Assert.assertTrue
20 | import org.junit.Test
21 | import java.time.Instant
22 |
23 | class ServiceUnavailableExceptionTest {
24 |
25 | val response503 = Response.Builder()
26 | .request(
27 | Request.Builder()
28 | .url("http://www.example.com")
29 | .get()
30 | .build()
31 | )
32 | .protocol(Protocol.HTTP_1_1)
33 | .code(503).message("Try later")
34 | .build()
35 |
36 | @Test
37 | fun testRetryAfter_NoTime() {
38 | val e = ServiceUnavailableException(response503)
39 | assertNull(e.retryAfter)
40 | }
41 |
42 | @Test
43 | fun testRetryAfter_Seconds() {
44 | val response = response503.newBuilder()
45 | .header("Retry-After", "120")
46 | .build()
47 | val e = ServiceUnavailableException(response)
48 | assertNotNull(e.retryAfter)
49 | assertTrue(withinTimeRange(e.retryAfter!!, 120))
50 | }
51 |
52 | @Test
53 | fun testRetryAfter_Date() {
54 | val after30min = Instant.now().plusSeconds(30 * 60)
55 | val response = response503.newBuilder()
56 | .header("Retry-After", HttpUtils.formatDate(after30min))
57 | .build()
58 | val e = ServiceUnavailableException(response)
59 | assertNotNull(e.retryAfter)
60 | assertTrue(withinTimeRange(e.retryAfter!!, 30 * 60))
61 | }
62 |
63 |
64 | private fun withinTimeRange(d: Instant, seconds: Long) =
65 | d.isBefore(
66 | Instant.now()
67 | .plusSeconds(seconds)
68 | .plusSeconds(5) // tolerance for test running
69 | )
70 |
71 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalendarColor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import org.xmlpull.v1.XmlPullParser
17 | import java.util.logging.Level
18 | import java.util.logging.Logger
19 | import java.util.regex.Pattern
20 |
21 | data class CalendarColor(
22 | val color: Int?
23 | ): Property {
24 |
25 | companion object {
26 |
27 | private val PATTERN = Pattern.compile("#?(\\p{XDigit}{6})(\\p{XDigit}{2})?")!!
28 |
29 | /**
30 | * Converts a WebDAV color from one of these formats:
31 | * #RRGGBB (alpha = 0xFF)
32 | * RRGGBB (alpha = 0xFF)
33 | * #RRGGBBAA
34 | * RRGGBBAA
35 | * to an [Int] with alpha.
36 | */
37 | @Throws(IllegalArgumentException::class)
38 | fun parseARGBColor(davColor: String): Int {
39 | val m = PATTERN.matcher(davColor)
40 | if (m.find()) {
41 | val color_rgb = Integer.parseInt(m.group(1), 16)
42 | val color_alpha = m.group(2)?.let { Integer.parseInt(m.group(2), 16) and 0xFF } ?: 0xFF
43 | return (color_alpha shl 24) or color_rgb
44 | } else
45 | throw IllegalArgumentException("Couldn't parse color value: $davColor")
46 | }
47 | }
48 |
49 |
50 | object Factory: PropertyFactory {
51 |
52 | override fun getName() = CalDAV.CalendarColor
53 |
54 | override fun create(parser: XmlPullParser): CalendarColor {
55 | XmlReader(parser).readText()?.let {
56 | try {
57 | return CalendarColor(parseARGBColor(it))
58 | } catch (e: IllegalArgumentException) {
59 | val logger = Logger.getLogger(javaClass.name)
60 | logger.log(Level.WARNING, "Couldn't parse color, ignoring", e)
61 | }
62 | }
63 | return CalendarColor(null)
64 | }
65 |
66 | }
67 |
68 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/ResourceType.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.property.caldav.CalDAV
16 | import at.bitfire.dav4jvm.property.carddav.CardDAV
17 | import org.xmlpull.v1.XmlPullParser
18 |
19 | class ResourceType(
20 | val types: Set = emptySet()
21 | ): Property {
22 |
23 | companion object {
24 |
25 |
26 | }
27 |
28 |
29 | object Factory: PropertyFactory {
30 |
31 | override fun getName() = WebDAV.ResourceType
32 |
33 | override fun create(parser: XmlPullParser): ResourceType {
34 | val types = mutableSetOf()
35 |
36 | val depth = parser.depth
37 | var eventType = parser.eventType
38 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
39 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
40 | // use static objects to allow types.contains()
41 | var typeName = Property.Name(parser.namespace, parser.name)
42 | when (typeName) { // if equals(), replace by our instance
43 | WebDAV.Collection -> typeName = WebDAV.Collection
44 | WebDAV.Principal -> typeName = WebDAV.Principal
45 | CardDAV.Addressbook -> typeName = CardDAV.Addressbook
46 | CalDAV.Calendar -> typeName = CalDAV.Calendar
47 | CalDAV.CalendarProxyRead -> typeName = CalDAV.CalendarProxyRead
48 | CalDAV.CalendarProxyWrite -> typeName = CalDAV.CalendarProxyWrite
49 | CalDAV.Subscribed -> typeName = CalDAV.Subscribed
50 | }
51 | types.add(typeName)
52 | }
53 | eventType = parser.next()
54 | }
55 | assert(parser.depth == depth)
56 |
57 | return ResourceType(types)
58 | }
59 |
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/okhttp/PropStat.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.XmlUtils.propertyName
15 | import at.bitfire.dav4jvm.property.webdav.WebDAV
16 | import okhttp3.Protocol
17 | import okhttp3.internal.http.StatusLine
18 | import org.xmlpull.v1.XmlPullParser
19 | import java.net.ProtocolException
20 | import java.util.LinkedList
21 |
22 | /**
23 | * Represents a WebDAV propstat XML element.
24 | *
25 | *
26 | */
27 | data class PropStat(
28 | val properties: List,
29 | val status: StatusLine,
30 | val error: List? = null
31 | ) {
32 |
33 | companion object {
34 |
35 | private val ASSUMING_OK = StatusLine(Protocol.HTTP_1_1, 200, "Assuming OK")
36 | private val INVALID_STATUS = StatusLine(Protocol.HTTP_1_1, 500, "Invalid status line")
37 |
38 | fun parse(parser: XmlPullParser): PropStat {
39 | val depth = parser.depth
40 |
41 | var status: StatusLine? = null
42 | val prop = LinkedList()
43 |
44 | var eventType = parser.eventType
45 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
46 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1)
47 | when (parser.propertyName()) {
48 | WebDAV.Prop ->
49 | prop.addAll(Property.parse(parser))
50 | WebDAV.Status ->
51 | status = try {
52 | StatusLine.parse(parser.nextText())
53 | } catch (e: ProtocolException) {
54 | // invalid status line, treat as 500 Internal Server Error
55 | INVALID_STATUS
56 | }
57 | }
58 | eventType = parser.next()
59 | }
60 |
61 | return PropStat(prop, status ?: ASSUMING_OK)
62 | }
63 |
64 | }
65 |
66 |
67 | fun isSuccess() = status.code/100 == 2
68 |
69 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushRegister.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.push
12 |
13 | import at.bitfire.dav4jvm.HttpUtils
14 | import at.bitfire.dav4jvm.Property
15 | import at.bitfire.dav4jvm.PropertyFactory
16 | import at.bitfire.dav4jvm.XmlReader
17 | import at.bitfire.dav4jvm.XmlUtils.propertyName
18 | import org.xmlpull.v1.XmlPullParser
19 | import java.time.Instant
20 |
21 | /**
22 | * Represents a [NS_WEBDAV_PUSH]`:push-register` property.
23 | *
24 | * Experimental! See https://github.com/bitfireAT/webdav-push/
25 | */
26 | data class PushRegister(
27 | val expires: Instant? = null,
28 | val subscription: Subscription? = null,
29 | val trigger: Trigger? = null
30 | ): Property {
31 |
32 | object Factory: PropertyFactory {
33 |
34 | override fun getName() = WebDAVPush.PushRegister
35 |
36 | override fun create(parser: XmlPullParser): PushRegister {
37 | var register = PushRegister()
38 |
39 | val depth = parser.depth
40 | var eventType = parser.eventType
41 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
42 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1)
43 | when (parser.propertyName()) {
44 | WebDAVPush.Expires ->
45 | register = register.copy(
46 | expires = XmlReader(parser).readText()?.let {
47 | HttpUtils.parseDate(it)
48 | }
49 | )
50 | WebDAVPush.Subscription ->
51 | register = register.copy(
52 | subscription = Subscription.Factory.create(parser)
53 | )
54 | WebDAVPush.Trigger ->
55 | register = register.copy(
56 | trigger = Trigger.Factory.create(parser)
57 | )
58 | }
59 | eventType = parser.next()
60 | }
61 |
62 | return register
63 | }
64 |
65 | }
66 |
67 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/HttpException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp.exception
12 |
13 | import at.bitfire.dav4jvm.Error
14 | import okhttp3.Response
15 | import javax.annotation.WillNotClose
16 |
17 | /**
18 | * Signals that a HTTP error was sent by the server in the context of a WebDAV operation.
19 | */
20 | open class HttpException(
21 | message: String? = null,
22 | cause: Throwable? = null,
23 | override val statusCode: Int,
24 | requestExcerpt: String?,
25 | responseExcerpt: String?,
26 | errors: List = emptyList()
27 | ): DavException(message, cause, statusCode, requestExcerpt, responseExcerpt, errors) {
28 |
29 | // constructor from Response
30 |
31 | /**
32 | * Takes the request, response and errors from a given HTTP response.
33 | *
34 | * @param response response to extract status code and request/response excerpt from (if possible)
35 | * @param message optional exception message
36 | * @param cause optional exception cause
37 | */
38 | constructor(
39 | @WillNotClose response: Response,
40 | message: String = "HTTP ${response.code} ${response.message}",
41 | cause: Throwable? = null
42 | ) : this(HttpResponseInfo.fromResponse(response), message, cause)
43 |
44 | private constructor(
45 | httpResponseInfo: HttpResponseInfo,
46 | message: String?,
47 | cause: Throwable? = null
48 | ): this(
49 | message = message,
50 | cause = cause,
51 | statusCode = httpResponseInfo.statusCode,
52 | requestExcerpt = httpResponseInfo.requestExcerpt,
53 | responseExcerpt = httpResponseInfo.responseExcerpt,
54 | errors = httpResponseInfo.errors
55 | )
56 |
57 |
58 | // status code classes
59 |
60 | /** Whether the [statusCode] is 3xx and thus indicates a redirection. */
61 | val isRedirect
62 | get() = statusCode / 100 == 3
63 |
64 | /** Whether the [statusCode] is 4xx and thus indicates a client error. */
65 | val isClientError
66 | get() = statusCode / 100 == 4
67 |
68 | /** Whether the [statusCode] is 5xx and thus indicates a server error. */
69 | val isServerError
70 | get() = statusCode / 100 == 5
71 |
72 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/PropertyTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm
12 |
13 | import at.bitfire.dav4jvm.property.webdav.GetETag
14 | import org.junit.Assert.assertEquals
15 | import org.junit.Test
16 | import org.xmlpull.v1.XmlPullParser
17 | import org.xmlpull.v1.XmlPullParserFactory
18 | import java.io.StringReader
19 |
20 | class PropertyTest {
21 |
22 | @Test
23 | fun testParse_InvalidProperty() {
24 | val parser = XmlPullParserFactory.newInstance().apply {
25 | isNamespaceAware = true
26 | }.newPullParser()
27 | parser.setInput(StringReader(""))
28 | do {
29 | parser.next()
30 | } while (parser.eventType != XmlPullParser.START_TAG && parser.name != "multistatus")
31 |
32 | // we're now at the start of
33 | assertEquals(XmlPullParser.START_TAG, parser.eventType)
34 | assertEquals("multistatus", parser.name)
35 |
36 | // parse invalid DAV:getetag
37 | Property.Companion.parse(parser).let {
38 | assertEquals(1, it.size)
39 | assertEquals(GetETag(null), it[0])
40 | }
41 |
42 | // we're now at the end of
43 | assertEquals(XmlPullParser.END_TAG, parser.eventType)
44 | assertEquals("multistatus", parser.name)
45 | }
46 |
47 | @Test
48 | fun testParse_ValidProperty() {
49 | val parser = XmlPullParserFactory.newInstance().apply {
50 | isNamespaceAware = true
51 | }.newPullParser()
52 | parser.setInput(StringReader("12345"))
53 | do {
54 | parser.next()
55 | } while (parser.eventType != XmlPullParser.START_TAG && parser.name != "multistatus")
56 |
57 | // we're now at the start of
58 | assertEquals(XmlPullParser.START_TAG, parser.eventType)
59 | assertEquals("multistatus", parser.name)
60 |
61 | val etag = Property.Companion.parse(parser).first()
62 | assertEquals(GetETag("12345"), etag)
63 |
64 | // we're now at the end of
65 | assertEquals(XmlPullParser.END_TAG, parser.eventType)
66 | assertEquals("multistatus", parser.name)
67 | }
68 |
69 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/ktor/DavCalendarTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor
12 |
13 | import io.ktor.client.HttpClient
14 | import io.ktor.client.engine.mock.MockEngine
15 | import io.ktor.client.engine.mock.respond
16 | import io.ktor.client.engine.mock.toByteArray
17 | import io.ktor.http.ContentType
18 | import io.ktor.http.HttpHeaders
19 | import io.ktor.http.HttpStatusCode
20 | import io.ktor.http.Url
21 | import io.ktor.http.headersOf
22 | import io.ktor.http.withCharset
23 | import kotlinx.coroutines.test.runTest
24 | import org.junit.Assert.assertEquals
25 | import org.junit.Test
26 | import java.time.Instant
27 |
28 | class DavCalendarTest {
29 |
30 | @Test
31 | fun calendarQuery_formatStartEnd() = runTest {
32 | val mockEngine = MockEngine { request ->
33 | respond(
34 | content = "",
35 | status = HttpStatusCode.MultiStatus, // 207
36 | headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Xml.withCharset(Charsets.UTF_8).toString())
37 | )
38 | }
39 | val httpClient = HttpClient(mockEngine) { followRedirects = false }
40 | val cal = DavCalendar(httpClient, Url("/"))
41 |
42 | cal.calendarQuery(
43 | "VEVENT",
44 | start = Instant.ofEpochSecond(784111777),
45 | end = Instant.ofEpochSecond(1689324577)
46 | ) { _, _ -> }
47 |
48 | assertEquals(
49 | "" +
50 | "" +
51 | "" +
52 | "" +
53 | "" +
54 | "" +
55 | "" +
56 | "" +
57 | "" +
58 | "" +
59 | "" +
60 | "" +
61 | "",
62 | mockEngine.requestHistory.last().body.toByteArray().toString(Charsets.UTF_8)
63 | )
64 | }
65 |
66 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/ktor/PropertyTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.property.webdav.GetETag
15 | import org.junit.Assert.assertEquals
16 | import org.junit.Test
17 | import org.xmlpull.v1.XmlPullParser
18 | import org.xmlpull.v1.XmlPullParserFactory
19 | import java.io.StringReader
20 |
21 | class PropertyTest {
22 |
23 | @Test
24 | fun testParse_InvalidProperty() {
25 | val parser = XmlPullParserFactory.newInstance().apply {
26 | isNamespaceAware = true
27 | }.newPullParser()
28 | parser.setInput(StringReader(""))
29 | do {
30 | parser.next()
31 | } while (parser.eventType != XmlPullParser.START_TAG && parser.name != "multistatus")
32 |
33 | // we're now at the start of
34 | assertEquals(XmlPullParser.START_TAG, parser.eventType)
35 | assertEquals("multistatus", parser.name)
36 |
37 | // parse invalid DAV:getetag
38 | Property.parse(parser).let {
39 | assertEquals(1, it.size)
40 | assertEquals(GetETag(null), it[0])
41 | }
42 |
43 | // we're now at the end of
44 | assertEquals(XmlPullParser.END_TAG, parser.eventType)
45 | assertEquals("multistatus", parser.name)
46 | }
47 |
48 | @Test
49 | fun testParse_ValidProperty() {
50 | val parser = XmlPullParserFactory.newInstance().apply {
51 | isNamespaceAware = true
52 | }.newPullParser()
53 | parser.setInput(StringReader("12345"))
54 | do {
55 | parser.next()
56 | } while (parser.eventType != XmlPullParser.START_TAG && parser.name != "multistatus")
57 |
58 | // we're now at the start of
59 | assertEquals(XmlPullParser.START_TAG, parser.eventType)
60 | assertEquals("multistatus", parser.name)
61 |
62 | val etag = Property.parse(parser).first()
63 | assertEquals(GetETag("12345"), etag)
64 |
65 | // we're now at the end of
66 | assertEquals(XmlPullParser.END_TAG, parser.eventType)
67 | assertEquals("multistatus", parser.name)
68 | }
69 |
70 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/Property.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm
12 |
13 | import org.xmlpull.v1.XmlPullParser
14 | import java.io.Serializable
15 | import java.util.LinkedList
16 | import java.util.logging.Level
17 | import java.util.logging.Logger
18 |
19 | /**
20 | * Represents a WebDAV property.
21 | *
22 | * Every [Property] must define a static field (use `@JvmStatic`) called `NAME` of type [Property.Name],
23 | * which will be accessed by reflection.
24 | *
25 | * Every [Property] should be a data class in order to be able to compare it against others, and convert to a useful
26 | * string for debugging.
27 | */
28 | interface Property {
29 |
30 | data class Name(
31 | val namespace: String,
32 | val name: String
33 | ): Serializable {
34 |
35 | override fun toString() = "$namespace:$name"
36 |
37 | }
38 |
39 | companion object {
40 |
41 | fun parse(parser: XmlPullParser): List {
42 | val logger = Logger.getLogger(Property::javaClass.name)
43 |
44 | //
45 | val depth = parser.depth
46 | val properties = LinkedList()
47 |
48 | var eventType = parser.eventType
49 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
50 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
51 | val depthBeforeParsing = parser.depth
52 | val name = Name(parser.namespace, parser.name)
53 |
54 | try {
55 | val property = PropertyRegistry.create(name, parser)
56 | assert(parser.depth == depthBeforeParsing)
57 |
58 | if (property != null) {
59 | properties.add(property)
60 | } else
61 | logger.fine("Ignoring unknown property $name")
62 | } catch (e: Exception) { // catching here generic exception in order to avoid dependency on specific okhttp or ktor Exception
63 | logger.log(Level.WARNING, "Ignoring invalid property", e)
64 | }
65 | }
66 |
67 | eventType = parser.next()
68 | }
69 |
70 | return properties
71 | }
72 |
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/SupportedCalendarComponentSet.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlUtils.propertyName
16 | import org.xmlpull.v1.XmlPullParser
17 |
18 | data class SupportedCalendarComponentSet(
19 | val supportsEvents: Boolean,
20 | val supportsTasks: Boolean,
21 | val supportsJournal: Boolean
22 | ): Property {
23 |
24 |
25 | object Factory: PropertyFactory {
26 |
27 | override fun getName() = CalDAV.SupportedCalendarComponentSet
28 |
29 | override fun create(parser: XmlPullParser): SupportedCalendarComponentSet {
30 | /*
31 |
32 |
33 | */
34 | var components = SupportedCalendarComponentSet(
35 | supportsEvents = false,
36 | supportsTasks = false,
37 | supportsJournal = false
38 | )
39 |
40 | val depth = parser.depth
41 | var eventType = parser.eventType
42 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
43 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
44 | when (parser.propertyName()) {
45 | CalDAV.AllComp -> {
46 | components = SupportedCalendarComponentSet(
47 | supportsEvents = true,
48 | supportsTasks = true,
49 | supportsJournal = true
50 | )
51 | }
52 | CalDAV.Comp ->
53 | when (parser.getAttributeValue(null, "name")?.uppercase()) {
54 | "VEVENT" -> components = components.copy(supportsEvents = true)
55 | "VTODO" -> components = components.copy(supportsTasks = true)
56 | "VJOURNAL" -> components = components.copy(supportsJournal = true)
57 | }
58 | }
59 | }
60 | eventType = parser.next()
61 | }
62 |
63 | return components
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/GetETag.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.QuotedStringUtils
16 | import at.bitfire.dav4jvm.XmlReader
17 | import io.ktor.client.statement.HttpResponse
18 | import io.ktor.http.HttpHeaders
19 | import okhttp3.Response
20 | import org.xmlpull.v1.XmlPullParser
21 |
22 | /**
23 | * The GetETag property.
24 | *
25 | * Can also be used to parse ETags from HTTP responses – just pass the raw ETag
26 | * header value to the constructor and then use [eTag] and [weak].
27 | */
28 | data class GetETag(
29 | val rawETag: String?
30 | ): Property {
31 |
32 | companion object {
33 |
34 | fun fromHttpResponse(response: HttpResponse) =
35 | response.headers[HttpHeaders.ETag]?.let { GetETag(it) }
36 |
37 | fun fromResponse(response: Response) =
38 | response.header(HttpHeaders.ETag)?.let { GetETag(it) }
39 | }
40 |
41 | /**
42 | * The parsed ETag value, excluding the weakness indicator and the quotes.
43 | */
44 | val eTag: String?
45 |
46 | /**
47 | * Whether the ETag is weak.
48 | */
49 | var weak: Boolean
50 |
51 | init {
52 | /* entity-tag = [ weak ] opaque-tag
53 | weak = "W/"
54 | opaque-tag = quoted-string
55 | */
56 |
57 | if (rawETag != null) {
58 | val tag: String?
59 | // remove trailing "W/"
60 | if (rawETag.startsWith("W/")) {
61 | // entity tag is weak
62 | tag = rawETag.substring(2)
63 | weak = true
64 | } else {
65 | tag = rawETag
66 | weak = false
67 | }
68 | eTag = QuotedStringUtils.decodeQuotedString(tag)
69 | } else {
70 | eTag = null
71 | weak = false
72 | }
73 | }
74 |
75 | override fun equals(other: Any?): Boolean {
76 | if (other !is GetETag)
77 | return false
78 | return eTag == other.eTag && weak == other.weak
79 | }
80 |
81 | override fun hashCode(): Int {
82 | return eTag.hashCode() xor weak.hashCode()
83 | }
84 |
85 |
86 | object Factory: PropertyFactory {
87 |
88 | override fun getName() = WebDAV.GetETag
89 |
90 | override fun create(parser: XmlPullParser): GetETag =
91 | GetETag(XmlReader(parser).readText())
92 |
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/XmlUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm
12 |
13 | import org.xmlpull.v1.XmlPullParser
14 | import org.xmlpull.v1.XmlPullParserException
15 | import org.xmlpull.v1.XmlPullParserFactory
16 | import org.xmlpull.v1.XmlSerializer
17 |
18 | object XmlUtils {
19 |
20 | /**
21 | * Requests the parser to be as lenient as possible when parsing invalid XML.
22 | *
23 | * See [https://www.xmlpull.org/](xmlpull.org) and specific implementations, for instance
24 | * [Android XML](https://developer.android.com/reference/android/util/Xml#FEATURE_RELAXED)
25 | */
26 | private const val FEATURE_RELAXED = "http://xmlpull.org/v1/doc/features.html#relaxed"
27 |
28 | /** [XmlPullParserFactory] that is namespace-aware and does relaxed parsing */
29 | private val relaxedFactory =
30 | XmlPullParserFactory.newInstance().apply {
31 | isNamespaceAware = true
32 | setFeature(FEATURE_RELAXED, true)
33 | }
34 |
35 | /** [XmlPullParserFactory] that is namespace-aware */
36 | private val standardFactory: XmlPullParserFactory =
37 | XmlPullParserFactory.newInstance().apply {
38 | isNamespaceAware = true
39 | }
40 |
41 | /**
42 | * Creates a new [XmlPullParser].
43 | *
44 | * First tries to create a namespace-aware parser that supports [FEATURE_RELAXED]. If that
45 | * fails, it falls back to a namespace-aware parser without relaxed parsing.
46 | *
47 | * @throws XmlPullParserException when no parser could be created
48 | */
49 | fun newPullParser(): XmlPullParser =
50 | try {
51 | relaxedFactory.newPullParser()
52 | } catch (_: XmlPullParserException) {
53 | // FEATURE_RELAXED may not be supported, try without it
54 | null
55 | }
56 | ?: standardFactory.newPullParser()
57 |
58 | fun newSerializer(): XmlSerializer = standardFactory.newSerializer()
59 |
60 |
61 | fun XmlSerializer.insertTag(name: Property.Name, contentGenerator: XmlSerializer.() -> Unit = {}) {
62 | startTag(name.namespace, name.name)
63 | contentGenerator(this)
64 | endTag(name.namespace, name.name)
65 | }
66 |
67 | fun XmlPullParser.propertyName(): Property.Name {
68 | val propNs = namespace
69 | val propName = name
70 | if (propNs == null || propName == null)
71 | throw IllegalStateException("Current event must be START_TAG or END_TAG")
72 | return Property.Name(propNs, propName)
73 | }
74 |
75 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/ktor/Response.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor
12 |
13 | import at.bitfire.dav4jvm.Error
14 | import at.bitfire.dav4jvm.Property
15 | import io.ktor.http.HttpStatusCode
16 | import io.ktor.http.Url
17 | import io.ktor.http.isSuccess
18 |
19 | /**
20 | * Represents a WebDAV response XML Element.
21 | *
22 | *
24 | */
25 | @Suppress("unused")
26 | data class Response(
27 | /**
28 | * URL of the requested resource. For instance, if `this` is a result
29 | * of a PROPFIND request, the `requestedUrl` would be the URL where the
30 | * PROPFIND request has been sent to (usually the collection URL).
31 | */
32 | val requestedUrl: Url,
33 |
34 | /**
35 | * URL of this response (`href` element)
36 | */
37 | val href: Url,
38 |
39 | /**
40 | * status of this response (`status` XML element)
41 | */
42 | val status: HttpStatusCode?,
43 |
44 | /**
45 | * property/status elements (`propstat` XML elements)
46 | */
47 | val propstat: List,
48 |
49 | /**
50 | * list of precondition/postcondition elements (`error` XML elements)
51 | */
52 | val error: List? = null,
53 |
54 | /**
55 | * new location of this response (`location` XML element), used for redirects
56 | */
57 | val newLocation: Url? = null
58 | ) {
59 |
60 | enum class HrefRelation {
61 | SELF, MEMBER, OTHER
62 | }
63 |
64 | /**
65 | * All properties from propstat elements with empty status or status code 2xx.
66 | */
67 | val properties: List by lazy {
68 | if (isSuccess())
69 | propstat.filter { it.status.isSuccess() }.map { it.properties }.flatten()
70 | else
71 | emptyList()
72 | }
73 |
74 | /**
75 | * Convenience method to get a certain property with empty status or status code 2xx
76 | * from the current response.
77 | */
78 | operator fun get(clazz: Class) =
79 | properties.filterIsInstance(clazz).firstOrNull()
80 |
81 | /**
82 | * Returns whether the request was successful.
83 | *
84 | * @return true: no status XML element or status code 2xx; false: otherwise
85 | */
86 | fun isSuccess() = status == null || status.isSuccess()
87 |
88 | /**
89 | * Returns the name (last path segment) of the resource.
90 | */
91 | fun hrefName() = KtorHttpUtils.fileName(href)
92 |
93 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/CalDAV.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.caldav
12 |
13 | import at.bitfire.dav4jvm.Property
14 |
15 | object CalDAV {
16 |
17 | // CalDAV (RFC 4791)
18 |
19 | const val NS_CALDAV = "urn:ietf:params:xml:ns:caldav"
20 |
21 | val AllComp = Property.Name(NS_CALDAV, "allcomp")
22 | val Calendar = Property.Name(NS_CALDAV, "calendar")
23 | val Comp = Property.Name(NS_CALDAV, "comp")
24 | val CalendarData = Property.Name(NS_CALDAV, "calendar-data")
25 | val CalendarDescription = Property.Name(NS_CALDAV, "calendar-description")
26 | val CalendarHomeSet = Property.Name(NS_CALDAV, "calendar-home-set")
27 | val CalendarMultiget = Property.Name(NS_CALDAV, "calendar-multiget")
28 | val CalendarQuery = Property.Name(NS_CALDAV, "calendar-query")
29 | val CalendarTimezone = Property.Name(NS_CALDAV, "calendar-timezone")
30 | val CompFilter = Property.Name(NS_CALDAV, "comp-filter")
31 | val Filter = Property.Name(NS_CALDAV, "filter")
32 | val MaxResourceSize = Property.Name(NS_CALDAV, "max-resource-size")
33 | val SupportedCalendarComponentSet = Property.Name(NS_CALDAV, "supported-calendar-component-set")
34 | val SupportedCalendarData = Property.Name(NS_CALDAV, "supported-calendar-data")
35 | val TimeRange = Property.Name(NS_CALDAV, "time-range")
36 |
37 |
38 | // Scheduling Extensions to CalDAV (RFC 6638)
39 |
40 | val CalendarUserAddressSet = Property.Name(NS_CALDAV, "calendar-user-address-set")
41 | val ScheduleTag = Property.Name(NS_CALDAV, "schedule-tag")
42 |
43 |
44 | // Calendaring Extensions to WebDAV (CalDAV): Time Zones by Reference (RFC 7809)
45 |
46 | val CalendarTimezoneId = Property.Name(NS_CALDAV, "calendar-timezone-id")
47 |
48 |
49 | // Apple XML elements
50 |
51 | const val NS_APPLE_ICAL = "http://apple.com/ns/ical/"
52 |
53 | val CalendarColor = Property.Name(NS_APPLE_ICAL, "calendar-color")
54 |
55 |
56 | // CalendarServer XML elements
57 |
58 | const val NS_CALENDARSERVER = "http://calendarserver.org/ns/"
59 |
60 | val CalendarProxyRead = Property.Name(NS_CALENDARSERVER, "calendar-proxy-read")
61 | val CalendarProxyReadFor = Property.Name(NS_CALENDARSERVER, "calendar-proxy-read-for")
62 | val CalendarProxyWrite = Property.Name(NS_CALENDARSERVER, "calendar-proxy-write")
63 | val CalendarProxyWriteFor = Property.Name(NS_CALENDARSERVER, "calendar-proxy-write-for")
64 | val GetCTag = Property.Name(NS_CALENDARSERVER, "getctag")
65 | val Source = Property.Name(NS_CALENDARSERVER, "source")
66 | val Subscribed = Property.Name(NS_CALENDARSERVER, "subscribed")
67 |
68 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableExceptionTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor.exception
12 |
13 | import at.bitfire.dav4jvm.HttpUtils
14 | import io.ktor.client.HttpClient
15 | import io.ktor.client.engine.mock.MockEngine
16 | import io.ktor.client.engine.mock.respond
17 | import io.ktor.client.engine.mock.respondError
18 | import io.ktor.client.request.get
19 | import io.ktor.http.HttpHeaders
20 | import io.ktor.http.HttpStatusCode
21 | import io.ktor.http.Url
22 | import io.ktor.http.headersOf
23 | import kotlinx.coroutines.test.runTest
24 | import org.junit.Assert.assertNotNull
25 | import org.junit.Assert.assertNull
26 | import org.junit.Assert.assertTrue
27 | import org.junit.Test
28 | import java.time.Instant
29 |
30 | class ServiceUnavailableExceptionTest {
31 |
32 | private val sampleUrl = Url("http://www.example.com")
33 |
34 | @Test
35 | fun testRetryAfter_NoTime() = runTest {
36 | val mockEngine = MockEngine {
37 | respondError(HttpStatusCode.ServiceUnavailable) // 503
38 | }
39 | val httpClient = HttpClient(mockEngine)
40 | val response = httpClient.get(sampleUrl)
41 | val ex = HttpException.fromResponse(response) as ServiceUnavailableException
42 | assertNull(ex.retryAfter)
43 | assertNull(ex.retryAfterAbs)
44 | }
45 |
46 | @Test
47 | fun testRetryAfter_Seconds() = runTest {
48 | val mockEngine = MockEngine {
49 | respond(
50 | content = "",
51 | status = HttpStatusCode.ServiceUnavailable, // 503
52 | headers = headersOf(HttpHeaders.RetryAfter, "120")
53 | )
54 | }
55 | val httpClient = HttpClient(mockEngine)
56 |
57 | val response = httpClient.get(sampleUrl)
58 | val ex = HttpException.fromResponse(response) as ServiceUnavailableException
59 | assertNotNull(ex.retryAfter)
60 | assertTrue(withinTimeRange(ex.retryAfterAbs!!, 120))
61 | }
62 |
63 | @Test
64 | fun testRetryAfter_Date() = runTest {
65 | val after30min = Instant.now().plusSeconds(30*60)
66 | val mockEngine = MockEngine {
67 | respondError(
68 | status = HttpStatusCode.ServiceUnavailable, // 503
69 | headers = headersOf(HttpHeaders.RetryAfter, HttpUtils.formatDate(after30min))
70 | )
71 | }
72 | val httpClient = HttpClient(mockEngine)
73 |
74 | val response = httpClient.get(sampleUrl)
75 | val ex = HttpException.fromResponse(response) as ServiceUnavailableException
76 | assertNotNull(ex.retryAfter)
77 | assertTrue(withinTimeRange(ex.retryAfterAbs!!, 30*60))
78 | }
79 |
80 |
81 | // helpers
82 |
83 | private fun withinTimeRange(d: Instant, seconds: Long) =
84 | d.isBefore(
85 | Instant.now()
86 | .plusSeconds(seconds)
87 | .plusSeconds(5) // tolerance for test running
88 | )
89 |
90 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/HttpUtilsTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm
12 |
13 | import at.bitfire.dav4jvm.HttpUtils.toHttpUrl
14 | import at.bitfire.dav4jvm.HttpUtils.toKtorUrl
15 | import io.ktor.http.Url
16 | import okhttp3.HttpUrl.Companion.toHttpUrl
17 | import org.junit.Assert.assertEquals
18 | import org.junit.Assert.assertNull
19 | import org.junit.Test
20 | import java.time.Instant
21 | import java.time.LocalDate
22 | import java.time.LocalTime
23 | import java.time.ZoneOffset
24 | import java.time.ZonedDateTime
25 | import java.time.format.DateTimeFormatter
26 | import java.util.Calendar
27 | import java.util.Locale
28 | import java.util.TimeZone
29 | import java.util.logging.Logger
30 |
31 | class HttpUtilsTest {
32 |
33 | @Test
34 | fun formatDate() {
35 | val cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"))
36 | cal.set(2023, 4, 11, 17, 26, 35)
37 | cal.timeZone = TimeZone.getTimeZone("UTC")
38 | assertEquals("Sun, 06 Nov 1994 08:49:37 GMT", HttpUtils.formatDate(
39 | ZonedDateTime.of(
40 | LocalDate.of(1994, 11, 6),
41 | LocalTime.of(8, 49, 37),
42 | ZoneOffset.UTC
43 | ).toInstant()
44 | ))
45 | }
46 |
47 |
48 | @Test
49 | fun parseDate_IMF_FixDate() {
50 | // RFC 7231 IMF-fixdate (preferred format)
51 | assertEquals(Instant.ofEpochSecond(784111777), HttpUtils.parseDate("Sun, 06 Nov 1994 08:49:37 GMT"))
52 | }
53 |
54 | @Test
55 | fun parseDate_RFC850_1994() {
56 | // obsolete RFC 850 format – fails when run after year 2000 because 06 Nov 2094 (!) is not a Sunday
57 | assertNull(HttpUtils.parseDate("Sun, 06-Nov-94 08:49:37 GMT"))
58 | }
59 |
60 | @Test
61 | fun parseDate_RFC850_2004_CEST() {
62 | // obsolete RFC 850 format with European time zone
63 | assertEquals(Instant.ofEpochSecond(1689317377), HttpUtils.parseDate("Friday, 14-Jul-23 08:49:37 CEST"))
64 | }
65 |
66 | @Test
67 | fun parseDate_RFC850_2004_GMT() {
68 | // obsolete RFC 850 format
69 | assertEquals(Instant.ofEpochSecond(1689324577), HttpUtils.parseDate("Friday, 14-Jul-23 08:49:37 GMT"))
70 | }
71 |
72 | @Test
73 | fun parseDate_ANSI_C() {
74 | // ANSI C's asctime() format
75 | val logger = Logger.getLogger(javaClass.name)
76 | logger.info("Expected date: " + DateTimeFormatter.ofPattern("EEE MMM ppd HH:mm:ss yyyy", Locale.US).format(ZonedDateTime.now()))
77 |
78 | assertEquals(Instant.ofEpochSecond(784111777), HttpUtils.parseDate("Sun Nov 6 08:49:37 1994"))
79 | }
80 |
81 |
82 | @Test
83 | fun testHttpUrl_toKtorUrl() {
84 | assertEquals(Url("https://example.com:123/path"), "https://example.com:123/path".toHttpUrl().toKtorUrl())
85 | }
86 |
87 | @Test
88 | fun testUrl_ToHttpUrl() {
89 | assertEquals("https://example.com:123/path".toHttpUrl(), Url("https://example.com:123/path").toHttpUrl())
90 | }
91 |
92 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/DavException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp.exception
12 |
13 | import at.bitfire.dav4jvm.Error
14 | import okhttp3.Response
15 | import javax.annotation.WillNotClose
16 |
17 | /**
18 | * Signals that an error occurred during a WebDAV-related operation.
19 | *
20 | * This could be a logical error like when a required ETag has not been received, but also an explicit HTTP error
21 | * (usually with a subclass of [HttpException], which in turn extends this class).
22 | *
23 | * Often, HTTP response bodies contain valuable information about the error in text format (for instance, a HTML page
24 | * that contains details about the error) and/or as `` XML elements. However, such response bodies
25 | * are sometimes very large.
26 | *
27 | * So, if possible and useful, a size-limited excerpt of the associated HTTP request and response can be
28 | * attached and subsequently included in application-level debug info or shown to the user.
29 | *
30 | * Note: [Exception] is serializable, so objects of this class must contain only serializable objects.
31 | *
32 | * @param statusCode status code of associated HTTP response
33 | * @param requestExcerpt cached excerpt of associated HTTP request body
34 | * @param responseExcerpt cached excerpt of associated HTTP response body
35 | * @param errors precondition/postcondition XML elements which have been found in the XML response
36 | */
37 | open class DavException(
38 | message: String? = null,
39 | cause: Throwable? = null,
40 | open val statusCode: Int? = null,
41 | val requestExcerpt: String? = null,
42 | val responseExcerpt: String? = null,
43 | val errors: List = emptyList()
44 | ): Exception(message, cause) {
45 |
46 | // constructor from Response
47 |
48 | /**
49 | * Takes the request, response and errors from a given HTTP response.
50 | *
51 | * @param message optional exception message
52 | * @param cause optional exception cause
53 | * @param response response to extract status code and request/response excerpt from (if possible)
54 | */
55 | constructor(
56 | message: String,
57 | cause: Throwable? = null,
58 | @WillNotClose response: Response
59 | ) : this(message, cause, HttpResponseInfo.fromResponse(response))
60 |
61 | private constructor(
62 | message: String?,
63 | cause: Throwable? = null,
64 | httpResponseInfo: HttpResponseInfo
65 | ): this(
66 | message = message,
67 | cause = cause,
68 | statusCode = httpResponseInfo.statusCode,
69 | requestExcerpt = httpResponseInfo.requestExcerpt,
70 | responseExcerpt = httpResponseInfo.responseExcerpt,
71 | errors = httpResponseInfo.errors
72 | )
73 |
74 |
75 | companion object {
76 |
77 | /**
78 | * maximum size of extracted response body
79 | */
80 | const val MAX_EXCERPT_SIZE = 20*1024
81 |
82 | }
83 |
84 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/okhttp/exception/ServiceUnavailableException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp.exception
12 |
13 | import at.bitfire.dav4jvm.HttpUtils
14 | import at.bitfire.dav4jvm.okhttp.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_DEFAULT
15 | import at.bitfire.dav4jvm.okhttp.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_MAX
16 | import at.bitfire.dav4jvm.okhttp.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_MIN
17 | import okhttp3.Response
18 | import java.time.Instant
19 | import java.util.logging.Level
20 | import java.util.logging.Logger
21 |
22 | class ServiceUnavailableException(response: Response) : HttpException(response) {
23 |
24 | private val logger
25 | get() = Logger.getLogger(javaClass.name)
26 |
27 | val retryAfter: Instant?
28 |
29 | init {
30 | if (response.code != 503)
31 | throw IllegalArgumentException("Status code must be 503")
32 |
33 | // Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds )
34 | // HTTP-date = rfc1123-date | rfc850-date | asctime-date
35 |
36 | var retryAfterValue: Instant? = null
37 | response.header("Retry-After")?.let { after ->
38 | retryAfterValue = HttpUtils.parseDate(after) ?:
39 | // not a HTTP-date, must be delta-seconds
40 | try {
41 | val seconds = after.toLong()
42 | Instant.now().plusSeconds(seconds)
43 | } catch (e: NumberFormatException) {
44 | logger.log(Level.WARNING, "Received Retry-After which was not a HTTP-date nor delta-seconds: $after", e)
45 | null
46 | }
47 | }
48 |
49 | retryAfter = retryAfterValue
50 | }
51 |
52 |
53 | /**
54 | * Returns appropriate sync retry delay in seconds, considering the servers suggestion
55 | * in [retryAfter] ([DELAY_UNTIL_DEFAULT] if no server suggestion).
56 | *
57 | * Takes current time into account to calculate intervals. Interval
58 | * will be restricted to values between [DELAY_UNTIL_MIN] and [DELAY_UNTIL_MAX].
59 | *
60 | * @param start timestamp to calculate the delay from (default: now)
61 | *
62 | * @return until when to wait before sync can be retried
63 | */
64 | fun getDelayUntil(start: Instant = Instant.now()): Instant {
65 | if (retryAfter == null)
66 | return start.plusSeconds(DELAY_UNTIL_DEFAULT)
67 |
68 | // take server suggestion, but restrict to plausible min/max values
69 | return retryAfter.coerceIn(
70 | minimumValue = start.plusSeconds(DELAY_UNTIL_MIN),
71 | maximumValue = start.plusSeconds(DELAY_UNTIL_MAX)
72 | )
73 | }
74 |
75 |
76 | companion object {
77 |
78 | // default values for getDelayUntil
79 | const val DELAY_UNTIL_DEFAULT = 15 * 60L // 15 min
80 | const val DELAY_UNTIL_MIN = 1 * 60L // 1 min
81 | const val DELAY_UNTIL_MAX = 2 * 60 * 60L // 2 hours
82 |
83 | }
84 |
85 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/okhttp/exception/DavExceptionTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp.exception
12 |
13 | import at.bitfire.dav4jvm.Error
14 | import at.bitfire.dav4jvm.Property
15 | import okhttp3.MediaType.Companion.toMediaType
16 | import okhttp3.Protocol
17 | import okhttp3.Request
18 | import okhttp3.Response
19 | import okhttp3.ResponseBody.Companion.toResponseBody
20 | import org.junit.Assert.assertEquals
21 | import org.junit.Assert.assertTrue
22 | import org.junit.Test
23 | import java.io.ByteArrayInputStream
24 | import java.io.ByteArrayOutputStream
25 | import java.io.FileNotFoundException
26 | import java.io.ObjectInputStream
27 | import java.io.ObjectOutputStream
28 |
29 | class DavExceptionTest {
30 |
31 | @Test
32 | fun fromResponse() {
33 | val request = Request.Builder()
34 | .get()
35 | .url("https://example.com")
36 | .build()
37 | val cause = Exception()
38 | val result = Response.Builder()
39 | .request(request)
40 | .protocol(Protocol.HTTP_1_1)
41 | .code(200)
42 | .message("OK")
43 | .body("Your Information".toResponseBody("text/plain".toMediaType()))
44 | .build()
45 | .use { response ->
46 | DavException("Message", cause, response)
47 | }
48 | assertEquals("Message", result.message)
49 | assertEquals(cause, result.cause)
50 | assertEquals(200, result.statusCode)
51 | assertEquals("GET https://example.com/", result.requestExcerpt)
52 | assertEquals("Your Information", result.responseExcerpt)
53 | assertTrue(result.errors.isEmpty())
54 | }
55 |
56 | @Test
57 | fun `is Java-serializable`() {
58 | val ex = DavException(
59 | message = "Some Error",
60 | statusCode = 500,
61 | requestExcerpt = "Request Body",
62 | responseExcerpt = "Response Body",
63 | errors = listOf(
64 | Error(Property.Name("Serialized", "Name"))
65 | ),
66 | cause = FileNotFoundException()
67 | )
68 |
69 | // serialize (Java-style as in Serializable interface, not Kotlin serialization)
70 | val blob = ByteArrayOutputStream().use { baos ->
71 | ObjectOutputStream(baos).use { oos ->
72 | oos.writeObject(ex)
73 | }
74 | baos.toByteArray()
75 | }
76 |
77 | // deserialize
78 | ByteArrayInputStream(blob).use { bais ->
79 | ObjectInputStream(bais).use { ois ->
80 | val actual = ois.readObject() as DavException
81 | assertEquals(ex.message, actual.message)
82 | assertEquals(ex.statusCode, actual.statusCode)
83 | assertEquals(ex.requestExcerpt, actual.requestExcerpt)
84 | assertEquals(ex.responseExcerpt, actual.responseExcerpt)
85 | assertEquals(ex.errors, actual.errors)
86 | assertTrue(actual.cause is FileNotFoundException)
87 | }
88 | }
89 | }
90 |
91 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/DavException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor.exception
12 |
13 | import at.bitfire.dav4jvm.Error
14 | import io.ktor.client.statement.HttpResponse
15 | import io.ktor.utils.io.ByteReadChannel
16 |
17 | /**
18 | * Signals that an error occurred during a WebDAV-related operation.
19 | *
20 | * This could be a logical error like when a required ETag has not been received, but also an explicit HTTP error
21 | * (usually with a subclass of [HttpException], which in turn extends this class).
22 | *
23 | * Often, HTTP response bodies contain valuable information about the error in text format (for instance, a HTML page
24 | * that contains details about the error) and/or as `` XML elements. However, such response bodies
25 | * are sometimes very large.
26 | *
27 | * So, if possible and useful, a size-limited excerpt of the associated HTTP request and response can be
28 | * attached and subsequently included in application-level debug info or shown to the user.
29 | *
30 | * Note: [Exception] is serializable, so objects of this class must contain only serializable objects.
31 | *
32 | * @param statusCode status code of associated HTTP response
33 | * @param requestExcerpt cached excerpt of associated HTTP request body
34 | * @param responseExcerpt cached excerpt of associated HTTP response body
35 | * @param errors precondition/postcondition XML elements which have been found in the XML response
36 | */
37 | open class DavException(
38 | message: String? = null,
39 | open val statusCode: Int? = null,
40 | val requestExcerpt: String? = null,
41 | val responseExcerpt: String? = null,
42 | val errors: List = emptyList(),
43 | cause: Throwable? = null
44 | ): Exception(message, cause) {
45 |
46 | companion object {
47 |
48 | /**
49 | * Creates a [DavException] from the request, response and errors of a given HTTP response.
50 | *
51 | * @param message optional exception message
52 | * @param cause optional exception cause
53 | * @param response response to extract status code and request/response excerpt from (if possible)
54 | * @param responseBodyChannel optional existing response body channel that can be used to read the response body
55 | */
56 | suspend fun fromResponse(
57 | message: String,
58 | response: HttpResponse,
59 | responseBodyChannel: ByteReadChannel? = null,
60 | cause: Throwable? = null
61 | ): DavException {
62 | val responseInfo = HttpResponseInfo.fromResponse(response, responseBodyChannel)
63 | return DavException(
64 | message = message,
65 | cause = cause,
66 | statusCode = responseInfo.status.value,
67 | requestExcerpt = responseInfo.requestExcerpt,
68 | responseExcerpt = responseInfo.responseExcerpt,
69 | errors = responseInfo.errors
70 | )
71 | }
72 |
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/HttpUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm
12 |
13 | import at.bitfire.dav4jvm.HttpUtils.httpDateFormat
14 | import io.ktor.http.Url
15 | import okhttp3.HttpUrl
16 | import okhttp3.HttpUrl.Companion.toHttpUrl
17 | import java.time.Instant
18 | import java.time.LocalDateTime
19 | import java.time.ZoneOffset
20 | import java.time.ZonedDateTime
21 | import java.time.format.DateTimeFormatter
22 | import java.time.format.DateTimeParseException
23 | import java.util.Locale
24 | import java.util.logging.Logger
25 |
26 | object HttpUtils {
27 |
28 | /**
29 | * Preferred HTTP date/time format, see RFC 7231 7.1.1.1 IMF-fixdate
30 | */
31 | private const val httpDateFormatStr = "EEE, dd MMM yyyy HH:mm:ss ZZZZ"
32 | private val httpDateFormat = DateTimeFormatter.ofPattern(httpDateFormatStr, Locale.US)
33 |
34 | private val logger
35 | get() = Logger.getLogger(javaClass.name)
36 |
37 | /**
38 | * Formats a date for use in HTTP headers using [httpDateFormat].
39 | *
40 | * @param date date to be formatted
41 | *
42 | * @return date in HTTP-date format
43 | */
44 | fun formatDate(date: Instant): String =
45 | ZonedDateTime.ofInstant(date, ZoneOffset.UTC).format(httpDateFormat)
46 |
47 | /**
48 | * Parses a HTTP-date according to RFC 7231 section 7.1.1.1.
49 | *
50 | * @param dateStr date-time formatted in one of the three accepted formats:
51 | *
52 | * - preferred format (`IMF-fixdate`)
53 | * - obsolete RFC 850 format
54 | * - ANSI C's `asctime()` format
55 | *
56 | * @return date-time, or null if date could not be parsed
57 | */
58 | fun parseDate(dateStr: String): Instant? {
59 | val zonedFormats = arrayOf(
60 | // preferred format
61 | httpDateFormat,
62 |
63 | // obsolete RFC 850 format
64 | DateTimeFormatter.ofPattern("EEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
65 | )
66 |
67 | // try the two formats with zone info
68 | for (format in zonedFormats)
69 | try {
70 | return ZonedDateTime.parse(dateStr, format).toInstant()
71 | } catch (ignored: DateTimeParseException) {
72 | }
73 |
74 | // try ANSI C's asctime() format
75 | try {
76 | val formatC = DateTimeFormatter.ofPattern("EEE MMM ppd HH:mm:ss yyyy", Locale.US)
77 | val local = LocalDateTime.parse(dateStr, formatC)
78 | return local.atZone(ZoneOffset.UTC).toInstant()
79 | } catch (ignored: DateTimeParseException) {
80 | }
81 |
82 | // no success in parsing
83 | logger.warning("Couldn't parse HTTP date: $dateStr, ignoring")
84 | return null
85 | }
86 |
87 |
88 | // for migration between Ktor and okhttp
89 |
90 | /**
91 | * Converts an okhttp [HttpUrl] to a Ktor [Url].
92 | */
93 | fun HttpUrl.toKtorUrl() = Url(toString())
94 |
95 | /**
96 | * Converts a Ktor [Url] to an okhttp [HttpUrl].
97 | */
98 | fun Url.toHttpUrl() = toString().toHttpUrl()
99 |
100 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/CurrentUserPrivilegeSet.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 | import at.bitfire.dav4jvm.PropertyFactory
15 | import at.bitfire.dav4jvm.XmlReader
16 | import at.bitfire.dav4jvm.XmlUtils.propertyName
17 | import org.xmlpull.v1.XmlPullParser
18 |
19 | data class CurrentUserPrivilegeSet(
20 | // not all privileges from RFC 3744 are implemented by now
21 | // feel free to add more if you need them for your project
22 | val mayRead: Boolean = false,
23 | val mayWriteProperties: Boolean = false,
24 | val mayWriteContent: Boolean = false,
25 | val mayBind: Boolean = false,
26 | val mayUnbind: Boolean = false
27 | ): Property {
28 |
29 | object Factory: PropertyFactory {
30 |
31 | override fun getName() = WebDAV.CurrentUserPrivilegeSet
32 |
33 | override fun create(parser: XmlPullParser): CurrentUserPrivilegeSet {
34 | //
35 | //
36 | var privs = CurrentUserPrivilegeSet()
37 |
38 | XmlReader(parser).processTag(WebDAV.Privilege) {
39 | val depth = parser.depth
40 | var eventType = parser.eventType
41 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
42 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1)
43 | when (parser.propertyName()) {
44 | WebDAV.Read ->
45 | privs = privs.copy(mayRead = true)
46 | WebDAV.Write -> {
47 | privs = privs.copy(
48 | mayBind = true,
49 | mayUnbind = true,
50 | mayWriteProperties = true,
51 | mayWriteContent = true
52 | )
53 | }
54 | WebDAV.WriteProperties ->
55 | privs = privs.copy(mayWriteProperties = true)
56 | WebDAV.WriteContent ->
57 | privs = privs.copy(mayWriteContent = true)
58 | WebDAV.Bind ->
59 | privs = privs.copy(mayBind = true)
60 | WebDAV.Unbind ->
61 | privs = privs.copy(mayUnbind = true)
62 | WebDAV.All -> {
63 | privs = privs.copy(
64 | mayRead = true,
65 | mayBind = true,
66 | mayUnbind = true,
67 | mayWriteProperties = true,
68 | mayWriteContent = true
69 | )
70 | }
71 | }
72 | eventType = parser.next()
73 | }
74 | }
75 |
76 | return privs
77 | }
78 |
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/ktor/exception/ServiceUnavailableException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor.exception
12 |
13 | import at.bitfire.dav4jvm.HttpUtils
14 | import at.bitfire.dav4jvm.ktor.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_DEFAULT
15 | import at.bitfire.dav4jvm.ktor.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_MAX
16 | import at.bitfire.dav4jvm.ktor.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_MIN
17 | import io.ktor.http.HttpStatusCode
18 | import java.time.Instant
19 |
20 | class ServiceUnavailableException internal constructor(
21 | responseInfo: HttpResponseInfo,
22 |
23 | /** unprocessed value of the `Retry-After` header */
24 | val retryAfter: String?
25 | ): HttpException(
26 | status = responseInfo.status,
27 | requestExcerpt = responseInfo.requestExcerpt,
28 | responseExcerpt = responseInfo.responseExcerpt,
29 | errors = responseInfo.errors
30 | ) {
31 |
32 | init {
33 | if (responseInfo.status != HttpStatusCode.ServiceUnavailable)
34 | throw IllegalArgumentException("Status must be ${HttpStatusCode.ServiceUnavailable}")
35 | }
36 |
37 | /**
38 | * absolute time of [retryAfter] (if available)
39 | */
40 | val retryAfterAbs: Instant? =
41 | if (retryAfter != null) {
42 | // Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds )
43 | // HTTP-date = rfc1123-date | rfc850-date | asctime-date
44 | HttpUtils.parseDate(retryAfter) // parse as HTTP-date, if possible
45 | ?: parseAsSeconds(retryAfter) // not a HTTP-date, must be delta-seconds
46 | } else
47 | null
48 |
49 | private fun parseAsSeconds(retryAfter: String): Instant? {
50 | return try {
51 | val seconds = retryAfter.toLong()
52 | Instant.now().plusSeconds(seconds)
53 | } catch (_: NumberFormatException) {
54 | null
55 | }
56 | }
57 |
58 | /**
59 | * Returns appropriate sync retry delay in seconds, considering the servers suggestion
60 | * in [retryAfter] ([DELAY_UNTIL_DEFAULT] if no server suggestion).
61 | *
62 | * Takes current time into account to calculate intervals. Interval
63 | * will be restricted to values between [DELAY_UNTIL_MIN] and [DELAY_UNTIL_MAX].
64 | *
65 | * @param start timestamp to calculate the delay from (default: now)
66 | *
67 | * @return until when to wait before sync can be retried
68 | */
69 | fun getDelayUntil(start: Instant = Instant.now()): Instant {
70 | if (retryAfterAbs == null)
71 | return start.plusSeconds(DELAY_UNTIL_DEFAULT)
72 |
73 | // take server suggestion, but restrict to plausible min/max values
74 | return retryAfterAbs.coerceIn(
75 | minimumValue = start.plusSeconds(DELAY_UNTIL_MIN),
76 | maximumValue = start.plusSeconds(DELAY_UNTIL_MAX)
77 | )
78 | }
79 |
80 |
81 | companion object {
82 |
83 | // default values for getDelayUntil
84 | const val DELAY_UNTIL_DEFAULT = 15 * 60L // 15 min
85 | const val DELAY_UNTIL_MIN = 1 * 60L // 1 min
86 | const val DELAY_UNTIL_MAX = 2 * 60 * 60L // 2 hours
87 |
88 | }
89 |
90 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/ktor/exception/DavExceptionTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor.exception
12 |
13 | import at.bitfire.dav4jvm.Error
14 | import at.bitfire.dav4jvm.Property
15 | import io.ktor.client.HttpClient
16 | import io.ktor.client.engine.mock.MockEngine
17 | import io.ktor.client.engine.mock.respond
18 | import io.ktor.client.request.get
19 | import io.ktor.http.ContentType
20 | import io.ktor.http.HttpHeaders
21 | import io.ktor.http.HttpStatusCode
22 | import io.ktor.http.Url
23 | import io.ktor.http.headersOf
24 | import kotlinx.coroutines.test.runTest
25 | import org.junit.Assert.assertEquals
26 | import org.junit.Assert.assertTrue
27 | import org.junit.Test
28 | import java.io.ByteArrayInputStream
29 | import java.io.ByteArrayOutputStream
30 | import java.io.FileNotFoundException
31 | import java.io.ObjectInputStream
32 | import java.io.ObjectOutputStream
33 |
34 | class DavExceptionTest {
35 |
36 | private val sampleUrl = Url("https://127.0.0.1/dav/")
37 |
38 | @Test
39 | fun fromResponse() = runTest {
40 | val mockEngine = MockEngine {
41 | respond(
42 | status = HttpStatusCode.OK,
43 | content = "Your Information",
44 | headers = headersOf(HttpHeaders.ContentType, ContentType.Text.Plain.toString())
45 | )
46 | }
47 | val httpClient = HttpClient(mockEngine)
48 | val response = httpClient.get(sampleUrl)
49 | val cause = Exception()
50 | val result = DavException.fromResponse("Unexpected response", response, cause = cause)
51 |
52 | assertEquals("Unexpected response", result.message)
53 | assertEquals(cause, result.cause)
54 | assertEquals(200, result.statusCode)
55 | assertEquals("GET $sampleUrl", result.requestExcerpt)
56 | assertEquals("Your Information", result.responseExcerpt)
57 | assertTrue(result.errors.isEmpty())
58 | }
59 |
60 | @Test
61 | fun `is Java-serializable`() {
62 | val ex = DavException(
63 | message = "Some Error",
64 | statusCode = 500,
65 | requestExcerpt = "Request Body",
66 | responseExcerpt = "Response Body",
67 | errors = listOf(
68 | Error(Property.Name("Serialized", "Name"))
69 | ),
70 | cause = FileNotFoundException()
71 | )
72 |
73 | // serialize (Java-style as in Serializable interface, not Kotlin serialization)
74 | val blob = ByteArrayOutputStream().use { baos ->
75 | ObjectOutputStream(baos).use { oos ->
76 | oos.writeObject(ex)
77 | }
78 | baos.toByteArray()
79 | }
80 |
81 | // deserialize
82 | ByteArrayInputStream(blob).use { bais ->
83 | ObjectInputStream(bais).use { ois ->
84 | val actual = ois.readObject() as DavException
85 | assertEquals(ex.message, actual.message)
86 | assertEquals(ex.statusCode, actual.statusCode)
87 | assertEquals(ex.requestExcerpt, actual.requestExcerpt)
88 | assertEquals(ex.responseExcerpt, actual.responseExcerpt)
89 | assertEquals(ex.errors, actual.errors)
90 | assertTrue(actual.cause is FileNotFoundException)
91 | }
92 | }
93 | }
94 |
95 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/okhttp/UrlUtilsTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.okhttp
12 |
13 | import at.bitfire.dav4jvm.okhttp.UrlUtils
14 | import at.bitfire.dav4jvm.okhttp.equalsForWebDAV
15 | import okhttp3.HttpUrl.Companion.toHttpUrl
16 | import org.junit.Assert.assertEquals
17 | import org.junit.Assert.assertFalse
18 | import org.junit.Assert.assertNull
19 | import org.junit.Assert.assertTrue
20 | import org.junit.Test
21 |
22 | class UrlUtilsTest {
23 |
24 | @Test
25 | fun testHostToDomain() {
26 | assertNull(UrlUtils.hostToDomain(null))
27 | assertEquals("", UrlUtils.hostToDomain("."))
28 | assertEquals("com", UrlUtils.hostToDomain("com"))
29 | assertEquals("com", UrlUtils.hostToDomain("com."))
30 | assertEquals("example.com", UrlUtils.hostToDomain("example.com"))
31 | assertEquals("example.com", UrlUtils.hostToDomain("example.com."))
32 | assertEquals("example.com", UrlUtils.hostToDomain(".example.com"))
33 | assertEquals("example.com", UrlUtils.hostToDomain(".example.com."))
34 | assertEquals("example.com", UrlUtils.hostToDomain("host.example.com"))
35 | assertEquals("example.com", UrlUtils.hostToDomain("host.example.com."))
36 | assertEquals("example.com", UrlUtils.hostToDomain("sub.host.example.com"))
37 | assertEquals("example.com", UrlUtils.hostToDomain("sub.host.example.com."))
38 | }
39 |
40 | @Test
41 | fun testOmitTrailingSlash() {
42 | assertEquals("http://host/resource".toHttpUrl(), UrlUtils.omitTrailingSlash("http://host/resource".toHttpUrl()))
43 | assertEquals("http://host/resource".toHttpUrl(), UrlUtils.omitTrailingSlash("http://host/resource/".toHttpUrl()))
44 | }
45 |
46 | @Test
47 | fun testWithTrailingSlash() {
48 | assertEquals("http://host/resource/".toHttpUrl(), UrlUtils.withTrailingSlash("http://host/resource".toHttpUrl()))
49 | assertEquals("http://host/resource/".toHttpUrl(), UrlUtils.withTrailingSlash("http://host/resource/".toHttpUrl()))
50 | }
51 |
52 |
53 | @Test
54 | fun testHttpUrl_EqualsForWebDAV() {
55 | assertTrue("http://host/resource".toHttpUrl().equalsForWebDAV("http://host/resource".toHttpUrl()))
56 | assertTrue("http://host:80/resource".toHttpUrl().equalsForWebDAV("http://host/resource".toHttpUrl()))
57 | assertTrue("https://HOST:443/resource".toHttpUrl().equalsForWebDAV("https://host/resource".toHttpUrl()))
58 | assertTrue("https://host:443/my@dav/".toHttpUrl().equalsForWebDAV("https://host/my%40dav/".toHttpUrl()))
59 | assertTrue("http://host/resource".toHttpUrl().equalsForWebDAV("http://host/resource#frag1".toHttpUrl()))
60 |
61 | assertFalse("http://host/resource".toHttpUrl().equalsForWebDAV("http://host/resource/".toHttpUrl()))
62 | assertFalse("http://host/resource".toHttpUrl().equalsForWebDAV("http://host:81/resource".toHttpUrl()))
63 |
64 | assertTrue("https://www.example.com/folder/[X]Y!.txt".toHttpUrl().equalsForWebDAV("https://www.example.com/folder/[X]Y!.txt".toHttpUrl()))
65 | assertTrue("https://www.example.com/folder/%5BX%5DY!.txt".toHttpUrl().equalsForWebDAV("https://www.example.com/folder/[X]Y!.txt".toHttpUrl()))
66 | assertTrue("https://www.example.com/folder/%5bX%5dY%21.txt".toHttpUrl().equalsForWebDAV("https://www.example.com/folder/[X]Y!.txt".toHttpUrl()))
67 | }
68 |
69 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/ktor/UrlUtilsTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.ktor
12 |
13 | import io.ktor.http.Url
14 | import org.junit.Assert.assertEquals
15 | import org.junit.Assert.assertFalse
16 | import org.junit.Assert.assertNull
17 | import org.junit.Assert.assertTrue
18 | import org.junit.Test
19 |
20 | class UrlUtilsTest {
21 |
22 | @Test
23 | fun testHostToDomain() {
24 | assertNull(UrlUtils.hostToDomain(null))
25 | assertEquals("", UrlUtils.hostToDomain("."))
26 | assertEquals("com", UrlUtils.hostToDomain("com"))
27 | assertEquals("com", UrlUtils.hostToDomain("com."))
28 | assertEquals("example.com", UrlUtils.hostToDomain("example.com"))
29 | assertEquals("example.com", UrlUtils.hostToDomain("example.com."))
30 | assertEquals("example.com", UrlUtils.hostToDomain(".example.com"))
31 | assertEquals("example.com", UrlUtils.hostToDomain(".example.com."))
32 | assertEquals("example.com", UrlUtils.hostToDomain("host.example.com"))
33 | assertEquals("example.com", UrlUtils.hostToDomain("host.example.com."))
34 | assertEquals("example.com", UrlUtils.hostToDomain("sub.host.example.com"))
35 | assertEquals("example.com", UrlUtils.hostToDomain("sub.host.example.com."))
36 | }
37 |
38 | @Test
39 | fun testOmitTrailingSlash() {
40 | assertEquals(Url("http://host/resource"), UrlUtils.omitTrailingSlash(Url("http://host/resource")))
41 | assertEquals(Url("http://host/resource"), UrlUtils.omitTrailingSlash(Url("http://host/resource/")))
42 | assertEquals(Url("http://host"), UrlUtils.omitTrailingSlash(Url("http://host")))
43 | assertEquals(Url("http://host"), UrlUtils.omitTrailingSlash(Url("http://host/")))
44 |
45 | }
46 |
47 | @Test
48 | fun testWithTrailingSlash() {
49 | assertEquals(Url("http://host/resource/"), UrlUtils.withTrailingSlash(Url("http://host/resource")))
50 | assertEquals(Url("http://host/resource/"), UrlUtils.withTrailingSlash(Url("http://host/resource/")))
51 | assertEquals(Url("http://host/"), UrlUtils.withTrailingSlash(Url("http://host")))
52 | assertEquals(Url("http://host/"), UrlUtils.withTrailingSlash(Url("http://host/")))
53 | }
54 |
55 |
56 | @Test
57 | fun testHttpUrl_EqualsForWebDAV() {
58 | assertTrue(Url("http://host/resource").equalsForWebDAV(Url("http://host/resource")))
59 | assertTrue(Url("http://host:80/resource").equalsForWebDAV(Url("http://host/resource")))
60 | assertTrue(Url("https://HOST:443/resource").equalsForWebDAV(Url("https://host/resource")))
61 | assertTrue(Url("https://host:443/my@dav/").equalsForWebDAV(Url("https://host/my%40dav/")))
62 | assertTrue(Url("http://host/resource").equalsForWebDAV(Url("http://host/resource#frag1")))
63 |
64 | assertFalse(Url("http://host/resource").equalsForWebDAV(Url("http://host/resource/")))
65 | assertFalse(Url("http://host/resource").equalsForWebDAV(Url("http://host:81/resource")))
66 |
67 | assertTrue(Url("https://www.example.com/folder/[X]Y!.txt").equalsForWebDAV(Url("https://www.example.com/folder/[X]Y!.txt")))
68 | assertTrue(Url("https://www.example.com/folder/%5BX%5DY!.txt").equalsForWebDAV(Url("https://www.example.com/folder/[X]Y!.txt")))
69 | assertTrue(Url("https://www.example.com/folder/%5bX%5dY%21.txt").equalsForWebDAV(Url("https://www.example.com/folder/[X]Y!.txt")))
70 | }
71 |
72 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/WebDAV.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | *
8 | * SPDX-License-Identifier: MPL-2.0
9 | */
10 |
11 | package at.bitfire.dav4jvm.property.webdav
12 |
13 | import at.bitfire.dav4jvm.Property
14 |
15 | /**
16 | * WebDAV XML namespace and elements (as defined in RFC 4918)
17 | */
18 | object WebDAV {
19 |
20 | const val NS_WEBDAV = "DAV:"
21 |
22 |
23 | // WebDAV XML elements/properties (see Section 14 and 15 of RFC 4918)
24 |
25 | val Collection = Property.Name(NS_WEBDAV, "collection")
26 | val CreationDate = Property.Name(NS_WEBDAV, "creationdate")
27 | val Depth = Property.Name(NS_WEBDAV, "depth")
28 | val DisplayName = Property.Name(NS_WEBDAV, "displayname")
29 | val Error = Property.Name(NS_WEBDAV, "error")
30 | val GetContentLength = Property.Name(NS_WEBDAV, "getcontentlength")
31 | val GetContentType = Property.Name(NS_WEBDAV, "getcontenttype")
32 | val GetETag = Property.Name(NS_WEBDAV, "getetag")
33 | val GetLastModified = Property.Name(NS_WEBDAV, "getlastmodified")
34 | val Href = Property.Name(NS_WEBDAV, "href")
35 | val Location = Property.Name(NS_WEBDAV, "location")
36 | val MultiStatus = Property.Name(NS_WEBDAV, "multistatus")
37 | val Owner = Property.Name(NS_WEBDAV, "owner")
38 | val Principal = Property.Name(NS_WEBDAV, "principal")
39 | val Prop = Property.Name(NS_WEBDAV, "prop")
40 | val PropertyUpdate = Property.Name(NS_WEBDAV, "propertyupdate")
41 | val PropFind = Property.Name(NS_WEBDAV, "propfind")
42 | val PropStat = Property.Name(NS_WEBDAV, "propstat")
43 | val Remove = Property.Name(NS_WEBDAV, "remove")
44 | val ResourceType = Property.Name(NS_WEBDAV, "resourcetype")
45 | val Response = Property.Name(NS_WEBDAV, "response")
46 | val Set = Property.Name(NS_WEBDAV, "set")
47 | val Status = Property.Name(NS_WEBDAV, "status")
48 |
49 |
50 | // Versioning Extensions to WebDAV (RFC 3253)
51 |
52 | val Report = Property.Name(NS_WEBDAV, "report")
53 | val SupportedReportSet = Property.Name(NS_WEBDAV, "supported-report-set")
54 | val SupportedReport = Property.Name(NS_WEBDAV, "supported-report")
55 |
56 |
57 | // WebDAV ACL (RFC 3744)
58 |
59 | val All = Property.Name(NS_WEBDAV, "all")
60 | val Bind = Property.Name(NS_WEBDAV, "bind")
61 | val CurrentUserPrivilegeSet = Property.Name(NS_WEBDAV, "current-user-privilege-set")
62 | val GroupMembership = Property.Name(NS_WEBDAV, "group-membership")
63 | val NeedPrivileges = Property.Name(NS_WEBDAV, "need-privileges")
64 | val Privilege = Property.Name(NS_WEBDAV, "privilege")
65 | val Read = Property.Name(NS_WEBDAV, "read")
66 | val Unbind = Property.Name(NS_WEBDAV, "unbind")
67 | val Write = Property.Name(NS_WEBDAV, "write")
68 | val WriteContent = Property.Name(NS_WEBDAV, "write-content")
69 | val WriteProperties = Property.Name(NS_WEBDAV, "write-properties")
70 |
71 |
72 | // Quota and Size Properties for WebDAV (RFC 4331)
73 |
74 | val QuotaAvailableBytes = Property.Name(NS_WEBDAV, "quota-available-bytes")
75 | val QuotaUsedBytes = Property.Name(NS_WEBDAV, "quota-used-bytes")
76 |
77 |
78 | // WebDAV Current Principal Extension (RFC 5397)
79 |
80 | val CurrentUserPrincipal = Property.Name(NS_WEBDAV, "current-user-principal")
81 |
82 |
83 | // Using POST to Add Members (RFC 5995)
84 |
85 | val AddMember = Property.Name(NS_WEBDAV, "add-member")
86 |
87 |
88 | // Collection Synchronization (RFC 6578)
89 |
90 | val Limit = Property.Name(NS_WEBDAV, "limit")
91 | val NResults = Property.Name(NS_WEBDAV, "nresults")
92 | val SyncCollection = Property.Name(NS_WEBDAV, "sync-collection")
93 | val SyncLevel = Property.Name(NS_WEBDAV, "sync-level")
94 | val SyncToken = Property.Name(NS_WEBDAV, "sync-token")
95 | val ValidSyncToken = Property.Name(NS_WEBDAV, "valid-sync-token")
96 |
97 | }
--------------------------------------------------------------------------------