1
0
mirror of https://github.com/etesync/android synced 2024-12-23 15:18:14 +00:00

Upgrade cert4android and refactor httpclient based on upstream

This commit is contained in:
Tom Hacohen 2019-03-15 09:50:04 +00:00
parent 75020c1841
commit 4134f78da4
19 changed files with 214 additions and 421 deletions

View File

@ -1,61 +0,0 @@
/*
* Copyright © 2013 2015 Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package com.etesync.syncadapter;
import android.os.Build;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
import at.bitfire.cert4android.CustomCertManager;
import okhttp3.mockwebserver.MockWebServer;
import static androidx.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertTrue;
public class SSLSocketFactoryCompatTest {
SSLSocketFactoryCompat factory;
MockWebServer server = new MockWebServer();
@Before
public void startServer() throws Exception {
factory = new SSLSocketFactoryCompat(new CustomCertManager(getTargetContext().getApplicationContext(), true));
server.start();
}
@After
public void stopServer() throws Exception {
server.shutdown();
}
@Test
public void testUpgradeTLS() throws IOException {
Socket s = factory.createSocket(server.getHostName(), server.getPort());
assertTrue(s instanceof SSLSocket);
SSLSocket ssl = (SSLSocket)s;
assertFalse(org.apache.commons.lang3.ArrayUtils.contains(ssl.getEnabledProtocols(), "SSLv3"));
assertTrue(org.apache.commons.lang3.ArrayUtils.contains(ssl.getEnabledProtocols(), "TLSv1"));
if (Build.VERSION.SDK_INT >= 16) {
assertTrue(org.apache.commons.lang3.ArrayUtils.contains(ssl.getEnabledProtocols(), "TLSv1.1"));
assertTrue(org.apache.commons.lang3.ArrayUtils.contains(ssl.getEnabledProtocols(), "TLSv1.2"));
}
}
}

View File

@ -22,7 +22,6 @@ import android.os.StrictMode
import android.provider.CalendarContract
import android.provider.ContactsContract
import androidx.core.content.ContextCompat
import at.bitfire.cert4android.CustomCertManager
import at.bitfire.ical4android.AndroidCalendar
import at.bitfire.ical4android.CalendarStorageException
import at.bitfire.vcard4android.ContactsStorageException
@ -38,18 +37,12 @@ import io.requery.Persistable
import io.requery.android.sqlite.DatabaseSource
import io.requery.meta.EntityModel
import io.requery.sql.EntityDataStore
import okhttp3.internal.tls.OkHostnameVerifier
import org.acra.ACRA
import org.jetbrains.anko.doAsync
import java.util.*
import javax.net.ssl.HostnameVerifier
class App : Application() {
var certManager: CustomCertManager? = null
private set
/**
* @return [EntityDataStore] single instance for the application.
*
@ -70,7 +63,6 @@ class App : Application() {
@SuppressLint("HardwareIds")
override fun onCreate() {
super.onCreate()
reinitCertManager()
reinitLogger()
StrictMode.enableDefaults()
initPrefVersion()
@ -120,22 +112,6 @@ class App : Application() {
serviceDB.close()
}
fun reinitCertManager() {
if (BuildConfig.customCerts) {
if (certManager != null)
certManager!!.close()
val dbHelper = ServiceDB.OpenHelper(this)
val settings = Settings(dbHelper.readableDatabase)
certManager = CustomCertManager(this, !settings.getBoolean(DISTRUST_SYSTEM_CERTIFICATES, false))
sslSocketFactoryCompat = SSLSocketFactoryCompat(certManager!!)
hostnameVerifier = certManager!!.hostnameVerifier(OkHostnameVerifier.INSTANCE)
dbHelper.close()
}
}
fun reinitLogger() {
Logger.initialize(this)
}
@ -307,16 +283,6 @@ class App : Application() {
var appName: String = "EteSync"
var sslSocketFactoryCompat: SSLSocketFactoryCompat? = null
private set
var hostnameVerifier: HostnameVerifier? = null
private set
init {
at.bitfire.cert4android.Constants.log = java.util.logging.Logger.getLogger("etesync.cert4android")
}
lateinit var accountType: String
private set
lateinit var addressBookAccountType: String

View File

@ -10,141 +10,227 @@ package com.etesync.syncadapter
import android.content.Context
import android.os.Build
import android.security.KeyChain
import at.bitfire.cert4android.CertTlsSocketFactory
import at.bitfire.cert4android.CustomCertManager
import com.etesync.syncadapter.log.Logger
import com.etesync.syncadapter.model.ServiceDB
import com.etesync.syncadapter.model.Settings
import okhttp3.Cache
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import okhttp3.internal.tls.OkHostnameVerifier
import okhttp3.logging.HttpLoggingInterceptor
import java.io.File
import java.io.IOException
import java.net.InetSocketAddress
import java.net.Proxy
import java.net.Socket
import java.net.URI
import java.text.SimpleDateFormat
import java.security.KeyStore
import java.security.Principal
import java.util.*
import java.util.concurrent.TimeUnit
import java.util.logging.Level
import javax.net.ssl.KeyManager
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509ExtendedKeyManager
import javax.net.ssl.X509TrustManager
import java.util.logging.Logger as LoggerType
import com.etesync.syncadapter.log.Logger
object HttpClient {
private val client = OkHttpClient()
private val userAgentInterceptor = UserAgentInterceptor()
class HttpClient private constructor(
val okHttpClient: OkHttpClient,
private val certManager: CustomCertManager?
): AutoCloseable {
private val userAgent: String
companion object {
/** [OkHttpClient] singleton to build all clients from */
val sharedClient = OkHttpClient.Builder()
// set timeouts
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS)
init {
userAgent = "${App.appName}/${BuildConfig.VERSION_NAME} (okhttp3) Android/${Build.VERSION.RELEASE}"
// don't allow redirects by default, because it would break PROPFIND handling
.followRedirects(false)
// add User-Agent to every request
.addNetworkInterceptor(UserAgentInterceptor)
.build()
}
fun create(context: Context?, logger: LoggerType, host: String?, token: String): OkHttpClient {
var builder = defaultBuilder(context, logger)
// use account settings for authentication
builder = addAuthentication(builder, host, token)
return builder.build()
override fun close() {
certManager?.close()
}
@JvmOverloads
fun create(context: Context?, settings: AccountSettings, logger: LoggerType = Logger.log): OkHttpClient {
return create(context, logger, settings.uri!!.host, settings.authToken)
}
class Builder(
val context: Context? = null,
accountSettings: AccountSettings? = null,
val logger: java.util.logging.Logger = Logger.log
) {
private var certManager: CustomCertManager? = null
private var certificateAlias: String? = null
@JvmOverloads
fun create(context: Context?, logger: LoggerType = Logger.log): OkHttpClient {
return defaultBuilder(context, logger).build()
}
private val orig = sharedClient.newBuilder()
fun create(context: Context?, uri: URI, authToken: String): OkHttpClient {
return create(context, Logger.log, uri.host, authToken)
}
init {
// add network logging, if requested
if (logger.isLoggable(Level.FINEST)) {
val loggingInterceptor = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger {
message -> logger.finest(message)
})
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
orig.addInterceptor(loggingInterceptor)
}
private fun defaultBuilder(context: Context?, logger: LoggerType): OkHttpClient.Builder {
val builder = client.newBuilder()
// use MemorizingTrustManager to manage self-signed certificates
if (context != null) {
val app = context.applicationContext as App
if (App.sslSocketFactoryCompat != null && app.certManager != null)
builder.sslSocketFactory(App.sslSocketFactoryCompat!!, app.certManager!!)
if (App.hostnameVerifier != null)
builder.hostnameVerifier(App.hostnameVerifier!!)
}
// set timeouts
builder.connectTimeout(30, TimeUnit.SECONDS)
builder.writeTimeout(30, TimeUnit.SECONDS)
builder.readTimeout(120, TimeUnit.SECONDS)
// custom proxy support
if (context != null) {
val dbHelper = ServiceDB.OpenHelper(context)
try {
context?.let {
val dbHelper = ServiceDB.OpenHelper(context)
val settings = Settings(dbHelper.readableDatabase)
if (settings.getBoolean(App.OVERRIDE_PROXY, false)) {
val address = InetSocketAddress(
settings.getString(App.OVERRIDE_PROXY_HOST, App.OVERRIDE_PROXY_HOST_DEFAULT),
settings.getInt(App.OVERRIDE_PROXY_PORT, App.OVERRIDE_PROXY_PORT_DEFAULT)
)
val distrustSystemCerts = settings.getBoolean(App.DISTRUST_SYSTEM_CERTIFICATES, false)
val proxy = Proxy(Proxy.Type.HTTP, address)
builder.proxy(proxy)
Logger.log.log(Level.INFO, "Using proxy", proxy)
try {
if (settings.getBoolean(App.OVERRIDE_PROXY, false)) {
val address = InetSocketAddress(
settings.getString(App.OVERRIDE_PROXY_HOST, App.OVERRIDE_PROXY_HOST_DEFAULT),
settings.getInt(App.OVERRIDE_PROXY_PORT, App.OVERRIDE_PROXY_PORT_DEFAULT)
)
val proxy = Proxy(Proxy.Type.HTTP, address)
orig.proxy(proxy)
Logger.log.log(Level.INFO, "Using proxy", proxy)
}
} catch (e: Exception) {
Logger.log.log(Level.SEVERE, "Can't set proxy, ignoring", e)
} finally {
dbHelper.close()
}
} catch (e: IllegalArgumentException) {
Logger.log.log(Level.SEVERE, "Can't set proxy, ignoring", e)
} catch (e: NullPointerException) {
Logger.log.log(Level.SEVERE, "Can't set proxy, ignoring", e)
} finally {
dbHelper.close()
//if (BuildConfig.customCerts)
customCertManager(CustomCertManager(context, !distrustSystemCerts))
}
// use account settings for authentication
accountSettings?.let {
addAuthentication(accountSettings.uri!!.host, accountSettings.authToken)
}
}
// add User-Agent to every request
builder.addNetworkInterceptor(userAgentInterceptor)
// add network logging, if requested
if (logger.isLoggable(Level.FINEST)) {
val loggingInterceptor = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { message -> logger.finest(message) })
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
builder.addInterceptor(loggingInterceptor)
constructor(context: Context, host: String?, authToken: String): this(context) {
addAuthentication(host, authToken)
}
return builder
}
private fun addAuthentication(builder: OkHttpClient.Builder, host: String?, token: String): OkHttpClient.Builder {
val authHandler = TokenAuthenticator(host, token)
return builder.addNetworkInterceptor(authHandler)
}
private class TokenAuthenticator internal constructor(internal val host: String?, internal val token: String?) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
/* Only add to the host we want. */
if (host == null || request.url().host() == host) {
if (token != null && request.header(HEADER_AUTHORIZATION) == null) {
request = request.newBuilder()
.header(HEADER_AUTHORIZATION, "Token $token")
.build()
fun withDiskCache(): Builder {
val context = context ?: throw IllegalArgumentException("Context is required to find the cache directory")
for (dir in arrayOf(context.externalCacheDir, context.cacheDir).filterNotNull()) {
if (dir.exists() && dir.canWrite()) {
val cacheDir = File(dir, "HttpClient")
cacheDir.mkdir()
Logger.log.fine("Using disk cache: $cacheDir")
orig.cache(Cache(cacheDir, 10*1024*1024))
break
}
}
return chain.proceed(request)
return this
}
companion object {
protected val HEADER_AUTHORIZATION = "Authorization"
fun customCertManager(manager: CustomCertManager) {
certManager = manager
}
fun setForeground(foreground: Boolean): Builder {
certManager?.appInForeground = foreground
return this
}
private fun addAuthentication(host: String?, token: String): Builder {
val authHandler = TokenAuthenticator(host, token)
orig.addNetworkInterceptor(authHandler)
return this
}
private class TokenAuthenticator internal constructor(internal val host: String?, internal val token: String?) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
/* Only add to the host we want. */
if (host == null || request.url().host() == host) {
if (token != null && request.header(HEADER_AUTHORIZATION) == null) {
request = request.newBuilder()
.header(HEADER_AUTHORIZATION, "Token $token")
.build()
}
}
return chain.proceed(request)
}
companion object {
protected val HEADER_AUTHORIZATION = "Authorization"
}
}
fun build(): HttpClient {
val trustManager = certManager ?: {
val factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
factory.init(null as KeyStore?)
factory.trustManagers.first() as X509TrustManager
}()
val hostnameVerifier = certManager?.hostnameVerifier(OkHostnameVerifier.INSTANCE)
?: OkHostnameVerifier.INSTANCE
var keyManager: KeyManager? = null
try {
certificateAlias?.let { alias ->
val context = requireNotNull(context)
// get client certificate and private key
val certs = KeyChain.getCertificateChain(context, alias) ?: return@let
val key = KeyChain.getPrivateKey(context, alias) ?: return@let
logger.fine("Using client certificate $alias for authentication (chain length: ${certs.size})")
// create Android KeyStore (performs key operations without revealing secret data to DAVx5)
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
// create KeyManager
keyManager = object: X509ExtendedKeyManager() {
override fun getServerAliases(p0: String?, p1: Array<out Principal>?): Array<String>? = null
override fun chooseServerAlias(p0: String?, p1: Array<out Principal>?, p2: Socket?) = null
override fun getClientAliases(p0: String?, p1: Array<out Principal>?) =
arrayOf(alias)
override fun chooseClientAlias(p0: Array<out String>?, p1: Array<out Principal>?, p2: Socket?) =
alias
override fun getCertificateChain(forAlias: String?) =
certs.takeIf { forAlias == alias }
override fun getPrivateKey(forAlias: String?) =
key.takeIf { forAlias == alias }
}
}
} catch (e: Exception) {
logger.log(Level.SEVERE, "Couldn't set up client certificate authentication", e)
}
orig.sslSocketFactory(CertTlsSocketFactory(keyManager, trustManager), trustManager)
orig.hostnameVerifier(hostnameVerifier)
return HttpClient(orig.build(), certManager)
}
}
internal class UserAgentInterceptor : Interceptor {
private object UserAgentInterceptor : Interceptor {
private val userAgent = "${App.appName}/${BuildConfig.VERSION_NAME} (okhttp3) Android/${Build.VERSION.RELEASE}"
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val locale = Locale.getDefault()

View File

@ -1,169 +0,0 @@
/*
* Copyright © 2013 2015 Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package com.etesync.syncadapter
import android.os.Build
import com.etesync.syncadapter.log.Logger
import java.io.IOException
import java.net.InetAddress
import java.net.Socket
import java.security.GeneralSecurityException
import java.util.*
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
class SSLSocketFactoryCompat(trustManager: X509TrustManager) : SSLSocketFactory() {
private var delegate: SSLSocketFactory? = null
init {
try {
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, arrayOf(trustManager), null)
delegate = sslContext.socketFactory
} catch (e: GeneralSecurityException) {
throw AssertionError() // The system has no TLS. Just give up.
}
}
private fun upgradeTLS(ssl: SSLSocket) {
if (protocols != null)
ssl.enabledProtocols = protocols
if (cipherSuites != null)
ssl.enabledCipherSuites = cipherSuites
}
override fun getDefaultCipherSuites(): Array<String>? {
return cipherSuites
}
override fun getSupportedCipherSuites(): Array<String>? {
return cipherSuites
}
@Throws(IOException::class)
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
val ssl = delegate!!.createSocket(s, host, port, autoClose)
if (ssl is SSLSocket)
upgradeTLS(ssl)
return ssl
}
@Throws(IOException::class)
override fun createSocket(host: String, port: Int): Socket {
val ssl = delegate!!.createSocket(host, port)
if (ssl is SSLSocket)
upgradeTLS(ssl)
return ssl
}
@Throws(IOException::class)
override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket {
val ssl = delegate!!.createSocket(host, port, localHost, localPort)
if (ssl is SSLSocket)
upgradeTLS(ssl)
return ssl
}
@Throws(IOException::class)
override fun createSocket(host: InetAddress, port: Int): Socket {
val ssl = delegate!!.createSocket(host, port)
if (ssl is SSLSocket)
upgradeTLS(ssl)
return ssl
}
@Throws(IOException::class)
override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket {
val ssl = delegate!!.createSocket(address, port, localAddress, localPort)
if (ssl is SSLSocket)
upgradeTLS(ssl)
return ssl
}
companion object {
// Android 5.0+ (API level 21) provides reasonable default settings
// but it still allows SSLv3
// https://developer.android.com/reference/javax/net/ssl/SSLSocket.html
var protocols: Array<String>? = null
var cipherSuites: Array<String>? = null
init {
if (Build.VERSION.SDK_INT >= 23) {
// Since Android 6.0 (API level 23),
// - TLSv1.1 and TLSv1.2 is enabled by default
// - SSLv3 is disabled by default
// - all modern ciphers are activated by default
protocols = null
cipherSuites = null
Logger.log.fine("Using device default TLS protocols/ciphers")
} else {
(SSLSocketFactory.getDefault().createSocket() as? SSLSocket)?.use { socket ->
try {
/* set reasonable protocol versions */
// - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0)
// - remove all SSL versions (especially SSLv3) because they're insecure now
val whichProtocols = LinkedList<String>()
for (protocol in socket.supportedProtocols.filterNot { it.contains("SSL", true) })
whichProtocols += protocol
Logger.log.info("Enabling (only) these TLS protocols: ${whichProtocols.joinToString(", ")}")
protocols = whichProtocols.toTypedArray()
/* set up reasonable cipher suites */
val knownCiphers = arrayOf(
// TLS 1.2
"TLS_RSA_WITH_AES_256_GCM_SHA384",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
// maximum interoperability
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
"SSL_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA",
// additionally
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"
)
val availableCiphers = socket.supportedCipherSuites
Logger.log.info("Available cipher suites: ${availableCiphers.joinToString(", ")}")
/* For maximum security, preferredCiphers should *replace* enabled ciphers (thus
* disabling ciphers which are enabled by default, but have become unsecure), but for
* the security level of DAVx5 and maximum compatibility, disabling of insecure
* ciphers should be a server-side task */
// for the final set of enabled ciphers, take the ciphers enabled by default, ...
val whichCiphers = LinkedList<String>()
whichCiphers.addAll(socket.enabledCipherSuites)
Logger.log.fine("Cipher suites enabled by default: ${whichCiphers.joinToString(", ")}")
// ... add explicitly allowed ciphers ...
whichCiphers.addAll(knownCiphers)
// ... and keep only those which are actually available
whichCiphers.retainAll(availableCiphers)
Logger.log.info("Enabling (only) these TLS ciphers: " + whichCiphers.joinToString(", "))
cipherSuites = whichCiphers.toTypedArray()
} catch (e: IOException) {
Logger.log.severe("Couldn't determine default TLS settings")
}
}
}
}
}
}

