Use http client factory for caldav and etesync

pull/1991/head
Alex Baker 3 years ago
parent 37a59099cf
commit 0ae473e27f

@ -1,33 +1,25 @@
package org.tasks.caldav package org.tasks.caldav
import android.content.Context import android.content.Context
import at.bitfire.cert4android.CustomCertManager
import at.bitfire.dav4jvm.BasicDigestAuthHandler import at.bitfire.dav4jvm.BasicDigestAuthHandler
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.Authenticator import okhttp3.Authenticator
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.internal.tls.OkHostnameVerifier
import org.tasks.DebugNetworkInterceptor
import org.tasks.R import org.tasks.R
import org.tasks.billing.Inventory import org.tasks.billing.Inventory
import org.tasks.data.CaldavAccount import org.tasks.data.CaldavAccount
import org.tasks.http.UserAgentInterceptor import org.tasks.http.HttpClientFactory
import org.tasks.preferences.Preferences
import org.tasks.security.KeyStoreEncryption import org.tasks.security.KeyStoreEncryption
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import javax.net.ssl.SSLContext
class CaldavClientProvider @Inject constructor( class CaldavClientProvider @Inject constructor(
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val encryption: KeyStoreEncryption, private val encryption: KeyStoreEncryption,
private val preferences: Preferences, private val inventory: Inventory,
private val interceptor: DebugNetworkInterceptor, private val httpClientFactory: HttpClientFactory,
private val inventory: Inventory
) { ) {
private val tasksUrl = context.getString(R.string.tasks_caldav_url) private val tasksUrl = context.getString(R.string.tasks_caldav_url)
@ -37,11 +29,12 @@ class CaldavClientProvider @Inject constructor(
password: String? = null password: String? = null
): CaldavClient { ): CaldavClient {
val auth = getAuthInterceptor(username, password, url) val auth = getAuthInterceptor(username, password, url)
val customCertManager = newCertManager()
customCertManager.appInForeground = true
return CaldavClient( return CaldavClient(
this, this,
createHttpClient(auth, customCertManager), createHttpClient(
auth = auth,
foreground = true
),
url?.toHttpUrlOrNull() url?.toHttpUrlOrNull()
) )
} }
@ -59,8 +52,7 @@ class CaldavClientProvider @Inject constructor(
account.getPassword(encryption), account.getPassword(encryption),
account.url account.url
) )
val customCertManager = newCertManager() val client = createHttpClient(auth)
val client = createHttpClient(auth, customCertManager)
return if (account.isTasksOrg) { return if (account.isTasksOrg) {
TasksClient(this, client, url?.toHttpUrlOrNull()) TasksClient(this, client, url?.toHttpUrlOrNull())
} else { } else {
@ -68,10 +60,6 @@ class CaldavClientProvider @Inject constructor(
} }
} }
private suspend fun newCertManager() = withContext(Dispatchers.Default) {
CustomCertManager(context)
}
private fun getAuthInterceptor( private fun getAuthInterceptor(
username: String?, username: String?,
password: String?, password: String?,
@ -82,31 +70,21 @@ class CaldavClientProvider @Inject constructor(
else -> BasicDigestAuthHandler(null, username, password) else -> BasicDigestAuthHandler(null, username, password)
} }
private fun createHttpClient(auth: Interceptor?, customCertManager: CustomCertManager): OkHttpClient { private suspend fun createHttpClient(
val hostnameVerifier = customCertManager.hostnameVerifier(OkHostnameVerifier) auth: Interceptor?,
val sslContext = SSLContext.getInstance("TLS") foreground: Boolean = false,
sslContext.init(null, arrayOf(customCertManager), null) ): OkHttpClient {
val builder = OkHttpClient() return httpClientFactory.newClient(foreground = foreground) { builder ->
.newBuilder() builder
.cookieJar(MemoryCookieStore())
.followRedirects(false)
.followSslRedirects(true)
.sslSocketFactory(sslContext.socketFactory, customCertManager)
.hostnameVerifier(hostnameVerifier)
.connectTimeout(15, TimeUnit.SECONDS) .connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS) .readTimeout(120, TimeUnit.SECONDS)
.addNetworkInterceptor(UserAgentInterceptor)
auth?.let { auth?.let {
builder.addNetworkInterceptor(it) builder.addNetworkInterceptor(it)
if (it is Authenticator) { if (it is Authenticator) {
builder.authenticator(it) builder.authenticator(it)
} }
} }
if (preferences.isFlipperEnabled) {
interceptor.apply(builder)
} }
return builder.build()
} }
} }

