Simplify propfind requests

pull/1381/head
Alex Baker 5 years ago
parent 50c2094dd6
commit af28d42e2e

@ -1,8 +1,8 @@
package org.tasks.caldav package org.tasks.caldav
import androidx.annotation.WorkerThread
import at.bitfire.cert4android.CustomCertManager import at.bitfire.cert4android.CustomCertManager
import at.bitfire.dav4jvm.DavResource import at.bitfire.dav4jvm.DavResource
import at.bitfire.dav4jvm.Property
import at.bitfire.dav4jvm.Response import at.bitfire.dav4jvm.Response
import at.bitfire.dav4jvm.Response.HrefRelation import at.bitfire.dav4jvm.Response.HrefRelation
import at.bitfire.dav4jvm.XmlUtils.NS_APPLE_ICAL import at.bitfire.dav4jvm.XmlUtils.NS_APPLE_ICAL
@ -32,6 +32,7 @@ import java.io.StringWriter
import java.security.KeyManagementException import java.security.KeyManagementException
import java.security.NoSuchAlgorithmException import java.security.NoSuchAlgorithmException
import java.util.* import java.util.*
import kotlin.coroutines.suspendCoroutine
open class CaldavClient( open class CaldavClient(
private val provider: CaldavClientProvider, private val provider: CaldavClientProvider,
@ -43,49 +44,26 @@ open class CaldavClient(
suspend fun forAccount(account: CaldavAccount) = suspend fun forAccount(account: CaldavAccount) =
provider.forAccount(account) provider.forAccount(account)
@WorkerThread private suspend fun tryFindPrincipal(link: String): String? =
@Throws(DavException::class, IOException::class) httpUrl
private fun tryFindPrincipal(link: String): String? { ?.resolve(link)
val url = httpUrl!!.resolve(link) ?.let { DavResource(httpClient, it) }
Timber.d("Checking for principal: %s", url) ?.propfind(0, CurrentUserPrincipal.NAME)
val davResource = DavResource(httpClient, url!!) ?.firstOrNull()
val responses = ArrayList<Response>() ?.let { (response, _) -> response[CurrentUserPrincipal::class.java] }
davResource.propfind(0, CurrentUserPrincipal.NAME) { response, _ -> ?.href
responses.add(response) ?.takeIf { it.isNotBlank() }
}
if (responses.isNotEmpty()) {
val response = responses[0]
val currentUserPrincipal = response[CurrentUserPrincipal::class.java]
if (currentUserPrincipal != null) {
val href = currentUserPrincipal.href
if (!isNullOrEmpty(href)) {
return href
}
}
}
return null
}
@WorkerThread private suspend fun findHomeset(): String {
@Throws(DavException::class, IOException::class)
private fun findHomeset(): String {
val davResource = DavResource(httpClient, httpUrl!!) val davResource = DavResource(httpClient, httpUrl!!)
val responses = ArrayList<Response>() return davResource
davResource.propfind(0, CalendarHomeSet.NAME) { response, _ -> .propfind(0, CalendarHomeSet.NAME)
responses.add(response) .firstOrNull()
} ?.let { (response, _) -> response[CalendarHomeSet::class.java] }
val response = responses[0] ?.href
val calendarHomeSet = response[CalendarHomeSet::class.java] ?.takeIf { it.isNotBlank() }
?.let { davResource.location.resolve(it).toString() }
?: throw DisplayableException(R.string.caldav_home_set_not_found) ?: throw DisplayableException(R.string.caldav_home_set_not_found)
val hrefs: List<String> = calendarHomeSet.hrefs
if (hrefs.size != 1) {
throw DisplayableException(R.string.caldav_home_set_not_found)
}
val homeSet = hrefs[0]
if (isNullOrEmpty(homeSet)) {
throw DisplayableException(R.string.caldav_home_set_not_found)
}
return davResource.location.resolve(homeSet).toString()
} }
@Throws(IOException::class, DavException::class, NoSuchAlgorithmException::class, KeyManagementException::class) @Throws(IOException::class, DavException::class, NoSuchAlgorithmException::class, KeyManagementException::class)
@ -112,43 +90,15 @@ open class CaldavClient(
.findHomeset() .findHomeset()
} }
@Throws(IOException::class, DavException::class) suspend fun calendars(): List<Response> =
suspend fun calendars(): List<Response> = withContext(Dispatchers.IO) { DavResource(httpClient, httpUrl!!)
val davResource = DavResource(httpClient, httpUrl!!) .propfind(1, *calendarProperties)
val responses = ArrayList<Response>() .filter { (response, relation) ->
davResource.propfind( relation == HrefRelation.MEMBER &&
1, response[ResourceType::class.java]?.types?.contains(CALENDAR) == true &&
ResourceType.NAME, response[SupportedCalendarComponentSet::class.java]?.supportsTasks == true
DisplayName.NAME, }
SupportedCalendarComponentSet.NAME, .map { (response, _) -> response }
GetCTag.NAME,
CalendarColor.NAME,
SyncToken.NAME,
ShareAccess.NAME,
Invite.NAME
) { response: Response, relation: HrefRelation ->
if (relation == HrefRelation.MEMBER) {
responses.add(response)
}
}
val urls: MutableList<Response> = ArrayList()
for (member in responses) {
val resourceType = member[ResourceType::class.java]
if (resourceType == null
|| !resourceType.types.contains(CALENDAR)) {
Timber.d("%s is not a calendar", member)
continue
}
val supportedCalendarComponentSet = member.get(SupportedCalendarComponentSet::class.java)
if (supportedCalendarComponentSet == null
|| !supportedCalendarComponentSet.supportsTasks) {
Timber.d("%s does not support tasks", member)
continue
}
urls.add(member)
}
urls
}
@Throws(IOException::class, HttpException::class) @Throws(IOException::class, HttpException::class)
suspend fun deleteCollection() = withContext(Dispatchers.IO) { suspend fun deleteCollection() = withContext(Dispatchers.IO) {
@ -259,4 +209,31 @@ open class CaldavClient(
customCertManager.appInForeground = true customCertManager.appInForeground = true
return this return this
} }
companion object {
private val calendarProperties = arrayOf(
ResourceType.NAME,
DisplayName.NAME,
SupportedCalendarComponentSet.NAME,
GetCTag.NAME,
CalendarColor.NAME,
SyncToken.NAME,
ShareAccess.NAME,
Invite.NAME
)
private suspend fun DavResource.propfind(
depth: Int,
vararg reqProp: Property.Name
): List<Pair<Response, HrefRelation>> =
withContext(Dispatchers.IO) {
suspendCoroutine { cont ->
val responses = ArrayList<Pair<Response, HrefRelation>>()
propfind(depth, *reqProp) { response, relation ->
responses.add(Pair(response, relation))
}
cont.resumeWith(Result.success(responses))
}
}
}
} }
Loading…
Cancel
Save