@@ -17,10 +17,13 @@ import at.bitfire.dav4jvm.property.webdav.ResourceType
1717import at.bitfire.dav4jvm.property.webdav.WebDAV
1818import io.ktor.http.HttpStatusCode
1919import io.ktor.http.URLBuilder
20+ import io.ktor.http.URLParserException
2021import io.ktor.http.Url
2122import io.ktor.http.isSuccess
2223import io.ktor.http.takeFrom
24+ import org.jetbrains.annotations.VisibleForTesting
2325import org.xmlpull.v1.XmlPullParser
26+ import java.util.logging.Level
2427import java.util.logging.Logger
2528
2629/* *
@@ -60,38 +63,8 @@ class ResponseParser(
6063 while (! (eventType == XmlPullParser .END_TAG && parser.depth == depth)) {
6164 if (eventType == XmlPullParser .START_TAG && parser.depth == depth+ 1 )
6265 when (parser.propertyName()) {
63- WebDAV .Href -> {
64- var sHref = parser.nextText()
65- var hierarchical = false
66- if (! sHref.startsWith(" /" )) {
67- /* According to RFC 4918 8.3 URL Handling, only absolute paths are allowed as relative
68- URLs. However, some servers reply with relative paths. */
69- val firstColon = sHref.indexOf(' :' )
70- if (firstColon != - 1 ) {
71- /* There are some servers which return not only relative paths, but relative paths like "a:b.vcf",
72- which would be interpreted as scheme: "a", scheme-specific part: "b.vcf" normally.
73- For maximum compatibility, we prefix all relative paths which contain ":" (but not "://"),
74- with "./" to allow resolving by HttpUrl. */
75- try {
76- if (sHref.substring(firstColon, firstColon + 3 ) == " ://" )
77- hierarchical = true
78- } catch (e: IndexOutOfBoundsException ) {
79- // no "://"
80- }
81- if (! hierarchical)
82- sHref = " ./$sHref "
83-
84- }
85- }
86-
87- if (! hierarchical) {
88- val urlBuilder = URLBuilder (location).takeFrom(sHref)
89- urlBuilder.pathSegments = urlBuilder.pathSegments.filterNot { it == " ." } // Drop segments that are "./"
90- hrefOrNull = urlBuilder.build()
91- } else {
92- hrefOrNull = URLBuilder (location).takeFrom(sHref).build()
93- }
94- }
66+ WebDAV .Href ->
67+ hrefOrNull = resolveHref(parser.nextText())
9568 WebDAV .Status ->
9669 status = KtorHttpUtils .parseStatusLine(parser.nextText())
9770 WebDAV .PropStat ->
@@ -168,4 +141,42 @@ class ResponseParser(
168141 )
169142 }
170143
144+ @VisibleForTesting
145+ internal fun resolveHref (hrefString : String ): Url ? {
146+ var sHref = hrefString
147+
148+ var preserve = false
149+ if (! sHref.startsWith(" /" )) {
150+ /* According to RFC 4918 8.3 URL Handling, only absolute paths are allowed as relative
151+ URLs. However, some servers reply with relative paths. */
152+ val firstColon = sHref.indexOf(' :' )
153+ if (firstColon != - 1 ) {
154+ /* There are some servers which return not only relative paths, but relative paths like "a:b.vcf",
155+ which would be interpreted as scheme: "a", scheme-specific part: "b.vcf" normally.
156+ For maximum compatibility, we prefix all relative paths which contain ":" (but not "://"),
157+ with "./" to allow resolving by HttpUrl. */
158+ try {
159+ if (sHref.substring(firstColon, firstColon + 3 ) == " ://" )
160+ preserve = true
161+ } catch (_: IndexOutOfBoundsException ) {
162+ // no "://"
163+ }
164+ if (! preserve)
165+ sHref = " ./$sHref "
166+ }
167+ }
168+
169+ val urlBuilder = try {
170+ URLBuilder (location).takeFrom(sHref)
171+ } catch (e: URLParserException ) {
172+ logger.log(Level .WARNING , " Unresolvable <href> in <response>: $hrefString " , e)
173+ return null
174+ }
175+
176+ if (! preserve) // drop segments that are "./"
177+ urlBuilder.pathSegments = urlBuilder.pathSegments.filterNot { it == " ." }
178+
179+ return urlBuilder.build()
180+ }
181+
171182}
0 commit comments