From 8b5941336678bae4ceea4687a0b2e043355b5e3f Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Mon, 2 Dec 2024 04:28:25 -0800 Subject: [PATCH] Incorporate latest changes from WebDAV-Push (#60) * Updated Push namespace Signed-off-by: Arnau Mora Gras * Implemented updates from new definitions Signed-off-by: Arnau Mora Gras * Added encryption properties Signed-off-by: Arnau Mora Gras * Added generic public key class Signed-off-by: Arnau Mora Gras * Replaced namespaces in docs Signed-off-by: Arnau Mora Gras * Got rid of unused const Signed-off-by: Arnau Mora Gras * Formatting Signed-off-by: Arnau Mora Gras * Moved file Signed-off-by: Arnau Mora Gras * Got rid of `PushPublicKey.Factory` Signed-off-by: Arnau Mora Gras * Renamed `resource` to `uri` Signed-off-by: Arnau Mora Gras * Simplified expression Signed-off-by: Arnau Mora Gras * Added `ServerPublicKey` to test Signed-off-by: Arnau Mora Gras * Typos Signed-off-by: Arnau Mora Gras * Made all properties data classes to comply with #61 Signed-off-by: Arnau Mora Gras * Fixed constructor Signed-off-by: Arnau Mora Gras * Minor changes (drop PushPublicKey) --------- Signed-off-by: Arnau Mora Gras Co-authored-by: Ricki Hirner --- .../at/bitfire/dav4jvm/PropertyRegistry.kt | 2 +- .../dav4jvm/property/push/AuthSecret.kt | 40 +++++++ .../dav4jvm/property/push/ClientPublicKey.kt | 39 ++++++ .../dav4jvm/property/push/PushMessage.kt | 17 ++- .../{PushSubscribe.kt => PushRegister.kt} | 32 ++--- .../dav4jvm/property/push/PushResource.kt | 50 ++++++++ .../dav4jvm/property/push/PushTransport.kt | 8 ++ .../dav4jvm/property/push/PushTransports.kt | 29 ++--- .../dav4jvm/property/push/ServerPublicKey.kt | 39 ++++++ .../dav4jvm/property/push/Subscription.kt | 6 +- .../at/bitfire/dav4jvm/property/push/Topic.kt | 6 +- .../bitfire/dav4jvm/property/push/WebPush.kt | 44 +++++++ .../property/push/WebPushSubscription.kt | 36 ++++-- .../dav4jvm/property/push/namespace.kt | 2 +- .../bitfire/dav4jvm/property/WebPushTest.kt | 50 -------- .../dav4jvm/property/push/WebPushTest.kt | 112 ++++++++++++++++++ 16 files changed, 409 insertions(+), 103 deletions(-) create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/property/push/AuthSecret.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/property/push/ClientPublicKey.kt rename src/main/kotlin/at/bitfire/dav4jvm/property/push/{PushSubscribe.kt => PushRegister.kt} (61%) create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/property/push/PushResource.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransport.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/property/push/ServerPublicKey.kt create mode 100644 src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPush.kt delete mode 100644 src/test/kotlin/at/bitfire/dav4jvm/property/WebPushTest.kt create mode 100644 src/test/kotlin/at/bitfire/dav4jvm/property/push/WebPushTest.kt diff --git a/src/main/kotlin/at/bitfire/dav4jvm/PropertyRegistry.kt b/src/main/kotlin/at/bitfire/dav4jvm/PropertyRegistry.kt index 2e9e7ea..ff6ebbd 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/PropertyRegistry.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/PropertyRegistry.kt @@ -59,7 +59,7 @@ object PropertyRegistry { at.bitfire.dav4jvm.property.carddav.MaxResourceSize.Factory, Owner.Factory, PushMessage.Factory, - PushSubscribe.Factory, + PushRegister.Factory, PushTransports.Factory, QuotaAvailableBytes.Factory, QuotaUsedBytes.Factory, diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/push/AuthSecret.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/AuthSecret.kt new file mode 100644 index 0000000..e429709 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/AuthSecret.kt @@ -0,0 +1,40 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package at.bitfire.dav4jvm.property.push + +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:auth-secret` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class AuthSecret( + val secret: String? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "auth-secret") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): AuthSecret = + AuthSecret(XmlReader(parser).readText()) + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/push/ClientPublicKey.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/ClientPublicKey.kt new file mode 100644 index 0000000..9994b7f --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/ClientPublicKey.kt @@ -0,0 +1,39 @@ +package at.bitfire.dav4jvm.property.push + +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:client-public-key` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class ClientPublicKey( + val type: String? = null, + val key: String? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "client-public-key") + + } + + + object Factory : PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): ClientPublicKey { + return ClientPublicKey( + type = parser.getAttributeValue(null, "type"), + key = XmlReader(parser).readText() + ) + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushMessage.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushMessage.kt index daed758..2c8790c 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushMessage.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushMessage.kt @@ -6,18 +6,19 @@ package at.bitfire.dav4jvm.property.push +import at.bitfire.dav4jvm.PropStat import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.PropertyFactory import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser /** - * Represents a `{DAV:Push}push-message` property. + * Represents a [NS_WEBDAV_PUSH]`:push-message` property. * * Experimental! See https://github.com/bitfireAT/webdav-push/ */ -class PushMessage( - val topic: String? +data class PushMessage( + val propStat: PropStat? = null ): Property { companion object { @@ -32,7 +33,15 @@ class PushMessage( override fun getName() = NAME - override fun create(parser: XmlPullParser) = PushMessage(XmlReader(parser).readTextProperty(Topic.NAME)) + override fun create(parser: XmlPullParser): PushMessage { + var propStat: PropStat? = null + + XmlReader(parser).processTag(PropStat.NAME) { + propStat = PropStat.parse(parser) + } + + return PushMessage(propStat) + } } diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushSubscribe.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushRegister.kt similarity index 61% rename from src/main/kotlin/at/bitfire/dav4jvm/property/push/PushSubscribe.kt rename to src/main/kotlin/at/bitfire/dav4jvm/property/push/PushRegister.kt index 83dc05d..70ffc11 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushSubscribe.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushRegister.kt @@ -11,35 +11,35 @@ import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.PropertyFactory import at.bitfire.dav4jvm.XmlReader import at.bitfire.dav4jvm.XmlUtils.propertyName -import org.xmlpull.v1.XmlPullParser import java.time.Instant +import org.xmlpull.v1.XmlPullParser /** - * Represents a `{DAV:Push}push-subscribe` property. + * Represents a [NS_WEBDAV_PUSH]`:push-register` property. * * Experimental! See https://github.com/bitfireAT/webdav-push/ */ -class PushSubscribe: Property { +data class PushRegister( + val expires: Instant? = null, + val subscription: Subscription? = null +): Property { companion object { @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "push-subscribe") + val NAME = Property.Name(NS_WEBDAV_PUSH, "push-register") val EXPIRES = Property.Name(NS_WEBDAV_PUSH, "expires") } - var expires: Instant? = null - var subscription: Subscription? = null - object Factory: PropertyFactory { override fun getName() = NAME - override fun create(parser: XmlPullParser): PushSubscribe { - val subscribe = PushSubscribe() + override fun create(parser: XmlPullParser): PushRegister { + var register = PushRegister() val depth = parser.depth var eventType = parser.eventType @@ -47,16 +47,20 @@ class PushSubscribe: Property { if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) when (parser.propertyName()) { EXPIRES -> - subscribe.expires = XmlReader(parser).readText()?.let { - HttpUtils.parseDate(it) - } + register = register.copy( + expires = XmlReader(parser).readText()?.let { + HttpUtils.parseDate(it) + } + ) Subscription.NAME -> - subscribe.subscription = Subscription.Factory.create(parser) + register = register.copy( + subscription = Subscription.Factory.create(parser) + ) } eventType = parser.next() } - return subscribe + return register } } diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushResource.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushResource.kt new file mode 100644 index 0000000..2b2b800 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushResource.kt @@ -0,0 +1,50 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package at.bitfire.dav4jvm.property.push + +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader +import org.xmlpull.v1.XmlPullParser +import java.net.URI +import java.net.URISyntaxException + +/** + * Represents a [NS_WEBDAV_PUSH]`:push-resource` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class PushResource( + val uri: URI? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "push-resource") + + } + + + object Factory: PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): PushResource = + PushResource( + uri = XmlReader(parser).readText()?.let { uri -> + try { + URI(uri) + } catch (_: URISyntaxException) { + null + } + } + ) + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransport.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransport.kt new file mode 100644 index 0000000..ff0c5dc --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransport.kt @@ -0,0 +1,8 @@ +package at.bitfire.dav4jvm.property.push + +import at.bitfire.dav4jvm.Property + +/** + * Identifies a property as a push transport. + */ +interface PushTransport: Property diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransports.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransports.kt index f10364c..8f25d2a 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransports.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/PushTransports.kt @@ -8,28 +8,24 @@ package at.bitfire.dav4jvm.property.push import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.PropertyFactory -import at.bitfire.dav4jvm.XmlReader import at.bitfire.dav4jvm.XmlUtils.propertyName import org.xmlpull.v1.XmlPullParser /** - * Represents a `{DAV:Push}push-transports` property. + * Represents a [NS_WEBDAV_PUSH]`:push-transports` property. * * Experimental! See https://github.com/bitfireAT/webdav-push/ */ class PushTransports private constructor( - val transports: Set + val transports: Set ): Property { companion object { @JvmField - val NAME = Property.Name(NS_WEBDAV_PUSH, "push-transports") - - val TRANSPORT = Property.Name(NS_WEBDAV_PUSH, "transport") - val WEB_PUSH = Property.Name(NS_WEBDAV_PUSH, "web-push") + val NAME = Property.Name(NS_WEBDAV_PUSH, "transports") } - fun hasWebPush() = transports.contains(WEB_PUSH) + fun hasWebPush() = transports.any { it is WebPush } object Factory: PropertyFactory { @@ -37,15 +33,16 @@ class PushTransports private constructor( override fun getName() = NAME override fun create(parser: XmlPullParser): PushTransports { - val transports = mutableListOf() - XmlReader(parser).processTag(TRANSPORT) { - val depth = parser.depth - var eventType = parser.eventType - while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { - if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) - transports += parser.propertyName() - eventType = parser.next() + val transports = mutableListOf() + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { + when (parser.propertyName()) { + WebPush.NAME -> transports += WebPush.Factory.create(parser) + } } + eventType = parser.next() } return PushTransports(transports.toSet()) } diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/push/ServerPublicKey.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/ServerPublicKey.kt new file mode 100644 index 0000000..bc10ff8 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/ServerPublicKey.kt @@ -0,0 +1,39 @@ +package at.bitfire.dav4jvm.property.push + +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlReader +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:server-public-key` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class ServerPublicKey( + val type: String? = null, + val key: String? = null +): Property { + + companion object { + + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "server-public-key") + + } + + + object Factory : PropertyFactory { + + override fun getName() = NAME + + override fun create(parser: XmlPullParser): ServerPublicKey { + return ServerPublicKey( + type = parser.getAttributeValue(null, "type"), + key = XmlReader(parser).readText() + ) + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/push/Subscription.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/Subscription.kt index 5024c5c..b88967a 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/property/push/Subscription.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/Subscription.kt @@ -12,12 +12,12 @@ import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser /** - * Represents a `{DAV:Push}subscription` property. + * Represents a [NS_WEBDAV_PUSH]`:subscription` property. * * Experimental! See https://github.com/bitfireAT/webdav-push/ */ -class Subscription private constructor( - val webPushSubscription: WebPushSubscription? +data class Subscription private constructor( + val webPushSubscription: WebPushSubscription? = null ): Property { companion object { diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/push/Topic.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/Topic.kt index 8b54fbe..23dc865 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/property/push/Topic.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/Topic.kt @@ -12,12 +12,12 @@ import at.bitfire.dav4jvm.XmlReader import org.xmlpull.v1.XmlPullParser /** - * Represents a `{DAV:Push}topic` property. + * Represents a [NS_WEBDAV_PUSH]`:topic` property. * * Experimental! See https://github.com/bitfireAT/webdav-push/ */ -class Topic private constructor( - val topic: String? +data class Topic( + val topic: String? = null ): Property { companion object { diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPush.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPush.kt new file mode 100644 index 0000000..c79dd99 --- /dev/null +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPush.kt @@ -0,0 +1,44 @@ +package at.bitfire.dav4jvm.property.push + +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import org.xmlpull.v1.XmlPullParser + +/** + * Represents a [NS_WEBDAV_PUSH]`:web-push` property. + * + * Experimental! See https://github.com/bitfireAT/webdav-push/ + */ +data class WebPush( + val serverPublicKey: ServerPublicKey? = null +): PushTransport { + + companion object { + @JvmField + val NAME = Property.Name(NS_WEBDAV_PUSH, "web-push") + } + + + object Factory: PropertyFactory { + + override fun getName(): Property.Name = NAME + + override fun create(parser: XmlPullParser): WebPush { + var serverPublicKey: ServerPublicKey? = null + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.namespace == NS_WEBDAV_PUSH) { + when (parser.name) { + ServerPublicKey.NAME.name + -> serverPublicKey = ServerPublicKey.Factory.create(parser) + } + } + eventType = parser.next() + } + return WebPush(serverPublicKey) + } + + } + +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPushSubscription.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPushSubscription.kt index eddb0a3..ad8d22e 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPushSubscription.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/WebPushSubscription.kt @@ -8,37 +8,51 @@ package at.bitfire.dav4jvm.property.push import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.PropertyFactory -import at.bitfire.dav4jvm.XmlReader +import at.bitfire.dav4jvm.XmlUtils.propertyName import org.xmlpull.v1.XmlPullParser /** - * Represents a `{DAV:Push}web-push-subscription` property. + * Represents a [NS_WEBDAV_PUSH]`:web-push-subscription` property. * * Experimental! See https://github.com/bitfireAT/webdav-push/ */ -class WebPushSubscription: Property { +data class WebPushSubscription( + val pushResource: PushResource? = null, + val clientPublicKey: ClientPublicKey? = null, + val authSecret: AuthSecret? = null +): Property { companion object { @JvmField val NAME = Property.Name(NS_WEBDAV_PUSH, "web-push-subscription") - val PUSH_RESOURCE = Property.Name(NS_WEBDAV_PUSH, "push-resource") - } - var pushResource: String? = null - object Factory: PropertyFactory { override fun getName() = NAME - override fun create(parser: XmlPullParser) = - WebPushSubscription().apply { - pushResource = XmlReader(parser).readTextProperty(PUSH_RESOURCE) + override fun create(parser: XmlPullParser): WebPushSubscription { + var subscription = WebPushSubscription() + + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { + when (parser.propertyName()) { + PushResource.NAME -> subscription = subscription.copy(pushResource = PushResource.Factory.create(parser)) + ClientPublicKey.NAME -> subscription = subscription.copy(clientPublicKey = ClientPublicKey.Factory.create(parser)) + AuthSecret.NAME -> subscription = subscription.copy(authSecret = AuthSecret.Factory.create(parser)) + } + } + eventType = parser.next() } + return subscription + } + } -} \ No newline at end of file +} diff --git a/src/main/kotlin/at/bitfire/dav4jvm/property/push/namespace.kt b/src/main/kotlin/at/bitfire/dav4jvm/property/push/namespace.kt index cc928a5..9ff1214 100644 --- a/src/main/kotlin/at/bitfire/dav4jvm/property/push/namespace.kt +++ b/src/main/kotlin/at/bitfire/dav4jvm/property/push/namespace.kt @@ -12,4 +12,4 @@ package at.bitfire.dav4jvm.property.push * * Experimental! */ -const val NS_WEBDAV_PUSH = "DAV:Push" +const val NS_WEBDAV_PUSH = "https://bitfire.at/webdav-push" diff --git a/src/test/kotlin/at/bitfire/dav4jvm/property/WebPushTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/property/WebPushTest.kt deleted file mode 100644 index dae0841..0000000 --- a/src/test/kotlin/at/bitfire/dav4jvm/property/WebPushTest.kt +++ /dev/null @@ -1,50 +0,0 @@ -package at.bitfire.dav4jvm.property - -import at.bitfire.dav4jvm.Property -import at.bitfire.dav4jvm.property.push.NS_WEBDAV_PUSH -import at.bitfire.dav4jvm.property.push.PushSubscribe -import at.bitfire.dav4jvm.property.push.PushTransports -import at.bitfire.dav4jvm.property.push.Topic -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Test -import java.time.Instant - -class WebPushTest: PropertyTest() { - - @Test - fun testPushSubscribe() { - val results = parseProperty( - "" + - " " + - " \n" + - " https://up.example.net/yohd4yai5Phiz1wi\n" + - " \n" + - " " + - " Wed, 20 Dec 2023 10:03:31 GMT" + - "") - val result = results.first() as PushSubscribe - assertEquals(Instant.ofEpochSecond(1703066611), result.expires) - assertEquals("https://up.example.net/yohd4yai5Phiz1wi", result.subscription?.webPushSubscription?.pushResource) - } - - @Test - fun testServiceDetection() { - val results = parseProperty( - "" + - " " + - " " + - "" + - "SomeTopic") - val result = results.first() as PushTransports - - assertEquals(setOf( - Property.Name(NS_WEBDAV_PUSH, "something"), - PushTransports.WEB_PUSH - ), result.transports) - assertTrue(result.hasWebPush()) - - assertEquals("SomeTopic", (results[1] as Topic).topic) - } - -} \ No newline at end of file diff --git a/src/test/kotlin/at/bitfire/dav4jvm/property/push/WebPushTest.kt b/src/test/kotlin/at/bitfire/dav4jvm/property/push/WebPushTest.kt new file mode 100644 index 0000000..7ef6f9d --- /dev/null +++ b/src/test/kotlin/at/bitfire/dav4jvm/property/push/WebPushTest.kt @@ -0,0 +1,112 @@ +package at.bitfire.dav4jvm.property.push + +import at.bitfire.dav4jvm.property.PropertyTest +import at.bitfire.dav4jvm.property.webdav.SyncToken +import java.time.Instant +import okhttp3.Protocol +import okhttp3.internal.http.StatusLine +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test + +class WebPushTest: PropertyTest() { + + @Test + fun testPushRegister() { + val results = parseProperty( + "" + + " " + + " \n" + + " https://up.example.net/yohd4yai5Phiz1wi\n" + + " BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4\n" + + " BTBZMqHH6r4Tts7J_aSIgg" + + " \n" + + " " + + " Wed, 20 Dec 2023 10:03:31 GMT" + + "") + val result = results.first() as PushRegister + assertEquals(Instant.ofEpochSecond(1703066611), result.expires) + val subscription = result.subscription?.webPushSubscription + assertEquals("https://up.example.net/yohd4yai5Phiz1wi", subscription?.pushResource?.uri?.toString()) + assertEquals("BTBZMqHH6r4Tts7J_aSIgg", subscription?.authSecret?.secret) + + val publicKey = subscription?.clientPublicKey + assertEquals("p256dh", publicKey?.type) + assertEquals("BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4", publicKey?.key) + } + + @Test + fun testServiceDetection() { + val results = parseProperty( + "" + + " " + + " " + + " BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4" + + " " + + "" + + "SomeTopic") + val result = results.first() as PushTransports + + assertEquals(setOf( + // something else is ignored because it's not a recognized transport + WebPush( + ServerPublicKey( + type = "p256dh", + key = "BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4" + ) + ) + ), result.transports) + assertTrue(result.hasWebPush()) + + assertEquals("SomeTopic", (results[1] as Topic).topic) + } + + @Test + fun testMessage() { + val results = parseProperty( + """ + + + + O7M1nQ7cKkKTKsoS_j6Z3w + http://example.com/ns/sync/1234 + + + + """.trimIndent() + ) + val message = results.first() as PushMessage + + val propStat = message.propStat + assertNotNull(propStat) + // Since the status line is not set, it's assumed to be OK by default + assertStatusEqual(StatusLine(Protocol.HTTP_1_1, 200, "Assuming OK"), propStat?.status) + + val properties = propStat?.properties + assertNotNull(properties) + assertEquals(2, properties?.size) + + val topic = properties!![0] as Topic + assertEquals("O7M1nQ7cKkKTKsoS_j6Z3w", topic.topic) + + val token = properties[1] as SyncToken + assertEquals("http://example.com/ns/sync/1234", token.token) + } + + /** + * StatusLine doesn't have an `equals` method, so we need to compare its fields manually. + */ + private fun assertStatusEqual(expected: StatusLine?, actual: StatusLine?) { + if (expected == null) { + assertNull(actual) + return + } + assertNotNull(actual) + assertEquals(expected.protocol, actual?.protocol) + assertEquals(expected.code, actual?.code) + assertEquals(expected.message, actual?.message) + } + +} \ No newline at end of file