View File

@ -236,15 +236,7 @@ constructor(context: Context, account: Account, settings: AccountSettings, extra
return null
}
var resourceClient = HttpClient.create(context)
// authenticate only against a certain host, and only upon request
// resourceClient = HttpClient.addAuthentication(resourceClient, baseUrl.host(), settings.username(), settings.password());
// allow redirects
resourceClient = resourceClient.newBuilder()
.followRedirects(true)
.build()
val resourceClient = HttpClient.Builder(context).build().okHttpClient
try {
val response = resourceClient.newCall(Request.Builder()

View File

@ -111,7 +111,7 @@ abstract class SyncAdapterService : Service() {
Logger.log.info("Refreshing " + serviceType + " collections of service #" + serviceType.toString())
val settings = AccountSettings(context, account)
val httpClient = HttpClient.create(context, settings)
val httpClient = HttpClient.Builder(context, settings).build().okHttpClient
val journalsManager = JournalManager(httpClient, HttpUrl.get(settings.uri!!)!!)

View File

@ -87,7 +87,7 @@ constructor(protected val context: Context, protected val account: Account, prot
init {
// create HttpClient with given logger
httpClient = HttpClient.create(context, settings)
httpClient = HttpClient.Builder(context, settings).build().okHttpClient
data = (context.applicationContext as App).data
val serviceEntity = JournalModel.Service.fetch(data, accountName, serviceType)

View File

@ -60,7 +60,7 @@ class AddMemberFragment : DialogFragment() {
private inner class MemberAdd : AsyncTask<Void, Void, MemberAdd.AddResult>() {
override fun doInBackground(vararg voids: Void): AddResult {
try {
val httpClient = HttpClient.create(ctx!!, settings!!)
val httpClient = HttpClient.Builder(ctx, settings).build().okHttpClient
val userInfoManager = UserInfoManager(httpClient, remote!!)
val userInfo = userInfoManager.fetch(memberEmail)
@ -102,7 +102,7 @@ class AddMemberFragment : DialogFragment() {
override fun doInBackground(vararg voids: Void): AddResultSecond {
try {
val settings = settings!!
val httpClient = HttpClient.create(ctx!!, settings)
val httpClient = HttpClient.Builder(ctx!!, settings).build().okHttpClient
val journalsManager = JournalManager(httpClient, remote!!)
val data = (ctx!!.applicationContext as App).data

View File

@ -14,6 +14,7 @@ import android.os.Build
import android.os.Bundle
import com.google.android.material.snackbar.Snackbar
import androidx.preference.*
import at.bitfire.cert4android.CustomCertManager
import com.etesync.syncadapter.App
import com.etesync.syncadapter.BuildConfig
import com.etesync.syncadapter.R
@ -43,7 +44,6 @@ class AppSettingsActivity : BaseActivity() {
internal lateinit var settings: Settings
internal lateinit var prefResetHints: Preference
internal lateinit var prefResetCertificates: Preference
internal lateinit var prefOverrideProxy: SwitchPreferenceCompat
internal lateinit var prefDistrustSystemCerts: SwitchPreferenceCompat
@ -125,7 +125,15 @@ class AppSettingsActivity : BaseActivity() {
prefDistrustSystemCerts = findPreference("distrust_system_certs") as SwitchPreferenceCompat
prefDistrustSystemCerts.isChecked = settings.getBoolean(App.DISTRUST_SYSTEM_CERTIFICATES, false)
prefResetCertificates = findPreference("reset_certificates")
findPreference("reset_certificates").apply {
isVisible = BuildConfig.customCerts
isEnabled = true
onPreferenceClickListener = Preference.OnPreferenceClickListener {
resetCertificates()
false
}
}
val prefChangeNotification = findPreference("show_change_notification") as SwitchPreferenceCompat
prefChangeNotification.isChecked = context!!.defaultSharedPreferences.getBoolean(App.CHANGE_NOTIFICATION, true)
@ -143,8 +151,6 @@ class AppSettingsActivity : BaseActivity() {
resetHints()
else if (preference === prefDistrustSystemCerts)
setDistrustSystemCerts(preference.isChecked)
else if (preference === prefResetCertificates)
resetCertificates()
else
return false
return true
@ -157,15 +163,11 @@ class AppSettingsActivity : BaseActivity() {
private fun setDistrustSystemCerts(distrust: Boolean) {
settings.putBoolean(App.DISTRUST_SYSTEM_CERTIFICATES, distrust)
// re-initialize certificate manager
val app = context!!.applicationContext as App
app.reinitCertManager()
}
private fun resetCertificates() {
(context!!.applicationContext as App).certManager?.resetCertificates()
Snackbar.make(view!!, getString(R.string.app_settings_reset_certificates_success), Snackbar.LENGTH_LONG).show()
if (CustomCertManager.resetCertificates(activity!!))
Snackbar.make(view!!, getString(R.string.app_settings_reset_certificates_success), Snackbar.LENGTH_LONG).show()
}
private inner class LanguageTask internal constructor(private val mListPreference: ListPreference) : AsyncTask<Void, Void, LanguageUtils.LocaleList>() {

View File

@ -15,22 +15,4 @@ open class BaseActivity : AppCompatActivity() {
}
return false
}
override fun onResume() {
super.onResume()
val app = applicationContext as App
val certManager = app.certManager
if (certManager != null)
certManager.appInForeground = true
}
override fun onPause() {
super.onPause()
val app = applicationContext as App
val certManager = app.certManager
if (certManager != null)
certManager.appInForeground = false
}
}

View File

@ -61,7 +61,7 @@ open class ChangeEncryptionPasswordActivity : BaseActivity() {
fun changePasswordDo(old_password: String, new_password: String) {
val settings = AccountSettings(this, account)
val httpClient = HttpClient.create(this, settings)
val httpClient = HttpClient.Builder(this, settings).build().okHttpClient
doAsync {
Logger.log.info("Started deriving old key")

View File

@ -53,7 +53,7 @@ class CollectionMembersListFragment : ListFragment(), AdapterView.OnItemClickLis
asyncTask = doAsync {
try {
val settings = AccountSettings(context!!, account)
val httpClient = HttpClient.create(context!!, settings)
val httpClient = HttpClient.Builder(context, settings).build().okHttpClient
val journalsManager = JournalManager(httpClient, HttpUrl.get(settings.uri!!)!!)
val journal = JournalManager.Journal.fakeWithUid(journalEntity.uid)

View File

@ -101,7 +101,8 @@ class CreateCollectionFragment : DialogFragment(), LoaderManager.LoaderCallbacks
val settings = AccountSettings(context, account)
val principal = HttpUrl.get(settings.uri!!)
val journalManager = JournalManager(HttpClient.create(context, settings), principal!!)
val httpClient = HttpClient.Builder(context, settings).build().okHttpClient
val journalManager = JournalManager(httpClient, principal!!)
var uid = info.uid
if (uid == null) {

View File

@ -88,7 +88,8 @@ class DeleteCollectionFragment : DialogFragment(), LoaderManager.LoaderCallbacks
val settings = AccountSettings(context, account)
val principal = HttpUrl.get(settings.uri!!)
val journalManager = JournalManager(HttpClient.create(context, settings), principal!!)
val httpClient = HttpClient.Builder(context, settings).build().okHttpClient
val journalManager = JournalManager(httpClient, principal!!)
val crypto = Crypto.CryptoManager(collectionInfo.version, settings.password(), collectionInfo.uid!!)
journalManager.delete(JournalManager.Journal(crypto, collectionInfo.toJson(), collectionInfo.uid!!))

View File

@ -27,7 +27,7 @@ class RemoveMemberFragment : DialogFragment() {
memberEmail = arguments!!.getString(KEY_MEMBER)
try {
settings = AccountSettings(context!!, account!!)
httpClient = HttpClient.create(context!!, settings!!)
httpClient = HttpClient.Builder(context, settings).build().okHttpClient
} catch (e: InvalidAccountException) {
e.printStackTrace()
}

View File

@ -12,6 +12,7 @@ import com.etesync.syncadapter.HttpClient
import com.etesync.syncadapter.journalmanager.Crypto
import com.etesync.syncadapter.journalmanager.Exceptions
import com.etesync.syncadapter.journalmanager.JournalAuthenticator
import com.etesync.syncadapter.log.Logger
import com.etesync.syncadapter.log.StringHandler
import com.etesync.syncadapter.model.CollectionInfo
import okhttp3.HttpUrl
@ -20,22 +21,14 @@ import java.io.IOException
import java.io.Serializable
import java.net.URI
import java.util.*
import java.util.logging.Level
import java.util.logging.Logger
class BaseConfigurationFinder(protected val context: Context, protected val credentials: LoginCredentials) {
protected val log: Logger
protected val logBuffer = StringHandler()
protected var httpClient: OkHttpClient
init {
log = Logger.getLogger("syncadapter.BaseConfigurationFinder")
log.level = Level.FINEST
log.addHandler(logBuffer)
httpClient = HttpClient.create(context, log)
httpClient = HttpClient.Builder(context).build().okHttpClient
}
@ -50,11 +43,11 @@ class BaseConfigurationFinder(protected val context: Context, protected val cred
try {
authtoken = authenticator.getAuthToken(credentials.userName, credentials.password)
} catch (e: Exceptions.HttpException) {
log.warning(e.message)
Logger.log.warning(e.message)
failed = true
} catch (e: IOException) {
log.warning(e.message)
Logger.log.warning(e.message)
failed = true
}
@ -69,7 +62,7 @@ class BaseConfigurationFinder(protected val context: Context, protected val cred
protected fun findInitialConfiguration(service: CollectionInfo.Type): Configuration.ServiceInfo {
// put discovered information here
val config = Configuration.ServiceInfo()
log.info("Finding initial " + service.toString() + " service configuration")
Logger.log.info("Finding initial " + service.toString() + " service configuration")
return config
}

View File

@ -95,7 +95,7 @@ class SetupEncryptionFragment : DialogFragment() {
try {
val cryptoManager: Crypto.CryptoManager
val httpClient = HttpClient.create(getContext(), config.url, config.authtoken!!)
val httpClient = HttpClient.Builder(context, config.url.host, config.authtoken!!).build().okHttpClient
val userInfoManager = UserInfoManager(httpClient, HttpUrl.get(config.url)!!)
val userInfo = userInfoManager.fetch(config.userName)

View File

@ -51,7 +51,7 @@ class SetupUserInfoFragment : DialogFragment() {
override fun doInBackground(vararg accounts: Account): SetupUserInfo.SetupUserInfoResult {
try {
val cryptoManager: Crypto.CryptoManager
val httpClient = HttpClient.create(context, settings)
val httpClient = HttpClient.Builder(context, settings).build().okHttpClient
val userInfoManager = UserInfoManager(httpClient, HttpUrl.get(settings.uri!!)!!)
var userInfo: UserInfoManager.UserInfo? = userInfoManager.fetch(account.name)

@ -1 +1 @@
Subproject commit af1ae810e8aceddd79fed17e6af8a88cb726bd55
Subproject commit f57a8ee9b11a74ca48c067b42dc0411259c567c8