@ -1,33 +1,26 @@
package org.tasks.etebase package org.tasks.etebase
import android.content.Context import android.content.Context
import at.bitfire.cert4android.CustomCertManager
import com.etebase.client.Account import com.etebase.client.Account
import com.etebase.client.Client import com.etebase.client.Client
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.internal.tls.OkHostnameVerifier
import org.tasks.DebugNetworkInterceptor
import org.tasks.caldav.MemoryCookieStore
import org.tasks.data.CaldavAccount import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavDao import org.tasks.data.CaldavDao
import org.tasks.http.UserAgentInterceptor import org.tasks.http.HttpClientFactory
import org.tasks.preferences.Preferences
import org.tasks.security.KeyStoreEncryption import org.tasks.security.KeyStoreEncryption
import java.security.KeyManagementException import java.security.KeyManagementException
import java.security.NoSuchAlgorithmException import java.security.NoSuchAlgorithmException
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import javax.net.ssl.SSLContext
class EtebaseClientProvider @Inject constructor( class EtebaseClientProvider @Inject constructor(
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val encryption: KeyStoreEncryption, private val encryption: KeyStoreEncryption,
private val preferences: Preferences, private val caldavDao: CaldavDao,
private val interceptor: DebugNetworkInterceptor, private val httpClientFactory: HttpClientFactory,
private val caldavDao: CaldavDao
) { ) {
@Throws(NoSuchAlgorithmException::class, KeyManagementException::class) @Throws(NoSuchAlgorithmException::class, KeyManagementException::class)
suspend fun forAccount(account: CaldavAccount): EtebaseClient = forUrl( suspend fun forAccount(account: CaldavAccount): EtebaseClient = forUrl(
@ -47,26 +40,11 @@ class EtebaseClientProvider @Inject constructor(
} }
private suspend fun createHttpClient(foreground: Boolean): OkHttpClient { private suspend fun createHttpClient(foreground: Boolean): OkHttpClient {
val customCertManager = withContext(Dispatchers.Default) { return httpClientFactory.newClient(foreground = foreground) { builder ->
CustomCertManager(context, foreground) builder
}
val hostnameVerifier = customCertManager.hostnameVerifier(OkHostnameVerifier)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, arrayOf(customCertManager), null)
val builder = OkHttpClient()
.newBuilder()
.addNetworkInterceptor(UserAgentInterceptor)
.cookieJar(MemoryCookieStore())
.followRedirects(false)
.followSslRedirects(true)
.sslSocketFactory(sslContext.socketFactory, customCertManager)
.hostnameVerifier(hostnameVerifier)
.connectTimeout(15, TimeUnit.SECONDS) .connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS) .readTimeout(120, TimeUnit.SECONDS)
if (preferences.isFlipperEnabled) {
interceptor.apply(builder)
} }
return builder.build()
} }
} }

