Commit 13b74997 authored by Ricki Hirner's avatar Ricki Hirner

Rewrite to Kotlin

* rewrite some UI classes
* move logic from App to CustomCertificates and Logger singletons
parent f65ac727
......@@ -29,6 +29,7 @@ android {
productFlavors {
standard {
versionName "1.6.4-ose"
buildConfigField "boolean", "customCerts", "true"
}
}
......
......@@ -92,7 +92,7 @@ class AccountSettings @Throws(InvalidAccountException::class) constructor(
version = Integer.parseInt(versionStr)
} catch (e: NumberFormatException) {
}
App.log.fine("Account ${account.name} has version $version, current version: $CURRENT_VERSION")
Logger.log.fine("Account ${account.name} has version $version, current version: $CURRENT_VERSION")
if (version < CURRENT_VERSION)
update(version)
......@@ -179,13 +179,13 @@ class AccountSettings @Throws(InvalidAccountException::class) constructor(
private fun update(baseVersion: Int) {
for (toVersion in baseVersion+1 .. CURRENT_VERSION) {
val fromVersion = toVersion-1
App.log.info("Updating account ${account.name} from version $fromVersion to $toVersion")
Logger.log.info("Updating account ${account.name} from version $fromVersion to $toVersion")
try {
val updateProc = this::class.java.getDeclaredMethod("update_${fromVersion}_$toVersion")
updateProc.invoke(this)
accountManager.setUserData(account, KEY_SETTINGS_VERSION, toVersion.toString())
} catch (e: Exception) {
App.log.log(Level.SEVERE, "Couldn't update account settings", e)
Logger.log.log(Level.SEVERE, "Couldn't update account settings", e)
}
}
}
......@@ -245,7 +245,7 @@ class AccountSettings @Throws(InvalidAccountException::class) constructor(
try {
val addrBook = LocalAddressBook(context, account, client)
val url = addrBook.getURL()
App.log.fine("Migrating address book $url")
Logger.log.fine("Migrating address book $url")
// insert CardDAV service
val values = ContentValues(3)
......@@ -269,7 +269,7 @@ class AccountSettings @Throws(InvalidAccountException::class) constructor(
db.insert(HomeSets._TABLE, null, values)
}
} catch (e: ContactsStorageException) {
App.log.log(Level.SEVERE, "Couldn't migrate address book", e)
Logger.log.log(Level.SEVERE, "Couldn't migrate address book", e)
} finally {
if (Build.VERSION.SDK_INT >= 24)
client.close()
......@@ -288,12 +288,12 @@ class AccountSettings @Throws(InvalidAccountException::class) constructor(
val calendars = AndroidCalendar.find(account, client, LocalCalendar.Factory, null, null)
for (calendar in calendars)
calendar.name?.let { url ->
App.log.fine("Migrating calendar $url")
Logger.log.fine("Migrating calendar $url")
collections.add(url)
HttpUrl.parse(url)?.resolve("../")?.let { homeSets.add(it) }
}
} catch (e: CalendarStorageException) {
App.log.log(Level.SEVERE, "Couldn't migrate calendars", e)
Logger.log.log(Level.SEVERE, "Couldn't migrate calendars", e)
} finally {
if (Build.VERSION.SDK_INT >= 24)
client.close()
......@@ -308,12 +308,12 @@ class AccountSettings @Throws(InvalidAccountException::class) constructor(
val taskLists = AndroidTaskList.find(account, provider, LocalTaskList.Factory, null, null)
for (taskList in taskLists)
taskList.syncId?.let { url ->
App.log.fine("Migrating task list $url")
Logger.log.fine("Migrating task list $url")
collections.add(url)
HttpUrl.parse(url)?.resolve("../")?.let { homeSets.add(it) }
}
} catch (e: CalendarStorageException) {
App.log.log(Level.SEVERE, "Couldn't migrate task lists", e)
Logger.log.log(Level.SEVERE, "Couldn't migrate task lists", e)
}
}
......@@ -381,26 +381,26 @@ class AccountSettings @Throws(InvalidAccountException::class) constructor(
// get previous address book settings (including URL)
val raw = ContactsContract.SyncState.get(provider, account)
if (raw == null)
App.log.info("No contacts sync state, ignoring account")
Logger.log.info("No contacts sync state, ignoring account")
else {
parcel.unmarshall(raw, 0, raw.size)
parcel.setDataPosition(0)
val params = parcel.readBundle()
val url = params.getString("url")
if (url == null)
App.log.info("No address book URL, ignoring account")
Logger.log.info("No address book URL, ignoring account")
else {
// create new address book
val info = CollectionInfo(url)
info.type = CollectionInfo.Type.ADDRESS_BOOK
info.displayName = account.name
App.log.log(Level.INFO, "Creating new address book account", url)
Logger.log.log(Level.INFO, "Creating new address book account", url)
val addressBookAccount = Account(LocalAddressBook.accountName(account, info), App.addressBookAccountType)
if (!accountManager.addAccountExplicitly(addressBookAccount, null, LocalAddressBook.initialUserData(account, info.url)))
throw ContactsStorageException("Couldn't create address book account")
// move contacts to new address book
App.log.info("Moving contacts from $account to $addressBookAccount")
Logger.log.info("Moving contacts from $account to $addressBookAccount")
val newAccount = ContentValues(2)
newAccount.put(ContactsContract.RawContacts.ACCOUNT_NAME, addressBookAccount.name)
newAccount.put(ContactsContract.RawContacts.ACCOUNT_TYPE, addressBookAccount.type)
......@@ -411,7 +411,7 @@ class AccountSettings @Throws(InvalidAccountException::class) constructor(
newAccount,
"${ContactsContract.RawContacts.ACCOUNT_NAME}=? AND ${ContactsContract.RawContacts.ACCOUNT_TYPE}=?",
arrayOf(account.name, account.type))
App.log.info("$affected contacts moved to new address book")
Logger.log.info("$affected contacts moved to new address book")
}
ContactsContract.SyncState.set(provider, account, null)
......@@ -436,16 +436,16 @@ class AccountSettings @Throws(InvalidAccountException::class) constructor(
@SuppressLint("UnsafeProtectedBroadcastReceiver,MissingPermission")
override fun onReceive(context: Context, intent: Intent?) {
App.log.info("DAVdroid was updated, checking for AccountSettings version")
Logger.log.info("DAVdroid was updated, checking for AccountSettings version")
// peek into AccountSettings to initiate a possible migration
val accountManager = AccountManager.get(context)
for (account in accountManager.getAccountsByType(context.getString(R.string.account_type)))
try {
App.log.info("Checking account ${account.name}")
Logger.log.info("Checking account ${account.name}")
AccountSettings(context, account)
} catch(e: InvalidAccountException) {
App.log.log(Level.SEVERE, "Couldn't check for updated account settings", e)
Logger.log.log(Level.SEVERE, "Couldn't check for updated account settings", e)
}
}
......
......@@ -14,23 +14,6 @@ import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.os.Process
import android.support.v4.app.NotificationCompat
import android.support.v4.app.NotificationManagerCompat
import android.util.Log
import at.bitfire.cert4android.CustomCertManager
import at.bitfire.davdroid.log.LogcatHandler
import at.bitfire.davdroid.log.PlainTextFormatter
import at.bitfire.davdroid.model.ServiceDB
import at.bitfire.davdroid.model.Settings
import okhttp3.internal.tls.OkHostnameVerifier
import org.apache.commons.lang3.time.DateFormatUtils
import java.io.File
import java.io.IOException
import java.util.logging.FileHandler
import java.util.logging.Level
import java.util.logging.Logger
import javax.net.ssl.HostnameVerifier
class App: Application() {
......@@ -45,13 +28,6 @@ class App: Application() {
@JvmField val OVERRIDE_PROXY_HOST_DEFAULT = "localhost"
@JvmField val OVERRIDE_PROXY_PORT_DEFAULT = 8118
@JvmField
val log = Logger.getLogger("davdroid")!!
init {
at.bitfire.dav4android.Constants.log = Logger.getLogger("davdroid.dav4android")
at.bitfire.cert4android.Constants.log = Logger.getLogger("davdroid.cert4android")
}
lateinit var addressBookAccountType: String
lateinit var addressBooksAuthority: String
......@@ -70,95 +46,17 @@ class App: Application() {
}
var certManager: CustomCertManager? = null
var sslSocketFactoryCompat: SSLSocketFactoryCompat? = null
var hostnameVerifier: HostnameVerifier? = null
override fun onCreate() {
super.onCreate()
reinitCertManager()
reinitLogger()
CustomCertificates.reinitCertManager(this)
Logger.reinitLogger(this)
addressBookAccountType = getString(R.string.account_type_address_book)
addressBooksAuthority = getString(R.string.address_books_authority)
}
fun reinitCertManager() {
if (true /* custom certificate support */) {
certManager?.close()
ServiceDB.OpenHelper(this).use { dbHelper ->
val settings = Settings(dbHelper.readableDatabase)
val mgr = CustomCertManager(this, !settings.getBoolean(DISTRUST_SYSTEM_CERTIFICATES, false))
sslSocketFactoryCompat = SSLSocketFactoryCompat(mgr)
hostnameVerifier = mgr.hostnameVerifier(OkHostnameVerifier.INSTANCE)
certManager = mgr
}
}
}
fun reinitLogger() {
ServiceDB.OpenHelper(this).use { dbHelper ->
val settings = Settings(dbHelper.getReadableDatabase())
val logToFile = settings.getBoolean(LOG_TO_EXTERNAL_STORAGE, false)
val logVerbose = logToFile || Log.isLoggable(log.name, Log.DEBUG)
App.log.info("Verbose logging: $logVerbose")
// set logging level according to preferences
val rootLogger = Logger.getLogger("")
rootLogger.level = if (logVerbose) Level.ALL else Level.INFO
// remove all handlers and add our own logcat handler
rootLogger.useParentHandlers = false
rootLogger.handlers.forEach { rootLogger.removeHandler(it) }
rootLogger.addHandler(LogcatHandler)
val nm = NotificationManagerCompat.from(this)
// log to external file according to preferences
if (logToFile) {
val builder = NotificationCompat.Builder(this)
builder .setSmallIcon(R.drawable.ic_sd_storage_light)
.setLargeIcon(getLauncherBitmap(this))
.setContentTitle(getString(R.string.logging_davdroid_file_logging))
.setLocalOnly(true)
val dir = getExternalFilesDir(null)
if (dir != null)
try {
val fileName = File(dir, "davdroid-${Process.myPid()}-${DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMdd-HHmmss")}.txt").toString()
log.info("Logging to $fileName")
val fileHandler = FileHandler(fileName)
fileHandler.formatter = PlainTextFormatter.DEFAULT
log.addHandler(fileHandler)
builder .setContentText(dir.path)
.setSubText(getString(R.string.logging_to_external_storage_warning))
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setStyle(NotificationCompat.BigTextStyle()
.bigText(getString(R.string.logging_to_external_storage, dir.path)))
.setOngoing(true)
} catch (e: IOException) {
log.log(Level.SEVERE, "Couldn't create external log file", e)
builder .setContentText(getString(R.string.logging_couldnt_create_file, e.localizedMessage))
.setCategory(NotificationCompat.CATEGORY_ERROR)
}
else
builder.setContentText(getString(R.string.logging_no_external_storage))
nm.notify(Constants.NOTIFICATION_EXTERNAL_FILE_LOGGING, builder.build())
} else
nm.cancel(Constants.NOTIFICATION_EXTERNAL_FILE_LOGGING)
}
}
class ReinitSettingsReceiver: BroadcastReceiver() {
......@@ -167,15 +65,12 @@ class App: Application() {
}
override fun onReceive(context: Context, intent: Intent) {
log.info("Received broadcast: re-initializing settings (logger/cert manager)")
val app = context.applicationContext
if (app is App) {
app.reinitLogger()
app.reinitCertManager()
} else
App.log.severe("context is ${app::class.java.canonicalName} instead of App")
Logger.log.info("Received broadcast: re-initializing settings (logger/cert manager)")
CustomCertificates.reinitCertManager(context)
Logger.reinitLogger(context)
}
}
}
/*
* Copyright © 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 at.bitfire.davdroid
import android.content.Context
import android.os.Build
import at.bitfire.cert4android.CustomCertManager
import at.bitfire.davdroid.model.ServiceDB
import at.bitfire.davdroid.model.Settings
import okhttp3.internal.tls.OkHostnameVerifier
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSocketFactory
object CustomCertificates {
@JvmField var certManager: CustomCertManager? = null
var sslSocketFactoryCompat: SSLSocketFactory? = null
var hostnameVerifier: HostnameVerifier? = null
fun reinitCertManager(context: Context) {
if (BuildConfig.customCerts) {
certManager?.close()
ServiceDB.OpenHelper(context).use { dbHelper ->
val settings = Settings(dbHelper.readableDatabase)
val mgr = CustomCertManager(context.applicationContext, !settings.getBoolean(App.DISTRUST_SYSTEM_CERTIFICATES, false))
certManager = mgr
hostnameVerifier = mgr.hostnameVerifier(OkHostnameVerifier.INSTANCE)
sslSocketFactoryCompat = if (Build.VERSION.SDK_INT >= 23)
SSLSocketFactory.getDefault() as? SSLSocketFactory?
else
SSLSocketFactoryCompat(mgr)
}
}
}
}
\ No newline at end of file
......@@ -111,7 +111,7 @@ class DavService: Service() {
@SuppressLint("MissingPermission")
fun cleanupAccounts() {
App.log.info("Cleaning up orphaned accounts")
Logger.log.info("Cleaning up orphaned accounts")
OpenHelper(this).use { dbHelper ->
val db = dbHelper.writableDatabase
......@@ -132,7 +132,7 @@ class DavService: Service() {
if (!accountNames.contains(it.getMainAccount().name))
it.delete()
} catch(e: ContactsStorageException) {
App.log.log(Level.SEVERE, "Couldn't get address book main account", e)
Logger.log.log(Level.SEVERE, "Couldn't get address book main account", e)
}
}
......@@ -230,7 +230,7 @@ class DavService: Service() {
db.delete(Collections._TABLE, "${HomeSets.SERVICE_ID}=?", arrayOf(service.toString()))
for ((_,collection) in collections) {
val values = collection.toDB()
App.log.log(Level.FINE, "Saving collection", values)
Logger.log.log(Level.FINE, "Saving collection", values)
values.put(Collections.SERVICE_ID, service)
db.insertWithOnConflict(Collections._TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE)
}
......@@ -238,40 +238,40 @@ class DavService: Service() {
try {
App.log.info("Refreshing $serviceType collections of service #$service")
Logger.log.info("Refreshing $serviceType collections of service #$service")
// create authenticating OkHttpClient (credentials taken from account settings)
val httpClient = HttpClient.create(this@DavService, account)
// refresh home set list (from principal)
readPrincipal()?.let { principalUrl ->
App.log.fine("Querying principal $principalUrl for home sets")
Logger.log.fine("Querying principal $principalUrl for home sets")
val principal = DavResource(httpClient, principalUrl)
queryHomeSets(principal)
// refresh home sets: calendar-proxy-read/write-for
for ((resource, proxyRead) in principal.findProperties(CalendarProxyReadFor.NAME) as List<Pair<DavResource, CalendarProxyReadFor>>)
for (href in proxyRead.hrefs) {
App.log.fine("Principal is a read-only proxy for $href, checking for home sets")
Logger.log.fine("Principal is a read-only proxy for $href, checking for home sets")
resource.location.resolve(href)?.let { queryHomeSets(DavResource(httpClient, it)) }
}
for ((resource, proxyWrite) in principal.findProperties(CalendarProxyWriteFor.NAME) as List<Pair<DavResource, CalendarProxyWriteFor>>)
for (href in proxyWrite.hrefs) {
App.log.fine("Principal is a read/write proxy for $href, checking for home sets")
Logger.log.fine("Principal is a read/write proxy for $href, checking for home sets")
resource.location.resolve(href)?.let { queryHomeSets(DavResource(httpClient, it)) }
}
// refresh home sets: direct group memberships
(principal.properties[GroupMembership.NAME] as GroupMembership?)?.let { groupMembership ->
for (href in groupMembership.hrefs) {
App.log.fine("Principal is member of group $href, checking for home sets")
Logger.log.fine("Principal is member of group $href, checking for home sets")
principal.location.resolve(href)?.let { url ->
try {
queryHomeSets(DavResource(httpClient, url))
} catch(e: HttpException) {
App.log.log(Level.WARNING, "Couldn't query member group", e)
Logger.log.log(Level.WARNING, "Couldn't query member group", e)
} catch(e: DavException) {
App.log.log(Level.WARNING, "Couldn't query member group", e)
Logger.log.log(Level.WARNING, "Couldn't query member group", e)
}
}
}
......@@ -288,7 +288,7 @@ class DavService: Service() {
val itHomeSets = homeSets.iterator()
while (itHomeSets.hasNext()) {
val homeSetUrl = itHomeSets.next()
App.log.fine("Listing home set $homeSetUrl")
Logger.log.fine("Listing home set $homeSetUrl")
val homeSet = DavResource(httpClient, homeSetUrl)
try {
......@@ -298,7 +298,7 @@ class DavService: Service() {
val member = itCollections.next()
val info = CollectionInfo(member)
info.confirmed = true
App.log.log(Level.FINE, "Found collection", info)
Logger.log.log(Level.FINE, "Found collection", info)
if ((serviceType == Services.SERVICE_CARDDAV && info.type == CollectionInfo.Type.ADDRESS_BOOK) ||
(serviceType == Services.SERVICE_CALDAV && info.type == CollectionInfo.Type.CALENDAR))
......@@ -349,9 +349,9 @@ class DavService: Service() {
}
} catch(e: InvalidAccountException) {
App.log.log(Level.SEVERE, "Invalid account", e)
Logger.log.log(Level.SEVERE, "Invalid account", e)
} catch(e: Exception) {
App.log.log(Level.SEVERE, "Couldn't refresh collection list", e)
Logger.log.log(Level.SEVERE, "Couldn't refresh collection list", e)
val debugIntent = Intent(this@DavService, DebugInfoActivity::class.java)
debugIntent.putExtra(DebugInfoActivity.KEY_THROWABLE, e)
......
......@@ -25,7 +25,6 @@ import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import java.util.logging.Level
import java.util.logging.Logger
class HttpClient private constructor() {
......@@ -40,7 +39,7 @@ class HttpClient private constructor() {
@JvmStatic
@JvmOverloads
fun create(context: Context?, settings: AccountSettings? = null, logger: Logger = App.log): OkHttpClient {
fun create(context: Context?, settings: AccountSettings? = null, logger: java.util.logging.Logger = Logger.log): OkHttpClient {
var builder = defaultBuilder(context, logger)
// use account settings for authentication
......@@ -60,19 +59,13 @@ class HttpClient private constructor() {
create(context, AccountSettings(context, account))
private fun defaultBuilder(context: Context?, logger: Logger): OkHttpClient.Builder {
private fun defaultBuilder(context: Context?, logger: java.util.logging.Logger): OkHttpClient.Builder {
val builder = client.newBuilder()
// use MemorizingTrustManager to manage self-signed certificates
context?.applicationContext?.let { app ->
if (app is App) {
if (app.sslSocketFactoryCompat != null && app.certManager != null)
builder.sslSocketFactory(app.sslSocketFactoryCompat, app.certManager)
if (app.hostnameVerifier != null)
builder.hostnameVerifier(app.hostnameVerifier)
} else
App.log.severe("Application context is ${app::class.java.canonicalName} instead of App")
}
if (CustomCertificates.sslSocketFactoryCompat != null && CustomCertificates.certManager != null)
builder.sslSocketFactory(CustomCertificates.sslSocketFactoryCompat, CustomCertificates.certManager)
CustomCertificates.hostnameVerifier?.let { builder.hostnameVerifier(it) }
// set timeouts
builder.connectTimeout(30, TimeUnit.SECONDS)
......@@ -95,10 +88,10 @@ class HttpClient private constructor() {
val proxy = Proxy(Proxy.Type.HTTP, address)
builder.proxy(proxy)
App.log.log(Level.INFO, "Using proxy", proxy)
Logger.log.log(Level.INFO, "Using proxy", proxy)
}
} catch(e: Exception) {
App.log.log(Level.SEVERE, "Can't set proxy, ignoring", e)
Logger.log.log(Level.SEVERE, "Can't set proxy, ignoring", e)
}
}
}
......
/*
* Copyright © 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 at.bitfire.davdroid
import android.content.Context
import android.os.Process
import android.support.v4.app.NotificationCompat
import android.support.v4.app.NotificationManagerCompat
import android.util.Log
import at.bitfire.davdroid.log.LogcatHandler
import at.bitfire.davdroid.log.PlainTextFormatter
import at.bitfire.davdroid.model.ServiceDB
import at.bitfire.davdroid.model.Settings
import org.apache.commons.lang3.time.DateFormatUtils
import java.io.File
import java.io.IOException
import java.util.logging.FileHandler
import java.util.logging.Level
import java.util.logging.Logger
object Logger {
@JvmField
val log = Logger.getLogger("davdroid")!!
init {
at.bitfire.dav4android.Constants.log = Logger.getLogger("davdroid.dav4android")
at.bitfire.cert4android.Constants.log = Logger.getLogger("davdroid.cert4android")
}
fun reinitLogger(context: Context) {
ServiceDB.OpenHelper(context).use { dbHelper ->
val settings = Settings(dbHelper.getReadableDatabase())
val logToFile = settings.getBoolean(App.LOG_TO_EXTERNAL_STORAGE, false)
val logVerbose = logToFile || Log.isLoggable(log.name, Log.DEBUG)
log.info("Verbose logging: $logVerbose")
// set logging level according to preferences
val rootLogger = Logger.getLogger("")
rootLogger.level = if (logVerbose) Level.ALL else Level.INFO
// remove all handlers and add our own logcat handler
rootLogger.useParentHandlers = false
rootLogger.handlers.forEach { rootLogger.removeHandler(it) }
rootLogger.addHandler(LogcatHandler)
val nm = NotificationManagerCompat.from(context)
// log to external file according to preferences
if (logToFile) {
val builder = NotificationCompat.Builder(context)
builder .setSmallIcon(R.drawable.ic_sd_storage_light)
.setLargeIcon(App.getLauncherBitmap(context))
.setContentTitle(context.getString(R.string.logging_davdroid_file_logging))
.setLocalOnly(true)
val dir = context.getExternalFilesDir(null)
if (dir != null)
try {
val fileName = File(dir, "davdroid-${Process.myPid()}-${DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMdd-HHmmss")}.txt").toString()
log.info("Logging to $fileName")
val fileHandler = FileHandler(fileName)
fileHandler.formatter = PlainTextFormatter.DEFAULT
log.addHandler(fileHandler)
builder .setContentText(dir.path)
.setSubText(context.getString(R.string.logging_to_external_storage_warning))
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setStyle(NotificationCompat.BigTextStyle()
.bigText(context.getString(R.string.logging_to_external_storage, dir.path)))
.setOngoing(true)
} catch (e: IOException) {
log.log(Level.SEVERE, "Couldn't create external log file", e)
builder .setContentText(context.getString(R.string.logging_couldnt_create_file, e.localizedMessage))
.setCategory(NotificationCompat.CATEGORY_ERROR)
}
else
builder.setContentText(context.getString(R.string.logging_no_external_storage))
nm.notify(Constants.NOTIFICATION_EXTERNAL_FILE_LOGGING, builder.build())
} else
nm.cancel(Constants.NOTIFICATION_EXTERNAL_FILE_LOGGING)
}
}
}
\ No newline at end of file
......@@ -34,7 +34,7 @@ class PackageChangedReceiver: BroadcastReceiver() {
@JvmStatic
fun updateTaskSync(context: Context) {
val tasksInstalled = LocalTaskList.tasksProviderAvailable(context)
App.log.info("Package (un)installed; OpenTasks provider now available = $tasksInstalled")
Logger.log.info("Package (un)installed; OpenTasks provider now available = $tasksInstalled")
// check all accounts and (de)activate OpenTasks if a CalDAV service is defined
ServiceDB.OpenHelper(context).use { dbHelper ->
......
......@@ -8,7 +8,6 @@
package at.bitfire.davdroid
import android.text.TextUtils
import java.io.IOException
import java.net.InetAddress
import java.net.Socket
......@@ -24,9 +23,15 @@ class SSLSocketFactoryCompat(trustManager: X509TrustManager): SSLSocketFactory()
private var delegate: SSLSocketFactory
companion object {
// Android 5.0+ (API level21) provides reasonable default settings
// 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
// Since Android 6.0 (API level 23), SSLSocketFactoryCompat is not needed because
// - TLSv1.1 and TLSv1.2 is enabled by default
// - SSLv3 is disabled by default
// - all modern ciphers are activated by default
val protocols = LinkedList<String>()
val cipherSuites = LinkedList<String>()
init {
......@@ -38,7 +43,7 @@ class SSLSocketFactoryCompat(trustManager: X509TrustManager): SSLSocketFactory()
// - remove all SSL versions (especially SSLv3) because they're insecure now
for (protocol in socket.supportedProtocols.filterNot { it.contains("SSL", true) })
protocols += protocol
App.log.info("Setting allowed TLS protocols: ${protocols.joinToString(", ")}")
Logger.log.info("Setting allowed TLS protocols: ${protocols.joinToString(", ")}")
/* set up reasonable cipher suites */
val knownCiphers = arrayOf<String>(
......@@ -62,7 +67,7 @@ class SSLSocketFactoryCompat(trustManager: X509TrustManager): SSLSocketFactory()
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"
)
val availableCiphers = socket.supportedCipherSuites
App.log.info("Available cipher suites: ${availableCiphers.joinToString(", ")}")
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
......@@ -71,16 +76,16 @@ class SSLSocketFactoryCompat(trustManager: X509TrustManager): SSLSocketFactory()
// for the final set of enabled ciphers, take the ciphers enabled by default, ...
cipherSuites.addAll(socket.enabledCipherSuites)
App.log.info("Cipher suites enabled by default: ${cipherSuites.joinToString(", ")}")
Logger.log.info("Cipher suites enabled by default: ${cipherSuites.joinToString(", ")}")
// ... add explicitly allowed ciphers ...
cipherSuites.addAll(knownCiphers)
// ... and keep only those which are actually available
cipherSuites.retainAll(availableCiphers)
App.log.info("Enabling (only) those TLS ciphers: " + cipherSuites.joinToString(", "))
Logger.log.info("Enabling (only) those TLS ciphers: " + cipherSuites.joinToString(", "))
}
} catch(e: IOException) {
App.log.severe("Couldn't determine default TLS settings")
Logger.log.severe("Couldn't determine default TLS settings")
} finally {
socket?.close() // doesn't implement Closeable on all supported Android versions
}
......
......@@ -13,7 +13,7 @@ import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteException
import android.database.sqlite.SQLiteOpenHelper
import at.bitfire.davdroid.App
import at.bitfire.davdroid.Logger
import java.io.Closeable