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

View File

@@ -6,8 +6,8 @@ package at.bitfire.davdroid.servicedetection
import android.security.NetworkSecurityPolicy import android.security.NetworkSecurityPolicy
import at.bitfire.dav4jvm.okhttp.DavResource import at.bitfire.dav4jvm.okhttp.DavResource
import at.bitfire.dav4jvm.property.carddav.AddressbookHomeSet import at.bitfire.dav4jvm.property.carddav.CardDAV
import at.bitfire.dav4jvm.property.webdav.ResourceType import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.network.HttpClientBuilder import at.bitfire.davdroid.network.HttpClientBuilder
import at.bitfire.davdroid.servicedetection.DavResourceFinder.Configuration.ServiceInfo import at.bitfire.davdroid.servicedetection.DavResourceFinder.Configuration.ServiceInfo
import at.bitfire.davdroid.settings.Credentials import at.bitfire.davdroid.settings.Credentials
@@ -93,8 +93,8 @@ class DavResourceFinderTest {
// recognize home set // recognize home set
var info = ServiceInfo() var info = ServiceInfo()
DavResource(client, server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL)) DavResource(client, server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL))
.propfind(0, AddressbookHomeSet.NAME) { response, _ -> .propfind(0, CardDAV.AddressbookHomeSet) { response, _ ->
finder.scanResponse(ResourceType.ADDRESSBOOK, response, info) finder.scanResponse(CardDAV.Addressbook, response, info)
} }
assertEquals(0, info.collections.size) assertEquals(0, info.collections.size)
assertEquals(1, info.homeSets.size) assertEquals(1, info.homeSets.size)
@@ -103,8 +103,8 @@ class DavResourceFinderTest {
// recognize address book // recognize address book
info = ServiceInfo() info = ServiceInfo()
DavResource(client, server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK)) DavResource(client, server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK))
.propfind(0, ResourceType.NAME) { response, _ -> .propfind(0, WebDAV.ResourceType) { response, _ ->
finder.scanResponse(ResourceType.ADDRESSBOOK, response, info) finder.scanResponse(CardDAV.Addressbook, response, info)
} }
assertEquals(1, info.collections.size) assertEquals(1, info.collections.size)
assertEquals(server.url("$PATH_CARDDAV$SUBPATH_ADDRESSBOOK/"), info.collections.keys.first()) 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.DavCollection
import at.bitfire.dav4jvm.okhttp.MultiResponseCallback import at.bitfire.dav4jvm.okhttp.MultiResponseCallback
import at.bitfire.dav4jvm.okhttp.Response import at.bitfire.dav4jvm.okhttp.Response
import at.bitfire.dav4jvm.property.caldav.CalDAV
import at.bitfire.dav4jvm.property.caldav.GetCTag import at.bitfire.dav4jvm.property.caldav.GetCTag
import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.db.Collection
import at.bitfire.davdroid.di.SyncDispatcher import at.bitfire.davdroid.di.SyncDispatcher
@@ -64,7 +65,7 @@ class TestSyncManager @AssistedInject constructor(
didQueryCapabilities = true didQueryCapabilities = true
var cTag: SyncState? = null var cTag: SyncState? = null
davCollection.propfind(0, GetCTag.NAME) { response, rel -> davCollection.propfind(0, CalDAV.GetCTag) { response, rel ->
if (rel == Response.HrefRelation.SELF) if (rel == Response.HrefRelation.SELF)
response[GetCTag::class.java]?.cTag?.let { response[GetCTag::class.java]?.cTag?.let {
cTag = SyncState(SyncState.Type.CTAG, it) cTag = SyncState(SyncState.Type.CTAG, it)

View File

@@ -12,6 +12,7 @@ import androidx.room.Index
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import at.bitfire.dav4jvm.okhttp.Response import at.bitfire.dav4jvm.okhttp.Response
import at.bitfire.dav4jvm.okhttp.UrlUtils 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.CalendarColor
import at.bitfire.dav4jvm.property.caldav.CalendarDescription import at.bitfire.dav4jvm.property.caldav.CalendarDescription
import at.bitfire.dav4jvm.property.caldav.CalendarTimezone 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.Source
import at.bitfire.dav4jvm.property.caldav.SupportedCalendarComponentSet import at.bitfire.dav4jvm.property.caldav.SupportedCalendarComponentSet
import at.bitfire.dav4jvm.property.carddav.AddressbookDescription 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.PushTransports
import at.bitfire.dav4jvm.property.push.Topic import at.bitfire.dav4jvm.property.push.Topic
import at.bitfire.dav4jvm.property.push.WebPush import at.bitfire.dav4jvm.property.push.WebPush
@@ -166,9 +168,9 @@ data class Collection(
val url = UrlUtils.withTrailingSlash(dav.href) val url = UrlUtils.withTrailingSlash(dav.href)
val type: String = dav[ResourceType::class.java]?.let { resourceType -> val type: String = dav[ResourceType::class.java]?.let { resourceType ->
when { when {
resourceType.types.contains(ResourceType.ADDRESSBOOK) -> TYPE_ADDRESSBOOK resourceType.types.contains(CardDAV.Addressbook) -> TYPE_ADDRESSBOOK
resourceType.types.contains(ResourceType.CALENDAR) -> TYPE_CALENDAR resourceType.types.contains(CalDAV.Calendar) -> TYPE_CALENDAR
resourceType.types.contains(ResourceType.SUBSCRIBED) -> TYPE_WEBCAL resourceType.types.contains(CalDAV.Subscribed) -> TYPE_WEBCAL
else -> null else -> null
} }
} ?: return 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.okhttp.UrlUtils
import at.bitfire.dav4jvm.property.webdav.DisplayName import at.bitfire.dav4jvm.property.webdav.DisplayName
import at.bitfire.dav4jvm.property.webdav.ResourceType import at.bitfire.dav4jvm.property.webdav.ResourceType
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.util.trimToNull import at.bitfire.davdroid.util.trimToNull
import okhttp3.HttpUrl import okhttp3.HttpUrl
@@ -46,7 +47,7 @@ data class Principal(
fun fromDavResponse(serviceId: Long, dav: Response): Principal? { fun fromDavResponse(serviceId: Long, dav: Response): Principal? {
// Check if response is a principal // Check if response is a principal
val resourceType = dav[ResourceType::class.java] ?: return null val resourceType = dav[ResourceType::class.java] ?: return null
if (!resourceType.types.contains(ResourceType.PRINCIPAL)) if (!resourceType.types.contains(WebDAV.Principal))
return null return null
// Try getting the display name of the principal // Try getting the display name of the principal

View File

@@ -7,6 +7,7 @@ package at.bitfire.davdroid.push
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import at.bitfire.dav4jvm.XmlReader import at.bitfire.dav4jvm.XmlReader
import at.bitfire.dav4jvm.XmlUtils 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.db.Collection.Companion.TYPE_ADDRESSBOOK
import at.bitfire.davdroid.repository.AccountRepository import at.bitfire.davdroid.repository.AccountRepository
import at.bitfire.davdroid.repository.DavCollectionRepository import at.bitfire.davdroid.repository.DavCollectionRepository
@@ -105,7 +106,7 @@ class PushMessageHandler @Inject constructor(
try { try {
parser.setInput(StringReader(message)) parser.setInput(StringReader(message))
XmlReader(parser).processTag(DavPushMessage.NAME) { XmlReader(parser).processTag(WebDAVPush.PushMessage) {
val pushMessage = DavPushMessage.Factory.create(parser) val pushMessage = DavPushMessage.Factory.create(parser)
topic = pushMessage.topic?.topic 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.DavCollection
import at.bitfire.dav4jvm.okhttp.DavResource import at.bitfire.dav4jvm.okhttp.DavResource
import at.bitfire.dav4jvm.okhttp.exception.DavException import at.bitfire.dav4jvm.okhttp.exception.DavException
import at.bitfire.dav4jvm.property.push.AuthSecret import at.bitfire.dav4jvm.property.push.WebDAVPush
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.davdroid.db.Collection import at.bitfire.davdroid.db.Collection
import at.bitfire.davdroid.db.Service import at.bitfire.davdroid.db.Service
import at.bitfire.davdroid.di.IoDispatcher import at.bitfire.davdroid.di.IoDispatcher
@@ -237,26 +232,26 @@ class PushRegistrationManager @Inject constructor(
val writer = StringWriter() val writer = StringWriter()
serializer.setOutput(writer) serializer.setOutput(writer)
serializer.startDocument("UTF-8", true) serializer.startDocument("UTF-8", true)
serializer.insertTag(PushRegister.NAME) { serializer.insertTag(WebDAVPush.PushRegister) {
serializer.insertTag(Subscription.NAME) { serializer.insertTag(WebDAVPush.Subscription) {
// subscription URL // subscription URL
serializer.insertTag(WebPushSubscription.NAME) { serializer.insertTag(WebDAVPush.WebPushSubscription) {
serializer.insertTag(PushResource.NAME) { serializer.insertTag(WebDAVPush.PushResource) {
text(endpoint.url) text(endpoint.url)
} }
endpoint.pubKeySet?.let { pubKeySet -> endpoint.pubKeySet?.let { pubKeySet ->
serializer.insertTag(SubscriptionPublicKey.NAME) { serializer.insertTag(WebDAVPush.SubscriptionPublicKey) {
attribute(null, "type", "p256dh") attribute(null, "type", "p256dh")
text(pubKeySet.pubKey) text(pubKeySet.pubKey)
} }
serializer.insertTag(AuthSecret.NAME) { serializer.insertTag(WebDAVPush.AuthSecret) {
text(pubKeySet.auth) text(pubKeySet.auth)
} }
} }
} }
} }
// requested expiration // requested expiration
serializer.insertTag(PushRegister.EXPIRES) { serializer.insertTag(WebDAVPush.Expires) {
text(HttpUtils.formatDate(requestedExpiration)) 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.GoneException
import at.bitfire.dav4jvm.okhttp.exception.HttpException import at.bitfire.dav4jvm.okhttp.exception.HttpException
import at.bitfire.dav4jvm.okhttp.exception.NotFoundException import at.bitfire.dav4jvm.okhttp.exception.NotFoundException
import at.bitfire.dav4jvm.property.caldav.CalendarColor import at.bitfire.dav4jvm.property.caldav.CalDAV
import at.bitfire.dav4jvm.property.caldav.CalendarDescription import at.bitfire.dav4jvm.property.carddav.CardDAV
import at.bitfire.dav4jvm.property.caldav.CalendarTimezone import at.bitfire.dav4jvm.property.webdav.WebDAV
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.davdroid.Constants import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.R import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.AppDatabase
@@ -318,27 +310,27 @@ class DavCollectionRepository @Inject constructor(
setOutput(writer) setOutput(writer)
startDocument("UTF-8", null) startDocument("UTF-8", null)
setPrefix("", NS_WEBDAV) setPrefix("", WebDAV.NS_WEBDAV)
setPrefix("CAL", NS_CALDAV) setPrefix("CAL", CalDAV.NS_CALDAV)
setPrefix("CARD", NS_CARDDAV) setPrefix("CARD", CardDAV.NS_CARDDAV)
if (addressBook) if (addressBook)
startTag(NS_WEBDAV, "mkcol") startTag(WebDAV.NS_WEBDAV, "mkcol")
else else
startTag(NS_CALDAV, "mkcalendar") startTag(CalDAV.NS_CALDAV, "mkcalendar")
insertTag(DavResource.SET) { insertTag(WebDAV.Set) {
insertTag(DavResource.PROP) { insertTag(WebDAV.Prop) {
insertTag(ResourceType.NAME) { insertTag(WebDAV.ResourceType) {
insertTag(ResourceType.COLLECTION) insertTag(WebDAV.Collection)
if (addressBook) if (addressBook)
insertTag(ResourceType.ADDRESSBOOK) insertTag(CardDAV.Addressbook)
else else
insertTag(ResourceType.CALENDAR) insertTag(CalDAV.Calendar)
} }
displayName?.let { displayName?.let {
insertTag(DisplayName.NAME) { insertTag(WebDAV.DisplayName) {
text(it) text(it)
} }
} }
@@ -346,7 +338,7 @@ class DavCollectionRepository @Inject constructor(
if (addressBook) { if (addressBook) {
// addressbook-specific properties // addressbook-specific properties
description?.let { description?.let {
insertTag(AddressbookDescription.NAME) { insertTag(CardDAV.AddressbookDescription) {
text(it) text(it)
} }
} }
@@ -354,21 +346,21 @@ class DavCollectionRepository @Inject constructor(
} else { } else {
// calendar-specific properties // calendar-specific properties
description?.let { description?.let {
insertTag(CalendarDescription.NAME) { insertTag(CalDAV.CalendarDescription) {
text(it) text(it)
} }
} }
color?.let { color?.let {
insertTag(CalendarColor.NAME) { insertTag(CalDAV.CalendarColor) {
text(DavUtils.ARGBtoCalDAVColor(it)) text(DavUtils.ARGBtoCalDAVColor(it))
} }
} }
timezoneId?.let { id -> timezoneId?.let { id ->
insertTag(CalendarTimezoneId.NAME) { insertTag(CalDAV.CalendarTimezoneId) {
text(id) text(id)
} }
getVTimeZone(id)?.let { vTimezone -> getVTimeZone(id)?.let { vTimezone ->
insertTag(CalendarTimezone.NAME) { insertTag(CalDAV.CalendarTimezone) {
text( text(
// spec requires "an iCalendar object with exactly one VTIMEZONE component" // spec requires "an iCalendar object with exactly one VTIMEZONE component"
Calendar( Calendar(
@@ -386,19 +378,19 @@ class DavCollectionRepository @Inject constructor(
} }
if (!supportsVEVENT || !supportsVTODO || !supportsVJOURNAL) { if (!supportsVEVENT || !supportsVTODO || !supportsVJOURNAL) {
insertTag(SupportedCalendarComponentSet.NAME) { insertTag(CalDAV.SupportedCalendarComponentSet) {
// Only if there's at least one not explicitly supported calendar component set, // Only if there's at least one not explicitly supported calendar component set,
// otherwise don't include the property, which means "supports everything". // otherwise don't include the property, which means "supports everything".
if (supportsVEVENT) if (supportsVEVENT)
insertTag(SupportedCalendarComponentSet.COMP) { insertTag(CalDAV.Comp) {
attribute(null, "name", Component.VEVENT) attribute(null, "name", Component.VEVENT)
} }
if (supportsVTODO) if (supportsVTODO)
insertTag(SupportedCalendarComponentSet.COMP) { insertTag(CalDAV.Comp) {
attribute(null, "name", Component.VTODO) attribute(null, "name", Component.VTODO)
} }
if (supportsVJOURNAL) if (supportsVJOURNAL)
insertTag(SupportedCalendarComponentSet.COMP) { insertTag(CalDAV.Comp) {
attribute(null, "name", Component.VJOURNAL) attribute(null, "name", Component.VJOURNAL)
} }
} }
@@ -407,9 +399,9 @@ class DavCollectionRepository @Inject constructor(
} }
} }
if (addressBook) if (addressBook)
endTag(NS_WEBDAV, "mkcol") endTag(WebDAV.NS_WEBDAV, "mkcol")
else else
endTag(NS_CALDAV, "mkcalendar") endTag(CalDAV.NS_CALDAV, "mkcalendar")
endDocument() endDocument()
} }
return writer.toString() 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.DavException
import at.bitfire.dav4jvm.okhttp.exception.HttpException import at.bitfire.dav4jvm.okhttp.exception.HttpException
import at.bitfire.dav4jvm.okhttp.exception.UnauthorizedException import at.bitfire.dav4jvm.okhttp.exception.UnauthorizedException
import at.bitfire.dav4jvm.property.caldav.CalendarColor import at.bitfire.dav4jvm.property.caldav.CalDAV
import at.bitfire.dav4jvm.property.caldav.CalendarDescription
import at.bitfire.dav4jvm.property.caldav.CalendarHomeSet 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.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.AddressbookHomeSet
import at.bitfire.dav4jvm.property.carddav.CardDAV
import at.bitfire.dav4jvm.property.common.HrefListProperty import at.bitfire.dav4jvm.property.common.HrefListProperty
import at.bitfire.dav4jvm.property.webdav.CurrentUserPrincipal 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.ResourceType
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.db.Collection
import at.bitfire.davdroid.log.StringHandler import at.bitfire.davdroid.log.StringHandler
import at.bitfire.davdroid.network.DnsRecordResolver import at.bitfire.davdroid.network.DnsRecordResolver
@@ -230,21 +226,23 @@ class DavResourceFinder @AssistedInject constructor(
Service.CARDDAV -> { Service.CARDDAV -> {
davBaseURL.propfind( davBaseURL.propfind(
0, 0,
ResourceType.NAME, DisplayName.NAME, AddressbookDescription.NAME, WebDAV.ResourceType, WebDAV.DisplayName,
AddressbookHomeSet.NAME, WebDAV.CurrentUserPrincipal,
CurrentUserPrincipal.NAME CardDAV.AddressbookHomeSet,
CardDAV.AddressbookDescription
) { response, _ -> ) { response, _ ->
scanResponse(ResourceType.ADDRESSBOOK, response, config) scanResponse(CardDAV.Addressbook, response, config)
} }
} }
Service.CALDAV -> { Service.CALDAV -> {
davBaseURL.propfind( davBaseURL.propfind(
0, 0,
ResourceType.NAME, DisplayName.NAME, CalendarColor.NAME, CalendarDescription.NAME, CalendarTimezone.NAME, CurrentUserPrivilegeSet.NAME, SupportedCalendarComponentSet.NAME, WebDAV.ResourceType, WebDAV.DisplayName,
CalendarHomeSet.NAME, WebDAV.CurrentUserPrincipal, WebDAV.CurrentUserPrivilegeSet,
CurrentUserPrincipal.NAME CalDAV.CalendarHomeSet,
CalDAV.SupportedCalendarComponentSet, CalDAV.CalendarColor, CalDAV.CalendarDescription, CalDAV.CalendarTimezone
) { response, _ -> ) { 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> { fun queryEmailAddress(principal: HttpUrl): List<String> {
val mailboxes = LinkedList<String>() val mailboxes = LinkedList<String>()
try { 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 -> response[CalendarUserAddressSet::class.java]?.let { addressSet ->
for (href in addressSet.hrefs) for (href in addressSet.hrefs)
try { try {
@@ -301,11 +299,11 @@ class DavResourceFinder @AssistedInject constructor(
val homeSetClass: Class<out HrefListProperty> val homeSetClass: Class<out HrefListProperty>
val serviceType: Service val serviceType: Service
when (resourceType) { when (resourceType) {
ResourceType.ADDRESSBOOK -> { CardDAV.Addressbook -> {
homeSetClass = AddressbookHomeSet::class.java homeSetClass = AddressbookHomeSet::class.java
serviceType = Service.CARDDAV serviceType = Service.CARDDAV
} }
ResourceType.CALENDAR -> { CalDAV.Calendar -> {
homeSetClass = CalendarHomeSet::class.java homeSetClass = CalendarHomeSet::class.java
serviceType = Service.CALDAV serviceType = Service.CALDAV
} }
@@ -326,7 +324,7 @@ class DavResourceFinder @AssistedInject constructor(
} }
// ... and/or a principal? // ... and/or a principal?
if (it.types.contains(ResourceType.PRINCIPAL)) if (it.types.contains(WebDAV.Principal))
principal = davResponse.href principal = davResponse.href
} }
@@ -446,7 +444,7 @@ class DavResourceFinder @AssistedInject constructor(
*/ */
fun getCurrentUserPrincipal(url: HttpUrl, service: Service?): HttpUrl? { fun getCurrentUserPrincipal(url: HttpUrl, service: Service?): HttpUrl? {
var principal: HttpUrl? = null 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[CurrentUserPrincipal::class.java]?.href?.let { href ->
response.requestedUrl.resolve(href)?.let { response.requestedUrl.resolve(href)?.let {
log.info("Found current-user-principal: $it") 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.DavResource
import at.bitfire.dav4jvm.okhttp.exception.HttpException import at.bitfire.dav4jvm.okhttp.exception.HttpException
import at.bitfire.dav4jvm.property.webdav.DisplayName import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.dav4jvm.property.webdav.ResourceType
import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.AppDatabase
import at.bitfire.davdroid.db.Principal import at.bitfire.davdroid.db.Principal
import at.bitfire.davdroid.db.Service import at.bitfire.davdroid.db.Service
@@ -36,8 +35,8 @@ class PrincipalsRefresher @AssistedInject constructor(
* Principal properties to ask the server for. * Principal properties to ask the server for.
*/ */
private val principalProperties = arrayOf( private val principalProperties = arrayOf(
DisplayName.NAME, WebDAV.DisplayName,
ResourceType.NAME WebDAV.ResourceType
) )
/** /**

View File

@@ -5,19 +5,10 @@
package at.bitfire.davdroid.servicedetection package at.bitfire.davdroid.servicedetection
import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.property.caldav.CalendarColor import at.bitfire.dav4jvm.property.caldav.CalDAV
import at.bitfire.dav4jvm.property.caldav.CalendarDescription import at.bitfire.dav4jvm.property.carddav.CardDAV
import at.bitfire.dav4jvm.property.caldav.CalendarTimezone import at.bitfire.dav4jvm.property.push.WebDAVPush
import at.bitfire.dav4jvm.property.caldav.CalendarTimezoneId import at.bitfire.dav4jvm.property.webdav.WebDAV
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.davdroid.db.Collection import at.bitfire.davdroid.db.Collection
import at.bitfire.davdroid.db.Service import at.bitfire.davdroid.db.Service
import at.bitfire.davdroid.db.ServiceType import at.bitfire.davdroid.db.ServiceType
@@ -29,24 +20,24 @@ object ServiceDetectionUtils {
*/ */
fun collectionQueryProperties(@ServiceType serviceType: String): Array<Property.Name> = fun collectionQueryProperties(@ServiceType serviceType: String): Array<Property.Name> =
arrayOf( // generic WebDAV properties arrayOf( // generic WebDAV properties
CurrentUserPrivilegeSet.NAME, WebDAV.CurrentUserPrivilegeSet,
DisplayName.NAME, WebDAV.DisplayName,
Owner.NAME, WebDAV.Owner,
ResourceType.NAME, WebDAV.ResourceType,
PushTransports.NAME, // WebDAV-Push WebDAVPush.Transports,
Topic.NAME WebDAVPush.Topic
) + when (serviceType) { // service-specific CalDAV/CardDAV properties ) + when (serviceType) { // service-specific CalDAV/CardDAV properties
Service.TYPE_CARDDAV -> arrayOf( Service.TYPE_CARDDAV -> arrayOf(
AddressbookDescription.NAME CardDAV.AddressbookDescription
) )
Service.TYPE_CALDAV -> arrayOf( Service.TYPE_CALDAV -> arrayOf(
CalendarColor.NAME, CalDAV.CalendarColor,
CalendarDescription.NAME, CalDAV.CalendarDescription,
CalendarTimezone.NAME, CalDAV.CalendarTimezone,
CalendarTimezoneId.NAME, CalDAV.CalendarTimezoneId,
SupportedCalendarComponentSet.NAME, CalDAV.SupportedCalendarComponentSet,
Source.NAME CalDAV.Source
) )
else -> throw IllegalArgumentException() 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.DavResource
import at.bitfire.dav4jvm.okhttp.UrlUtils import at.bitfire.dav4jvm.okhttp.UrlUtils
import at.bitfire.dav4jvm.okhttp.exception.HttpException 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.CalendarHomeSet
import at.bitfire.dav4jvm.property.caldav.CalendarProxyReadFor import at.bitfire.dav4jvm.property.caldav.CalendarProxyReadFor
import at.bitfire.dav4jvm.property.caldav.CalendarProxyWriteFor import at.bitfire.dav4jvm.property.caldav.CalendarProxyWriteFor
import at.bitfire.dav4jvm.property.carddav.AddressbookHomeSet 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.common.HrefListProperty
import at.bitfire.dav4jvm.property.webdav.DisplayName
import at.bitfire.dav4jvm.property.webdav.GroupMembership import at.bitfire.dav4jvm.property.webdav.GroupMembership
import at.bitfire.dav4jvm.property.webdav.ResourceType 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.HomeSet
import at.bitfire.davdroid.db.Service import at.bitfire.davdroid.db.Service
import at.bitfire.davdroid.repository.DavHomeSetRepository import at.bitfire.davdroid.repository.DavHomeSetRepository
@@ -59,18 +61,18 @@ class ServiceRefresher @AssistedInject constructor(
*/ */
private val homeSetProperties: Array<Property.Name> = private val homeSetProperties: Array<Property.Name> =
arrayOf( // generic WebDAV properties arrayOf( // generic WebDAV properties
DisplayName.NAME, WebDAV.DisplayName,
GroupMembership.NAME, WebDAV.GroupMembership,
ResourceType.NAME WebDAV.ResourceType
) + when (service.type) { // service-specific CalDAV/CardDAV properties ) + when (service.type) { // service-specific CalDAV/CardDAV properties
Service.TYPE_CARDDAV -> arrayOf( Service.TYPE_CARDDAV -> arrayOf(
AddressbookHomeSet.NAME, CardDAV.AddressbookHomeSet,
) )
Service.TYPE_CALDAV -> arrayOf( Service.TYPE_CALDAV -> arrayOf(
CalendarHomeSet.NAME, CalDAV.CalendarHomeSet,
CalendarProxyReadFor.NAME, CalDAV.CalendarProxyReadFor,
CalendarProxyWriteFor.NAME CalDAV.CalendarProxyWriteFor
) )
else -> throw IllegalArgumentException() 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. // 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 -> davResponse[ResourceType::class.java]?.let { resourceType ->
val proxyProperties = arrayOf( val proxyProperties = arrayOf(
ResourceType.CALENDAR_PROXY_READ, CalDAV.CalendarProxyRead,
ResourceType.CALENDAR_PROXY_WRITE, CalDAV.CalendarProxyWrite
) )
if (proxyProperties.any { resourceType.types.contains(it) }) if (proxyProperties.any { resourceType.types.contains(it) })
relatedResources += davResponse.href.parent() 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.MultiResponseCallback
import at.bitfire.dav4jvm.okhttp.Response import at.bitfire.dav4jvm.okhttp.Response
import at.bitfire.dav4jvm.okhttp.exception.DavException 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.CalendarData
import at.bitfire.dav4jvm.property.caldav.GetCTag
import at.bitfire.dav4jvm.property.caldav.MaxResourceSize import at.bitfire.dav4jvm.property.caldav.MaxResourceSize
import at.bitfire.dav4jvm.property.caldav.ScheduleTag import at.bitfire.dav4jvm.property.caldav.ScheduleTag
import at.bitfire.dav4jvm.property.webdav.GetETag import at.bitfire.dav4jvm.property.webdav.GetETag
import at.bitfire.dav4jvm.property.webdav.SupportedReportSet 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.Constants
import at.bitfire.davdroid.R import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.db.Collection
@@ -109,14 +109,20 @@ class CalendarSyncManager @AssistedInject constructor(
SyncException.wrapWithRemoteResourceSuspending(collection.url) { SyncException.wrapWithRemoteResourceSuspending(collection.url) {
var syncState: SyncState? = null var syncState: SyncState? = null
runInterruptible { 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) { if (relation == Response.HrefRelation.SELF) {
response[MaxResourceSize::class.java]?.maxSize?.let { maxSize -> response[MaxResourceSize::class.java]?.maxSize?.let { maxSize ->
logger.info("Calendar accepts events up to ${Formatter.formatFileSize(context, maxSize)}") logger.info("Calendar accepts events up to ${Formatter.formatFileSize(context, maxSize)}")
} }
response[SupportedReportSet::class.java]?.let { supported -> response[SupportedReportSet::class.java]?.let { supported ->
hasCollectionSync = supported.reports.contains(SupportedReportSet.SYNC_COLLECTION) hasCollectionSync = supported.reports.contains(WebDAV.SyncCollection)
} }
syncState = syncState(response) 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.MultiResponseCallback
import at.bitfire.dav4jvm.okhttp.Response import at.bitfire.dav4jvm.okhttp.Response
import at.bitfire.dav4jvm.okhttp.exception.DavException 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.AddressData
import at.bitfire.dav4jvm.property.carddav.CardDAV
import at.bitfire.dav4jvm.property.carddav.MaxResourceSize import at.bitfire.dav4jvm.property.carddav.MaxResourceSize
import at.bitfire.dav4jvm.property.carddav.SupportedAddressData import at.bitfire.dav4jvm.property.carddav.SupportedAddressData
import at.bitfire.dav4jvm.property.webdav.GetContentType import at.bitfire.dav4jvm.property.webdav.GetContentType
import at.bitfire.dav4jvm.property.webdav.GetETag 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.SupportedReportSet
import at.bitfire.dav4jvm.property.webdav.SyncToken import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.Constants import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.R import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.db.Collection
@@ -175,7 +175,14 @@ class ContactsSyncManager @AssistedInject constructor(
return SyncException.wrapWithRemoteResourceSuspending(collection.url) { return SyncException.wrapWithRemoteResourceSuspending(collection.url) {
var syncState: SyncState? = null var syncState: SyncState? = null
runInterruptible { 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) { if (relation == Response.HrefRelation.SELF) {
response[MaxResourceSize::class.java]?.maxSize?.let { maxSize -> response[MaxResourceSize::class.java]?.maxSize?.let { maxSize ->
logger.info("Address book accepts vCards up to ${Formatter.formatFileSize(context, maxSize)}") logger.info("Address book accepts vCards up to ${Formatter.formatFileSize(context, maxSize)}")
@@ -188,7 +195,7 @@ class ContactsSyncManager @AssistedInject constructor(
// hasJCard = supported.hasJCard() // hasJCard = supported.hasJCard()
} }
response[SupportedReportSet::class.java]?.let { supported -> response[SupportedReportSet::class.java]?.let { supported ->
hasCollectionSync = supported.reports.contains(SupportedReportSet.SYNC_COLLECTION) hasCollectionSync = supported.reports.contains(WebDAV.SyncCollection)
} }
syncState = syncState(response) syncState = syncState(response)
} }
@@ -317,7 +324,7 @@ class ContactsSyncManager @AssistedInject constructor(
override suspend fun listAllRemote(callback: MultiResponseCallback) = override suspend fun listAllRemote(callback: MultiResponseCallback) =
SyncException.wrapWithRemoteResourceSuspending(collection.url) { SyncException.wrapWithRemoteResourceSuspending(collection.url) {
runInterruptible { 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.MultiResponseCallback
import at.bitfire.dav4jvm.okhttp.Response import at.bitfire.dav4jvm.okhttp.Response
import at.bitfire.dav4jvm.okhttp.exception.DavException 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.CalendarData
import at.bitfire.dav4jvm.property.caldav.GetCTag
import at.bitfire.dav4jvm.property.caldav.MaxResourceSize import at.bitfire.dav4jvm.property.caldav.MaxResourceSize
import at.bitfire.dav4jvm.property.webdav.GetETag 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.Constants
import at.bitfire.davdroid.R import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.db.Collection
@@ -84,7 +84,7 @@ class JtxSyncManager @AssistedInject constructor(
SyncException.wrapWithRemoteResourceSuspending(collection.url) { SyncException.wrapWithRemoteResourceSuspending(collection.url) {
var syncState: SyncState? = null var syncState: SyncState? = null
runInterruptible { 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) { if (relation == Response.HrefRelation.SELF) {
response[MaxResourceSize::class.java]?.maxSize?.let { maxSize -> response[MaxResourceSize::class.java]?.maxSize?.let { maxSize ->
logger.info("Collection accepts resources up to ${Formatter.formatFileSize(context, 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.PreconditionFailedException
import at.bitfire.dav4jvm.okhttp.exception.ServiceUnavailableException import at.bitfire.dav4jvm.okhttp.exception.ServiceUnavailableException
import at.bitfire.dav4jvm.okhttp.exception.UnauthorizedException 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.GetCTag
import at.bitfire.dav4jvm.property.caldav.ScheduleTag import at.bitfire.dav4jvm.property.caldav.ScheduleTag
import at.bitfire.dav4jvm.property.webdav.GetETag 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.SyncToken
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.R import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.db.Collection
import at.bitfire.davdroid.repository.AccountRepository import at.bitfire.davdroid.repository.AccountRepository
@@ -62,19 +65,19 @@ import javax.net.ssl.SSLHandshakeException
/** /**
* Synchronizes a local collection with a remote collection. * 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 CollectionType type of local collection
* @param RemoteType type of remote collection * @param RemoteType type of remote collection
* *
* @param account account to synchronize * @param account account to synchronize
* @param httpClient HTTP client to use for network requests, already authenticated with credentials from [account] * @param httpClient HTTP client to use for network requests, already authenticated with credentials from [account]
* @param dataType data type to synchronize * @param dataType data type to synchronize
* @param syncResult receiver for result of the synchronization (will be updated by [performSync]) * @param syncResult receiver for result of the synchronization (will be updated by [performSync])
* @param localCollection local collection to synchronize (interface to content provider) * @param localCollection local collection to synchronize (interface to content provider)
* @param collection collection info in the database * @param collection collection info in the database
* @param resync whether re-synchronization is requested * @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 account: Account,
val httpClient: OkHttpClient, val httpClient: OkHttpClient,
val dataType: SyncDataType, val dataType: SyncDataType,
@@ -209,7 +212,7 @@ abstract class SyncManager<ResourceType: LocalResource, out CollectionType: Loca
syncState = SyncState.fromSyncToken(result.first, initialSync) syncState = SyncState.fromSyncToken(result.first, initialSync)
furtherChanges = result.second furtherChanges = result.second
} catch (e: HttpException) { } 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") logger.info("Sync token invalid, performing initial sync")
initialSync = true initialSync = true
resetPresentRemotely() 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 * @param forceAsNew whether the ETag (and Schedule-Tag) of [local] are ignored and the resource
* is created as a new resource on the server * 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 existingFileName = local.fileName
val upload = generateUpload(local) val upload = generateUpload(local)
@@ -451,7 +454,7 @@ abstract class SyncManager<ResourceType: LocalResource, out CollectionType: Loca
is ForbiddenException -> { is ForbiddenException -> {
// HTTP 403 Forbidden // HTTP 403 Forbidden
// If and only if the upload failed because of missing permissions, treat it like 412. // 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) logger.log(Level.INFO, "Couldn't upload because of missing permissions, ignoring", ex)
else else
throw e 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 * @return iCalendar or vCard (content + Content-Type) that can be uploaded to the server
*/ */
@VisibleForTesting @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 * 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 * @param context properties that have been generated before the upload and that shall be persisted by this method
*/ */
private fun onSuccessfulUpload( private fun onSuccessfulUpload(
local: ResourceType, local: LocalType,
newFileName: String, newFileName: String,
eTag: String?, eTag: String?,
scheduleTag: String?, scheduleTag: String?,
@@ -612,7 +615,7 @@ abstract class SyncManager<ResourceType: LocalResource, out CollectionType: Loca
return@listRemote return@listRemote
// ignore collections // 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 return@listRemote
val name = response.hrefName() val name = response.hrefName()
@@ -670,7 +673,7 @@ abstract class SyncManager<ResourceType: LocalResource, out CollectionType: Loca
davCollection.reportChanges( davCollection.reportChanges(
syncState?.takeIf { syncState.type == SyncState.Type.SYNC_TOKEN }?.value, syncState?.takeIf { syncState.type == SyncState.Type.SYNC_TOKEN }?.value,
false, null, false, null,
GetETag.NAME WebDAV.GetETag
) { response, relation -> ) { response, relation ->
when (relation) { when (relation) {
Response.HrefRelation.SELF -> Response.HrefRelation.SELF ->
@@ -747,7 +750,7 @@ abstract class SyncManager<ResourceType: LocalResource, out CollectionType: Loca
private suspend fun querySyncState(): SyncState? { private suspend fun querySyncState(): SyncState? {
var state: SyncState? = null var state: SyncState? = null
runInterruptible { runInterruptible {
davCollection.propfind(0, GetCTag.NAME, SyncToken.NAME) { response, relation -> davCollection.propfind(0, CalDAV.GetCTag, WebDAV.SyncToken) { response, relation ->
if (relation == Response.HrefRelation.SELF) if (relation == Response.HrefRelation.SELF)
state = syncState(response) 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.MultiResponseCallback
import at.bitfire.dav4jvm.okhttp.Response import at.bitfire.dav4jvm.okhttp.Response
import at.bitfire.dav4jvm.okhttp.exception.DavException 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.CalendarData
import at.bitfire.dav4jvm.property.caldav.GetCTag
import at.bitfire.dav4jvm.property.caldav.MaxResourceSize import at.bitfire.dav4jvm.property.caldav.MaxResourceSize
import at.bitfire.dav4jvm.property.webdav.GetETag 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.Constants
import at.bitfire.davdroid.R import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.db.Collection
@@ -86,7 +86,7 @@ class TasksSyncManager @AssistedInject constructor(
SyncException.wrapWithRemoteResourceSuspending(collection.url) { SyncException.wrapWithRemoteResourceSuspending(collection.url) {
var syncState: SyncState? = null var syncState: SyncState? = null
runInterruptible { 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) { if (relation == Response.HrefRelation.SELF) {
response[MaxResourceSize::class.java]?.maxSize?.let { maxSize -> response[MaxResourceSize::class.java]?.maxSize?.let { maxSize ->
logger.info("Calendar accepts tasks up to ${Formatter.formatFileSize(context, 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.QuotaAvailableBytes
import at.bitfire.dav4jvm.property.webdav.QuotaUsedBytes import at.bitfire.dav4jvm.property.webdav.QuotaUsedBytes
import at.bitfire.dav4jvm.property.webdav.ResourceType import at.bitfire.dav4jvm.property.webdav.ResourceType
import at.bitfire.dav4jvm.property.webdav.WebDAV
import at.bitfire.davdroid.R import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.AppDatabase
import at.bitfire.davdroid.db.WebDavDocument import at.bitfire.davdroid.db.WebDavDocument
@@ -153,7 +154,7 @@ class QueryChildDocumentsOperation @Inject constructor(
} }
val updatedResource = resource.copy( val updatedResource = resource.copy(
isDirectory = response[ResourceType::class.java]?.types?.contains(ResourceType.COLLECTION) isDirectory = response[ResourceType::class.java]?.types?.contains(WebDAV.Collection)
?: resource.isDirectory, ?: resource.isDirectory,
displayName = response[DisplayName::class.java]?.displayName, displayName = response[DisplayName::class.java]?.displayName,
mimeType = response[GetContentType::class.java]?.type?.toMediaTypeOrNull(), mimeType = response[GetContentType::class.java]?.type?.toMediaTypeOrNull(),
@@ -191,15 +192,15 @@ class QueryChildDocumentsOperation @Inject constructor(
companion object { companion object {
val DAV_FILE_FIELDS = arrayOf( val DAV_FILE_FIELDS = arrayOf(
ResourceType.NAME, WebDAV.ResourceType,
CurrentUserPrivilegeSet.NAME, WebDAV.CurrentUserPrivilegeSet,
DisplayName.NAME, WebDAV.DisplayName,
GetETag.NAME, WebDAV.GetETag,
GetContentType.NAME, WebDAV.GetContentType,
GetContentLength.NAME, WebDAV.GetContentLength,
GetLastModified.NAME, WebDAV.GetLastModified,
QuotaAvailableBytes.NAME, WebDAV.QuotaAvailableBytes,
QuotaUsedBytes.NAME, WebDAV.QuotaUsedBytes,
) )
/** List of currently active [queryChildDocuments] runners. /** 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-test-junit = "1.3.0"
androidx-work = "2.11.0" androidx-work = "2.11.0"
bitfire-cert4android = "42d883e958" bitfire-cert4android = "42d883e958"
bitfire-dav4jvm = "ad80cdccac" bitfire-dav4jvm = "acd9bca096"
bitfire-synctools = "017187c6d8" bitfire-synctools = "017187c6d8"
compose-accompanist = "0.37.3" compose-accompanist = "0.37.3"
compose-bom = "2025.11.01" compose-bom = "2025.11.01"