@ -6,6 +6,7 @@ import at.bitfire.dav4jvm.BasicDigestAuthHandler
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.CookieJar
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.internal.tls.OkHostnameVerifier import okhttp3.internal.tls.OkHostnameVerifier
import org.tasks.DebugNetworkInterceptor import org.tasks.DebugNetworkInterceptor
@ -19,45 +20,48 @@ class HttpClientFactory @Inject constructor(
private val preferences: Preferences, private val preferences: Preferences,
private val interceptor: DebugNetworkInterceptor, private val interceptor: DebugNetworkInterceptor,
private val encryption: KeyStoreEncryption, private val encryption: KeyStoreEncryption,
private val cookieJar: CookieJar,
) { ) {
suspend fun newClient(
suspend fun newCertManager() = withContext(Dispatchers.Default) {
CustomCertManager(context)
}
suspend fun newBuilder(
foreground: Boolean = false, foreground: Boolean = false,
username: String? = null, username: String? = null,
encryptedPassword: String? = null encryptedPassword: String? = null
): OkHttpClient.Builder = newBuilder( ): OkHttpClient {
newCertManager(), val decrypted = encryptedPassword?.let { encryption.decrypt(it) }
foreground = foreground, return newClient(foreground = foreground) { builder ->
username = username, if (!username.isNullOrBlank() && !decrypted.isNullOrBlank()) {
password = encryptedPassword?.let { encryption.decrypt(it) } val auth = BasicDigestAuthHandler(null, username, decrypted)
) builder.addNetworkInterceptor(auth)
builder.authenticator(auth)
}
}
}
fun newBuilder( suspend fun newClient(
customCertManager: CustomCertManager,
foreground: Boolean = false, foreground: Boolean = false,
username: String? = null, block: (OkHttpClient.Builder) -> Unit = {}
password: String? = null ): OkHttpClient {
): OkHttpClient.Builder { val customCertManager = withContext(Dispatchers.Default) {
CustomCertManager(context)
}
customCertManager.appInForeground = foreground customCertManager.appInForeground = foreground
val hostnameVerifier = customCertManager.hostnameVerifier(OkHostnameVerifier) val hostnameVerifier = customCertManager.hostnameVerifier(OkHostnameVerifier)
val sslContext = SSLContext.getInstance("TLS") val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, arrayOf(customCertManager), null) sslContext.init(null, arrayOf(customCertManager), null)
val builder = OkHttpClient() val builder = OkHttpClient()
.newBuilder() .newBuilder()
.followRedirects(false)
.followSslRedirects(true)
.sslSocketFactory(sslContext.socketFactory, customCertManager) .sslSocketFactory(sslContext.socketFactory, customCertManager)
.hostnameVerifier(hostnameVerifier) .hostnameVerifier(hostnameVerifier)
.addInterceptor(UserAgentInterceptor)
.cookieJar(cookieJar)
block(builder)
if (preferences.isFlipperEnabled) { if (preferences.isFlipperEnabled) {
interceptor.apply(builder) interceptor.apply(builder)
} }
if (!username.isNullOrBlank() && !password.isNullOrBlank()) { return builder.build()
val auth = BasicDigestAuthHandler(null, username, password)
builder.addNetworkInterceptor(auth)
builder.authenticator(auth)
}
return builder
} }
} }

@ -12,10 +12,12 @@ import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import okhttp3.CookieJar
import org.tasks.analytics.Firebase import org.tasks.analytics.Firebase
import org.tasks.billing.BillingClient import org.tasks.billing.BillingClient
import org.tasks.billing.BillingClientImpl import org.tasks.billing.BillingClientImpl
import org.tasks.billing.Inventory import org.tasks.billing.Inventory
import org.tasks.caldav.MemoryCookieStore
import org.tasks.data.* import org.tasks.data.*
import org.tasks.jobs.WorkManager import org.tasks.jobs.WorkManager
import org.tasks.notifications.NotificationDao import org.tasks.notifications.NotificationDao
@ -121,4 +123,8 @@ class ApplicationModule {
@Provides @Provides
fun providesNotificationManager(@ApplicationContext context: Context) = fun providesNotificationManager(@ApplicationContext context: Context) =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@Provides
@Singleton
fun cookieJar(): CookieJar = MemoryCookieStore()
} }

@ -25,7 +25,7 @@ class GeocoderNominatim @Inject constructor(
override suspend fun reverseGeocode(mapPosition: MapPosition): Place? = override suspend fun reverseGeocode(mapPosition: MapPosition): Place? =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val client = httpClientFactory.newBuilder(foreground = true).build() val client = httpClientFactory.newClient(foreground = true)
val url = "$url/reverse?format=geocodejson&lat=${mapPosition.latitude}&lon=${mapPosition.longitude}" val url = "$url/reverse?format=geocodejson&lat=${mapPosition.latitude}&lon=${mapPosition.longitude}"
val response = client.newCall( val response = client.newCall(
Request.Builder().get().url(url).addHeader(USER_AGENT, UA_VALUE).build() Request.Builder().get().url(url).addHeader(USER_AGENT, UA_VALUE).build()

@ -68,12 +68,11 @@ class PlaceSearchGoogle @Inject constructor(
context.getString(R.string.tasks_org_account_required) context.getString(R.string.tasks_org_account_required)
) )
val client = httpClientFactory val client = httpClientFactory
.newBuilder( .newClient(
foreground = true, foreground = true,
username = account.username, username = account.username,
encryptedPassword = account.password encryptedPassword = account.password
) )
.build()
val response = client.newCall(Request.Builder().get().url(url).build()).execute() val response = client.newCall(Request.Builder().get().url(url).build()).execute()
if (response.isSuccessful) { if (response.isSuccessful) {
response.body?.string()?.toJson()?.apply { checkResult(this) } response.body?.string()?.toJson()?.apply { checkResult(this) }

Loading…
Cancel
Save