Update WebDAV property names (#1841)

* Update WebDAV property names according to new dav4jvm naming scheme

* Use new supported-report-set; fix `MaxResourceSize` property reference in CalendarSyncManager

* Remove comment
This commit is contained in:
Ricki Hirner
2025-11-27 11:46:10 +01:00
committed by GitHub
parent 84b9a14ba1
commit a38dc29cca
19 changed files with 168 additions and 169 deletions

View File

@@ -7,7 +7,7 @@ package at.bitfire.davdroid.db
import android.security.NetworkSecurityPolicy
import androidx.test.filters.SmallTest
import at.bitfire.dav4jvm.okhttp.DavResource
import at.bitfire.dav4jvm.property.webdav.ResourceType
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.network.HttpClientBuilder
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
@@ -65,7 +65,7 @@ class CollectionTest {
lateinit var info: Collection
DavResource(httpClient, server.url("/"))
.propfind(0, ResourceType.NAME) { response, _ ->
.propfind(0, WebDAV.ResourceType) { response, _ ->
info = Collection.fromDavResponse(response) ?: throw IllegalArgumentException()
}
assertEquals(Collection.TYPE_ADDRESSBOOK, info.type)
@@ -121,7 +121,7 @@ class CollectionTest {
lateinit var info: Collection
DavResource(httpClient, server.url("/"))
.propfind(0, ResourceType.NAME) { response, _ ->
.propfind(0, WebDAV.ResourceType) { response, _ ->
info = Collection.fromDavResponse(response)!!
}
assertEquals(Collection.TYPE_CALENDAR, info.type)
@@ -157,7 +157,7 @@ class CollectionTest {
lateinit var info: Collection
DavResource(httpClient, server.url("/"))
.propfind(0, ResourceType.NAME) { response, _ ->
.propfind(0, WebDAV.ResourceType) { response, _ ->
info = Collection.fromDavResponse(response)!!
}
assertEquals(Collection.TYPE_CALENDAR, info.type)
@@ -191,7 +191,7 @@ class CollectionTest {
lateinit var info: Collection
DavResource(httpClient, server.url("/"))
.propfind(0, ResourceType.NAME) { response, _ ->
.propfind(0, WebDAV.ResourceType) { response, _ ->
info = Collection.fromDavResponse(response) ?: throw IllegalArgumentException()
}
assertEquals(Collection.TYPE_WEBCAL, info.type)

View File

@@ -6,8 +6,8 @@ package at.bitfire.davdroid.servicedetection
import android.security.NetworkSecurityPolicy
import at.bitfire.dav4jvm.okhttp.DavResource
import at.bitfire.dav4jvm.property.carddav.AddressbookHomeSet
import at.bitfire.dav4jvm.property.webdav.ResourceType
import at.bitfire.dav4jvm.property.carddav.CardDAV
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.network.HttpClientBuilder
import at.bitfire.davdroid.servicedetection.DavResourceFinder.Configuration.ServiceInfo
import at.bitfire.davdroid.settings.Credentials
@@ -93,8 +93,8 @@ class DavResourceFinderTest {
// recognize home set
var info = ServiceInfo()
DavResource(client, server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL))
.propfind(0, AddressbookHomeSet.NAME) { response, _ ->
finder.scanResponse(ResourceType.ADDRESSBOOK, response, info)
.propfind(0, CardDAV.AddressbookHomeSet) { response, _ ->
finder.scanResponse(CardDAV.Addressbook, response, info)
}
assertEquals(0, info.collections.size)
assertEquals(1, info.homeSets.size)
@@ -103,8 +103,8 @@ class DavResourceFinderTest {
// recognize address book
info = ServiceInfo()
DavResource(client, server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK))
.propfind(0, ResourceType.NAME) { response, _ ->
finder.scanResponse(ResourceType.ADDRESSBOOK, response, info)
.propfind(0, WebDAV.ResourceType) { response, _ ->
finder.scanResponse(CardDAV.Addressbook, response, info)
}
assertEquals(1, info.collections.size)
assertEquals(server.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/"), info.collections.keys.first())

View File

@@ -8,6 +8,7 @@ import android.accounts.Account
import at.bitfire.dav4jvm.okhttp.DavCollection
import at.bitfire.dav4jvm.okhttp.MultiResponseCallback
import at.bitfire.dav4jvm.okhttp.Response
import at.bitfire.dav4jvm.property.caldav.CalDAV
import at.bitfire.dav4jvm.property.caldav.GetCTag
import at.bitfire.davdroid.db.Collection
import at.bitfire.davdroid.di.SyncDispatcher
@@ -64,7 +65,7 @@ class TestSyncManager @AssistedInject constructor(
didQueryCapabilities = true
var cTag: SyncState? = null
davCollection.propfind(0, GetCTag.NAME) { response, rel ->
davCollection.propfind(0, CalDAV.GetCTag) { response, rel ->
if (rel == Response.HrefRelation.SELF)
response[GetCTag::class.java]?.cTag?.let {
cTag = SyncState(SyncState.Type.CTAG, it)

View File

@@ -12,6 +12,7 @@ import androidx.room.Index
import androidx.room.PrimaryKey
import at.bitfire.dav4jvm.okhttp.Response
import at.bitfire.dav4jvm.okhttp.UrlUtils
import at.bitfire.dav4jvm.property.caldav.CalDAV
import at.bitfire.dav4jvm.property.caldav.CalendarColor
import at.bitfire.dav4jvm.property.caldav.CalendarDescription
import at.bitfire.dav4jvm.property.caldav.CalendarTimezone
@@ -19,6 +20,7 @@ import at.bitfire.dav4jvm.property.caldav.CalendarTimezoneId
import at.bitfire.dav4jvm.property.caldav.Source
import at.bitfire.dav4jvm.property.caldav.SupportedCalendarComponentSet
import at.bitfire.dav4jvm.property.carddav.AddressbookDescription
import at.bitfire.dav4jvm.property.carddav.CardDAV
import at.bitfire.dav4jvm.property.push.PushTransports
import at.bitfire.dav4jvm.property.push.Topic
import at.bitfire.dav4jvm.property.push.WebPush
@@ -166,9 +168,9 @@ data class Collection(
val url = UrlUtils.withTrailingSlash(dav.href)
val type: String = dav[ResourceType::class.java]?.let { resourceType ->
when {
resourceType.types.contains(ResourceType.ADDRESSBOOK) -> TYPE_ADDRESSBOOK
resourceType.types.contains(ResourceType.CALENDAR) -> TYPE_CALENDAR
resourceType.types.contains(ResourceType.SUBSCRIBED) -> TYPE_WEBCAL
resourceType.types.contains(CardDAV.Addressbook) -> TYPE_ADDRESSBOOK
resourceType.types.contains(CalDAV.Calendar) -> TYPE_CALENDAR
resourceType.types.contains(CalDAV.Subscribed) -> TYPE_WEBCAL
else -> null
}
} ?: return null

View File

@@ -12,6 +12,7 @@ import at.bitfire.dav4jvm.okhttp.Response
import at.bitfire.dav4jvm.okhttp.UrlUtils
import at.bitfire.dav4jvm.property.webdav.DisplayName
import at.bitfire.dav4jvm.property.webdav.ResourceType
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.util.trimToNull
import okhttp3.HttpUrl
@@ -46,7 +47,7 @@ data class Principal(
fun fromDavResponse(serviceId: Long, dav: Response): Principal? {
// Check if response is a principal
val resourceType = dav[ResourceType::class.java] ?: return null
if (!resourceType.types.contains(ResourceType.PRINCIPAL))
if (!resourceType.types.contains(WebDAV.Principal))
return null
// Try getting the display name of the principal

View File

@@ -7,6 +7,7 @@ package at.bitfire.davdroid.push
import androidx.annotation.VisibleForTesting
import at.bitfire.dav4jvm.XmlReader
import at.bitfire.dav4jvm.XmlUtils
import at.bitfire.dav4jvm.property.push.WebDAVPush
import at.bitfire.davdroid.db.Collection.Companion.TYPE_ADDRESSBOOK
import at.bitfire.davdroid.repository.AccountRepository
import at.bitfire.davdroid.repository.DavCollectionRepository
@@ -105,7 +106,7 @@ class PushMessageHandler @Inject constructor(
try {
parser.setInput(StringReader(message))
XmlReader(parser).processTag(DavPushMessage.NAME) {
XmlReader(parser).processTag(WebDAVPush.PushMessage) {
val pushMessage = DavPushMessage.Factory.create(parser)
topic = pushMessage.topic?.topic
}

View File

@@ -17,12 +17,7 @@ import at.bitfire.dav4jvm.XmlUtils.insertTag
import at.bitfire.dav4jvm.okhttp.DavCollection
import at.bitfire.dav4jvm.okhttp.DavResource
import at.bitfire.dav4jvm.okhttp.exception.DavException
import at.bitfire.dav4jvm.property.push.AuthSecret
import at.bitfire.dav4jvm.property.push.PushRegister
import at.bitfire.dav4jvm.property.push.PushResource
import at.bitfire.dav4jvm.property.push.Subscription
import at.bitfire.dav4jvm.property.push.SubscriptionPublicKey
import at.bitfire.dav4jvm.property.push.WebPushSubscription
import at.bitfire.dav4jvm.property.push.WebDAVPush
import at.bitfire.davdroid.db.Collection
import at.bitfire.davdroid.db.Service
import at.bitfire.davdroid.di.IoDispatcher
@@ -237,26 +232,26 @@ class PushRegistrationManager @Inject constructor(
val writer = StringWriter()
serializer.setOutput(writer)
serializer.startDocument("UTF-8", true)
serializer.insertTag(PushRegister.NAME) {
serializer.insertTag(Subscription.NAME) {
serializer.insertTag(WebDAVPush.PushRegister) {
serializer.insertTag(WebDAVPush.Subscription) {
// subscription URL
serializer.insertTag(WebPushSubscription.NAME) {
serializer.insertTag(PushResource.NAME) {
serializer.insertTag(WebDAVPush.WebPushSubscription) {
serializer.insertTag(WebDAVPush.PushResource) {
text(endpoint.url)
}
endpoint.pubKeySet?.let { pubKeySet ->
serializer.insertTag(SubscriptionPublicKey.NAME) {
serializer.insertTag(WebDAVPush.SubscriptionPublicKey) {
attribute(null, "type", "p256dh")
text(pubKeySet.pubKey)
}
serializer.insertTag(AuthSecret.NAME) {
serializer.insertTag(WebDAVPush.AuthSecret) {
text(pubKeySet.auth)
}
}
}
}
// requested expiration
serializer.insertTag(PushRegister.EXPIRES) {
serializer.insertTag(WebDAVPush.Expires) {
text(HttpUtils.formatDate(requestedExpiration))
}
}

View File

@@ -12,17 +12,9 @@ import at.bitfire.dav4jvm.okhttp.DavResource
import at.bitfire.dav4jvm.okhttp.exception.GoneException
import at.bitfire.dav4jvm.okhttp.exception.HttpException
import at.bitfire.dav4jvm.okhttp.exception.NotFoundException
import at.bitfire.dav4jvm.property.caldav.CalendarColor
import at.bitfire.dav4jvm.property.caldav.CalendarDescription
import at.bitfire.dav4jvm.property.caldav.CalendarTimezone
import at.bitfire.dav4jvm.property.caldav.CalendarTimezoneId
import at.bitfire.dav4jvm.property.caldav.NS_CALDAV
import at.bitfire.dav4jvm.property.caldav.SupportedCalendarComponentSet
import at.bitfire.dav4jvm.property.carddav.AddressbookDescription
import at.bitfire.dav4jvm.property.carddav.NS_CARDDAV
import at.bitfire.dav4jvm.property.webdav.DisplayName
import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV
import at.bitfire.dav4jvm.property.webdav.ResourceType
import at.bitfire.dav4jvm.property.caldav.CalDAV
import at.bitfire.dav4jvm.property.carddav.CardDAV
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.AppDatabase
@@ -318,27 +310,27 @@ class DavCollectionRepository @Inject constructor(
setOutput(writer)
startDocument("UTF-8", null)
setPrefix("", NS_WEBDAV)
setPrefix("CAL", NS_CALDAV)
setPrefix("CARD", NS_CARDDAV)
setPrefix("", WebDAV.NS_WEBDAV)
setPrefix("CAL", CalDAV.NS_CALDAV)
setPrefix("CARD", CardDAV.NS_CARDDAV)
if (addressBook)
startTag(NS_WEBDAV, "mkcol")
startTag(WebDAV.NS_WEBDAV, "mkcol")
else
startTag(NS_CALDAV, "mkcalendar")
startTag(CalDAV.NS_CALDAV, "mkcalendar")
insertTag(DavResource.SET) {
insertTag(DavResource.PROP) {
insertTag(ResourceType.NAME) {
insertTag(ResourceType.COLLECTION)
insertTag(WebDAV.Set) {
insertTag(WebDAV.Prop) {
insertTag(WebDAV.ResourceType) {
insertTag(WebDAV.Collection)
if (addressBook)
insertTag(ResourceType.ADDRESSBOOK)
insertTag(CardDAV.Addressbook)
else
insertTag(ResourceType.CALENDAR)
insertTag(CalDAV.Calendar)
}
displayName?.let {
insertTag(DisplayName.NAME) {
insertTag(WebDAV.DisplayName) {
text(it)
}
}
@@ -346,7 +338,7 @@ class DavCollectionRepository @Inject constructor(
if (addressBook) {
// addressbook-specific properties
description?.let {
insertTag(AddressbookDescription.NAME) {
insertTag(CardDAV.AddressbookDescription) {
text(it)
}
}
@@ -354,21 +346,21 @@ class DavCollectionRepository @Inject constructor(
} else {
// calendar-specific properties
description?.let {
insertTag(CalendarDescription.NAME) {
insertTag(CalDAV.CalendarDescription) {
text(it)
}
}
color?.let {
insertTag(CalendarColor.NAME) {
insertTag(CalDAV.CalendarColor) {
text(DavUtils.ARGBtoCalDAVColor(it))
}
}
timezoneId?.let { id ->
insertTag(CalendarTimezoneId.NAME) {
insertTag(CalDAV.CalendarTimezoneId) {
text(id)
}
getVTimeZone(id)?.let { vTimezone ->
insertTag(CalendarTimezone.NAME) {
insertTag(CalDAV.CalendarTimezone) {
text(
// spec requires "an iCalendar object with exactly one VTIMEZONE component"
Calendar(
@@ -386,19 +378,19 @@ class DavCollectionRepository @Inject constructor(
}
if (!supportsVEVENT || !supportsVTODO || !supportsVJOURNAL) {
insertTag(SupportedCalendarComponentSet.NAME) {
insertTag(CalDAV.SupportedCalendarComponentSet) {
// Only if there's at least one not explicitly supported calendar component set,
// otherwise don't include the property, which means "supports everything".
if (supportsVEVENT)
insertTag(SupportedCalendarComponentSet.COMP) {
insertTag(CalDAV.Comp) {
attribute(null, "name", Component.VEVENT)
}
if (supportsVTODO)
insertTag(SupportedCalendarComponentSet.COMP) {
insertTag(CalDAV.Comp) {
attribute(null, "name", Component.VTODO)
}
if (supportsVJOURNAL)
insertTag(SupportedCalendarComponentSet.COMP) {
insertTag(CalDAV.Comp) {
attribute(null, "name", Component.VJOURNAL)
}
}
@@ -407,9 +399,9 @@ class DavCollectionRepository @Inject constructor(
}
}
if (addressBook)
endTag(NS_WEBDAV, "mkcol")
endTag(WebDAV.NS_WEBDAV, "mkcol")
else
endTag(NS_CALDAV, "mkcalendar")
endTag(CalDAV.NS_CALDAV, "mkcalendar")
endDocument()
}
return writer.toString()

View File

@@ -13,19 +13,15 @@ import at.bitfire.dav4jvm.okhttp.UrlUtils
import at.bitfire.dav4jvm.okhttp.exception.DavException
import at.bitfire.dav4jvm.okhttp.exception.HttpException
import at.bitfire.dav4jvm.okhttp.exception.UnauthorizedException
import at.bitfire.dav4jvm.property.caldav.CalendarColor
import at.bitfire.dav4jvm.property.caldav.CalendarDescription
import at.bitfire.dav4jvm.property.caldav.CalDAV
import at.bitfire.dav4jvm.property.caldav.CalendarHomeSet
import at.bitfire.dav4jvm.property.caldav.CalendarTimezone
import at.bitfire.dav4jvm.property.caldav.CalendarUserAddressSet
import at.bitfire.dav4jvm.property.caldav.SupportedCalendarComponentSet
import at.bitfire.dav4jvm.property.carddav.AddressbookDescription
import at.bitfire.dav4jvm.property.carddav.AddressbookHomeSet
import at.bitfire.dav4jvm.property.carddav.CardDAV
import at.bitfire.dav4jvm.property.common.HrefListProperty
import at.bitfire.dav4jvm.property.webdav.CurrentUserPrincipal
import at.bitfire.dav4jvm.property.webdav.CurrentUserPrivilegeSet
import at.bitfire.dav4jvm.property.webdav.DisplayName
import at.bitfire.dav4jvm.property.webdav.ResourceType
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.db.Collection
import at.bitfire.davdroid.log.StringHandler
import at.bitfire.davdroid.network.DnsRecordResolver
@@ -230,21 +226,23 @@ class DavResourceFinder @AssistedInject constructor(
Service.CARDDAV -> {
davBaseURL.propfind(
0,
ResourceType.NAME, DisplayName.NAME, AddressbookDescription.NAME,
AddressbookHomeSet.NAME,
CurrentUserPrincipal.NAME
WebDAV.ResourceType, WebDAV.DisplayName,
WebDAV.CurrentUserPrincipal,
CardDAV.AddressbookHomeSet,
CardDAV.AddressbookDescription
) { response, _ ->
scanResponse(ResourceType.ADDRESSBOOK, response, config)
scanResponse(CardDAV.Addressbook, response, config)
}
}
Service.CALDAV -> {
davBaseURL.propfind(
0,
ResourceType.NAME, DisplayName.NAME, CalendarColor.NAME, CalendarDescription.NAME, CalendarTimezone.NAME, CurrentUserPrivilegeSet.NAME, SupportedCalendarComponentSet.NAME,
CalendarHomeSet.NAME,
CurrentUserPrincipal.NAME
WebDAV.ResourceType, WebDAV.DisplayName,
WebDAV.CurrentUserPrincipal, WebDAV.CurrentUserPrivilegeSet,
CalDAV.CalendarHomeSet,
CalDAV.SupportedCalendarComponentSet, CalDAV.CalendarColor, CalDAV.CalendarDescription, CalDAV.CalendarTimezone
) { response, _ ->
scanResponse(ResourceType.CALENDAR, response, config)
scanResponse(CalDAV.Calendar, response, config)
}
}
}
@@ -262,7 +260,7 @@ class DavResourceFinder @AssistedInject constructor(
fun queryEmailAddress(principal: HttpUrl): List<String> {
val mailboxes = LinkedList<String>()
try {
DavResource(httpClient, principal, log).propfind(0, CalendarUserAddressSet.NAME) { response, _ ->
DavResource(httpClient, principal, log).propfind(0, CalDAV.CalendarUserAddressSet) { response, _ ->
response[CalendarUserAddressSet::class.java]?.let { addressSet ->
for (href in addressSet.hrefs)
try {
@@ -301,11 +299,11 @@ class DavResourceFinder @AssistedInject constructor(
val homeSetClass: Class<out HrefListProperty>
val serviceType: Service
when (resourceType) {
ResourceType.ADDRESSBOOK -> {
CardDAV.Addressbook -> {
homeSetClass = AddressbookHomeSet::class.java
serviceType = Service.CARDDAV
}
ResourceType.CALENDAR -> {
CalDAV.Calendar -> {
homeSetClass = CalendarHomeSet::class.java
serviceType = Service.CALDAV
}
@@ -326,7 +324,7 @@ class DavResourceFinder @AssistedInject constructor(
}
// ... and/or a principal?
if (it.types.contains(ResourceType.PRINCIPAL))
if (it.types.contains(WebDAV.Principal))
principal = davResponse.href
}
@@ -446,7 +444,7 @@ class DavResourceFinder @AssistedInject constructor(
*/
fun getCurrentUserPrincipal(url: HttpUrl, service: Service?): HttpUrl? {
var principal: HttpUrl? = null
DavResource(httpClient, url, log).propfind(0, CurrentUserPrincipal.NAME) { response, _ ->
DavResource(httpClient, url, log).propfind(0, WebDAV.CurrentUserPrincipal) { response, _ ->
response[CurrentUserPrincipal::class.java]?.href?.let { href ->
response.requestedUrl.resolve(href)?.let {
log.info("Found current-user-principal: $it")

View File

@@ -6,8 +6,7 @@ package at.bitfire.davdroid.servicedetection
import at.bitfire.dav4jvm.okhttp.DavResource
import at.bitfire.dav4jvm.okhttp.exception.HttpException
import at.bitfire.dav4jvm.property.webdav.DisplayName
import at.bitfire.dav4jvm.property.webdav.ResourceType
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.db.AppDatabase
import at.bitfire.davdroid.db.Principal
import at.bitfire.davdroid.db.Service
@@ -36,8 +35,8 @@ class PrincipalsRefresher @AssistedInject constructor(
* Principal properties to ask the server for.
*/
private val principalProperties = arrayOf(
DisplayName.NAME,
ResourceType.NAME
WebDAV.DisplayName,
WebDAV.ResourceType
)
/**

View File

@@ -5,19 +5,10 @@
package at.bitfire.davdroid.servicedetection
import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.property.caldav.CalendarColor
import at.bitfire.dav4jvm.property.caldav.CalendarDescription
import at.bitfire.dav4jvm.property.caldav.CalendarTimezone
import at.bitfire.dav4jvm.property.caldav.CalendarTimezoneId
import at.bitfire.dav4jvm.property.caldav.Source
import at.bitfire.dav4jvm.property.caldav.SupportedCalendarComponentSet
import at.bitfire.dav4jvm.property.carddav.AddressbookDescription
import at.bitfire.dav4jvm.property.push.PushTransports
import at.bitfire.dav4jvm.property.push.Topic
import at.bitfire.dav4jvm.property.webdav.CurrentUserPrivilegeSet
import at.bitfire.dav4jvm.property.webdav.DisplayName
import at.bitfire.dav4jvm.property.webdav.Owner
import at.bitfire.dav4jvm.property.webdav.ResourceType
import at.bitfire.dav4jvm.property.caldav.CalDAV
import at.bitfire.dav4jvm.property.carddav.CardDAV
import at.bitfire.dav4jvm.property.push.WebDAVPush
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.db.Collection
import at.bitfire.davdroid.db.Service
import at.bitfire.davdroid.db.ServiceType
@@ -29,24 +20,24 @@ object ServiceDetectionUtils {
*/
fun collectionQueryProperties(@ServiceType serviceType: String): Array<Property.Name> =
arrayOf( // generic WebDAV properties
CurrentUserPrivilegeSet.NAME,
DisplayName.NAME,
Owner.NAME,
ResourceType.NAME,
PushTransports.NAME, // WebDAV-Push
Topic.NAME
WebDAV.CurrentUserPrivilegeSet,
WebDAV.DisplayName,
WebDAV.Owner,
WebDAV.ResourceType,
WebDAVPush.Transports,
WebDAVPush.Topic
) + when (serviceType) { // service-specific CalDAV/CardDAV properties
Service.TYPE_CARDDAV -> arrayOf(
AddressbookDescription.NAME
CardDAV.AddressbookDescription
)
Service.TYPE_CALDAV -> arrayOf(
CalendarColor.NAME,
CalendarDescription.NAME,
CalendarTimezone.NAME,
CalendarTimezoneId.NAME,
SupportedCalendarComponentSet.NAME,
Source.NAME
CalDAV.CalendarColor,
CalDAV.CalendarDescription,
CalDAV.CalendarTimezone,
CalDAV.CalendarTimezoneId,
CalDAV.SupportedCalendarComponentSet,
CalDAV.Source
)
else -> throw IllegalArgumentException()

View File

@@ -8,14 +8,16 @@ import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.okhttp.DavResource
import at.bitfire.dav4jvm.okhttp.UrlUtils
import at.bitfire.dav4jvm.okhttp.exception.HttpException
import at.bitfire.dav4jvm.property.caldav.CalDAV
import at.bitfire.dav4jvm.property.caldav.CalendarHomeSet
import at.bitfire.dav4jvm.property.caldav.CalendarProxyReadFor
import at.bitfire.dav4jvm.property.caldav.CalendarProxyWriteFor
import at.bitfire.dav4jvm.property.carddav.AddressbookHomeSet
import at.bitfire.dav4jvm.property.carddav.CardDAV
import at.bitfire.dav4jvm.property.common.HrefListProperty
import at.bitfire.dav4jvm.property.webdav.DisplayName
import at.bitfire.dav4jvm.property.webdav.GroupMembership
import at.bitfire.dav4jvm.property.webdav.ResourceType
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.db.HomeSet
import at.bitfire.davdroid.db.Service
import at.bitfire.davdroid.repository.DavHomeSetRepository
@@ -59,18 +61,18 @@ class ServiceRefresher @AssistedInject constructor(
*/
private val homeSetProperties: Array<Property.Name> =
arrayOf( // generic WebDAV properties
DisplayName.NAME,
GroupMembership.NAME,
ResourceType.NAME
WebDAV.DisplayName,
WebDAV.GroupMembership,
WebDAV.ResourceType
) + when (service.type) { // service-specific CalDAV/CardDAV properties
Service.TYPE_CARDDAV -> arrayOf(
AddressbookHomeSet.NAME,
CardDAV.AddressbookHomeSet,
)
Service.TYPE_CALDAV -> arrayOf(
CalendarHomeSet.NAME,
CalendarProxyReadFor.NAME,
CalendarProxyWriteFor.NAME
CalDAV.CalendarHomeSet,
CalDAV.CalendarProxyReadFor,
CalDAV.CalendarProxyWriteFor
)
else -> throw IllegalArgumentException()
@@ -147,8 +149,8 @@ class ServiceRefresher @AssistedInject constructor(
// If current resource is a calendar-proxy-read/write, it's likely that its parent is a principal, too.
davResponse[ResourceType::class.java]?.let { resourceType ->
val proxyProperties = arrayOf(
ResourceType.CALENDAR_PROXY_READ,
ResourceType.CALENDAR_PROXY_WRITE,
CalDAV.CalendarProxyRead,
CalDAV.CalendarProxyWrite
)
if (proxyProperties.any { resourceType.types.contains(it) })
relatedResources += davResponse.href.parent()

View File

@@ -10,13 +10,13 @@ import at.bitfire.dav4jvm.okhttp.DavCalendar
import at.bitfire.dav4jvm.okhttp.MultiResponseCallback
import at.bitfire.dav4jvm.okhttp.Response
import at.bitfire.dav4jvm.okhttp.exception.DavException
import at.bitfire.dav4jvm.property.caldav.CalDAV
import at.bitfire.dav4jvm.property.caldav.CalendarData
import at.bitfire.dav4jvm.property.caldav.GetCTag
import at.bitfire.dav4jvm.property.caldav.MaxResourceSize
import at.bitfire.dav4jvm.property.caldav.ScheduleTag
import at.bitfire.dav4jvm.property.webdav.GetETag
import at.bitfire.dav4jvm.property.webdav.SupportedReportSet
import at.bitfire.dav4jvm.property.webdav.SyncToken
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.Collection
@@ -109,14 +109,20 @@ class CalendarSyncManager @AssistedInject constructor(
SyncException.wrapWithRemoteResourceSuspending(collection.url) {
var syncState: SyncState? = null
runInterruptible {
davCollection.propfind(0, MaxResourceSize.NAME, SupportedReportSet.NAME, GetCTag.NAME, SyncToken.NAME) { response, relation ->
davCollection.propfind(
0,
CalDAV.MaxResourceSize,
WebDAV.SupportedReportSet,
CalDAV.GetCTag,
WebDAV.SyncToken
) { response, relation ->
if (relation == Response.HrefRelation.SELF) {
response[MaxResourceSize::class.java]?.maxSize?.let { maxSize ->
logger.info("Calendar accepts events up to ${Formatter.formatFileSize(context, maxSize)}")
}
response[SupportedReportSet::class.java]?.let { supported ->
hasCollectionSync = supported.reports.contains(SupportedReportSet.SYNC_COLLECTION)
hasCollectionSync = supported.reports.contains(WebDAV.SyncCollection)
}
syncState = syncState(response)
}

View File

@@ -11,15 +11,15 @@ import at.bitfire.dav4jvm.okhttp.DavAddressBook
import at.bitfire.dav4jvm.okhttp.MultiResponseCallback
import at.bitfire.dav4jvm.okhttp.Response
import at.bitfire.dav4jvm.okhttp.exception.DavException
import at.bitfire.dav4jvm.property.caldav.GetCTag
import at.bitfire.dav4jvm.property.caldav.CalDAV
import at.bitfire.dav4jvm.property.carddav.AddressData
import at.bitfire.dav4jvm.property.carddav.CardDAV
import at.bitfire.dav4jvm.property.carddav.MaxResourceSize
import at.bitfire.dav4jvm.property.carddav.SupportedAddressData
import at.bitfire.dav4jvm.property.webdav.GetContentType
import at.bitfire.dav4jvm.property.webdav.GetETag
import at.bitfire.dav4jvm.property.webdav.ResourceType
import at.bitfire.dav4jvm.property.webdav.SupportedReportSet
import at.bitfire.dav4jvm.property.webdav.SyncToken
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.Collection
@@ -175,7 +175,14 @@ class ContactsSyncManager @AssistedInject constructor(
return SyncException.wrapWithRemoteResourceSuspending(collection.url) {
var syncState: SyncState? = null
runInterruptible {
davCollection.propfind(0, MaxResourceSize.NAME, SupportedAddressData.NAME, SupportedReportSet.NAME, GetCTag.NAME, SyncToken.NAME) { response, relation ->
davCollection.propfind(
0,
CardDAV.MaxResourceSize,
CardDAV.SupportedAddressData,
WebDAV.SupportedReportSet,
CalDAV.GetCTag,
WebDAV.SyncToken
) { response, relation ->
if (relation == Response.HrefRelation.SELF) {
response[MaxResourceSize::class.java]?.maxSize?.let { maxSize ->
logger.info("Address book accepts vCards up to ${Formatter.formatFileSize(context, maxSize)}")
@@ -188,7 +195,7 @@ class ContactsSyncManager @AssistedInject constructor(
// hasJCard = supported.hasJCard()
}
response[SupportedReportSet::class.java]?.let { supported ->
hasCollectionSync = supported.reports.contains(SupportedReportSet.SYNC_COLLECTION)
hasCollectionSync = supported.reports.contains(WebDAV.SyncCollection)
}
syncState = syncState(response)
}
@@ -317,7 +324,7 @@ class ContactsSyncManager @AssistedInject constructor(
override suspend fun listAllRemote(callback: MultiResponseCallback) =
SyncException.wrapWithRemoteResourceSuspending(collection.url) {
runInterruptible {
davCollection.propfind(1, ResourceType.NAME, GetETag.NAME, callback = callback)
davCollection.propfind(1, WebDAV.ResourceType, WebDAV.GetETag, callback = callback)
}
}

View File

@@ -11,11 +11,11 @@ import at.bitfire.dav4jvm.okhttp.DavCalendar
import at.bitfire.dav4jvm.okhttp.MultiResponseCallback
import at.bitfire.dav4jvm.okhttp.Response
import at.bitfire.dav4jvm.okhttp.exception.DavException
import at.bitfire.dav4jvm.property.caldav.CalDAV
import at.bitfire.dav4jvm.property.caldav.CalendarData
import at.bitfire.dav4jvm.property.caldav.GetCTag
import at.bitfire.dav4jvm.property.caldav.MaxResourceSize
import at.bitfire.dav4jvm.property.webdav.GetETag
import at.bitfire.dav4jvm.property.webdav.SyncToken
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.Collection
@@ -84,7 +84,7 @@ class JtxSyncManager @AssistedInject constructor(
SyncException.wrapWithRemoteResourceSuspending(collection.url) {
var syncState: SyncState? = null
runInterruptible {
davCollection.propfind(0, GetCTag.NAME, MaxResourceSize.NAME, SyncToken.NAME) { response, relation ->
davCollection.propfind(0, CalDAV.GetCTag, CalDAV.MaxResourceSize, WebDAV.SyncToken) { response, relation ->
if (relation == Response.HrefRelation.SELF) {
response[MaxResourceSize::class.java]?.maxSize?.let { maxSize ->
logger.info("Collection accepts resources up to ${Formatter.formatFileSize(context, maxSize)}")

View File

@@ -24,10 +24,13 @@ import at.bitfire.dav4jvm.okhttp.exception.NotFoundException
import at.bitfire.dav4jvm.okhttp.exception.PreconditionFailedException
import at.bitfire.dav4jvm.okhttp.exception.ServiceUnavailableException
import at.bitfire.dav4jvm.okhttp.exception.UnauthorizedException
import at.bitfire.dav4jvm.property.caldav.CalDAV
import at.bitfire.dav4jvm.property.caldav.GetCTag
import at.bitfire.dav4jvm.property.caldav.ScheduleTag
import at.bitfire.dav4jvm.property.webdav.GetETag
import at.bitfire.dav4jvm.property.webdav.ResourceType
import at.bitfire.dav4jvm.property.webdav.SyncToken
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.Collection
import at.bitfire.davdroid.repository.AccountRepository
@@ -62,7 +65,7 @@ import javax.net.ssl.SSLHandshakeException
/**
* Synchronizes a local collection with a remote collection.
*
* @param ResourceType type of local resources
* @param LocalType type of local resources
* @param CollectionType type of local collection
* @param RemoteType type of remote collection
*
@@ -74,7 +77,7 @@ import javax.net.ssl.SSLHandshakeException
* @param collection collection info in the database
* @param resync whether re-synchronization is requested
*/
abstract class SyncManager<ResourceType: LocalResource, out CollectionType: LocalCollection<ResourceType>, RemoteType: DavCollection>(
abstract class SyncManager<LocalType: LocalResource, out CollectionType: LocalCollection<LocalType>, RemoteType: DavCollection>(
val account: Account,
val httpClient: OkHttpClient,
val dataType: SyncDataType,
@@ -209,7 +212,7 @@ abstract class SyncManager<ResourceType: LocalResource, out CollectionType: Loca
syncState = SyncState.fromSyncToken(result.first, initialSync)
furtherChanges = result.second
} catch (e: HttpException) {
if (e.errors.contains(Error.VALID_SYNC_TOKEN)) {
if (e.errors.contains(Error(WebDAV.ValidSyncToken))) {
logger.info("Sync token invalid, performing initial sync")
initialSync = true
resetPresentRemotely()
@@ -387,7 +390,7 @@ abstract class SyncManager<ResourceType: LocalResource, out CollectionType: Loca
* @param forceAsNew whether the ETag (and Schedule-Tag) of [local] are ignored and the resource
* is created as a new resource on the server
*/
protected open suspend fun uploadDirty(local: ResourceType, forceAsNew: Boolean = false) {
protected open suspend fun uploadDirty(local: LocalType, forceAsNew: Boolean = false) {
val existingFileName = local.fileName
val upload = generateUpload(local)
@@ -451,7 +454,7 @@ abstract class SyncManager<ResourceType: LocalResource, out CollectionType: Loca
is ForbiddenException -> {
// HTTP 403 Forbidden
// If and only if the upload failed because of missing permissions, treat it like 412.
if (ex.errors.contains(Error.NEED_PRIVILEGES))
if (ex.errors.contains(Error(WebDAV.NeedPrivileges)))
logger.log(Level.INFO, "Couldn't upload because of missing permissions, ignoring", ex)
else
throw e
@@ -490,7 +493,7 @@ abstract class SyncManager<ResourceType: LocalResource, out CollectionType: Loca
* @return iCalendar or vCard (content + Content-Type) that can be uploaded to the server
*/
@VisibleForTesting
internal abstract fun generateUpload(resource: ResourceType): GeneratedResource
internal abstract fun generateUpload(resource: LocalType): GeneratedResource
/**
* Called after a successful upload (either of a new or an updated resource) so that the local
@@ -503,7 +506,7 @@ abstract class SyncManager<ResourceType: LocalResource, out CollectionType: Loca
* @param context properties that have been generated before the upload and that shall be persisted by this method
*/
private fun onSuccessfulUpload(
local: ResourceType,
local: LocalType,
newFileName: String,
eTag: String?,
scheduleTag: String?,
@@ -612,7 +615,7 @@ abstract class SyncManager<ResourceType: LocalResource, out CollectionType: Loca
return@listRemote
// ignore collections
if (response[at.bitfire.dav4jvm.property.webdav.ResourceType::class.java]?.types?.contains(at.bitfire.dav4jvm.property.webdav.ResourceType.COLLECTION) == true)
if (response[ResourceType::class.java]?.types?.contains(WebDAV.Collection) == true)
return@listRemote
val name = response.hrefName()
@@ -670,7 +673,7 @@ abstract class SyncManager<ResourceType: LocalResource, out CollectionType: Loca
davCollection.reportChanges(
syncState?.takeIf { syncState.type == SyncState.Type.SYNC_TOKEN }?.value,
false, null,
GetETag.NAME
WebDAV.GetETag
) { response, relation ->
when (relation) {
Response.HrefRelation.SELF ->
@@ -747,7 +750,7 @@ abstract class SyncManager<ResourceType: LocalResource, out CollectionType: Loca
private suspend fun querySyncState(): SyncState? {
var state: SyncState? = null
runInterruptible {
davCollection.propfind(0, GetCTag.NAME, SyncToken.NAME) { response, relation ->
davCollection.propfind(0, CalDAV.GetCTag, WebDAV.SyncToken) { response, relation ->
if (relation == Response.HrefRelation.SELF)
state = syncState(response)
}

View File

@@ -10,11 +10,11 @@ import at.bitfire.dav4jvm.okhttp.DavCalendar
import at.bitfire.dav4jvm.okhttp.MultiResponseCallback
import at.bitfire.dav4jvm.okhttp.Response
import at.bitfire.dav4jvm.okhttp.exception.DavException
import at.bitfire.dav4jvm.property.caldav.CalDAV
import at.bitfire.dav4jvm.property.caldav.CalendarData
import at.bitfire.dav4jvm.property.caldav.GetCTag
import at.bitfire.dav4jvm.property.caldav.MaxResourceSize
import at.bitfire.dav4jvm.property.webdav.GetETag
import at.bitfire.dav4jvm.property.webdav.SyncToken
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.Collection
@@ -86,7 +86,7 @@ class TasksSyncManager @AssistedInject constructor(
SyncException.wrapWithRemoteResourceSuspending(collection.url) {
var syncState: SyncState? = null
runInterruptible {
davCollection.propfind(0, MaxResourceSize.NAME, GetCTag.NAME, SyncToken.NAME) { response, relation ->
davCollection.propfind(0, CalDAV.MaxResourceSize, CalDAV.GetCTag, WebDAV.SyncToken) { response, relation ->
if (relation == Response.HrefRelation.SELF) {
response[MaxResourceSize::class.java]?.maxSize?.let { maxSize ->
logger.info("Calendar accepts tasks up to ${Formatter.formatFileSize(context, maxSize)}")

View File

@@ -18,6 +18,7 @@ import at.bitfire.dav4jvm.property.webdav.GetLastModified
import at.bitfire.dav4jvm.property.webdav.QuotaAvailableBytes
import at.bitfire.dav4jvm.property.webdav.QuotaUsedBytes
import at.bitfire.dav4jvm.property.webdav.ResourceType
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.AppDatabase
import at.bitfire.davdroid.db.WebDavDocument
@@ -153,7 +154,7 @@ class QueryChildDocumentsOperation @Inject constructor(
}
val updatedResource = resource.copy(
isDirectory = response[ResourceType::class.java]?.types?.contains(ResourceType.COLLECTION)
isDirectory = response[ResourceType::class.java]?.types?.contains(WebDAV.Collection)
?: resource.isDirectory,
displayName = response[DisplayName::class.java]?.displayName,
mimeType = response[GetContentType::class.java]?.type?.toMediaTypeOrNull(),
@@ -191,15 +192,15 @@ class QueryChildDocumentsOperation @Inject constructor(
companion object {
val DAV_FILE_FIELDS = arrayOf(
ResourceType.NAME,
CurrentUserPrivilegeSet.NAME,
DisplayName.NAME,
GetETag.NAME,
GetContentType.NAME,
GetContentLength.NAME,
GetLastModified.NAME,
QuotaAvailableBytes.NAME,
QuotaUsedBytes.NAME,
WebDAV.ResourceType,
WebDAV.CurrentUserPrivilegeSet,
WebDAV.DisplayName,
WebDAV.GetETag,
WebDAV.GetContentType,
WebDAV.GetContentLength,
WebDAV.GetLastModified,
WebDAV.QuotaAvailableBytes,
WebDAV.QuotaUsedBytes,
)
/** List of currently active [queryChildDocuments] runners.

View File

@@ -19,7 +19,7 @@ androidx-test-rules = "1.7.0"
androidx-test-junit = "1.3.0"
androidx-work = "2.11.0"
bitfire-cert4android = "42d883e958"
bitfire-dav4jvm = "ad80cdccac"
bitfire-dav4jvm = "acd9bca096"
bitfire-synctools = "017187c6d8"
compose-accompanist = "0.37.3"
compose-bom = "2025.11.01"