Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit aa982a4b authored by Ricki Hirner's avatar Ricki Hirner
Browse files

207 Multi-Status: try to guess XML responses by XML signature

- try to guess when a 207 Multi-Status is XML although the Content-Type indicates otherwise
- packaging: change group to com.github.bitfireAT (was: com.gitlab.bitfireAT)
parent d7af678e
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -13,11 +13,11 @@ repositories {
    mavenCentral()
}

group="com.gitlab.bitfireAT"
group="com.github.bitfireAT"
version="2.1.3"

plugins {
    kotlin("jvm") version "1.6.10"
    kotlin("jvm") version "1.6.20"
    `maven-publish`

    id("org.jetbrains.dokka") version "1.5.0"
+25 −5
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import java.io.IOException
import java.io.Reader
import java.io.StringWriter
import java.net.HttpURLConnection
import java.util.logging.Level
import java.util.logging.Logger
import at.bitfire.dav4jvm.Response as DavResponse

@@ -54,6 +55,8 @@ open class DavResource @JvmOverloads constructor(
        val PROPFIND = Property.Name(XmlUtils.NS_WEBDAV, "propfind")
        val PROP = Property.Name(XmlUtils.NS_WEBDAV, "prop")
        val HREF = Property.Name(XmlUtils.NS_WEBDAV, "href")

        val XML_SIGNATURE = "<?xml".toByteArray()
    }

    /**
@@ -509,22 +512,39 @@ open class DavResource @JvmOverloads constructor(
    }

    /**
     * Asserts a Multi-Status response.
     * Validates a 207 Multi-Status response.
     *
     * @param response will be checked for Multi-Status response
     *
     * @throws DavException if the response is not a Multi-Status response
     */
    private fun assertMultiStatus(response: Response) {
    fun assertMultiStatus(response: Response) {
        if (response.code != HTTP_MULTISTATUS)
            throw DavException("Expected 207 Multi-Status, got ${response.code} ${response.message}", httpResponse = response)

        if (response.body == null)
        val body = response.body ?:
            throw DavException("Received 207 Multi-Status without body", httpResponse = response)

        response.body?.contentType()?.let {
            if (((it.type != "application" && it.type != "text")) || it.subtype != "xml")
        body.contentType()?.let { mimeType ->
            if (((mimeType.type != "application" && mimeType.type != "text")) || mimeType.subtype != "xml") {
                /* Content-Type is not application/xml or text/xml although that is expected here.
                   Some broken servers return an XML response with some other MIME type. So we try to see
                   whether the response is maybe XML although the Content-Type is something else. */
                try {
                    val firstBytes = ByteArray(5)
                    body.source().peek().read(firstBytes)
                    if (XML_SIGNATURE.contentEquals(firstBytes)) {
                        Dav4jvm.log.warning("Received 207 Multi-Status that seems to be XML but has MIME type $mimeType")

                        // response is OK, return and do not throw Exception below
                        return
                    }
                } catch (e: Exception) {
                    Dav4jvm.log.log(Level.WARNING, "Couldn't scan for XML signature", e)
                }

                throw DavException("Received non-XML 207 Multi-Status", httpResponse = response)
            }
        } ?: log.warning("Received 207 Multi-Status without Content-Type, assuming XML")
    }

+84 −0
Original line number Diff line number Diff line
@@ -794,4 +794,88 @@ class DavResourceTest {
        }
    }


    /** test helpers **/

    @Test(expected = DavException::class)
    fun testAssertMultiStatus_NoBody_NoXML() {
        val dav = DavResource(httpClient, "https://from.com".toHttpUrl())
        dav.assertMultiStatus(okhttp3.Response.Builder()
            .request(Request.Builder().url(dav.location).build())
            .protocol(Protocol.HTTP_1_1)
            .code(207).message("Multi-Status")
            .build())
    }

    @Test(expected = DavException::class)
    fun testAssertMultiStatus_NoBody_XML() {
        val dav = DavResource(httpClient, "https://from.com".toHttpUrl())
        dav.assertMultiStatus(okhttp3.Response.Builder()
            .request(Request.Builder().url(dav.location).build())
            .protocol(Protocol.HTTP_1_1)
            .code(207).message("Multi-Status")
            .addHeader("Content-Type", "text/xml")
            .build())
    }

    @Test
    fun testAssertMultiStatus_NonXML_ButContentIsXML() {
        val dav = DavResource(httpClient, "https://from.com".toHttpUrl())
        dav.assertMultiStatus(okhttp3.Response.Builder()
            .request(Request.Builder().url(dav.location).build())
            .protocol(Protocol.HTTP_1_1)
            .code(207).message("Multi-Status")
            .addHeader("Content-Type", "application/octet-stream")
            .body("<?xml version=\"1.0\"><test/>".toResponseBody())
            .build())
    }

    @Test
    fun testAssertMultiStatus_NonXML_ReallyNotXML() {
        val dav = DavResource(httpClient, "https://from.com".toHttpUrl())
        dav.assertMultiStatus(okhttp3.Response.Builder()
            .request(Request.Builder().url(dav.location).build())
            .protocol(Protocol.HTTP_1_1)
            .code(207).message("Multi-Status")
            .addHeader("Content-Type", "text/plain")
            .body("Some error occurred".toResponseBody())
            .build())
    }

    @Test(expected = DavException::class)
    fun testAssertMultiStatus_Not207() {
        val dav = DavResource(httpClient, "https://from.com".toHttpUrl())
        dav.assertMultiStatus(okhttp3.Response.Builder()
            .request(Request.Builder().url(dav.location).build())
            .protocol(Protocol.HTTP_1_1)
            .code(403).message("Multi-Status")
            .addHeader("Content-Type", "application/xml")
            .body("".toResponseBody())
            .build())
    }

    @Test
    fun testAssertMultiStatus_Ok_ApplicationXml() {
        val dav = DavResource(httpClient, "https://from.com".toHttpUrl())
        dav.assertMultiStatus(okhttp3.Response.Builder()
            .request(Request.Builder().url(dav.location).build())
            .protocol(Protocol.HTTP_1_1)
            .code(207).message("Multi-Status")
            .addHeader("Content-Type", "application/xml")
            .body("".toResponseBody())
            .build())
    }

    @Test
    fun testAssertMultiStatus_Ok_TextXml() {
        val dav = DavResource(httpClient, "https://from.com".toHttpUrl())
        dav.assertMultiStatus(okhttp3.Response.Builder()
            .request(Request.Builder().url(dav.location).build())
            .protocol(Protocol.HTTP_1_1)
            .code(207).message("Multi-Status")
            .addHeader("Content-Type", "text/xml")
            .body("".toResponseBody())
            .build())
    }

}