From f5673aadd2c519325e4fddb663f1d875e1d431ad Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Sun, 19 Jan 2025 21:39:51 +0100 Subject: [PATCH] Minor changes, enable cache support again --- .../at/bitfire/davdroid/network/HttpClient.kt | 36 +++--- .../davdroid/push/PushRegistrationWorker.kt | 112 +++++++++--------- .../repository/DavCollectionRepository.kt | 25 ++-- .../davdroid/sync/AddressBookSyncer.kt | 10 +- .../davdroid/webdav/DavDocumentsProvider.kt | 2 +- 5 files changed, 94 insertions(+), 91 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/network/HttpClient.kt b/app/src/main/kotlin/at/bitfire/davdroid/network/HttpClient.kt index 1785c476b..78e25da20 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/network/HttpClient.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/network/HttpClient.kt @@ -18,6 +18,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import net.openid.appauth.AuthState import net.openid.appauth.AuthorizationService import okhttp3.Authenticator +import okhttp3.Cache import okhttp3.ConnectionSpec import okhttp3.CookieJar import okhttp3.Interceptor @@ -26,6 +27,7 @@ import okhttp3.Protocol import okhttp3.brotli.BrotliInterceptor import okhttp3.internal.tls.OkHostnameVerifier import okhttp3.logging.HttpLoggingInterceptor +import java.io.File import java.net.InetSocketAddress import java.net.Proxy import java.util.concurrent.TimeUnit @@ -38,7 +40,7 @@ import javax.net.ssl.SSLContext class HttpClient( val okHttpClient: OkHttpClient, - val authorizationService: AuthorizationService? = null + private val authorizationService: AuthorizationService? = null ): AutoCloseable { override fun close() { @@ -74,9 +76,9 @@ class HttpClient( return this } - private var loggerLevel: HttpLoggingInterceptor.Level = HttpLoggingInterceptor.Level.BODY - fun setLoggerLevel(level: HttpLoggingInterceptor.Level): Builder { - loggerLevel = level + private var loggerInterceptorLevel: HttpLoggingInterceptor.Level = HttpLoggingInterceptor.Level.BODY + fun loggerInterceptorLevel(level: HttpLoggingInterceptor.Level): Builder { + loggerInterceptorLevel = level return this } @@ -92,17 +94,17 @@ class HttpClient( private var authorizationService: AuthorizationService? = null private var certificateAlias: String? = null fun authenticate(host: String?, credentials: Credentials, authStateCallback: BearerAuthInterceptor.AuthStateUpdateCallback? = null): Builder { - if (credentials.username != null && credentials.password != null) { - // basic/digest auth - val authHandler = BasicDigestAuthHandler(UrlUtils.hostToDomain(host), credentials.username, credentials.password, insecurePreemptive = true) - authenticationInterceptor = authHandler - authenticator = authHandler - - } else if (credentials.authState != null) { + if (credentials.authState != null) { // OAuth val authService = authorizationServiceProvider.get() authenticationInterceptor = BearerAuthInterceptor.fromAuthState(authService, credentials.authState, authStateCallback) authorizationService = authService + } else if (credentials.username != null && credentials.password != null) { + + // basic/digest auth + val authHandler = BasicDigestAuthHandler(UrlUtils.hostToDomain(host), credentials.username, credentials.password, insecurePreemptive = true) + authenticationInterceptor = authHandler + authenticator = authHandler } // client certificate @@ -124,17 +126,18 @@ class HttpClient( return this } + private var cache: Cache? = null @Suppress("unused") fun withDiskCache(): Builder { - /*for (dir in arrayOf(context.externalCacheDir, context.cacheDir).filterNotNull()) { + for (dir in arrayOf(context.externalCacheDir, context.cacheDir).filterNotNull()) { if (dir.exists() && dir.canWrite()) { val cacheDir = File(dir, "HttpClient") cacheDir.mkdir() logger.fine("Using disk cache: $cacheDir") - orig.cache(Cache(cacheDir, DISK_CACHE_MAX_SIZE)) + cache = Cache(cacheDir, DISK_CACHE_MAX_SIZE) break } - }*/ + } return this } @@ -189,6 +192,9 @@ class HttpClient( // offer Brotli and gzip compression (can be disabled per request with `Accept-Encoding: identity`) .addInterceptor(BrotliInterceptor) + // add cache, if requested + .cache(cache) + // app-wide custom proxy support buildProxy(okBuilder) @@ -198,7 +204,7 @@ class HttpClient( // add network logging, if requested if (logger.isLoggable(Level.FINEST)) { val loggingInterceptor = HttpLoggingInterceptor { message -> logger.finest(message) } - loggingInterceptor.level = loggerLevel + loggingInterceptor.level = loggerInterceptorLevel okBuilder.addNetworkInterceptor(loggingInterceptor) } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationWorker.kt b/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationWorker.kt index 883551a40..06d311a83 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationWorker.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationWorker.kt @@ -68,54 +68,54 @@ class PushRegistrationWorker @AssistedInject constructor( } private suspend fun registerPushSubscription(collection: Collection, account: Account, endpoint: String) { - val httpClient = httpClientBuilder.get() + httpClientBuilder.get() .fromAccount(account) .inForeground(true) .build() - runInterruptible { - httpClient.use { client -> - val httpClient = client.okHttpClient - - // requested expiration time: 3 days - val requestedExpiration = Instant.now() + Duration.ofDays(3) - - val serializer = XmlUtils.newSerializer() - val writer = StringWriter() - serializer.setOutput(writer) - serializer.startDocument("UTF-8", true) - serializer.insertTag(Property.Name(NS_WEBDAV_PUSH, "push-register")) { - serializer.insertTag(Property.Name(NS_WEBDAV_PUSH, "subscription")) { - // subscription URL - serializer.insertTag(Property.Name(NS_WEBDAV_PUSH, "web-push-subscription")) { - serializer.insertTag(Property.Name(NS_WEBDAV_PUSH, "push-resource")) { - text(endpoint) + .use { client -> + runInterruptible { + val httpClient = client.okHttpClient + + // requested expiration time: 3 days + val requestedExpiration = Instant.now() + Duration.ofDays(3) + + val serializer = XmlUtils.newSerializer() + val writer = StringWriter() + serializer.setOutput(writer) + serializer.startDocument("UTF-8", true) + serializer.insertTag(Property.Name(NS_WEBDAV_PUSH, "push-register")) { + serializer.insertTag(Property.Name(NS_WEBDAV_PUSH, "subscription")) { + // subscription URL + serializer.insertTag(Property.Name(NS_WEBDAV_PUSH, "web-push-subscription")) { + serializer.insertTag(Property.Name(NS_WEBDAV_PUSH, "push-resource")) { + text(endpoint) + } } } + // requested expiration + serializer.insertTag(Property.Name(NS_WEBDAV_PUSH, "expires")) { + text(HttpUtils.formatDate(requestedExpiration)) + } } - // requested expiration - serializer.insertTag(Property.Name(NS_WEBDAV_PUSH, "expires")) { - text(HttpUtils.formatDate(requestedExpiration)) + serializer.endDocument() + + val xml = writer.toString().toRequestBody(DavResource.MIME_XML) + DavCollection(httpClient, collection.url).post(xml) { response -> + if (response.isSuccessful) { + val subscriptionUrl = response.header("Location") + val expires = response.header("Expires")?.let { expiresDate -> + HttpUtils.parseDate(expiresDate) + } ?: requestedExpiration + collectionRepository.updatePushSubscription( + id = collection.id, + subscriptionUrl = subscriptionUrl, + expires = expires?.epochSecond + ) + } else + logger.warning("Couldn't register push for ${collection.url}: $response") } } - serializer.endDocument() - - val xml = writer.toString().toRequestBody(DavResource.MIME_XML) - DavCollection(httpClient, collection.url).post(xml) { response -> - if (response.isSuccessful) { - val subscriptionUrl = response.header("Location") - val expires = response.header("Expires")?.let { expiresDate -> - HttpUtils.parseDate(expiresDate) - } ?: requestedExpiration - collectionRepository.updatePushSubscription( - id = collection.id, - subscriptionUrl = subscriptionUrl, - expires = expires?.epochSecond - ) - } else - logger.warning("Couldn't register push for ${collection.url}: $response") - } } - } } private suspend fun registerSyncable() { @@ -149,30 +149,30 @@ class PushRegistrationWorker @AssistedInject constructor( } private suspend fun unregisterPushSubscription(collection: Collection, account: Account, url: HttpUrl) { - val httpClient = httpClientBuilder.get() + httpClientBuilder.get() .fromAccount(account) .inForeground(true) .build() - runInterruptible { - httpClient.use { client -> - val httpClient = client.okHttpClient + .use { httpClient -> + runInterruptible { + val httpClient = httpClient.okHttpClient - try { - DavResource(httpClient, url).delete { - // deleted + try { + DavResource(httpClient, url).delete { + // deleted + } + } catch (e: DavException) { + logger.log(Level.WARNING, "Couldn't unregister push for ${collection.url}", e) } - } catch (e: DavException) { - logger.log(Level.WARNING, "Couldn't unregister push for ${collection.url}", e) - } - // remove registration URL from DB in any case - collectionRepository.updatePushSubscription( - id = collection.id, - subscriptionUrl = null, - expires = null - ) + // remove registration URL from DB in any case + collectionRepository.updatePushSubscription( + id = collection.id, + subscriptionUrl = null, + expires = null + ) + } } - } } private suspend fun unregisterNotSyncable() { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/repository/DavCollectionRepository.kt b/app/src/main/kotlin/at/bitfire/davdroid/repository/DavCollectionRepository.kt index 5cb2d5b1b..a4f9ec255 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/repository/DavCollectionRepository.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/repository/DavCollectionRepository.kt @@ -36,7 +36,6 @@ import dagger.hilt.components.SingletonComponent import dagger.multibindings.Multibinds import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runInterruptible -import kotlinx.coroutines.withContext import net.fortuna.ical4j.model.Calendar import net.fortuna.ical4j.model.Component import net.fortuna.ical4j.model.ComponentList @@ -181,12 +180,10 @@ class DavCollectionRepository @Inject constructor( .inForeground(true) .build() .use { httpClient -> - withContext(Dispatchers.IO) { - runInterruptible { - DavResource(httpClient.okHttpClient, collection.url).delete { - // success, otherwise an exception would have been thrown → delete locally, too - delete(collection) - } + runInterruptible(Dispatchers.IO) { + DavResource(httpClient.okHttpClient, collection.url).delete { + // success, otherwise an exception would have been thrown → delete locally, too + delete(collection) } } } @@ -297,14 +294,12 @@ class DavCollectionRepository @Inject constructor( .inForeground(true) .build() .use { httpClient -> - withContext(Dispatchers.IO) { - runInterruptible { - DavResource(httpClient.okHttpClient, url).mkCol( - xmlBody = xmlBody, - method = method - ) { - // success, otherwise an exception would have been thrown - } + runInterruptible(Dispatchers.IO) { + DavResource(httpClient.okHttpClient, url).mkCol( + xmlBody = xmlBody, + method = method + ) { + // success, otherwise an exception would have been thrown } } } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/sync/AddressBookSyncer.kt b/app/src/main/kotlin/at/bitfire/davdroid/sync/AddressBookSyncer.kt index ac428fdd4..4067d6b57 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/sync/AddressBookSyncer.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/sync/AddressBookSyncer.kt @@ -5,6 +5,7 @@ package at.bitfire.davdroid.sync import android.accounts.Account +import android.accounts.AccountManager import android.content.ContentProviderClient import android.provider.ContactsContract import at.bitfire.davdroid.db.Collection @@ -80,11 +81,12 @@ class AddressBookSyncer @AssistedInject constructor( collection: Collection ) { try { - val accountSettings = accountSettingsFactory.create(account) - // handle group method change + val accountSettings = accountSettingsFactory.create(account) val groupMethod = accountSettings.getGroupMethod().name - accountSettings.accountManager.getUserData(addressBook.addressBookAccount, PREVIOUS_GROUP_METHOD)?.let { previousGroupMethod -> + + val accountManager = AccountManager.get(context) + accountManager.getUserData(addressBook.addressBookAccount, PREVIOUS_GROUP_METHOD)?.let { previousGroupMethod -> if (previousGroupMethod != groupMethod) { logger.info("Group method changed, deleting all local contacts/groups") @@ -96,7 +98,7 @@ class AddressBookSyncer @AssistedInject constructor( addressBook.syncState = null } } - accountSettings.accountManager.setAndVerifyUserData(addressBook.addressBookAccount, PREVIOUS_GROUP_METHOD, groupMethod) + accountManager.setAndVerifyUserData(addressBook.addressBookAccount, PREVIOUS_GROUP_METHOD, groupMethod) val syncManager = contactsSyncManagerFactory.contactsSyncManager(account, httpClient.value, extras, authority, syncResult, provider, addressBook, collection) syncManager.performSync() diff --git a/app/src/main/kotlin/at/bitfire/davdroid/webdav/DavDocumentsProvider.kt b/app/src/main/kotlin/at/bitfire/davdroid/webdav/DavDocumentsProvider.kt index 30c9b4374..8fa8ba225 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/webdav/DavDocumentsProvider.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/webdav/DavDocumentsProvider.kt @@ -725,7 +725,7 @@ class DavDocumentsProvider: DocumentsProvider() { */ internal fun httpClient(mountId: Long, logBody: Boolean = true): HttpClient { val builder = httpClientBuilder.get() - .setLoggerLevel(if (logBody) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.HEADERS) + .loggerInterceptorLevel(if (logBody) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.HEADERS) .setCookieStore( cookieStores.getOrPut(mountId) { MemoryCookieStore() } )