├── .github
├── FUNDING.yml
└── workflows
│ ├── build-kdoc.yml
│ └── test.yml
├── .gitignore
├── .idea
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
└── copyright
│ ├── MPL.xml
│ └── profiles_settings.xml
├── AUTHORS
├── CHANGELOG.md
├── LICENSE
├── README.md
├── SECURITY.md
├── build.gradle.kts
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── src
├── main
└── kotlin
│ └── at
│ └── bitfire
│ └── dav4jvm
│ ├── BasicDigestAuthHandler.kt
│ ├── CallbackInterfaces.kt
│ ├── DavAddressBook.kt
│ ├── DavCalendar.kt
│ ├── DavCollection.kt
│ ├── DavResource.kt
│ ├── Error.kt
│ ├── HttpUtils.kt
│ ├── PropStat.kt
│ ├── Property.kt
│ ├── PropertyFactory.kt
│ ├── PropertyRegistry.kt
│ ├── QuotedStringUtils.kt
│ ├── Response.kt
│ ├── UrlUtils.kt
│ ├── XmlReader.kt
│ ├── XmlUtils.kt
│ ├── exception
│ ├── ConflictException.kt
│ ├── DavException.kt
│ ├── ForbiddenException.kt
│ ├── GoneException.kt
│ ├── HttpException.kt
│ ├── InvalidPropertyException.kt
│ ├── NotFoundException.kt
│ ├── PreconditionFailedException.kt
│ ├── ServiceUnavailableException.kt
│ └── UnauthorizedException.kt
│ └── property
│ ├── caldav
│ ├── CalendarColor.kt
│ ├── CalendarData.kt
│ ├── CalendarDescription.kt
│ ├── CalendarHomeSet.kt
│ ├── CalendarProxyReadFor.kt
│ ├── CalendarProxyWriteFor.kt
│ ├── CalendarTimezone.kt
│ ├── CalendarTimezoneId.kt
│ ├── CalendarUserAddressSet.kt
│ ├── GetCTag.kt
│ ├── MaxResourceSize.kt
│ ├── ScheduleTag.kt
│ ├── Source.kt
│ ├── SupportedCalendarComponentSet.kt
│ ├── SupportedCalendarData.kt
│ └── namespace.kt
│ ├── carddav
│ ├── AddressData.kt
│ ├── AddressbookDescription.kt
│ ├── AddressbookHomeSet.kt
│ ├── MaxResourceSize.kt
│ ├── SupportedAddressData.kt
│ └── namespace.kt
│ ├── push
│ ├── AuthSecret.kt
│ ├── ContentUpdate.kt
│ ├── PropertyUpdate.kt
│ ├── PushMessage.kt
│ ├── PushRegister.kt
│ ├── PushResource.kt
│ ├── PushTransport.kt
│ ├── PushTransports.kt
│ ├── Subscription.kt
│ ├── SubscriptionPublicKey.kt
│ ├── SupportedTriggers.kt
│ ├── Topic.kt
│ ├── Trigger.kt
│ ├── VapidPublicKey.kt
│ ├── WebPush.kt
│ ├── WebPushSubscription.kt
│ └── namespace.kt
│ └── webdav
│ ├── AddMember.kt
│ ├── CreationDate.kt
│ ├── CurrentUserPrincipal.kt
│ ├── CurrentUserPrivilegeSet.kt
│ ├── Depth.kt
│ ├── DisplayName.kt
│ ├── GetContentLength.kt
│ ├── GetContentType.kt
│ ├── GetETag.kt
│ ├── GetLastModified.kt
│ ├── GroupMembership.kt
│ ├── HrefListProperty.kt
│ ├── Owner.kt
│ ├── QuotaAvailableBytes.kt
│ ├── QuotaUsedBytes.kt
│ ├── ResourceType.kt
│ ├── SupportedReportSet.kt
│ ├── SyncLevel.kt
│ ├── SyncToken.kt
│ └── namespace.kt
└── test
└── kotlin
└── at
└── bitfire
└── dav4jvm
├── BasicDigestAuthHandlerTest.kt
├── DavCalendarTest.kt
├── DavCollectionTest.kt
├── DavResourceTest.kt
├── ErrorTest.kt
├── HttpUtilsTest.kt
├── PropertyTest.kt
├── QuotedStringUtilsTest.kt
├── UrlUtilsTest.kt
├── XmlReaderTest.kt
├── exception
├── DavExceptionTest.kt
├── HttpExceptionTest.kt
└── ServiceUnavailableExceptionTest.kt
└── property
├── CalendarDescriptionTest.kt
├── GetETagTest.kt
├── OwnerTest.kt
├── PropertyTest.kt
└── push
└── WebPushTest.kt
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 |
2 | github: bitfireAT
3 | custom: https://www.davx5.com/donate
4 |
--------------------------------------------------------------------------------
/.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@v4
18 | - uses: actions/setup-java@v4
19 | with:
20 | distribution: temurin
21 | java-version: 17
22 | - uses: gradle/actions/setup-gradle@v3
23 |
24 | - name: Build KDoc
25 | run: ./gradlew --no-daemon dokkaHtml
26 |
27 | - uses: actions/upload-pages-artifact@v3
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 |
--------------------------------------------------------------------------------
/.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@v4
9 | - uses: actions/setup-java@v4
10 | with:
11 | distribution: temurin
12 | java-version: 17
13 | - uses: gradle/actions/setup-gradle@v3
14 |
15 | - name: Check
16 | run: ./gradlew --no-daemon check
17 |
18 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/copyright/MPL.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | [](https://github.com/bitfireAT/dav4jvm/blob/main/LICENSE)
3 | [](https://github.com/bitfireAT/dav4jvm/actions/workflows/test.yml)
4 | [](https://jitpack.io/#bitfireAT/dav4jvm)
5 | [](https://bitfireat.github.io/dav4jvm/)
6 |
7 |
8 | # dav4jvm
9 |
10 | dav4jvm is a WebDAV/CalDAV/CardDAV library for JVM (Java/Kotlin). It has
11 | been developed for [DAVx⁵](https://www.davx5.com) initially.
12 |
13 | Repository: https://github.com/bitfireAT/dav4jvm/
14 |
15 | Generated KDoc: https://bitfireat.github.io/dav4jvm/
16 |
17 | For questions, suggestions etc. use [Github discussions](https://github.com/bitfireAT/dav4jvm/discussions).
18 | We're happy about contributions, but please let us know in the discussions before. Then make the changes
19 | in your own repository and send a pull request.
20 |
21 |
22 | ## Installation
23 |
24 | You can use jitpack.io to include dav4jvm:
25 |
26 | allprojects {
27 | repositories {
28 | maven { url 'https://jitpack.io' }
29 | }
30 | }
31 | dependencies {
32 | implementation 'com.github.bitfireAT:dav4jvm:' // usually the latest commit ID from main branch
33 | //implementation 'com.github.bitfireAT:dav4jvm:main-SNAPSHOT' // use it only for testing because it doesn't generate reproducible builds
34 | }
35 |
36 | dav4jvm needs a working XmlPullParser (XPP). On Android, the system already comes with
37 | XPP and you don't need to include one; on other systems, you may need to
38 | import for instance `org.ogce:xpp3` to get dav4jvm to work.
39 |
40 |
41 | ## Usage
42 |
43 | First, you'll need to set up an OkHttp instance. Use `BasicDigestAuthHandler` to configure the credentials:
44 |
45 | val authHandler = BasicDigestAuthHandler(
46 | domain = null, // Optional, to only authenticate against hosts with this domain.
47 | username = "user1",
48 | password = "pass1"
49 | )
50 | val okHttpClient = OkHttpClient.Builder()
51 | .followRedirects(false)
52 | .authenticator(authHandler)
53 | .addNetworkInterceptor(authHandler)
54 | .build()
55 |
56 |
57 | ### Files
58 |
59 | Here's an example to create and download a file:
60 |
61 | val location = "https://example.com/webdav/hello.txt".toHttpUrl()
62 | val davCollection = DavCollection(account.requireClient(), location)
63 |
64 | // Create a text file
65 | davCollection.put("World".toRequestBody(contentType = "text/plain".toMediaType()) { response ->
66 | // Upload successful!
67 | }
68 |
69 | // Download a text file
70 | davCollection.get(accept = "", headers = null) { response ->
71 | response.body?.string()
72 | // Download successful!
73 | }
74 |
75 | To list a folder's contents, you need to pass in which properties to fetch:
76 |
77 | val location = "https://example.com/webdav/".toHttpUrl()
78 | val davCollection = DavCollection(account.requireClient(), location)
79 |
80 | davCollection.propfind(depth = 1, DisplayName.NAME, GetLastModified.NAME) { response, relation ->
81 | // This callback will be called for every file in the folder.
82 | // Use `response.properties` to access the successfully retrieved properties.
83 | }
84 |
85 | ## Custom properties
86 |
87 | If you use custom WebDAV properties, register the corresponding factories with `PropertyRegistry.register()`
88 | before calling other dav4jvm methods.
89 |
90 |
91 | # Useful forks
92 |
93 | For specific use-cases, we have a list of forks that cover them:
94 | - Kotlin Multiplatform (maintained by [McDjuady](https://github.com/McDjuady)): https://github.com/McDjuady/dav4jvm
95 |
96 |
97 | ## Contact / License
98 |
99 | dav4jvm is licensed under [Mozilla Public License, v. 2.0](LICENSE).
100 |
101 |
102 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import java.net.URL
2 | import org.jetbrains.dokka.gradle.DokkaTask
3 |
4 | repositories {
5 | mavenCentral()
6 | }
7 |
8 | group="com.github.bitfireAT"
9 | version=System.getenv("GIT_COMMIT") // set by jitpack.io
10 |
11 | plugins {
12 | alias(libs.plugins.kotlin.jvm)
13 | `maven-publish`
14 |
15 | alias(libs.plugins.dokka)
16 | }
17 |
18 | publishing {
19 | publications {
20 | create("maven") {
21 | from(components["java"])
22 | }
23 | }
24 |
25 | repositories {
26 | maven {
27 | name = "dav4jvm"
28 | url = uri(layout.buildDirectory.dir("repo"))
29 | }
30 | }
31 | }
32 |
33 | tasks.withType().configureEach {
34 | dokkaSourceSets {
35 | named("main") {
36 | moduleName.set("dav4jvm")
37 | sourceLink {
38 | localDirectory.set(file("src/main/kotlin"))
39 | remoteUrl.set(URL("https://github.com/bitfireAT/dav4jvm/tree/main/src/main/kotlin/"))
40 | remoteLineSuffix.set("#L")
41 | }
42 | }
43 | }
44 | }
45 |
46 | dependencies {
47 | api(libs.okhttp)
48 | api(libs.xpp3)
49 |
50 | testImplementation(libs.junit4)
51 | testImplementation(libs.okhttp.mockwebserver)
52 | }
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | dokka = "1.9.20"
3 | junit4 = "4.13.2"
4 | kotlin = "2.1.20"
5 | okhttpVersion = "4.12.0"
6 | xpp3Version = "1.1.6"
7 |
8 | [libraries]
9 | junit4 = { module = "junit:junit", version.ref = "junit4" }
10 | okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttpVersion" }
11 | okhttp-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttpVersion" }
12 | xpp3 = { module = "org.ogce:xpp3", version.ref = "xpp3Version" }
13 |
14 | [plugins]
15 | dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
16 | kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
17 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitfireAT/dav4jvm/8691192e28295d4a8438210573f2c58cb6aa00b2/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MSYS* | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/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/main/kotlin/at/bitfire/dav4jvm/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
12 |
13 | /**
14 | * Callback for the OPTIONS request.
15 | */
16 | fun interface CapabilitiesCallback {
17 | fun onCapabilities(davCapabilities: Set, response: okhttp3.Response)
18 | }
19 |
20 | /**
21 | * Callback for 207 Multi-Status responses.
22 | */
23 | fun interface MultiResponseCallback {
24 | /**
25 | * Called for every `` element in the `` body. For instance,
26 | * in response to a `PROPFIND` request, this callback will be called once for every found
27 | * member resource.
28 | *
29 | * Known collections have [response] `href` with trailing slash, see [at.bitfire.dav4jvm.Response.parse] for details.
30 | *
31 | * @param response the parsed response (including URL)
32 | * @param relation relation of the response to the called resource
33 | */
34 | fun onResponse(response: Response, relation: Response.HrefRelation)
35 | }
36 |
37 | /**
38 | * Callback for HTTP responses.
39 | */
40 | fun interface ResponseCallback {
41 | /**
42 | * Called for a HTTP response. Typically this is only called for successful/redirect
43 | * responses because HTTP errors throw an exception before this callback is called.
44 | */
45 | fun onResponse(response: okhttp3.Response)
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/DavAddressBook.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.XmlUtils.insertTag
14 | import at.bitfire.dav4jvm.exception.DavException
15 | import at.bitfire.dav4jvm.exception.HttpException
16 | import at.bitfire.dav4jvm.property.carddav.AddressData
17 | import at.bitfire.dav4jvm.property.carddav.NS_CARDDAV
18 | import at.bitfire.dav4jvm.property.webdav.GetContentType
19 | import at.bitfire.dav4jvm.property.webdav.GetETag
20 | import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV
21 | import okhttp3.HttpUrl
22 | import okhttp3.MediaType.Companion.toMediaType
23 | import okhttp3.OkHttpClient
24 | import okhttp3.Request
25 | import okhttp3.RequestBody.Companion.toRequestBody
26 | import java.io.IOException
27 | import java.io.StringWriter
28 | import java.util.logging.Logger
29 |
30 | @Suppress("unused")
31 | class DavAddressBook @JvmOverloads constructor(
32 | httpClient: OkHttpClient,
33 | location: HttpUrl,
34 | logger: Logger = Logger.getLogger(DavAddressBook::javaClass.name)
35 | ): DavCollection(httpClient, location, logger) {
36 |
37 | companion object {
38 | val MIME_JCARD = "application/vcard+json".toMediaType()
39 | val MIME_VCARD3_UTF8 = "text/vcard;charset=utf-8".toMediaType()
40 | val MIME_VCARD4 = "text/vcard;version=4.0".toMediaType()
41 |
42 | val ADDRESSBOOK_QUERY = Property.Name(NS_CARDDAV, "addressbook-query")
43 | val ADDRESSBOOK_MULTIGET = Property.Name(NS_CARDDAV, "addressbook-multiget")
44 | val FILTER = Property.Name(NS_CARDDAV, "filter")
45 | }
46 |
47 | /**
48 | * Sends an addressbook-query REPORT request to the resource.
49 | *
50 | * @param callback called for every WebDAV response XML element in the result
51 | *
52 | * @return list of properties which have been received in the Multi-Status response, but
53 | * are not part of response XML elements
54 | *
55 | * @throws IOException on I/O error
56 | * @throws HttpException on HTTP error
57 | * @throws DavException on WebDAV error
58 | */
59 | fun addressbookQuery(callback: MultiResponseCallback): List {
60 | /*
63 |
64 | */
65 | val serializer = XmlUtils.newSerializer()
66 | val writer = StringWriter()
67 | serializer.setOutput(writer)
68 | serializer.startDocument("UTF-8", null)
69 | serializer.setPrefix("", NS_WEBDAV)
70 | serializer.setPrefix("CARD", NS_CARDDAV)
71 | serializer.insertTag(ADDRESSBOOK_QUERY) {
72 | insertTag(PROP) {
73 | insertTag(GetETag.NAME)
74 | }
75 | insertTag(FILTER)
76 | }
77 | serializer.endDocument()
78 |
79 | followRedirects {
80 | httpClient.newCall(Request.Builder()
81 | .url(location)
82 | .method("REPORT", writer.toString().toRequestBody(MIME_XML))
83 | .header("Depth", "1")
84 | .build()).execute()
85 | }.use { response ->
86 | return processMultiStatus(response, callback)
87 | }
88 | }
89 |
90 | /**
91 | * Sends an addressbook-multiget REPORT request to the resource.
92 | *
93 | * @param urls list of vCard URLs to be requested
94 | * @param contentType MIME type of requested format; may be "text/vcard" for vCard or
95 | * "application/vcard+json" for jCard. *null*: don't request specific representation type
96 | * @param version vCard version subtype of the requested format. Should only be specified together with a [contentType] of "text/vcard".
97 | * Currently only useful value: "4.0" for vCard 4. *null*: don't request specific version
98 | * @param callback called for every WebDAV response XML element in the result
99 | *
100 | * @return list of properties which have been received in the Multi-Status response, but
101 | * are not part of response XML elements
102 | *
103 | * @throws IOException on I/O error
104 | * @throws HttpException on HTTP error
105 | * @throws DavException on WebDAV error
106 | */
107 | fun multiget(urls: List, contentType: String? = null, version: String? = null, callback: MultiResponseCallback): List {
108 | /*
112 | */
113 | val serializer = XmlUtils.newSerializer()
114 | val writer = StringWriter()
115 | serializer.setOutput(writer)
116 | serializer.startDocument("UTF-8", null)
117 | serializer.setPrefix("", NS_WEBDAV)
118 | serializer.setPrefix("CARD", NS_CARDDAV)
119 | serializer.insertTag(ADDRESSBOOK_MULTIGET) {
120 | insertTag(PROP) {
121 | insertTag(GetContentType.NAME)
122 | insertTag(GetETag.NAME)
123 | insertTag(AddressData.NAME) {
124 | if (contentType != null)
125 | attribute(null, AddressData.CONTENT_TYPE, contentType)
126 | if (version != null)
127 | attribute(null, AddressData.VERSION, version)
128 | }
129 | }
130 | for (url in urls)
131 | insertTag(HREF) {
132 | text(url.encodedPath)
133 | }
134 | }
135 | serializer.endDocument()
136 |
137 | followRedirects {
138 | httpClient.newCall(Request.Builder()
139 | .url(location)
140 | .method("REPORT", writer.toString().toRequestBody(MIME_XML))
141 | .header("Depth", "0") // "The request MUST include a Depth: 0 header [...]"
142 | .build()).execute()
143 | }.use {
144 | return processMultiStatus(it, callback)
145 | }
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/DavCollection.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.XmlUtils.insertTag
14 | import at.bitfire.dav4jvm.exception.DavException
15 | import at.bitfire.dav4jvm.exception.HttpException
16 | import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV
17 | import at.bitfire.dav4jvm.property.webdav.SyncToken
18 | import okhttp3.HttpUrl
19 | import okhttp3.OkHttpClient
20 | import okhttp3.Request
21 | import okhttp3.RequestBody.Companion.toRequestBody
22 | import java.io.StringWriter
23 | import java.util.logging.Logger
24 |
25 | /**
26 | * Represents a WebDAV collection.
27 | */
28 | open class DavCollection @JvmOverloads constructor(
29 | httpClient: OkHttpClient,
30 | location: HttpUrl,
31 | logger: Logger = Logger.getLogger(DavCollection::class.java.name)
32 | ): DavResource(httpClient, location, logger) {
33 |
34 | companion object {
35 | val SYNC_COLLECTION = Property.Name(NS_WEBDAV, "sync-collection")
36 | val SYNC_LEVEL = Property.Name(NS_WEBDAV, "sync-level")
37 | val LIMIT = Property.Name(NS_WEBDAV, "limit")
38 | val NRESULTS = Property.Name(NS_WEBDAV, "nresults")
39 | }
40 |
41 | /**
42 | * Sends a REPORT sync-collection request.
43 | *
44 | * @param syncToken sync-token to be sent with the request
45 | * @param infiniteDepth sync-level to be sent with the request: false = "1", true = "infinite"
46 | * @param limit maximum number of results (may cause truncation)
47 | * @param properties WebDAV properties to be requested
48 | * @param callback called for every WebDAV response XML element in the result
49 | *
50 | * @return list of properties which have been received in the Multi-Status response, but
51 | * are not part of response XML elements (like `sync-token` which is returned as [SyncToken])
52 | *
53 | * @throws java.io.IOException on I/O error
54 | * @throws HttpException on HTTP error
55 | * @throws DavException on WebDAV error
56 | */
57 | fun reportChanges(syncToken: String?, infiniteDepth: Boolean, limit: Int?, vararg properties: Property.Name, callback: MultiResponseCallback): List {
58 | /*
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | */
68 | val serializer = XmlUtils.newSerializer()
69 | val writer = StringWriter()
70 | serializer.setOutput(writer)
71 | serializer.startDocument("UTF-8", null)
72 | serializer.setPrefix("", NS_WEBDAV)
73 | serializer.insertTag(SYNC_COLLECTION) {
74 | insertTag(SyncToken.NAME) {
75 | if (syncToken != null)
76 | text(syncToken)
77 | }
78 | insertTag(SYNC_LEVEL) {
79 | text(if (infiniteDepth) "infinite" else "1")
80 | }
81 | if (limit != null)
82 | insertTag(LIMIT) {
83 | insertTag(NRESULTS) {
84 | text(limit.toString())
85 | }
86 | }
87 | insertTag(PROP) {
88 | for (prop in properties)
89 | insertTag(prop)
90 | }
91 | }
92 | serializer.endDocument()
93 |
94 | followRedirects {
95 | httpClient.newCall(Request.Builder()
96 | .url(location)
97 | .method("REPORT", writer.toString().toRequestBody(MIME_XML))
98 | .header("Depth", "0")
99 | .build()).execute()
100 | }.use {
101 | return processMultiStatus(it, callback)
102 | }
103 | }
104 |
105 | }
--------------------------------------------------------------------------------
/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 at.bitfire.dav4jvm.property.webdav.NS_WEBDAV
14 | import org.xmlpull.v1.XmlPullParser
15 | import java.io.Serializable
16 |
17 | /**
18 | * Represents an XML precondition/postcondition error. Every error has a name, which is the XML element
19 | * name. Subclassed errors may have more specific information available.
20 | *
21 | * At the moment, there is no logic for subclassing errors.
22 | */
23 | class Error(
24 | val name: Property.Name
25 | ): Serializable {
26 |
27 | companion object {
28 |
29 | val NAME = Property.Name(NS_WEBDAV, "error")
30 |
31 | fun parseError(parser: XmlPullParser): List {
32 | val names = mutableSetOf()
33 |
34 | val depth = parser.depth
35 | var eventType = parser.eventType
36 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
37 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1)
38 | names += Property.Name(parser.namespace, parser.name)
39 | eventType = parser.next()
40 | }
41 |
42 | return names.map { Error(it) }
43 | }
44 |
45 |
46 | // some pre-defined errors
47 |
48 | val NEED_PRIVILEGES = Error(Property.Name(NS_WEBDAV, "need-privileges"))
49 | val VALID_SYNC_TOKEN = Error(Property.Name(NS_WEBDAV, "valid-sync-token"))
50 |
51 | }
52 |
53 | override fun equals(other: Any?) =
54 | (other is Error) && other.name == name
55 |
56 | override fun hashCode() = name.hashCode()
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/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 okhttp3.HttpUrl
15 | import okhttp3.Response
16 | import java.time.Instant
17 | import java.time.LocalDateTime
18 | import java.time.ZoneOffset
19 | import java.time.ZonedDateTime
20 | import java.time.format.DateTimeFormatter
21 | import java.time.format.DateTimeParseException
22 | import java.util.Locale
23 | import java.util.logging.Logger
24 |
25 | object HttpUtils {
26 |
27 | /**
28 | * Preferred HTTP date/time format, see RFC 7231 7.1.1.1 IMF-fixdate
29 | */
30 | private const val httpDateFormatStr = "EEE, dd MMM yyyy HH:mm:ss ZZZZ"
31 | private val httpDateFormat = DateTimeFormatter.ofPattern(httpDateFormatStr, Locale.US)
32 |
33 | private val logger
34 | get() = Logger.getLogger(javaClass.name)
35 |
36 |
37 | /**
38 | * Gets the resource name (the last segment of the path) from an URL.
39 | * Empty if the resource is the base directory.
40 | *
41 | * * `dir` for `https://example.com/dir/`
42 | * * `file` for `https://example.com/file`
43 | * * `` for `https://example.com` or `https://example.com/`
44 | *
45 | * @return resource name
46 | */
47 | fun fileName(url: HttpUrl): String {
48 | val pathSegments = url.pathSegments.dropLastWhile { it == "" }
49 | return pathSegments.lastOrNull() ?: ""
50 | }
51 |
52 | fun listHeader(response: Response, name: String): Array {
53 | val value = response.headers(name).joinToString(",")
54 | return value.split(',').filter { it.isNotEmpty() }.toTypedArray()
55 | }
56 |
57 |
58 | /**
59 | * Formats a date for use in HTTP headers using [httpDateFormat].
60 | *
61 | * @param date date to be formatted
62 | *
63 | * @return date in HTTP-date format
64 | */
65 | fun formatDate(date: Instant): String =
66 | ZonedDateTime.ofInstant(date, ZoneOffset.UTC).format(httpDateFormat)
67 |
68 | /**
69 | * Parses a HTTP-date according to RFC 7231 section 7.1.1.1.
70 | *
71 | * @param dateStr date-time formatted in one of the three accepted formats:
72 | *
73 | * - preferred format (`IMF-fixdate`)
74 | * - obsolete RFC 850 format
75 | * - ANSI C's `asctime()` format
76 | *
77 | * @return date-time, or null if date could not be parsed
78 | */
79 | fun parseDate(dateStr: String): Instant? {
80 | val zonedFormats = arrayOf(
81 | // preferred format
82 | httpDateFormat,
83 |
84 | // obsolete RFC 850 format
85 | DateTimeFormatter.ofPattern("EEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
86 | )
87 |
88 | // try the two formats with zone info
89 | for (format in zonedFormats)
90 | try {
91 | return ZonedDateTime.parse(dateStr, format).toInstant()
92 | } catch (ignored: DateTimeParseException) {
93 | }
94 |
95 | // try ANSI C's asctime() format
96 | try {
97 | val formatC = DateTimeFormatter.ofPattern("EEE MMM ppd HH:mm:ss yyyy", Locale.US)
98 | val local = LocalDateTime.parse(dateStr, formatC)
99 | return local.atZone(ZoneOffset.UTC).toInstant()
100 | } catch (ignored: DateTimeParseException) {
101 | }
102 |
103 | // no success in parsing
104 | logger.warning("Couldn't parse HTTP date: $dateStr, ignoring")
105 | return null
106 | }
107 |
108 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/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
12 |
13 | import at.bitfire.dav4jvm.Response.Companion.STATUS
14 | import at.bitfire.dav4jvm.XmlUtils.propertyName
15 | import at.bitfire.dav4jvm.property.webdav.NS_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 | @JvmField
36 | val NAME = Property.Name(NS_WEBDAV, "propstat")
37 |
38 | private val ASSUMING_OK = StatusLine(Protocol.HTTP_1_1, 200, "Assuming OK")
39 | private val INVALID_STATUS = StatusLine(Protocol.HTTP_1_1, 500, "Invalid status line")
40 |
41 | fun parse(parser: XmlPullParser): PropStat {
42 | val depth = parser.depth
43 |
44 | var status: StatusLine? = null
45 | val prop = LinkedList()
46 |
47 | var eventType = parser.eventType
48 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
49 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1)
50 | when (parser.propertyName()) {
51 | DavResource.PROP ->
52 | prop.addAll(Property.parse(parser))
53 | STATUS ->
54 | status = try {
55 | StatusLine.parse(parser.nextText())
56 | } catch (e: ProtocolException) {
57 | // invalid status line, treat as 500 Internal Server Error
58 | INVALID_STATUS
59 | }
60 | }
61 | eventType = parser.next()
62 | }
63 |
64 | return PropStat(prop, status ?: ASSUMING_OK)
65 | }
66 |
67 | }
68 |
69 |
70 | fun isSuccess() = status.code/100 == 2
71 |
72 | }
--------------------------------------------------------------------------------
/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 at.bitfire.dav4jvm.exception.InvalidPropertyException
14 | import org.xmlpull.v1.XmlPullParser
15 | import java.io.Serializable
16 | import java.util.LinkedList
17 | import java.util.logging.Level
18 | import java.util.logging.Logger
19 |
20 | /**
21 | * Represents a WebDAV property.
22 | *
23 | * Every [Property] must define a static field (use `@JvmStatic`) called `NAME` of type [Property.Name],
24 | * which will be accessed by reflection.
25 | *
26 | * Every [Property] should be a data class in order to be able to compare it against others, and convert to a useful
27 | * string for debugging.
28 | */
29 | interface Property {
30 |
31 | data class Name(
32 | val namespace: String,
33 | val name: String
34 | ): Serializable {
35 |
36 | override fun toString() = "$namespace:$name"
37 |
38 | }
39 |
40 | companion object {
41 |
42 | fun parse(parser: XmlPullParser): List {
43 | val logger = Logger.getLogger(Property::javaClass.name)
44 |
45 | //
46 | val depth = parser.depth
47 | val properties = LinkedList()
48 |
49 | var eventType = parser.eventType
50 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
51 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
52 | val depthBeforeParsing = parser.depth
53 | val name = Name(parser.namespace, parser.name)
54 |
55 | try {
56 | val property = PropertyRegistry.create(name, parser)
57 | assert(parser.depth == depthBeforeParsing)
58 |
59 | if (property != null) {
60 | properties.add(property)
61 | } else
62 | logger.fine("Ignoring unknown property $name")
63 | } catch (e: InvalidPropertyException) {
64 | logger.log(Level.WARNING, "Ignoring invalid property", e)
65 | }
66 | }
67 |
68 | eventType = parser.next()
69 | }
70 |
71 | return properties
72 | }
73 |
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/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/PropertyRegistry.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.caldav.CalendarColor
14 | import at.bitfire.dav4jvm.property.caldav.CalendarData
15 | import at.bitfire.dav4jvm.property.caldav.CalendarDescription
16 | import at.bitfire.dav4jvm.property.caldav.CalendarHomeSet
17 | import at.bitfire.dav4jvm.property.caldav.CalendarProxyReadFor
18 | import at.bitfire.dav4jvm.property.caldav.CalendarProxyWriteFor
19 | import at.bitfire.dav4jvm.property.caldav.CalendarTimezone
20 | import at.bitfire.dav4jvm.property.caldav.CalendarTimezoneId
21 | import at.bitfire.dav4jvm.property.caldav.CalendarUserAddressSet
22 | import at.bitfire.dav4jvm.property.caldav.GetCTag
23 | import at.bitfire.dav4jvm.property.caldav.ScheduleTag
24 | import at.bitfire.dav4jvm.property.caldav.Source
25 | import at.bitfire.dav4jvm.property.caldav.SupportedCalendarComponentSet
26 | import at.bitfire.dav4jvm.property.caldav.SupportedCalendarData
27 | import at.bitfire.dav4jvm.property.carddav.AddressData
28 | import at.bitfire.dav4jvm.property.carddav.AddressbookDescription
29 | import at.bitfire.dav4jvm.property.carddav.AddressbookHomeSet
30 | import at.bitfire.dav4jvm.property.carddav.SupportedAddressData
31 | import at.bitfire.dav4jvm.property.push.PushMessage
32 | import at.bitfire.dav4jvm.property.push.PushRegister
33 | import at.bitfire.dav4jvm.property.push.PushTransports
34 | import at.bitfire.dav4jvm.property.push.Subscription
35 | import at.bitfire.dav4jvm.property.push.Topic
36 | import at.bitfire.dav4jvm.property.push.WebPushSubscription
37 | import at.bitfire.dav4jvm.property.webdav.AddMember
38 | import at.bitfire.dav4jvm.property.webdav.CreationDate
39 | import at.bitfire.dav4jvm.property.webdav.CurrentUserPrincipal
40 | import at.bitfire.dav4jvm.property.webdav.CurrentUserPrivilegeSet
41 | import at.bitfire.dav4jvm.property.webdav.DisplayName
42 | import at.bitfire.dav4jvm.property.webdav.GetContentLength
43 | import at.bitfire.dav4jvm.property.webdav.GetContentType
44 | import at.bitfire.dav4jvm.property.webdav.GetETag
45 | import at.bitfire.dav4jvm.property.webdav.GetLastModified
46 | import at.bitfire.dav4jvm.property.webdav.GroupMembership
47 | import at.bitfire.dav4jvm.property.webdav.Owner
48 | import at.bitfire.dav4jvm.property.webdav.QuotaAvailableBytes
49 | import at.bitfire.dav4jvm.property.webdav.QuotaUsedBytes
50 | import at.bitfire.dav4jvm.property.webdav.ResourceType
51 | import at.bitfire.dav4jvm.property.webdav.SupportedReportSet
52 | import at.bitfire.dav4jvm.property.webdav.SyncToken
53 | import org.xmlpull.v1.XmlPullParser
54 | import org.xmlpull.v1.XmlPullParserException
55 | import java.util.logging.Level
56 | import java.util.logging.Logger
57 |
58 | object PropertyRegistry {
59 |
60 | private val factories = mutableMapOf()
61 | private val logger
62 | get() = Logger.getLogger(javaClass.name)
63 |
64 |
65 | init {
66 | logger.info("Registering DAV property factories")
67 | registerDefaultFactories()
68 | }
69 |
70 | private fun registerDefaultFactories() {
71 | register(listOf(
72 | AddMember.Factory,
73 | AddressbookDescription.Factory,
74 | AddressbookHomeSet.Factory,
75 | AddressData.Factory,
76 | CalendarColor.Factory,
77 | CalendarData.Factory,
78 | CalendarDescription.Factory,
79 | CalendarHomeSet.Factory,
80 | CalendarProxyReadFor.Factory,
81 | CalendarProxyWriteFor.Factory,
82 | CalendarTimezone.Factory,
83 | CalendarTimezoneId.Factory,
84 | CalendarUserAddressSet.Factory,
85 | CreationDate.Factory,
86 | CurrentUserPrincipal.Factory,
87 | CurrentUserPrivilegeSet.Factory,
88 | DisplayName.Factory,
89 | GetContentLength.Factory,
90 | GetContentType.Factory,
91 | GetCTag.Factory,
92 | GetETag.Factory,
93 | GetLastModified.Factory,
94 | GroupMembership.Factory,
95 | at.bitfire.dav4jvm.property.caldav.MaxResourceSize.Factory,
96 | at.bitfire.dav4jvm.property.carddav.MaxResourceSize.Factory,
97 | Owner.Factory,
98 | PushMessage.Factory,
99 | PushRegister.Factory,
100 | PushTransports.Factory,
101 | QuotaAvailableBytes.Factory,
102 | QuotaUsedBytes.Factory,
103 | ResourceType.Factory,
104 | ScheduleTag.Factory,
105 | Source.Factory,
106 | Subscription.Factory,
107 | SupportedAddressData.Factory,
108 | SupportedCalendarComponentSet.Factory,
109 | SupportedCalendarData.Factory,
110 | SupportedReportSet.Factory,
111 | SyncToken.Factory,
112 | Topic.Factory,
113 | WebPushSubscription.Factory
114 | ))
115 | }
116 |
117 |
118 | /**
119 | * Registers a property factory, so that objects for all WebDAV properties which are handled
120 | * by this factory can be created.
121 | *
122 | * @param factory property factory to be registered
123 | */
124 | fun register(factory: PropertyFactory) {
125 | logger.fine("Registering ${factory::class.java.name} for ${factory.getName()}")
126 | factories[factory.getName()] = factory
127 | }
128 |
129 | /**
130 | * Registers some property factories, so that objects for all WebDAV properties which are handled
131 | * by these factories can be created.
132 |
133 | * @param factories property factories to be registered
134 | */
135 | fun register(factories: Iterable) {
136 | factories.forEach {
137 | register(it)
138 | }
139 | }
140 |
141 | fun create(name: Property.Name, parser: XmlPullParser) =
142 | try {
143 | factories[name]?.create(parser)
144 | } catch (e: XmlPullParserException) {
145 | logger.log(Level.WARNING, "Couldn't parse $name", e)
146 | null
147 | }
148 |
149 | }
--------------------------------------------------------------------------------
/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/UrlUtils.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.UrlUtils.omitTrailingSlash
14 | import at.bitfire.dav4jvm.UrlUtils.withTrailingSlash
15 | import okhttp3.HttpUrl
16 |
17 | object UrlUtils {
18 |
19 | @Deprecated("Use equalsForWebDAV instead", ReplaceWith("url1.equalsForWebDAV(url2)"))
20 | fun equals(url1: HttpUrl, url2: HttpUrl) = url1.equalsForWebDAV(url2)
21 |
22 | /**
23 | * Gets the first-level domain name (without subdomains) from a host name.
24 | * Also removes trailing dots.
25 | *
26 | * @param host name (e.g. `www.example.com.`)
27 | *
28 | * @return domain name (e.g. `example.com`)
29 | */
30 | fun hostToDomain(host: String?): String? {
31 | if (host == null)
32 | return null
33 |
34 | // remove optional dot at end
35 | val withoutTrailingDot = host.removeSuffix(".")
36 |
37 | // split into labels
38 | val labels = withoutTrailingDot.split('.')
39 | return if (labels.size >= 2) {
40 | labels[labels.size - 2] + "." + labels[labels.size - 1]
41 | } else
42 | withoutTrailingDot
43 | }
44 |
45 | /**
46 | * Ensures that a given URL doesn't have a trailing slash after member names.
47 | * If the path is the root path (`/`), the slash is preserved.
48 | *
49 | * @param url URL to process (e.g. 'http://host/test1/')
50 | *
51 | * @return URL without trailing slash (except when the path is the root path), e.g. `http://host/test1`
52 | */
53 | fun omitTrailingSlash(url: HttpUrl): HttpUrl {
54 | val idxLast = url.pathSize - 1
55 | val hasTrailingSlash = url.pathSegments[idxLast] == ""
56 |
57 | return if (hasTrailingSlash)
58 | url.newBuilder().removePathSegment(idxLast).build()
59 | else
60 | url
61 | }
62 |
63 | /**
64 | * Ensures that a given URL has a trailing slash after member names.
65 | *
66 | * @param url URL to process (e.g. 'http://host/test1')
67 | *
68 | * @return URL with trailing slash, e.g. `http://host/test1/`
69 | */
70 | fun withTrailingSlash(url: HttpUrl): HttpUrl {
71 | val idxLast = url.pathSize - 1
72 | val hasTrailingSlash = url.pathSegments[idxLast] == ""
73 |
74 | return if (hasTrailingSlash)
75 | url
76 | else
77 | url.newBuilder().addPathSegment("").build()
78 | }
79 |
80 | }
81 |
82 | /**
83 | * Compares two [HttpUrl]s in WebDAV context. If two URLs are considered *equal*, both
84 | * represent the same WebDAV resource.
85 | *
86 | * The fragment of an URL is ignored, e.g. `http://host:80/folder1` and `http://HOST/folder1#somefragment`
87 | * are considered to be equal.
88 | *
89 | * [HttpUrl] is less strict than [java.net.URI] and allows for instance (not encoded) square brackets in the path.
90 | * So this method tries to normalize the URI by converting it to a [java.net.URI] (encodes for instance square brackets)
91 | * and then comparing the scheme and scheme-specific part (without fragment).
92 | *
93 | * Attention: **This method does not deal with trailing slashes**, so if you want to compare collection URLs,
94 | * make sure they both (don't) have a trailing slash before calling this method, for instance
95 | * with [omitTrailingSlash] or [withTrailingSlash].
96 | *
97 | * @param other the URL to compare the current object with
98 | *
99 | * @return whether the URLs are considered to represent the same WebDAV resource
100 | */
101 | fun HttpUrl.equalsForWebDAV(other: HttpUrl): Boolean {
102 | // if okhttp thinks the two URLs are equal, they're in any case
103 | // (and it's a simple String comparison)
104 | if (this == other)
105 | return true
106 |
107 | // convert to java.net.URI (also corrects some mistakes and escapes for instance square brackets)
108 | val uri1 = toUri()
109 | val uri2 = other.toUri()
110 |
111 | // if the URIs are the same (ignoring scheme case and fragments), they're equal for us
112 | return uri1.scheme.equals(uri2.scheme, true) && uri1.schemeSpecificPart == uri2.schemeSpecificPart
113 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/XmlReader.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.XmlUtils.propertyName
14 | import at.bitfire.dav4jvm.property.caldav.SupportedCalendarData.Companion.CONTENT_TYPE
15 | import at.bitfire.dav4jvm.property.caldav.SupportedCalendarData.Companion.VERSION
16 | import okhttp3.MediaType
17 | import okhttp3.MediaType.Companion.toMediaTypeOrNull
18 | import org.xmlpull.v1.XmlPullParser
19 | import org.xmlpull.v1.XmlPullParserException
20 | import java.io.IOException
21 | import java.time.Instant
22 | import java.util.logging.Level
23 | import java.util.logging.Logger
24 |
25 | /**
26 | * Reads/processes XML tags which are used for WebDAV.
27 | *
28 | * @param parser The parser to read from.
29 | */
30 | class XmlReader(
31 | private val parser: XmlPullParser
32 | ) {
33 |
34 | // base processing
35 |
36 | /**
37 | * Reads child elements of the current element. Whenever a direct child with the given name is found,
38 | * [processor] is called for each one.
39 | */
40 | @Throws(IOException::class, XmlPullParserException::class)
41 | fun processTag(name: Property.Name, processor: XmlReader.() -> Unit) {
42 | val depth = parser.depth
43 | var eventType = parser.eventType
44 | while (!((eventType == XmlPullParser.END_TAG || eventType == XmlPullParser.END_DOCUMENT) && parser.depth == depth)) {
45 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1 && parser.propertyName() == name)
46 | processor()
47 | eventType = parser.next()
48 | }
49 | }
50 |
51 | /**
52 | * Reads the inline text of the current element.
53 | *
54 | * For instance, if the parser is at the beginning of this XML:
55 | *
56 | * ```
57 | * text
58 | * ```
59 | *
60 | * this function will return "text".
61 | *
62 | * @return text or `null` if no text is found
63 | */
64 | @Throws(IOException::class, XmlPullParserException::class)
65 | fun readText(): String? {
66 | var text: String? = null
67 |
68 | val depth = parser.depth
69 | var eventType = parser.eventType
70 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
71 | if (eventType == XmlPullParser.TEXT && parser.depth == depth)
72 | text = parser.text
73 | eventType = parser.next()
74 | }
75 |
76 | return text
77 | }
78 |
79 | /**
80 | * Reads child elements of the current element. When a direct child with the given name is found,
81 | * its text is returned.
82 | *
83 | * @param name The name of the tag to read.
84 | * @return The text inside the tag, or `null` if the tag is not found.
85 | */
86 | @Throws(IOException::class, XmlPullParserException::class)
87 | fun readTextProperty(name: Property.Name): String? {
88 | var result: String? = null
89 |
90 | val depth = parser.depth
91 | var eventType = parser.eventType
92 | while (!((eventType == XmlPullParser.END_TAG || eventType == XmlPullParser.END_DOCUMENT) && parser.depth == depth)) {
93 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1 && parser.propertyName() == name)
94 | result = parser.nextText()
95 | eventType = parser.next()
96 | }
97 | return result
98 | }
99 |
100 | /**
101 | * Reads child elements of the current element. Whenever a direct child with the given name is
102 | * found, its text is added to the given list.
103 | *
104 | * @param name The name of the tag to read.
105 | * @param list The list to add the text to.
106 | */
107 | @Throws(IOException::class, XmlPullParserException::class)
108 | fun readTextPropertyList(name: Property.Name, list: MutableCollection) {
109 | val depth = parser.depth
110 | var eventType = parser.eventType
111 | while (!((eventType == XmlPullParser.END_TAG || eventType == XmlPullParser.END_DOCUMENT) && parser.depth == depth)) {
112 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1 && parser.propertyName() == name)
113 | list.add(parser.nextText())
114 | eventType = parser.next()
115 | }
116 | }
117 |
118 |
119 | // extended processing (uses readText etc.)
120 |
121 | /**
122 | * Uses [readText] to read the tag's value (which is expected to be in _HTTP-date_ format), and converts
123 | * it into an [Instant] using [HttpUtils.parseDate].
124 | *
125 | * If the conversion fails for any reason, null is returned, and a message is displayed in log.
126 | */
127 | fun readHttpDate(): Instant? {
128 | return readText()?.let { rawDate ->
129 | val date = HttpUtils.parseDate(rawDate)
130 | if (date != null)
131 | date
132 | else {
133 | val logger = Logger.getLogger(javaClass.name)
134 | logger.warning("Couldn't parse HTTP-date")
135 | null
136 | }
137 | }
138 | }
139 |
140 | /**
141 | * Uses [readText] to read the tag's value (which is expected to be a number), and converts it
142 | * into a [Long] with [String.toLong].
143 | *
144 | * If the conversion fails for any reason, null is returned, and a message is displayed in log.
145 | */
146 | fun readLong(): Long? {
147 | return readText()?.let { valueStr ->
148 | try {
149 | valueStr.toLong()
150 | } catch(e: NumberFormatException) {
151 | val logger = Logger.getLogger(javaClass.name)
152 | logger.log(Level.WARNING, "Couldn't parse as Long: $valueStr", e)
153 | null
154 | }
155 | }
156 | }
157 |
158 | /**
159 | * Processes all the tags named [tagName], and sends every tag that has the [CONTENT_TYPE]
160 | * attribute with [onNewType].
161 | *
162 | * @param tagName The name of the tag that contains the [CONTENT_TYPE] attribute value.
163 | * @param onNewType Called every time a new [MediaType] is found.
164 | */
165 | fun readContentTypes(tagName: Property.Name, onNewType: (MediaType) -> Unit) {
166 | try {
167 | processTag(tagName) {
168 | parser.getAttributeValue(null, CONTENT_TYPE)?.let { contentType ->
169 | var type = contentType
170 | parser.getAttributeValue(null, VERSION)?.let { version -> type += "; version=$version" }
171 | type.toMediaTypeOrNull()?.let(onNewType)
172 | }
173 | }
174 | } catch(e: XmlPullParserException) {
175 | val logger = Logger.getLogger(javaClass.name)
176 | logger.log(Level.SEVERE, "Couldn't parse content types", e)
177 | }
178 | }
179 |
180 | }
--------------------------------------------------------------------------------
/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 at.bitfire.dav4jvm.XmlUtils.FEATURE_RELAXED
14 | import at.bitfire.dav4jvm.exception.DavException
15 | import org.xmlpull.v1.XmlPullParser
16 | import org.xmlpull.v1.XmlPullParserException
17 | import org.xmlpull.v1.XmlPullParserFactory
18 | import org.xmlpull.v1.XmlSerializer
19 |
20 | object XmlUtils {
21 |
22 | /**
23 | * Requests the parser to be as lenient as possible when parsing invalid XML.
24 | *
25 | * See [https://www.xmlpull.org/](xmlpull.org) and specific implementations, for instance
26 | * [Android XML](https://developer.android.com/reference/android/util/Xml#FEATURE_RELAXED)
27 | */
28 | private const val FEATURE_RELAXED = "http://xmlpull.org/v1/doc/features.html#relaxed"
29 |
30 | /** [XmlPullParserFactory] that is namespace-aware and does relaxed parsing */
31 | private val relaxedFactory =
32 | XmlPullParserFactory.newInstance().apply {
33 | isNamespaceAware = true
34 | setFeature(FEATURE_RELAXED, true)
35 | }
36 |
37 | /** [XmlPullParserFactory] that is namespace-aware */
38 | private val standardFactory: XmlPullParserFactory =
39 | XmlPullParserFactory.newInstance().apply {
40 | isNamespaceAware = true
41 | }
42 |
43 | /**
44 | * Creates a new [XmlPullParser].
45 | *
46 | * First tries to create a namespace-aware parser that supports [FEATURE_RELAXED]. If that
47 | * fails, it falls back to a namespace-aware parser without relaxed parsing.
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 | ?: throw DavException("Couldn't create XML parser")
58 |
59 | fun newSerializer(): XmlSerializer = standardFactory.newSerializer()
60 | ?: throw DavException("Couldn't create XML serializer")
61 |
62 |
63 | fun XmlSerializer.insertTag(name: Property.Name, contentGenerator: XmlSerializer.() -> Unit = {}) {
64 | startTag(name.namespace, name.name)
65 | contentGenerator(this)
66 | endTag(name.namespace, name.name)
67 | }
68 |
69 | fun XmlPullParser.propertyName(): Property.Name {
70 | val propNs = namespace
71 | val propName = name
72 | if (propNs == null || propName == null)
73 | throw IllegalStateException("Current event must be START_TAG or END_TAG")
74 | return Property.Name(propNs, propName)
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/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.exception
12 |
13 | import okhttp3.Response
14 | import java.net.HttpURLConnection
15 |
16 | class ConflictException: HttpException {
17 |
18 | constructor(response: Response): super(response)
19 | constructor(message: String?): super(HttpURLConnection.HTTP_CONFLICT, message)
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/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.exception
12 |
13 | import at.bitfire.dav4jvm.Error
14 | import at.bitfire.dav4jvm.XmlUtils
15 | import at.bitfire.dav4jvm.XmlUtils.propertyName
16 | import at.bitfire.dav4jvm.exception.DavException.Companion.MAX_EXCERPT_SIZE
17 | import okhttp3.MediaType
18 | import okhttp3.Response
19 | import okio.Buffer
20 | import org.xmlpull.v1.XmlPullParser
21 | import org.xmlpull.v1.XmlPullParserException
22 | import java.io.ByteArrayOutputStream
23 | import java.io.IOException
24 | import java.io.Serializable
25 | import java.lang.Long.min
26 | import java.util.logging.Level
27 | import java.util.logging.Logger
28 |
29 | /**
30 | * Signals that an error occurred during a WebDAV-related operation.
31 | *
32 | * This could be a logical error like when a required ETag has not been
33 | * received, but also an explicit HTTP error.
34 | */
35 | open class DavException @JvmOverloads constructor(
36 | message: String,
37 | ex: Throwable? = null,
38 |
39 | /**
40 | * An associated HTTP [Response]. Will be closed after evaluation.
41 | */
42 | httpResponse: Response? = null
43 | ): Exception(message, ex), Serializable {
44 |
45 | companion object {
46 |
47 | const val MAX_EXCERPT_SIZE = 10*1024 // don't dump more than 20 kB
48 |
49 | fun isPlainText(type: MediaType) =
50 | type.type == "text" ||
51 | (type.type == "application" && type.subtype in arrayOf("html", "xml"))
52 |
53 | }
54 |
55 | private val logger
56 | get() = Logger.getLogger(javaClass.name)
57 |
58 | var request: String? = null
59 |
60 | /**
61 | * Body excerpt of [request] (up to [MAX_EXCERPT_SIZE] characters). Only available
62 | * if the HTTP request body was textual content and could be read again.
63 | */
64 | var requestBody: String? = null
65 | private set
66 |
67 | val response: String?
68 |
69 | /**
70 | * Body excerpt of [response] (up to [MAX_EXCERPT_SIZE] characters). Only available
71 | * if the HTTP response body was textual content.
72 | */
73 | var responseBody: String? = null
74 | private set
75 |
76 | /**
77 | * Precondition/postcondition XML elements which have been found in the XML response.
78 | */
79 | var errors: List = listOf()
80 | private set
81 |
82 |
83 | init {
84 | if (httpResponse != null) {
85 | response = httpResponse.toString()
86 |
87 | try {
88 | request = httpResponse.request.toString()
89 |
90 | httpResponse.request.body?.let { body ->
91 | body.contentType()?.let { type ->
92 | if (isPlainText(type)) {
93 | val buffer = Buffer()
94 | body.writeTo(buffer)
95 |
96 | val baos = ByteArrayOutputStream()
97 | buffer.writeTo(baos, min(buffer.size, MAX_EXCERPT_SIZE.toLong()))
98 |
99 | requestBody = baos.toString(type.charset(Charsets.UTF_8)!!.name())
100 | }
101 | }
102 | }
103 | } catch (e: Exception) {
104 | logger.log(Level.WARNING, "Couldn't read HTTP request", e)
105 | requestBody = "Couldn't read HTTP request: ${e.message}"
106 | }
107 |
108 | try {
109 | // save response body excerpt
110 | if (httpResponse.body?.source() != null) {
111 | // response body has a source
112 |
113 | httpResponse.peekBody(MAX_EXCERPT_SIZE.toLong()).let { body ->
114 | body.contentType()?.let { mimeType ->
115 | if (isPlainText(mimeType))
116 | responseBody = body.string()
117 | }
118 | }
119 |
120 | httpResponse.body?.use { body ->
121 | body.contentType()?.let {
122 | if (it.type in arrayOf("application", "text") && it.subtype == "xml") {
123 | // look for precondition/postcondition XML elements
124 | try {
125 | val parser = XmlUtils.newPullParser()
126 | parser.setInput(body.charStream())
127 |
128 | var eventType = parser.eventType
129 | while (eventType != XmlPullParser.END_DOCUMENT) {
130 | if (eventType == XmlPullParser.START_TAG && parser.depth == 1)
131 | if (parser.propertyName() == Error.NAME)
132 | errors = Error.parseError(parser)
133 | eventType = parser.next()
134 | }
135 | } catch (e: XmlPullParserException) {
136 | logger.log(Level.WARNING, "Couldn't parse XML response", e)
137 | }
138 | }
139 | }
140 | }
141 | }
142 | } catch (e: IOException) {
143 | logger.log(Level.WARNING, "Couldn't read HTTP response", e)
144 | responseBody = "Couldn't read HTTP response: ${e.message}"
145 | } finally {
146 | httpResponse.body?.close()
147 | }
148 | } else
149 | response = null
150 | }
151 |
152 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/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.exception
12 |
13 | import okhttp3.Response
14 | import java.net.HttpURLConnection
15 |
16 | class ForbiddenException: HttpException {
17 |
18 | constructor(response: Response): super(response)
19 | constructor(message: String?): super(HttpURLConnection.HTTP_FORBIDDEN, message)
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/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.exception
12 |
13 | import okhttp3.Response
14 | import java.net.HttpURLConnection
15 |
16 | class GoneException: HttpException {
17 |
18 | constructor(response: Response): super(response)
19 | constructor(message: String?): super(HttpURLConnection.HTTP_GONE, message)
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/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.exception
12 |
13 | import okhttp3.Response
14 |
15 | /**
16 | * Signals that a HTTP error was sent by the server.
17 | */
18 | open class HttpException: DavException {
19 |
20 | var code: Int
21 |
22 | constructor(response: Response): super(
23 | "HTTP ${response.code} ${response.message}",
24 | httpResponse = response
25 | ) {
26 | code = response.code
27 | }
28 |
29 | constructor(code: Int, message: String?): super("HTTP $code $message") {
30 | this.code = code
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/exception/InvalidPropertyException.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.exception
12 |
13 | /**
14 | * Represents an invalid XML (WebDAV) property. This is for instance thrown
15 | * when parsing something like `...`
16 | * because a text value would be expected.
17 | */
18 | class InvalidPropertyException(message: String): Exception(message)
19 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/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.exception
12 |
13 | import okhttp3.Response
14 | import java.net.HttpURLConnection
15 |
16 | class NotFoundException: HttpException {
17 |
18 | constructor(response: Response): super(response)
19 | constructor(message: String?): super(HttpURLConnection.HTTP_NOT_FOUND, message)
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/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.exception
12 |
13 | import okhttp3.Response
14 | import java.net.HttpURLConnection
15 |
16 | class PreconditionFailedException: HttpException {
17 |
18 | constructor(response: Response): super(response)
19 | constructor(message: String?): super(HttpURLConnection.HTTP_PRECON_FAILED, message)
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/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.exception
12 |
13 | import at.bitfire.dav4jvm.HttpUtils
14 | import at.bitfire.dav4jvm.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_DEFAULT
15 | import at.bitfire.dav4jvm.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_MAX
16 | import at.bitfire.dav4jvm.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_MIN
17 | import okhttp3.Response
18 | import java.net.HttpURLConnection
19 | import java.time.Instant
20 | import java.util.logging.Level
21 | import java.util.logging.Logger
22 |
23 | class ServiceUnavailableException : HttpException {
24 |
25 | private val logger
26 | get() = Logger.getLogger(javaClass.name)
27 |
28 | val retryAfter: Instant?
29 |
30 | constructor(message: String?) : super(HttpURLConnection.HTTP_UNAVAILABLE, message) {
31 | retryAfter = null
32 | }
33 |
34 | constructor(response: Response) : super(response) {
35 | // Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds )
36 | // HTTP-date = rfc1123-date | rfc850-date | asctime-date
37 |
38 | var retryAfterValue: Instant? = null
39 | response.header("Retry-After")?.let { after ->
40 | retryAfterValue = HttpUtils.parseDate(after) ?:
41 | // not a HTTP-date, must be delta-seconds
42 | try {
43 | val seconds = after.toLong()
44 | Instant.now().plusSeconds(seconds)
45 | } catch (e: NumberFormatException) {
46 | logger.log(Level.WARNING, "Received Retry-After which was not a HTTP-date nor delta-seconds: $after", e)
47 | null
48 | }
49 | }
50 |
51 | retryAfter = retryAfterValue
52 | }
53 |
54 |
55 | /**
56 | * Returns appropriate sync retry delay in seconds, considering the servers suggestion
57 | * in [retryAfter] ([DELAY_UNTIL_DEFAULT] if no server suggestion).
58 | *
59 | * Takes current time into account to calculate intervals. Interval
60 | * will be restricted to values between [DELAY_UNTIL_MIN] and [DELAY_UNTIL_MAX].
61 | *
62 | * @param start timestamp to calculate the delay from (default: now)
63 | *
64 | * @return until when to wait before sync can be retried
65 | */
66 | fun getDelayUntil(start: Instant = Instant.now()): Instant {
67 | if (retryAfter == null)
68 | return start.plusSeconds(DELAY_UNTIL_DEFAULT)
69 |
70 | // take server suggestion, but restrict to plausible min/max values
71 | return retryAfter.coerceIn(
72 | minimumValue = start.plusSeconds(DELAY_UNTIL_MIN),
73 | maximumValue = start.plusSeconds(DELAY_UNTIL_MAX)
74 | )
75 | }
76 |
77 |
78 | companion object {
79 |
80 | // default values for getDelayUntil
81 | const val DELAY_UNTIL_DEFAULT = 15 * 60L // 15 min
82 | const val DELAY_UNTIL_MIN = 1 * 60L // 1 min
83 | const val DELAY_UNTIL_MAX = 2 * 60 * 60L // 2 hours
84 |
85 | }
86 |
87 | }
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/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.exception
12 |
13 | import okhttp3.Response
14 | import java.net.HttpURLConnection
15 |
16 | class UnauthorizedException: HttpException {
17 |
18 | constructor(response: Response): super(response)
19 | constructor(message: String?): super(HttpURLConnection.HTTP_UNAUTHORIZED, message)
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/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 | @JvmField
27 | val NAME = Property.Name(NS_APPLE_ICAL, "calendar-color")
28 |
29 | private val PATTERN = Pattern.compile("#?(\\p{XDigit}{6})(\\p{XDigit}{2})?")!!
30 |
31 | /**
32 | * Converts a WebDAV color from one of these formats:
33 | * #RRGGBB (alpha = 0xFF)
34 | * RRGGBB (alpha = 0xFF)
35 | * #RRGGBBAA
36 | * RRGGBBAA
37 | * to an [Int] with alpha.
38 | */
39 | @Throws(IllegalArgumentException::class)
40 | fun parseARGBColor(davColor: String): Int {
41 | val m = PATTERN.matcher(davColor)
42 | if (m.find()) {
43 | val color_rgb = Integer.parseInt(m.group(1), 16)
44 | val color_alpha = m.group(2)?.let { Integer.parseInt(m.group(2), 16) and 0xFF } ?: 0xFF
45 | return (color_alpha shl 24) or color_rgb
46 | } else
47 | throw IllegalArgumentException("Couldn't parse color value: $davColor")
48 | }
49 | }
50 |
51 |
52 | object Factory: PropertyFactory {
53 |
54 | override fun getName() = NAME
55 |
56 | override fun create(parser: XmlPullParser): CalendarColor {
57 | XmlReader(parser).readText()?.let {
58 | try {
59 | return CalendarColor(parseARGBColor(it))
60 | } catch (e: IllegalArgumentException) {
61 | val logger = Logger.getLogger(javaClass.name)
62 | logger.log(Level.WARNING, "Couldn't parse color, ignoring", e)
63 | }
64 | }
65 | return CalendarColor(null)
66 | }
67 |
68 | }
69 |
70 | }
--------------------------------------------------------------------------------
/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 | @JvmField
24 | val NAME = Property.Name(NS_CALDAV, "calendar-data")
25 |
26 | // attributes
27 | const val CONTENT_TYPE = "content-type"
28 | const val VERSION = "version"
29 | }
30 |
31 |
32 | object Factory: PropertyFactory {
33 |
34 | override fun getName() = NAME
35 |
36 | override fun create(parser: XmlPullParser) =
37 | //
38 | CalendarData(XmlReader(parser).readText())
39 |
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/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 | companion object {
23 | @JvmField
24 | val NAME = Property.Name(NS_CALDAV, "calendar-description")
25 | }
26 |
27 |
28 | object Factory: PropertyFactory {
29 |
30 | override fun getName() = NAME
31 |
32 | override fun create(parser: XmlPullParser) =
33 | //
34 | CalendarDescription(XmlReader(parser).readText())
35 |
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/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
14 | import at.bitfire.dav4jvm.property.webdav.HrefListProperty
15 | import org.xmlpull.v1.XmlPullParser
16 |
17 | data class CalendarHomeSet(
18 | override val hrefs: List = emptyList()
19 | ): HrefListProperty(hrefs) {
20 |
21 | companion object {
22 |
23 | @JvmField
24 | val NAME = Property.Name(NS_CALDAV, "calendar-home-set")
25 |
26 | }
27 |
28 |
29 | object Factory: HrefListProperty.Factory() {
30 |
31 | override fun getName() = NAME
32 |
33 | override fun create(parser: XmlPullParser) = create(parser, ::CalendarHomeSet)
34 |
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/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
14 | import at.bitfire.dav4jvm.property.webdav.HrefListProperty
15 | import org.xmlpull.v1.XmlPullParser
16 |
17 | data class CalendarProxyReadFor(
18 | override val hrefs: List = emptyList()
19 | ): HrefListProperty(hrefs) {
20 |
21 | companion object {
22 | @JvmField
23 | val NAME = Property.Name(NS_CALENDARSERVER, "calendar-proxy-read-for")
24 | }
25 |
26 |
27 | object Factory: HrefListProperty.Factory() {
28 |
29 | override fun getName() = NAME
30 |
31 | override fun create(parser: XmlPullParser) = create(parser, ::CalendarProxyReadFor)
32 |
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/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
14 | import at.bitfire.dav4jvm.property.webdav.HrefListProperty
15 | import org.xmlpull.v1.XmlPullParser
16 |
17 | data class CalendarProxyWriteFor(
18 | override val hrefs: List = emptyList()
19 | ): HrefListProperty(hrefs) {
20 |
21 | companion object {
22 | @JvmField
23 | val NAME = Property.Name(NS_CALENDARSERVER, "calendar-proxy-write-for")
24 | }
25 |
26 |
27 | object Factory: HrefListProperty.Factory() {
28 |
29 | override fun getName() = NAME
30 |
31 | override fun create(parser: XmlPullParser) = create(parser, ::CalendarProxyWriteFor)
32 |
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/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 | companion object {
23 | @JvmField
24 | val NAME = Property.Name(NS_CALDAV, "calendar-timezone")
25 | }
26 |
27 |
28 | object Factory: PropertyFactory {
29 |
30 | override fun getName() = NAME
31 |
32 | override fun create(parser: XmlPullParser) =
33 | //
34 | CalendarTimezone(XmlReader(parser).readText())
35 |
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/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 | companion object {
23 | @JvmField
24 | val NAME = Property.Name(NS_CALDAV, "calendar-timezone-id")
25 | }
26 |
27 |
28 | object Factory: PropertyFactory {
29 |
30 | override fun getName() = NAME
31 |
32 | override fun create(parser: XmlPullParser) =
33 | //
34 | CalendarTimezoneId(XmlReader(parser).readText())
35 |
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/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
14 | import at.bitfire.dav4jvm.property.webdav.HrefListProperty
15 | import org.xmlpull.v1.XmlPullParser
16 |
17 | data class CalendarUserAddressSet(
18 | override val hrefs: List = emptyList()
19 | ): HrefListProperty(hrefs) {
20 |
21 | companion object {
22 | @JvmField
23 | val NAME = Property.Name(NS_CALDAV, "calendar-user-address-set")
24 | }
25 |
26 |
27 | object Factory: HrefListProperty.Factory() {
28 |
29 | override fun getName() = NAME
30 |
31 | override fun create(parser: XmlPullParser) = create(parser, ::CalendarUserAddressSet)
32 |
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/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 | companion object {
23 | @JvmField
24 | val NAME = Property.Name(NS_CALENDARSERVER, "getctag")
25 | }
26 |
27 |
28 | object Factory: PropertyFactory {
29 |
30 | override fun getName() = NAME
31 |
32 | override fun create(parser: XmlPullParser) = GetCTag(XmlReader(parser).readText())
33 |
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/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 | companion object {
22 | @JvmField
23 | val NAME = Property.Name(NS_CALDAV, "max-resource-size")
24 | }
25 |
26 | object Factory: PropertyFactory {
27 | override fun getName() = NAME
28 |
29 | override fun create(parser: XmlPullParser) =
30 | MaxResourceSize(XmlReader(parser).readLong())
31 | }
32 | }
--------------------------------------------------------------------------------
/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 okhttp3.Response
18 | import org.xmlpull.v1.XmlPullParser
19 |
20 | data class ScheduleTag(
21 | val rawScheduleTag: String?
22 | ): Property {
23 |
24 | companion object {
25 |
26 | @JvmField
27 | val NAME = Property.Name(NS_CALDAV, "schedule-tag")
28 |
29 | fun fromResponse(response: Response) =
30 | response.header("Schedule-Tag")?.let { ScheduleTag(it) }
31 |
32 | }
33 |
34 | /* Value: opaque-tag
35 | opaque-tag = quoted-string
36 | */
37 | val scheduleTag: String? = rawScheduleTag?.let { QuotedStringUtils.decodeQuotedString(it) }
38 |
39 | override fun toString() = scheduleTag ?: "(null)"
40 |
41 |
42 | object Factory: PropertyFactory {
43 |
44 | override fun getName() = NAME
45 |
46 | override fun create(parser: XmlPullParser) = ScheduleTag(XmlReader(parser).readText())
47 |
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/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
14 | import at.bitfire.dav4jvm.property.webdav.HrefListProperty
15 | import org.xmlpull.v1.XmlPullParser
16 |
17 | class Source(
18 | override val hrefs: List = emptyList()
19 | ): HrefListProperty(hrefs) {
20 |
21 | companion object {
22 |
23 | @JvmField
24 | val NAME = Property.Name(NS_CALENDARSERVER, "source")
25 |
26 | }
27 |
28 |
29 | object Factory: HrefListProperty.Factory() {
30 |
31 | override fun getName() = NAME
32 |
33 | override fun create(parser: XmlPullParser) = create(parser, ::Source)
34 |
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/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 | companion object {
25 |
26 | @JvmField
27 | val NAME = Property.Name(NS_CALDAV, "supported-calendar-component-set")
28 |
29 | val ALLCOMP = Property.Name(NS_CALDAV, "allcomp")
30 | val COMP = Property.Name(NS_CALDAV, "comp")
31 |
32 | }
33 |
34 |
35 | object Factory: PropertyFactory {
36 |
37 | override fun getName() = NAME
38 |
39 | override fun create(parser: XmlPullParser): SupportedCalendarComponentSet {
40 | /*
41 |
42 |
43 | */
44 | var components = SupportedCalendarComponentSet(
45 | supportsEvents = false,
46 | supportsTasks = false,
47 | supportsJournal = false
48 | )
49 |
50 | val depth = parser.depth
51 | var eventType = parser.eventType
52 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
53 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
54 | when (parser.propertyName()) {
55 | ALLCOMP -> {
56 | components = SupportedCalendarComponentSet(
57 | supportsEvents = true,
58 | supportsTasks = true,
59 | supportsJournal = true
60 | )
61 | }
62 | COMP ->
63 | when (parser.getAttributeValue(null, "name")?.uppercase()) {
64 | "VEVENT" -> components = components.copy(supportsEvents = true)
65 | "VTODO" -> components = components.copy(supportsTasks = true)
66 | "VJOURNAL" -> components = components.copy(supportsJournal = true)
67 | }
68 | }
69 | }
70 | eventType = parser.next()
71 | }
72 |
73 | return components
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/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 okhttp3.MediaType
17 | import org.xmlpull.v1.XmlPullParser
18 |
19 | data class SupportedCalendarData(
20 | val types: Set = emptySet()
21 | ): Property {
22 |
23 | companion object {
24 |
25 | @JvmField
26 | val NAME = Property.Name(NS_CALDAV, "supported-calendar-data")
27 |
28 | val CALENDAR_DATA_TYPE = Property.Name(NS_CALDAV, "calendar-data")
29 | const val CONTENT_TYPE = "content-type"
30 | const val VERSION = "version"
31 |
32 | }
33 |
34 | fun hasJCal() = types.any { "application".equals(it.type, true) && "calendar+json".equals(it.subtype, true) }
35 |
36 |
37 | object Factory: PropertyFactory {
38 |
39 | override fun getName() = NAME
40 |
41 | override fun create(parser: XmlPullParser): SupportedCalendarData {
42 | val supportedTypes = mutableSetOf()
43 |
44 | XmlReader(parser).readContentTypes(CALENDAR_DATA_TYPE, supportedTypes::add)
45 |
46 | return SupportedCalendarData(supportedTypes)
47 | }
48 |
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/caldav/namespace.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 | /**
14 | * CalDAV XML namespace (defined in RFC 4791)
15 | */
16 | const val NS_CALDAV = "urn:ietf:params:xml:ns:caldav"
17 |
18 | const val NS_APPLE_ICAL = "http://apple.com/ns/ical/"
19 | const val NS_CALENDARSERVER = "http://calendarserver.org/ns/"
20 |
--------------------------------------------------------------------------------
/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 | @JvmField
25 | val NAME = Property.Name(NS_CARDDAV, "address-data")
26 |
27 | // attributes
28 | const val CONTENT_TYPE = "content-type"
29 | const val VERSION = "version"
30 |
31 | }
32 |
33 |
34 | object Factory: PropertyFactory {
35 |
36 | override fun getName() = NAME
37 |
38 | override fun create(parser: XmlPullParser) =
39 | //
40 | AddressData(XmlReader(parser).readText())
41 |
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/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 | companion object {
23 | @JvmField
24 | val NAME = Property.Name(NS_CARDDAV, "addressbook-description")
25 | }
26 |
27 | object Factory: PropertyFactory {
28 |
29 | override fun getName() = NAME
30 |
31 | override fun create(parser: XmlPullParser) =
32 | //
33 | AddressbookDescription(XmlReader(parser).readText())
34 |
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/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
14 | import at.bitfire.dav4jvm.property.webdav.HrefListProperty
15 | import org.xmlpull.v1.XmlPullParser
16 |
17 | class AddressbookHomeSet(
18 | override val hrefs: List = emptyList()
19 | ): HrefListProperty(hrefs) {
20 |
21 | companion object {
22 |
23 | @JvmField
24 | val NAME = Property.Name(NS_CARDDAV, "addressbook-home-set")
25 |
26 | }
27 |
28 |
29 | object Factory: HrefListProperty.Factory() {
30 |
31 | override fun getName() = NAME
32 |
33 | override fun create(parser: XmlPullParser) = create(parser, ::AddressbookHomeSet)
34 |
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/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 | companion object {
23 |
24 | @JvmField
25 | val NAME = Property.Name(NS_CARDDAV, "max-resource-size")
26 |
27 | }
28 |
29 | object Factory: PropertyFactory {
30 |
31 | override fun getName() = NAME
32 |
33 | override fun create(parser: XmlPullParser) =
34 | MaxResourceSize(XmlReader(parser).readLong())
35 |
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/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 okhttp3.MediaType
17 | import org.xmlpull.v1.XmlPullParser
18 |
19 | class SupportedAddressData(
20 | val types: Set = emptySet()
21 | ): Property {
22 |
23 | companion object {
24 |
25 | @JvmField
26 | val NAME = Property.Name(NS_CARDDAV, "supported-address-data")
27 |
28 | val ADDRESS_DATA_TYPE = Property.Name(NS_CARDDAV, "address-data-type")
29 | const val CONTENT_TYPE = "content-type"
30 | const val VERSION = "version"
31 |
32 | }
33 |
34 | fun hasVCard4() = types.any { "text/vcard; version=4.0".equals(it.toString(), true) }
35 | fun hasJCard() = types.any { "application".equals(it.type, true) && "vcard+json".equals(it.subtype, true) }
36 |
37 | override fun toString() = "[${types.joinToString(", ")}]"
38 |
39 |
40 | object Factory: PropertyFactory {
41 |
42 | override fun getName() = NAME
43 |
44 | override fun create(parser: XmlPullParser): SupportedAddressData {
45 | val supportedTypes = mutableSetOf()
46 |
47 | XmlReader(parser).readContentTypes(ADDRESS_DATA_TYPE, supportedTypes::add)
48 |
49 | return SupportedAddressData(supportedTypes)
50 | }
51 |
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/carddav/namespace.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 | /**
14 | * CardDAV XML namespace (defined in RFC 6352)
15 | */
16 | const val NS_CARDDAV = "urn:ietf:params:xml:ns:carddav"
17 |
--------------------------------------------------------------------------------
/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 a [NS_WEBDAV_PUSH]`: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 | companion object {
28 |
29 | @JvmField
30 | val NAME = Property.Name(NS_WEBDAV_PUSH, "auth-secret")
31 |
32 | }
33 |
34 |
35 | object Factory: PropertyFactory {
36 |
37 | override fun getName() = NAME
38 |
39 | override fun create(parser: XmlPullParser): AuthSecret =
40 | AuthSecret(XmlReader(parser).readText())
41 |
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/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.SyncLevel
18 | import at.bitfire.dav4jvm.property.webdav.SyncToken
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 | companion object {
32 |
33 | @JvmField
34 | val NAME = Property.Name(NS_WEBDAV_PUSH, "content-update")
35 |
36 | }
37 |
38 |
39 | object Factory: PropertyFactory {
40 |
41 | override fun getName() = NAME
42 |
43 | override fun create(parser: XmlPullParser): ContentUpdate {
44 | var contentUpdate = ContentUpdate()
45 |
46 | val depth = parser.depth
47 | var eventType = parser.eventType
48 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
49 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
50 | when (parser.propertyName()) {
51 | SyncLevel.NAME -> contentUpdate = contentUpdate.copy(
52 | depth = Depth.Factory.create(parser)
53 | )
54 | SyncToken.NAME -> contentUpdate = contentUpdate.copy(
55 | syncToken = SyncToken.Factory.create(parser)
56 | )
57 | }
58 | }
59 | eventType = parser.next()
60 | }
61 |
62 | return contentUpdate
63 | }
64 |
65 | }
66 |
67 | }
--------------------------------------------------------------------------------
/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 org.xmlpull.v1.XmlPullParser
18 |
19 | /**
20 | * Represents a [NS_WEBDAV_PUSH]`:property-update` property.
21 | *
22 | * Experimental! See https://github.com/bitfireAT/webdav-push/
23 | */
24 | data class PropertyUpdate(
25 | val syncLevel: SyncLevel? = null,
26 | ): Property {
27 |
28 | companion object {
29 |
30 | @JvmField
31 | val NAME = Property.Name(NS_WEBDAV_PUSH, "property-update")
32 |
33 | }
34 |
35 |
36 | object Factory: PropertyFactory {
37 |
38 | override fun getName() = NAME
39 |
40 | override fun create(parser: XmlPullParser): PropertyUpdate {
41 | var propertyUpdate = PropertyUpdate()
42 |
43 | val depth = parser.depth
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 | SyncLevel.NAME -> propertyUpdate = propertyUpdate.copy(
49 | syncLevel = SyncLevel.Factory.create(parser)
50 | )
51 | }
52 | }
53 | eventType = parser.next()
54 | }
55 | return propertyUpdate
56 | }
57 |
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/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 | companion object {
30 |
31 | @JvmField
32 | val NAME = Property.Name(NS_WEBDAV_PUSH, "push-message")
33 |
34 | }
35 |
36 |
37 | object Factory: PropertyFactory {
38 |
39 | override fun getName() = NAME
40 |
41 | override fun create(parser: XmlPullParser): PushMessage {
42 | var message = PushMessage()
43 |
44 | val depth = parser.depth
45 | var eventType = parser.eventType
46 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
47 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
48 | when (parser.propertyName()) {
49 | Topic.NAME -> message = message.copy(
50 | topic = Topic.Factory.create(parser)
51 | )
52 | ContentUpdate.NAME -> message = message.copy(
53 | contentUpdate = ContentUpdate.Factory.create(parser)
54 | )
55 | PropertyUpdate.NAME -> message = message.copy(
56 | propertyUpdate = PropertyUpdate.Factory.create(parser)
57 | )
58 | }
59 | }
60 | eventType = parser.next()
61 | }
62 |
63 | return message
64 | }
65 |
66 | }
67 |
68 | }
--------------------------------------------------------------------------------
/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 | companion object {
33 |
34 | @JvmField
35 | val NAME = Property.Name(NS_WEBDAV_PUSH, "push-register")
36 |
37 | val EXPIRES = Property.Name(NS_WEBDAV_PUSH, "expires")
38 |
39 | }
40 |
41 |
42 | object Factory: PropertyFactory {
43 |
44 | override fun getName() = NAME
45 |
46 | override fun create(parser: XmlPullParser): PushRegister {
47 | var register = PushRegister()
48 |
49 | val depth = parser.depth
50 | var eventType = parser.eventType
51 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
52 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1)
53 | when (parser.propertyName()) {
54 | EXPIRES ->
55 | register = register.copy(
56 | expires = XmlReader(parser).readText()?.let {
57 | HttpUtils.parseDate(it)
58 | }
59 | )
60 | Subscription.NAME ->
61 | register = register.copy(
62 | subscription = Subscription.Factory.create(parser)
63 | )
64 | Trigger.NAME ->
65 | register = register.copy(
66 | trigger = Trigger.Factory.create(parser)
67 | )
68 | }
69 | eventType = parser.next()
70 | }
71 |
72 | return register
73 | }
74 |
75 | }
76 |
77 | }
--------------------------------------------------------------------------------
/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 | companion object {
30 |
31 | @JvmField
32 | val NAME = Property.Name(NS_WEBDAV_PUSH, "push-resource")
33 |
34 | }
35 |
36 |
37 | object Factory: PropertyFactory {
38 |
39 | override fun getName() = NAME
40 |
41 | override fun create(parser: XmlPullParser): PushResource =
42 | PushResource(
43 | uri = XmlReader(parser).readText()?.let { uri ->
44 | try {
45 | URI(uri)
46 | } catch (_: URISyntaxException) {
47 | null
48 | }
49 | }
50 | )
51 |
52 | }
53 |
54 | }
--------------------------------------------------------------------------------
/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/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 | companion object {
28 | @JvmField
29 | val NAME = Property.Name(NS_WEBDAV_PUSH, "transports")
30 | }
31 |
32 | fun hasWebPush() = transports.any { it is WebPush }
33 |
34 |
35 | object Factory: PropertyFactory {
36 |
37 | override fun getName() = NAME
38 |
39 | override fun create(parser: XmlPullParser): PushTransports {
40 | val transports = mutableListOf()
41 | val depth = parser.depth
42 | var eventType = parser.eventType
43 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
44 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
45 | when (parser.propertyName()) {
46 | WebPush.NAME -> transports += WebPush.Factory.create(parser)
47 | }
48 | }
49 | eventType = parser.next()
50 | }
51 | return PushTransports(transports.toSet())
52 | }
53 |
54 | }
55 |
56 | }
--------------------------------------------------------------------------------
/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 | companion object {
28 |
29 | @JvmField
30 | val NAME = Property.Name(NS_WEBDAV_PUSH, "subscription")
31 |
32 | }
33 |
34 |
35 | object Factory: PropertyFactory {
36 |
37 | override fun getName() = NAME
38 |
39 | override fun create(parser: XmlPullParser): Subscription {
40 | // currently we only support WebPushSubscription
41 | var webPushSubscription: WebPushSubscription? = null
42 |
43 | XmlReader(parser).processTag(WebPushSubscription.NAME) {
44 | webPushSubscription = WebPushSubscription.Factory.create(parser)
45 | }
46 |
47 | return Subscription(webPushSubscription)
48 | }
49 |
50 | }
51 |
52 | }
--------------------------------------------------------------------------------
/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 | companion object {
29 |
30 | @JvmField
31 | val NAME = Property.Name(NS_WEBDAV_PUSH, "subscription-public-key")
32 |
33 | }
34 |
35 |
36 | object Factory : PropertyFactory {
37 |
38 | override fun getName() = NAME
39 |
40 | override fun create(parser: XmlPullParser): SubscriptionPublicKey {
41 | return SubscriptionPublicKey(
42 | type = parser.getAttributeValue(null, "type"),
43 | key = XmlReader(parser).readText()
44 | )
45 | }
46 |
47 | }
48 |
49 | }
--------------------------------------------------------------------------------
/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 | companion object {
29 |
30 | @JvmField
31 | val NAME = Property.Name(NS_WEBDAV_PUSH, "supported-triggers")
32 |
33 | }
34 |
35 |
36 | object Factory: PropertyFactory {
37 |
38 | override fun getName() = NAME
39 |
40 | override fun create(parser: XmlPullParser): SupportedTriggers {
41 | var supportedTriggers = SupportedTriggers()
42 |
43 | val depth = parser.depth
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 | ContentUpdate.NAME -> supportedTriggers = supportedTriggers.copy(
49 | contentUpdate = ContentUpdate.Factory.create(parser)
50 | )
51 | PropertyUpdate.NAME -> supportedTriggers = supportedTriggers.copy(
52 | propertyUpdate = PropertyUpdate.Factory.create(parser)
53 | )
54 | }
55 | }
56 | eventType = parser.next()
57 | }
58 |
59 | return supportedTriggers
60 | }
61 |
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/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 | companion object {
28 |
29 | @JvmField
30 | val NAME = Property.Name(NS_WEBDAV_PUSH, "topic")
31 |
32 | }
33 |
34 |
35 | object Factory: PropertyFactory {
36 |
37 | override fun getName() = NAME
38 |
39 | override fun create(parser: XmlPullParser): Topic =
40 | Topic(XmlReader(parser).readText())
41 |
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/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 | companion object {
24 |
25 | @JvmField
26 | val NAME = Property.Name(NS_WEBDAV_PUSH, "trigger")
27 |
28 | }
29 |
30 |
31 | object Factory : PropertyFactory {
32 |
33 | override fun getName() = NAME
34 |
35 | override fun create(parser: XmlPullParser): Trigger {
36 | var trigger = Trigger()
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 | ContentUpdate.NAME -> trigger = trigger.copy(
44 | contentUpdate = ContentUpdate.Factory.create(parser)
45 | )
46 | PropertyUpdate.NAME -> trigger = trigger.copy(
47 | propertyUpdate = PropertyUpdate.Factory.create(parser)
48 | )
49 | }
50 | }
51 | eventType = parser.next()
52 | }
53 |
54 | return trigger
55 | }
56 |
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/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 | companion object {
29 |
30 | @JvmField
31 | val NAME = Property.Name(NS_WEBDAV_PUSH, "vapid-public-key")
32 |
33 | }
34 |
35 |
36 | object Factory : PropertyFactory {
37 |
38 | override fun getName() = NAME
39 |
40 | override fun create(parser: XmlPullParser): VapidPublicKey {
41 | return VapidPublicKey(
42 | type = parser.getAttributeValue(null, "type"),
43 | key = XmlReader(parser).readText()
44 | )
45 | }
46 |
47 | }
48 |
49 | }
--------------------------------------------------------------------------------
/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 org.xmlpull.v1.XmlPullParser
16 |
17 | /**
18 | * Represents a [NS_WEBDAV_PUSH]`:web-push` property.
19 | *
20 | * Experimental! See https://github.com/bitfireAT/webdav-push/
21 | */
22 | data class WebPush(
23 | val vapidPublicKey: VapidPublicKey? = null
24 | ) : PushTransport {
25 |
26 | companion object {
27 | @JvmField
28 | val NAME = Property.Name(NS_WEBDAV_PUSH, "web-push")
29 | }
30 |
31 |
32 | object Factory : PropertyFactory {
33 |
34 | override fun getName(): Property.Name = NAME
35 |
36 | override fun create(parser: XmlPullParser): WebPush {
37 | var vapidPublicKey: VapidPublicKey? = null
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.namespace == NS_WEBDAV_PUSH) {
42 | when (parser.name) {
43 | VapidPublicKey.NAME.name -> vapidPublicKey = VapidPublicKey.Factory.create(parser)
44 | }
45 | }
46 | eventType = parser.next()
47 | }
48 | return WebPush(vapidPublicKey)
49 | }
50 |
51 | }
52 |
53 | }
--------------------------------------------------------------------------------
/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 | companion object {
30 |
31 | @JvmField
32 | val NAME = Property.Name(NS_WEBDAV_PUSH, "web-push-subscription")
33 |
34 | }
35 |
36 |
37 | object Factory: PropertyFactory {
38 |
39 | override fun getName() = NAME
40 |
41 | override fun create(parser: XmlPullParser): WebPushSubscription {
42 | var subscription = WebPushSubscription()
43 |
44 | val depth = parser.depth
45 | var eventType = parser.eventType
46 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
47 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
48 | when (parser.propertyName()) {
49 | PushResource.NAME -> subscription = subscription.copy(pushResource = PushResource.Factory.create(parser))
50 | SubscriptionPublicKey.NAME -> subscription = subscription.copy(subscriptionPublicKey = SubscriptionPublicKey.Factory.create(parser))
51 | AuthSecret.NAME -> subscription = subscription.copy(authSecret = AuthSecret.Factory.create(parser))
52 | }
53 | }
54 | eventType = parser.next()
55 | }
56 |
57 | return subscription
58 | }
59 |
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/push/namespace.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 | /**
14 | * XML namespace of WebDAV-Push (draft), see:
15 | * https://github.com/bitfireAT/webdav-push/
16 | *
17 | * Experimental!
18 | */
19 | const val NS_WEBDAV_PUSH = "https://bitfire.at/webdav-push"
20 |
--------------------------------------------------------------------------------
/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.DavResource
14 | import at.bitfire.dav4jvm.Property
15 | import at.bitfire.dav4jvm.PropertyFactory
16 | import at.bitfire.dav4jvm.XmlReader
17 | import org.xmlpull.v1.XmlPullParser
18 |
19 | /**
20 | * Defined in RFC 5995 3.2.1 DAV:add-member Property (Protected).
21 | */
22 | data class AddMember(
23 | val href: String?
24 | ): Property {
25 |
26 | companion object {
27 |
28 | @JvmField
29 | val NAME = Property.Name(NS_WEBDAV, "add-member")
30 |
31 | }
32 |
33 | object Factory: PropertyFactory {
34 |
35 | override fun getName() = NAME
36 |
37 | override fun create(parser: XmlPullParser) = AddMember(XmlReader(parser).readTextProperty(DavResource.HREF))
38 |
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/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 | companion object {
23 |
24 | @JvmField
25 | val NAME = Property.Name(NS_WEBDAV, "creationdate")
26 |
27 | }
28 |
29 | object Factory: PropertyFactory {
30 |
31 | override fun getName() = NAME
32 |
33 | override fun create(parser: XmlPullParser) =
34 | CreationDate(XmlReader(parser).readText())
35 |
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/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.DavResource
14 | import at.bitfire.dav4jvm.Property
15 | import at.bitfire.dav4jvm.PropertyFactory
16 | import at.bitfire.dav4jvm.XmlReader
17 | import org.xmlpull.v1.XmlPullParser
18 |
19 | // see RFC 5397: WebDAV Current Principal Extension
20 |
21 | data class CurrentUserPrincipal(
22 | val href: String?
23 | ): Property {
24 |
25 | companion object {
26 |
27 | @JvmField
28 | val NAME = Property.Name(NS_WEBDAV, "current-user-principal")
29 |
30 | }
31 |
32 |
33 | object Factory: PropertyFactory {
34 |
35 | override fun getName() = NAME
36 |
37 | override fun create(parser: XmlPullParser): CurrentUserPrincipal {
38 | //
39 | var href: String? = null
40 | XmlReader(parser).processTag(DavResource.HREF) {
41 | href = readText()
42 | }
43 | return CurrentUserPrincipal(href)
44 | }
45 |
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/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 | companion object {
30 |
31 | @JvmField
32 | val NAME = Property.Name(NS_WEBDAV, "current-user-privilege-set")
33 |
34 | val PRIVILEGE = Property.Name(NS_WEBDAV, "privilege")
35 | val READ = Property.Name(NS_WEBDAV, "read")
36 | val WRITE = Property.Name(NS_WEBDAV, "write")
37 | val WRITE_PROPERTIES = Property.Name(NS_WEBDAV, "write-properties")
38 | val WRITE_CONTENT = Property.Name(NS_WEBDAV, "write-content")
39 | val BIND = Property.Name(NS_WEBDAV, "bind")
40 | val UNBIND = Property.Name(NS_WEBDAV, "unbind")
41 | val ALL = Property.Name(NS_WEBDAV, "all")
42 |
43 | }
44 |
45 |
46 | object Factory: PropertyFactory {
47 |
48 | override fun getName() = NAME
49 |
50 | override fun create(parser: XmlPullParser): CurrentUserPrivilegeSet {
51 | //
52 | //
53 | var privs = CurrentUserPrivilegeSet()
54 |
55 | XmlReader(parser).processTag(PRIVILEGE) {
56 | val depth = parser.depth
57 | var eventType = parser.eventType
58 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
59 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1)
60 | when (parser.propertyName()) {
61 | READ ->
62 | privs = privs.copy(mayRead = true)
63 | WRITE -> {
64 | privs = privs.copy(
65 | mayBind = true,
66 | mayUnbind = true,
67 | mayWriteProperties = true,
68 | mayWriteContent = true
69 | )
70 | }
71 | WRITE_PROPERTIES ->
72 | privs = privs.copy(mayWriteProperties = true)
73 | WRITE_CONTENT ->
74 | privs = privs.copy(mayWriteContent = true)
75 | BIND ->
76 | privs = privs.copy(mayBind = true)
77 | UNBIND ->
78 | privs = privs.copy(mayUnbind = true)
79 | ALL -> {
80 | privs = privs.copy(
81 | mayRead = true,
82 | mayBind = true,
83 | mayUnbind = true,
84 | mayWriteProperties = true,
85 | mayWriteContent = true
86 | )
87 | }
88 | }
89 | eventType = parser.next()
90 | }
91 | }
92 |
93 | return privs
94 | }
95 |
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/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 |
28 | @JvmField
29 | val NAME = Property.Name(NS_WEBDAV, "depth")
30 |
31 | const val INFINITY = Int.MAX_VALUE
32 |
33 | }
34 |
35 |
36 | object Factory: PropertyFactory {
37 |
38 | override fun getName() = NAME
39 |
40 | override fun create(parser: XmlPullParser): Depth {
41 | val text = XmlReader(parser).readText()
42 | val level = if (text.equals("infinity", true))
43 | INFINITY
44 | else
45 | text?.toIntOrNull()
46 | return Depth(level)
47 | }
48 |
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/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 | companion object {
23 |
24 | @JvmField
25 | val NAME = Property.Name(NS_WEBDAV, "displayname")
26 |
27 | }
28 |
29 |
30 | object Factory: PropertyFactory {
31 |
32 | override fun getName() = NAME
33 |
34 | override fun create(parser: XmlPullParser) =
35 | //
36 | DisplayName(XmlReader(parser).readText())
37 |
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/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 | companion object {
23 |
24 | @JvmField
25 | val NAME = Property.Name(NS_WEBDAV, "getcontentlength")
26 |
27 | }
28 |
29 | object Factory: PropertyFactory {
30 |
31 | override fun getName() = NAME
32 |
33 | override fun create(parser: XmlPullParser) =
34 | GetContentLength(XmlReader(parser).readLong())
35 |
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/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 okhttp3.MediaType
17 | import okhttp3.MediaType.Companion.toMediaTypeOrNull
18 | import org.xmlpull.v1.XmlPullParser
19 |
20 | data class GetContentType(
21 | val type: MediaType?
22 | ): Property {
23 |
24 | companion object {
25 |
26 | @JvmField
27 | val NAME = Property.Name(NS_WEBDAV, "getcontenttype")
28 |
29 | }
30 |
31 |
32 | object Factory: PropertyFactory {
33 |
34 | override fun getName() = NAME
35 |
36 | override fun create(parser: XmlPullParser) =
37 | //
38 | GetContentType(XmlReader(parser).readText()?.toMediaTypeOrNull())
39 |
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/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 okhttp3.Response
18 | import org.xmlpull.v1.XmlPullParser
19 |
20 | /**
21 | * The GetETag property.
22 | *
23 | * Can also be used to parse ETags from HTTP responses – just pass the raw ETag
24 | * header value to the constructor and then use [eTag] and [weak].
25 | */
26 | data class GetETag(
27 | val rawETag: String?
28 | ): Property {
29 |
30 | companion object {
31 | @JvmField
32 | val NAME = Property.Name(NS_WEBDAV, "getetag")
33 |
34 | fun fromResponse(response: Response) =
35 | response.header("ETag")?.let { GetETag(it) }
36 | }
37 |
38 | /**
39 | * The parsed ETag value, excluding the weakness indicator and the quotes.
40 | */
41 | val eTag: String?
42 |
43 | /**
44 | * Whether the ETag is weak.
45 | */
46 | var weak: Boolean
47 |
48 | init {
49 | /* entity-tag = [ weak ] opaque-tag
50 | weak = "W/"
51 | opaque-tag = quoted-string
52 | */
53 |
54 | if (rawETag != null) {
55 | val tag: String?
56 | // remove trailing "W/"
57 | if (rawETag.startsWith("W/")) {
58 | // entity tag is weak
59 | tag = rawETag.substring(2)
60 | weak = true
61 | } else {
62 | tag = rawETag
63 | weak = false
64 | }
65 | eTag = QuotedStringUtils.decodeQuotedString(tag)
66 | } else {
67 | eTag = null
68 | weak = false
69 | }
70 | }
71 |
72 | override fun equals(other: Any?): Boolean {
73 | if (other !is GetETag)
74 | return false
75 | return eTag == other.eTag && weak == other.weak
76 | }
77 |
78 | override fun hashCode(): Int {
79 | return eTag.hashCode() xor weak.hashCode()
80 | }
81 |
82 |
83 | object Factory: PropertyFactory {
84 |
85 | override fun getName() = NAME
86 |
87 | override fun create(parser: XmlPullParser): GetETag =
88 | GetETag(XmlReader(parser).readText())
89 |
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/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 | companion object {
24 |
25 | @JvmField
26 | val NAME = Property.Name(NS_WEBDAV, "getlastmodified")
27 |
28 | }
29 |
30 |
31 | object Factory: PropertyFactory {
32 |
33 | override fun getName() = NAME
34 |
35 | override fun create(parser: XmlPullParser): GetLastModified {
36 | //
37 | return GetLastModified(
38 | XmlReader(parser).readHttpDate()
39 | )
40 | }
41 |
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/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
14 | import org.xmlpull.v1.XmlPullParser
15 |
16 | class GroupMembership(
17 | override val hrefs: List
18 | ): HrefListProperty(hrefs) {
19 |
20 | companion object {
21 |
22 | @JvmField
23 | val NAME = Property.Name(NS_WEBDAV, "group-membership")
24 |
25 | }
26 |
27 |
28 | object Factory: HrefListProperty.Factory() {
29 |
30 | override fun getName() = NAME
31 |
32 | override fun create(parser: XmlPullParser) = create(parser, ::GroupMembership)
33 |
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/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.webdav
12 |
13 | import at.bitfire.dav4jvm.DavResource
14 | import at.bitfire.dav4jvm.Property
15 | import at.bitfire.dav4jvm.PropertyFactory
16 | import at.bitfire.dav4jvm.XmlReader
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 | val href get() = hrefs.firstOrNull()
29 |
30 |
31 | abstract class Factory : PropertyFactory {
32 |
33 | @Deprecated("hrefs is no longer mutable.", level = DeprecationLevel.ERROR)
34 | fun create(parser: XmlPullParser, list: HrefListProperty): HrefListProperty {
35 | val hrefs = list.hrefs.toMutableList()
36 | XmlReader(parser).readTextPropertyList(DavResource.HREF, hrefs)
37 | return list
38 | }
39 |
40 | fun create(
41 | parser: XmlPullParser,
42 | constructor: (hrefs: List
43 | ) -> PropertyType): PropertyType {
44 | val hrefs = mutableListOf()
45 | XmlReader(parser).readTextPropertyList(DavResource.HREF, hrefs)
46 | return constructor(hrefs)
47 | }
48 |
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/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 org.xmlpull.v1.XmlPullParser
15 |
16 | data class Owner(
17 | override val hrefs: List
18 | ): HrefListProperty(hrefs) {
19 |
20 | companion object {
21 |
22 | @JvmField
23 | val NAME = Property.Name(NS_WEBDAV, "owner")
24 |
25 | }
26 |
27 |
28 | object Factory: HrefListProperty.Factory() {
29 |
30 | override fun getName() = NAME
31 |
32 | override fun create(parser: XmlPullParser) = create(parser, ::Owner)
33 |
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/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 | companion object {
23 |
24 | @JvmField
25 | val NAME = Property.Name(NS_WEBDAV, "quota-available-bytes")
26 |
27 | }
28 |
29 | object Factory: PropertyFactory {
30 |
31 | override fun getName() = NAME
32 |
33 | override fun create(parser: XmlPullParser) =
34 | QuotaAvailableBytes(XmlReader(parser).readLong())
35 |
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/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 | companion object {
23 |
24 | @JvmField
25 | val NAME = Property.Name(NS_WEBDAV, "quota-used-bytes")
26 |
27 | }
28 |
29 | object Factory: PropertyFactory {
30 |
31 | override fun getName() = NAME
32 |
33 | override fun create(parser: XmlPullParser) =
34 | QuotaUsedBytes(XmlReader(parser).readLong())
35 |
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/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.NS_CALDAV
16 | import at.bitfire.dav4jvm.property.caldav.NS_CALENDARSERVER
17 | import at.bitfire.dav4jvm.property.carddav.NS_CARDDAV
18 | import org.xmlpull.v1.XmlPullParser
19 |
20 | class ResourceType(
21 | val types: Set = emptySet()
22 | ): Property {
23 |
24 | companion object {
25 |
26 | @JvmField
27 | val NAME = Property.Name(NS_WEBDAV, "resourcetype")
28 |
29 | val COLLECTION = Property.Name(NS_WEBDAV, "collection") // WebDAV
30 | val PRINCIPAL = Property.Name(NS_WEBDAV, "principal") // WebDAV ACL
31 | val ADDRESSBOOK = Property.Name(NS_CARDDAV, "addressbook") // CardDAV
32 | val CALENDAR = Property.Name(NS_CALDAV, "calendar") // CalDAV
33 |
34 | // CalendarServer extensions
35 | val CALENDAR_PROXY_READ = Property.Name(NS_CALENDARSERVER, "calendar-proxy-read") // CalDAV Proxy
36 | val CALENDAR_PROXY_WRITE = Property.Name(NS_CALENDARSERVER, "calendar-proxy-write") // CalDAV Proxy
37 | val SUBSCRIBED = Property.Name(NS_CALENDARSERVER, "subscribed")
38 |
39 | }
40 |
41 |
42 | object Factory: PropertyFactory {
43 |
44 | override fun getName() = NAME
45 |
46 | override fun create(parser: XmlPullParser): ResourceType {
47 | val types = mutableSetOf()
48 |
49 | val depth = parser.depth
50 | var eventType = parser.eventType
51 | while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
52 | if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) {
53 | // use static objects to allow types.contains()
54 | var typeName = Property.Name(parser.namespace, parser.name)
55 | when (typeName) { // if equals(), replace by our instance
56 | COLLECTION -> typeName = COLLECTION
57 | PRINCIPAL -> typeName = PRINCIPAL
58 | ADDRESSBOOK -> typeName = ADDRESSBOOK
59 | CALENDAR -> typeName = CALENDAR
60 | CALENDAR_PROXY_READ -> typeName = CALENDAR_PROXY_READ
61 | CALENDAR_PROXY_WRITE -> typeName = CALENDAR_PROXY_WRITE
62 | SUBSCRIBED -> typeName = SUBSCRIBED
63 | }
64 | types.add(typeName)
65 | }
66 | eventType = parser.next()
67 | }
68 | assert(parser.depth == depth)
69 |
70 | return ResourceType(types)
71 | }
72 |
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/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 | companion object {
23 |
24 | @JvmField
25 | val NAME = Property.Name(NS_WEBDAV, "supported-report-set")
26 |
27 | val SUPPORTED_REPORT = Property.Name(NS_WEBDAV, "supported-report")
28 | val REPORT = Property.Name(NS_WEBDAV, "report")
29 |
30 | const val SYNC_COLLECTION = "DAV:sync-collection" // collection synchronization (RFC 6578)
31 |
32 | }
33 |
34 |
35 | object Factory: PropertyFactory {
36 |
37 | override fun getName() = NAME
38 |
39 | override fun create(parser: XmlPullParser): SupportedReportSet {
40 | /*
41 |
42 |
43 | */
44 |
45 | val reports = mutableSetOf()
46 | XmlReader(parser).processTag(SUPPORTED_REPORT) {
47 | processTag(REPORT) {
48 | parser.nextTag()
49 | if (parser.eventType == XmlPullParser.TEXT)
50 | reports += parser.text
51 | else if (parser.eventType == XmlPullParser.START_TAG)
52 | reports += "${parser.namespace}${parser.name}"
53 | }
54 | }
55 | return SupportedReportSet(reports)
56 | }
57 |
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/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 | companion object {
27 |
28 | @JvmField
29 | val NAME = Property.Name(NS_WEBDAV, "sync-level")
30 |
31 | }
32 |
33 |
34 | object Factory: PropertyFactory {
35 |
36 | override fun getName() = NAME
37 |
38 | override fun create(parser: XmlPullParser): SyncLevel {
39 | val text = XmlReader(parser).readText()
40 | val level = if (text == "infinite") Int.MAX_VALUE else text?.toIntOrNull()
41 | return SyncLevel(level)
42 | }
43 |
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/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 | companion object {
23 |
24 | @JvmField
25 | val NAME = Property.Name(NS_WEBDAV, "sync-token")
26 |
27 | }
28 |
29 |
30 | object Factory: PropertyFactory {
31 |
32 | override fun getName() = NAME
33 |
34 | override fun create(parser: XmlPullParser) =
35 | //
36 | SyncToken(XmlReader(parser).readText())
37 |
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/kotlin/at/bitfire/dav4jvm/property/webdav/namespace.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 | /**
14 | * WebDAV XML namespace (defined in RFC 4918)
15 | */
16 | const val NS_WEBDAV = "DAV:"
17 |
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/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
12 |
13 | import okhttp3.OkHttpClient
14 | import okhttp3.mockwebserver.MockResponse
15 | import okhttp3.mockwebserver.MockWebServer
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.shutdown()
37 | }
38 |
39 |
40 | @Test
41 | fun calendarQuery_formatStartEnd() {
42 | val cal = DavCalendar(httpClient, mockServer.url("/"))
43 | mockServer.enqueue(MockResponse().setResponseCode(207).setBody(""))
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.readUtf8())
61 | }
62 |
63 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/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
12 |
13 | import org.junit.Assert.assertTrue
14 | import org.junit.Test
15 |
16 | class ErrorTest {
17 |
18 | @Test
19 | fun testEquals() {
20 | val errors = listOf(Error(Property.Name("DAV:", "valid-sync-token")))
21 | assertTrue(errors.contains(Error.VALID_SYNC_TOKEN))
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/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 okhttp3.HttpUrl.Companion.toHttpUrl
14 | import org.junit.Assert.assertEquals
15 | import org.junit.Assert.assertNull
16 | import org.junit.Test
17 | import java.time.Instant
18 | import java.time.LocalDate
19 | import java.time.LocalTime
20 | import java.time.ZoneOffset
21 | import java.time.ZonedDateTime
22 | import java.time.format.DateTimeFormatter
23 | import java.util.Calendar
24 | import java.util.Locale
25 | import java.util.TimeZone
26 | import java.util.logging.Logger
27 |
28 | class HttpUtilsTest {
29 |
30 | @Test
31 | fun fileName() {
32 | assertEquals("", HttpUtils.fileName("https://example.com".toHttpUrl()))
33 | assertEquals("", HttpUtils.fileName("https://example.com/".toHttpUrl()))
34 | assertEquals("file1", HttpUtils.fileName("https://example.com/file1".toHttpUrl()))
35 | assertEquals("dir1", HttpUtils.fileName("https://example.com/dir1/".toHttpUrl()))
36 | assertEquals("file2", HttpUtils.fileName("https://example.com/dir1/file2".toHttpUrl()))
37 | assertEquals("dir2", HttpUtils.fileName("https://example.com/dir1/dir2/".toHttpUrl()))
38 | }
39 |
40 | @Test
41 | fun formatDate() {
42 | val cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"))
43 | cal.set(2023, 4, 11, 17, 26, 35)
44 | cal.timeZone = TimeZone.getTimeZone("UTC")
45 | assertEquals("Sun, 06 Nov 1994 08:49:37 GMT", HttpUtils.formatDate(
46 | ZonedDateTime.of(
47 | LocalDate.of(1994, 11, 6),
48 | LocalTime.of(8, 49, 37),
49 | ZoneOffset.UTC
50 | ).toInstant()
51 | ))
52 | }
53 |
54 |
55 | @Test
56 | fun parseDate_IMF_FixDate() {
57 | // RFC 7231 IMF-fixdate (preferred format)
58 | assertEquals(Instant.ofEpochSecond(784111777), HttpUtils.parseDate("Sun, 06 Nov 1994 08:49:37 GMT"))
59 | }
60 |
61 | @Test
62 | fun parseDate_RFC850_1994() {
63 | // obsolete RFC 850 format – fails when run after year 2000 because 06 Nov 2094 (!) is not a Sunday
64 | assertNull(HttpUtils.parseDate("Sun, 06-Nov-94 08:49:37 GMT"))
65 | }
66 |
67 | @Test
68 | fun parseDate_RFC850_2004_CEST() {
69 | // obsolete RFC 850 format with European time zone
70 | assertEquals(Instant.ofEpochSecond(1689317377), HttpUtils.parseDate("Friday, 14-Jul-23 08:49:37 CEST"))
71 | }
72 |
73 | @Test
74 | fun parseDate_RFC850_2004_GMT() {
75 | // obsolete RFC 850 format
76 | assertEquals(Instant.ofEpochSecond(1689324577), HttpUtils.parseDate("Friday, 14-Jul-23 08:49:37 GMT"))
77 | }
78 |
79 | @Test
80 | fun parseDate_ANSI_C() {
81 | // ANSI C's asctime() format
82 | val logger = Logger.getLogger(javaClass.name)
83 | logger.info("Expected date: " + DateTimeFormatter.ofPattern("EEE MMM ppd HH:mm:ss yyyy", Locale.US).format(ZonedDateTime.now()))
84 |
85 | assertEquals(Instant.ofEpochSecond(784111777), HttpUtils.parseDate("Sun Nov 6 08:49:37 1994"))
86 | }
87 |
88 | }
--------------------------------------------------------------------------------
/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.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.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/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
12 |
13 | import org.junit.Assert.assertEquals
14 | import org.junit.Test
15 |
16 | class QuotedStringUtilsTest {
17 |
18 | @Test
19 | fun testAsQuotedString() {
20 | assertEquals("\"\"", QuotedStringUtils.asQuotedString(""))
21 | assertEquals("\"\\\"\"", QuotedStringUtils.asQuotedString("\""))
22 | assertEquals("\"\\\\\"", QuotedStringUtils.asQuotedString("\\"))
23 | }
24 |
25 | fun testDecodeQuotedString() {
26 | assertEquals("\"", QuotedStringUtils.decodeQuotedString("\""))
27 | assertEquals("\\", QuotedStringUtils.decodeQuotedString("\"\\\""))
28 | assertEquals("\"test", QuotedStringUtils.decodeQuotedString("\"test"))
29 | assertEquals("test", QuotedStringUtils.decodeQuotedString("test"))
30 | assertEquals("", QuotedStringUtils.decodeQuotedString("\"\""))
31 | assertEquals("test", QuotedStringUtils.decodeQuotedString("\"test\""))
32 | assertEquals("test\\", QuotedStringUtils.decodeQuotedString("\"test\\\""))
33 | assertEquals("test", QuotedStringUtils.decodeQuotedString("\"t\\e\\st\""))
34 | assertEquals("12\"34", QuotedStringUtils.decodeQuotedString("\"12\\\"34\""))
35 | assertEquals("1234\"", QuotedStringUtils.decodeQuotedString("\"1234\\\"\""))
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/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
12 |
13 | import okhttp3.HttpUrl.Companion.toHttpUrl
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("http://host/resource".toHttpUrl(), UrlUtils.omitTrailingSlash("http://host/resource".toHttpUrl()))
41 | assertEquals("http://host/resource".toHttpUrl(), UrlUtils.omitTrailingSlash("http://host/resource/".toHttpUrl()))
42 | }
43 |
44 | @Test
45 | fun testWithTrailingSlash() {
46 | assertEquals("http://host/resource/".toHttpUrl(), UrlUtils.withTrailingSlash("http://host/resource".toHttpUrl()))
47 | assertEquals("http://host/resource/".toHttpUrl(), UrlUtils.withTrailingSlash("http://host/resource/".toHttpUrl()))
48 | }
49 |
50 |
51 | @Test
52 | fun testHttpUrl_EqualsForWebDAV() {
53 | assertTrue("http://host/resource".toHttpUrl().equalsForWebDAV("http://host/resource".toHttpUrl()))
54 | assertTrue("http://host:80/resource".toHttpUrl().equalsForWebDAV("http://host/resource".toHttpUrl()))
55 | assertTrue("https://HOST:443/resource".toHttpUrl().equalsForWebDAV("https://host/resource".toHttpUrl()))
56 | assertTrue("https://host:443/my@dav/".toHttpUrl().equalsForWebDAV("https://host/my%40dav/".toHttpUrl()))
57 | assertTrue("http://host/resource".toHttpUrl().equalsForWebDAV("http://host/resource#frag1".toHttpUrl()))
58 |
59 | assertFalse("http://host/resource".toHttpUrl().equalsForWebDAV("http://host/resource/".toHttpUrl()))
60 | assertFalse("http://host/resource".toHttpUrl().equalsForWebDAV("http://host:81/resource".toHttpUrl()))
61 |
62 | assertTrue("https://www.example.com/folder/[X]Y!.txt".toHttpUrl().equalsForWebDAV("https://www.example.com/folder/[X]Y!.txt".toHttpUrl()))
63 | assertTrue("https://www.example.com/folder/%5BX%5DY!.txt".toHttpUrl().equalsForWebDAV("https://www.example.com/folder/[X]Y!.txt".toHttpUrl()))
64 | assertTrue("https://www.example.com/folder/%5bX%5dY%21.txt".toHttpUrl().equalsForWebDAV("https://www.example.com/folder/[X]Y!.txt".toHttpUrl()))
65 | }
66 |
67 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/XmlReaderTest.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 okhttp3.MediaType
14 | import okhttp3.MediaType.Companion.toMediaType
15 | import org.junit.Assert.assertEquals
16 | import org.junit.Assert.assertNull
17 | import org.junit.Assert.assertTrue
18 | import org.junit.Test
19 | import org.xmlpull.v1.XmlPullParser
20 | import java.io.StringReader
21 | import java.time.Instant
22 |
23 | class XmlReaderTest {
24 |
25 | @Test
26 | fun testProcessTag_Root() {
27 | val parser = XmlUtils.newPullParser()
28 | parser.setInput(StringReader(""))
29 | // now on START_DOCUMENT [0]
30 |
31 | var processed = false
32 | XmlReader(parser).processTag(Property.Name("", "test")) {
33 | processed = true
34 | }
35 | assertTrue(processed)
36 | }
37 |
38 | @Test
39 | fun testProcessTag_Depth1() {
40 | val parser = XmlUtils.newPullParser()
41 | parser.setInput(StringReader(""))
42 | parser.next() // now on START_TAG
43 |
44 | var processed = false
45 | XmlReader(parser).processTag(Property.Name("", "test")) {
46 | processed = true
47 | }
48 | assertTrue(processed)
49 | }
50 |
51 |
52 | @Test
53 | fun testReadText() {
54 | val parser = XmlUtils.newPullParser()
55 | parser.setInput(StringReader("Test 1Test 2"))
56 | parser.next()
57 | parser.next() // now on START_TAG
58 | val reader = XmlReader(parser)
59 |
60 | assertEquals("Test 1", reader.readText())
61 | assertEquals(XmlPullParser.END_TAG, parser.eventType)
62 | parser.next()
63 |
64 | assertEquals("Test 2", reader.readText())
65 | assertEquals(XmlPullParser.END_TAG, parser.eventType)
66 | }
67 |
68 | @Test
69 | fun testReadText_CDATA() {
70 | val parser = XmlUtils.newPullParser()
71 | parser.setInput(StringReader("Test 2]]>"))
72 | parser.next() // now on START_TAG
73 |
74 | assertEquals("Test 1Test 2", XmlReader(parser).readText())
75 | assertEquals(XmlPullParser.END_TAG, parser.eventType)
76 | }
77 |
78 | @Test
79 | fun testReadText_PropertyRoot() {
80 | val parser = XmlUtils.newPullParser()
81 | parser.setInput(StringReader("Test 1Test 2"))
82 | parser.next() // now on START_TAG
83 |
84 | val entries = mutableListOf()
85 | XmlReader(parser).readTextPropertyList(Property.Name("", "entry"), entries)
86 | assertEquals("Test 1", entries[0])
87 | assertEquals("Test 2", entries[1])
88 |
89 | parser.next() // END_TAG
90 | assertEquals(XmlPullParser.END_DOCUMENT, parser.eventType)
91 | }
92 |
93 |
94 | @Test
95 | fun testReadTextPropertyList_Depth1() {
96 | val parser = XmlUtils.newPullParser()
97 | parser.setInput(StringReader("Test 1Test 2"))
98 | parser.next() // now on START_TAG [1]
99 |
100 | val entries = mutableListOf()
101 | XmlReader(parser).readTextPropertyList(Property.Name("", "entry"), entries)
102 | assertEquals("Test 1", entries[0])
103 | assertEquals("Test 2", entries[1])
104 | assertEquals(XmlPullParser.END_TAG, parser.eventType)
105 | assertEquals("test", parser.name)
106 | }
107 |
108 |
109 | @Test
110 | fun testReadLong() {
111 | val parser = XmlUtils.newPullParser()
112 | parser.setInput(StringReader("12a"))
113 | parser.next()
114 | parser.next() // now on START_TAG
115 | val reader = XmlReader(parser)
116 |
117 | assertEquals(1L, reader.readLong())
118 | assertEquals(XmlPullParser.END_TAG, parser.eventType)
119 | parser.next()
120 |
121 | assertEquals(2L, reader.readLong())
122 | assertEquals(XmlPullParser.END_TAG, parser.eventType)
123 | parser.next()
124 |
125 | assertNull(reader.readLong())
126 | assertEquals(XmlPullParser.END_TAG, parser.eventType)
127 | }
128 |
129 |
130 | @Test
131 | fun testReadHttpDate() {
132 | val parser = XmlUtils.newPullParser()
133 | parser.setInput(StringReader("Sun, 06 Nov 1994 08:49:37 GMTSun, 06 Nov 1994 08:49:37 GMTinvalid"))
134 | parser.next()
135 | parser.next() // now on START_TAG
136 | val reader = XmlReader(parser)
137 |
138 | assertEquals(Instant.ofEpochSecond(784111777), reader.readHttpDate())
139 | assertEquals(XmlPullParser.END_TAG, parser.eventType)
140 | parser.next()
141 |
142 | assertEquals(Instant.ofEpochSecond(784111777), reader.readHttpDate())
143 | assertEquals(XmlPullParser.END_TAG, parser.eventType)
144 | parser.next()
145 |
146 | assertNull(reader.readHttpDate())
147 | assertEquals(XmlPullParser.END_TAG, parser.eventType)
148 | }
149 |
150 |
151 | @Test
152 | fun testReadContentTypes() {
153 | val parser = XmlUtils.newPullParser()
154 | parser.setInput(StringReader("text{}"))
155 | parser.next()
156 | val reader = XmlReader(parser)
157 |
158 | val types = mutableListOf()
159 | reader.readContentTypes(Property.Name("", "test"), types::add)
160 | assertEquals(2, types.size)
161 | assertEquals("text/plain".toMediaType(), types[0])
162 | assertEquals("application/json".toMediaType(), types[1])
163 | }
164 |
165 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/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.exception
12 |
13 | import at.bitfire.dav4jvm.DavResource
14 | import at.bitfire.dav4jvm.Property
15 | import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV
16 | import at.bitfire.dav4jvm.property.webdav.ResourceType
17 | import okhttp3.MediaType.Companion.toMediaType
18 | import okhttp3.OkHttpClient
19 | import okhttp3.Protocol
20 | import okhttp3.Request
21 | import okhttp3.RequestBody.Companion.toRequestBody
22 | import okhttp3.Response
23 | import okhttp3.mockwebserver.MockResponse
24 | import okhttp3.mockwebserver.MockWebServer
25 | import org.junit.After
26 | import org.junit.Assert.assertEquals
27 | import org.junit.Assert.assertNull
28 | import org.junit.Assert.assertTrue
29 | import org.junit.Assert.fail
30 | import org.junit.Before
31 | import org.junit.Test
32 | import java.io.ByteArrayInputStream
33 | import java.io.ByteArrayOutputStream
34 | import java.io.ObjectInputStream
35 | import java.io.ObjectOutputStream
36 |
37 | class DavExceptionTest {
38 |
39 | private val httpClient = OkHttpClient.Builder()
40 | .followRedirects(false)
41 | .build()
42 | private val mockServer = MockWebServer()
43 | private fun sampleUrl() = mockServer.url("/dav/")
44 |
45 | @Before
46 | fun startServer() = mockServer.start()
47 |
48 | @After
49 | fun stopServer() = mockServer.shutdown()
50 |
51 |
52 | /**
53 | * Test truncation of a too large plain text request in [DavException].
54 | */
55 | @Test
56 | fun testRequestLargeTextError() {
57 | val url = sampleUrl()
58 | val dav = DavResource(httpClient, url)
59 |
60 | val builder = StringBuilder()
61 | builder.append(CharArray(DavException.MAX_EXCERPT_SIZE+100) { '*' })
62 | val body = builder.toString()
63 |
64 | val e = DavException("Error with large request body", null, Response.Builder()
65 | .request(Request.Builder()
66 | .url("http://example.com")
67 | .post(body.toRequestBody("text/plain".toMediaType()))
68 | .build())
69 | .protocol(Protocol.HTTP_1_1)
70 | .code(204)
71 | .message("No Content")
72 | .build())
73 |
74 | assertTrue(e.errors.isEmpty())
75 | assertEquals(
76 | body.substring(0, DavException.MAX_EXCERPT_SIZE),
77 | e.requestBody
78 | )
79 | }
80 |
81 | /**
82 | * Test a large HTML response which has a multi-octet UTF-8 character
83 | * exactly at the cut-off position.
84 | */
85 | @Test
86 | fun testResponseLargeTextError() {
87 | val url = sampleUrl()
88 | val dav = DavResource(httpClient, url)
89 |
90 | val builder = StringBuilder()
91 | builder.append(CharArray(DavException.MAX_EXCERPT_SIZE-1) { '*' })
92 | builder.append("\u03C0") // Pi
93 | val body = builder.toString()
94 |
95 | mockServer.enqueue(MockResponse()
96 | .setResponseCode(404)
97 | .setHeader("Content-Type", "text/html")
98 | .setBody(body))
99 | try {
100 | dav.propfind(0, ResourceType.NAME) { _, _ -> }
101 | fail("Expected HttpException")
102 | } catch (e: HttpException) {
103 | assertEquals(e.code, 404)
104 | assertTrue(e.errors.isEmpty())
105 | assertEquals(
106 | body.substring(0, DavException.MAX_EXCERPT_SIZE-1),
107 | e.responseBody!!.substring(0, DavException.MAX_EXCERPT_SIZE-1)
108 | )
109 | }
110 | }
111 |
112 | @Test
113 | fun testResponseNonTextError() {
114 | val url = sampleUrl()
115 | val dav = DavResource(httpClient, url)
116 |
117 | mockServer.enqueue(MockResponse()
118 | .setResponseCode(403)
119 | .setHeader("Content-Type", "application/octet-stream")
120 | .setBody("12345"))
121 | try {
122 | dav.propfind(0, ResourceType.NAME) { _, _ -> }
123 | fail("Expected HttpException")
124 | } catch (e: HttpException) {
125 | assertEquals(e.code, 403)
126 | assertTrue(e.errors.isEmpty())
127 | assertNull(e.responseBody)
128 | }
129 | }
130 |
131 | @Test
132 | fun testSerialization() {
133 | val url = sampleUrl()
134 | val dav = DavResource(httpClient, url)
135 |
136 | mockServer.enqueue(MockResponse()
137 | .setResponseCode(500)
138 | .setHeader("Content-Type", "text/plain")
139 | .setBody("12345"))
140 | try {
141 | dav.propfind(0, ResourceType.NAME) { _, _ -> }
142 | fail("Expected DavException")
143 | } catch (e: DavException) {
144 | val baos = ByteArrayOutputStream()
145 | val oos = ObjectOutputStream(baos)
146 | oos.writeObject(e)
147 |
148 | val ois = ObjectInputStream(ByteArrayInputStream(baos.toByteArray()))
149 | val e2 = ois.readObject() as HttpException
150 | assertEquals(500, e2.code)
151 | assertTrue(e2.responseBody!!.contains("12345"))
152 | }
153 | }
154 |
155 | /**
156 | * Test precondition XML element (sample from RFC 4918 16)
157 | */
158 | @Test
159 | fun testXmlError() {
160 | val url = sampleUrl()
161 | val dav = DavResource(httpClient, url)
162 |
163 | val body = "\n" +
164 | "\n" +
165 | " \n" +
166 | " /workspace/webdav/\n" +
167 | " \n" +
168 | "\n"
169 | mockServer.enqueue(MockResponse()
170 | .setResponseCode(423)
171 | .setHeader("Content-Type", "application/xml; charset=\"utf-8\"")
172 | .setBody(body))
173 | try {
174 | dav.propfind(0, ResourceType.NAME) { _, _ -> }
175 | fail("Expected HttpException")
176 | } catch (e: HttpException) {
177 | assertEquals(e.code, 423)
178 | assertTrue(e.errors.any { it.name == Property.Name(NS_WEBDAV, "lock-token-submitted") })
179 | assertEquals(body, e.responseBody)
180 | }
181 | }
182 |
183 | }
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/exception/HttpExceptionTest.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.exception
12 |
13 | import okhttp3.MediaType.Companion.toMediaType
14 | import okhttp3.Protocol
15 | import okhttp3.Request
16 | import okhttp3.RequestBody.Companion.toRequestBody
17 | import okhttp3.Response
18 | import okhttp3.ResponseBody.Companion.toResponseBody
19 | import org.junit.Assert.assertTrue
20 | import org.junit.Test
21 |
22 | class HttpExceptionTest {
23 |
24 | private val responseMessage = "Unknown error"
25 |
26 | @Test
27 | fun testHttpFormatting() {
28 | val request = Request.Builder()
29 | .post("REQUEST\nBODY".toRequestBody("text/something".toMediaType()))
30 | .url("http://example.com")
31 | .build()
32 |
33 | val response = Response.Builder()
34 | .request(request)
35 | .protocol(Protocol.HTTP_1_1)
36 | .code(500)
37 | .message(responseMessage)
38 | .body("SERVER\r\nRESPONSE".toResponseBody("text/something-other".toMediaType()))
39 | .build()
40 | val e = HttpException(response)
41 | assertTrue(e.message!!.contains("500"))
42 | assertTrue(e.message!!.contains(responseMessage))
43 | assertTrue(e.requestBody!!.contains("REQUEST\nBODY"))
44 | assertTrue(e.responseBody!!.contains("SERVER\r\nRESPONSE"))
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/test/kotlin/at/bitfire/dav4jvm/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.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(Request.Builder()
27 | .url("http://www.example.com")
28 | .get()
29 | .build())
30 | .protocol(Protocol.HTTP_1_1)
31 | .code(503).message("Try later")
32 | .build()
33 |
34 | @Test
35 | fun testRetryAfter_NoTime() {
36 | val e = ServiceUnavailableException(response503)
37 | assertNull(e.retryAfter)
38 | }
39 |
40 | @Test
41 | fun testRetryAfter_Seconds() {
42 | val response = response503.newBuilder()
43 | .header("Retry-After", "120")
44 | .build()
45 | val e = ServiceUnavailableException(response)
46 | assertNotNull(e.retryAfter)
47 | assertTrue(withinTimeRange(e.retryAfter!!, 120))
48 | }
49 |
50 | @Test
51 | fun testRetryAfter_Date() {
52 | val after30min = Instant.now().plusSeconds(30*60)
53 | val response = response503.newBuilder()
54 | .header("Retry-After", HttpUtils.formatDate(after30min))
55 | .build()
56 | val e = ServiceUnavailableException(response)
57 | assertNotNull(e.retryAfter)
58 | assertTrue(withinTimeRange(e.retryAfter!!, 30*60))
59 | }
60 |
61 |
62 | private fun withinTimeRange(d: Instant, seconds: Long) =
63 | d.isBefore(
64 | Instant.now()
65 | .plusSeconds(seconds)
66 | .plusSeconds(5) // tolerance for test running
67 | )
68 |
69 | }
--------------------------------------------------------------------------------
/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/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/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.Test
17 |
18 | class OwnerTest: PropertyTest() {
19 |
20 | @Test
21 | fun testOwner_PlainText() {
22 | val results = parseProperty("https://example.com")
23 | val owner = results.first() as Owner
24 | assertNull(owner.href)
25 | }
26 |
27 | @Test
28 | fun testOwner_PlainTextAndHref() {
29 | val results = parseProperty("Principal Name. mailto:owner@example.com (test)")
30 | val owner = results.first() as Owner
31 | assertEquals("mailto:owner@example.com", owner.href)
32 | }
33 |
34 | @Test
35 | fun testOwner_Href() {
36 | val results = parseProperty("https://example.com")
37 | val owner = results.first() as Owner
38 | assertEquals("https://example.com", owner.href)
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/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/test/kotlin/at/bitfire/dav4jvm/property/push/WebPushTest.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.PropertyTest
14 | import org.junit.Assert.assertEquals
15 | import org.junit.Assert.assertNotNull
16 | import org.junit.Assert.assertTrue
17 | import org.junit.Test
18 | import java.time.Instant
19 |
20 | class WebPushTest: PropertyTest() {
21 |
22 | @Test
23 | fun testPushRegister() {
24 | val results = parseProperty(
25 | "" +
26 | " " +
27 | " \n" +
28 | " https://up.example.net/yohd4yai5Phiz1wi\n" +
29 | " BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4\n" +
30 | " BTBZMqHH6r4Tts7J_aSIgg" +
31 | " \n" +
32 | " " +
33 | " Wed, 20 Dec 2023 10:03:31 GMT" +
34 | "")
35 | val result = results.first() as PushRegister
36 | assertEquals(Instant.ofEpochSecond(1703066611), result.expires)
37 | val subscription = result.subscription?.webPushSubscription
38 | assertEquals("https://up.example.net/yohd4yai5Phiz1wi", subscription?.pushResource?.uri?.toString())
39 | assertEquals("BTBZMqHH6r4Tts7J_aSIgg", subscription?.authSecret?.secret)
40 |
41 | val publicKey = subscription?.subscriptionPublicKey
42 | assertEquals("p256dh", publicKey?.type)
43 | assertEquals("BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4", publicKey?.key)
44 | }
45 |
46 | @Test
47 | fun testServiceDetection() {
48 | val results = parseProperty(
49 | "" +
50 | " " +
51 | " " +
52 | " BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4" +
53 | " " +
54 | "" +
55 | "SomeTopic")
56 | val result = results.first() as PushTransports
57 |
58 | assertEquals(setOf(
59 | // something else is ignored because it's not a recognized transport
60 | WebPush(
61 | VapidPublicKey(
62 | type = "p256dh",
63 | key = "BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4"
64 | )
65 | )
66 | ), result.transports)
67 | assertTrue(result.hasWebPush())
68 |
69 | assertEquals("SomeTopic", (results[1] as Topic).topic)
70 | }
71 |
72 | @Test
73 | fun testMessage() {
74 | val results = parseProperty(
75 | """
76 |
77 | O7M1nQ7cKkKTKsoS_j6Z3w
78 |
79 | http://example.com/sync/10
80 |
81 |
82 |
83 | """.trimIndent()
84 | )
85 | val message = results.first() as PushMessage
86 |
87 | val topic = message.topic
88 | assertNotNull(topic)
89 | assertEquals("O7M1nQ7cKkKTKsoS_j6Z3w", topic?.topic)
90 |
91 | val contentUpdate = message.contentUpdate
92 | assertNotNull(contentUpdate)
93 | val syncToken = contentUpdate?.syncToken
94 | assertNotNull(syncToken)
95 | assertEquals("http://example.com/sync/10", syncToken?.token)
96 |
97 | val propertyUpdate = message.propertyUpdate
98 | assertNotNull(propertyUpdate)
99 | }
100 |
101 | }
--------------------------------------------------------------------------------