diff --git a/app/src/main/kotlin/com/github/gotify/Settings.kt b/app/src/main/kotlin/com/github/gotify/Settings.kt index 4aa9b65b..a6e0fd55 100644 --- a/app/src/main/kotlin/com/github/gotify/Settings.kt +++ b/app/src/main/kotlin/com/github/gotify/Settings.kt @@ -6,6 +6,7 @@ import com.github.gotify.client.model.User internal class Settings(context: Context) { private val sharedPreferences: SharedPreferences + val filesDir: String var url: String get() = sharedPreferences.getString("url", "")!! set(value) = sharedPreferences.edit().putString("url", value).apply() @@ -26,6 +27,9 @@ internal class Settings(context: Context) { var serverVersion: String get() = sharedPreferences.getString("version", "UNKNOWN")!! set(value) = sharedPreferences.edit().putString("version", value).apply() + var legacyCert: String? + get() = sharedPreferences.getString("cert", null) + set(value) = sharedPreferences.edit().putString("cert", value).apply() var caCertPath: String? get() = sharedPreferences.getString("caCertPath", null) set(value) = sharedPreferences.edit().putString("caCertPath", value).apply() @@ -44,6 +48,7 @@ internal class Settings(context: Context) { init { sharedPreferences = context.getSharedPreferences("gotify", Context.MODE_PRIVATE) + filesDir = context.filesDir.absolutePath } fun tokenExists(): Boolean = !token.isNullOrEmpty() @@ -52,6 +57,7 @@ internal class Settings(context: Context) { url = "" token = null validateSSL = true + legacyCert = null caCertPath = null caCertCN = null clientCertPath = null diff --git a/app/src/main/kotlin/com/github/gotify/api/ClientFactory.kt b/app/src/main/kotlin/com/github/gotify/api/ClientFactory.kt index 1edf4e03..936b88e6 100644 --- a/app/src/main/kotlin/com/github/gotify/api/ClientFactory.kt +++ b/app/src/main/kotlin/com/github/gotify/api/ClientFactory.kt @@ -7,57 +7,78 @@ import com.github.gotify.client.api.UserApi import com.github.gotify.client.api.VersionApi import com.github.gotify.client.auth.ApiKeyAuth import com.github.gotify.client.auth.HttpBasicAuth +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import org.tinylog.kotlin.Logger internal object ClientFactory { - private fun unauthorized(baseUrl: String, sslSettings: SSLSettings): ApiClient { - return defaultClient(arrayOf(), "$baseUrl/", sslSettings) + private fun unauthorized( + settings: Settings, + sslSettings: SSLSettings, + baseUrl: String + ): ApiClient { + return defaultClient(arrayOf(), settings, sslSettings, baseUrl) } fun basicAuth( - baseUrl: String, + settings: Settings, sslSettings: SSLSettings, username: String, password: String ): ApiClient { - val client = defaultClient( - arrayOf("basicAuth"), - "$baseUrl/", - sslSettings - ) + val client = defaultClient(arrayOf("basicAuth"), settings, sslSettings) val auth = client.apiAuthorizations["basicAuth"] as HttpBasicAuth auth.username = username auth.password = password return client } - fun clientToken(baseUrl: String, sslSettings: SSLSettings, token: String?): ApiClient { - val client = defaultClient( - arrayOf("clientTokenHeader"), - "$baseUrl/", - sslSettings - ) + fun clientToken(settings: Settings, token: String? = settings.token): ApiClient { + val client = defaultClient(arrayOf("clientTokenHeader"), settings) val tokenAuth = client.apiAuthorizations["clientTokenHeader"] as ApiKeyAuth tokenAuth.apiKey = token return client } - fun versionApi(baseUrl: String, sslSettings: SSLSettings): VersionApi { - return unauthorized(baseUrl, sslSettings).createService(VersionApi::class.java) + fun versionApi( + settings: Settings, + sslSettings: SSLSettings = settings.sslSettings(), + baseUrl: String = settings.url + ): VersionApi { + return unauthorized(settings, sslSettings, baseUrl).createService(VersionApi::class.java) } fun userApiWithToken(settings: Settings): UserApi { - return clientToken(settings.url, settings.sslSettings(), settings.token) - .createService(UserApi::class.java) + return clientToken(settings).createService(UserApi::class.java) } private fun defaultClient( authentications: Array, - baseUrl: String, - sslSettings: SSLSettings + settings: Settings, + sslSettings: SSLSettings = settings.sslSettings(), + baseUrl: String = settings.url ): ApiClient { val client = ApiClient(authentications) + if (settings.legacyCert != null) { + Logger.info("Migrating legacy CA cert to new location") + var legacyCert: String? = null + try { + legacyCert = settings.legacyCert + settings.legacyCert = null + val caCertFile = File(settings.filesDir, CertUtils.CA_CERT_NAME) + FileOutputStream(caCertFile).use { + it.write(legacyCert?.encodeToByteArray()) + } + settings.caCertPath = caCertFile.absolutePath + Logger.info("Migration of legacy CA cert succeeded") + } catch (e: IOException) { + Logger.error(e, "Migration of legacy CA cert failed") + if (legacyCert != null) settings.legacyCert = legacyCert + } + } CertUtils.applySslSettings(client.okBuilder, sslSettings) - client.adapterBuilder.baseUrl(baseUrl) + client.adapterBuilder.baseUrl("$baseUrl/") return client } } diff --git a/app/src/main/kotlin/com/github/gotify/init/InitializationActivity.kt b/app/src/main/kotlin/com/github/gotify/init/InitializationActivity.kt index 474c3af9..ea42ef75 100644 --- a/app/src/main/kotlin/com/github/gotify/init/InitializationActivity.kt +++ b/app/src/main/kotlin/com/github/gotify/init/InitializationActivity.kt @@ -167,7 +167,7 @@ internal class InitializationActivity : AppCompatActivity() { callback: SuccessCallback, errorCallback: Callback.ErrorCallback ) { - ClientFactory.versionApi(settings.url, settings.sslSettings()) + ClientFactory.versionApi(settings) .version .enqueue(Callback.callInUI(this, callback, errorCallback)) } diff --git a/app/src/main/kotlin/com/github/gotify/login/LoginActivity.kt b/app/src/main/kotlin/com/github/gotify/login/LoginActivity.kt index 7b328897..3358569a 100644 --- a/app/src/main/kotlin/com/github/gotify/login/LoginActivity.kt +++ b/app/src/main/kotlin/com/github/gotify/login/LoginActivity.kt @@ -144,7 +144,7 @@ internal class LoginActivity : AppCompatActivity() { binding.checkurl.visibility = View.GONE try { - ClientFactory.versionApi(url, tempSslSettings()) + ClientFactory.versionApi(settings, tempSslSettings(), url) .version .enqueue(Callback.callInUI(this, onValidUrl(url), onInvalidUrl(url))) } catch (e: Exception) { @@ -252,7 +252,7 @@ internal class LoginActivity : AppCompatActivity() { binding.login.visibility = View.GONE binding.loginProgress.visibility = View.VISIBLE - val client = ClientFactory.basicAuth(settings.url, tempSslSettings(), username, password) + val client = ClientFactory.basicAuth(settings, tempSslSettings(), username, password) client.createService(UserApi::class.java) .currentUser() .enqueue( diff --git a/app/src/main/kotlin/com/github/gotify/messages/MessagesActivity.kt b/app/src/main/kotlin/com/github/gotify/messages/MessagesActivity.kt index f2f95f80..7cad267e 100644 --- a/app/src/main/kotlin/com/github/gotify/messages/MessagesActivity.kt +++ b/app/src/main/kotlin/com/github/gotify/messages/MessagesActivity.kt @@ -540,7 +540,7 @@ internal class MessagesActivity : private fun deleteApp(appId: Long) { val settings = viewModel.settings - val client = ClientFactory.clientToken(settings.url, settings.sslSettings(), settings.token) + val client = ClientFactory.clientToken(settings) client.createService(ApplicationApi::class.java) .deleteApp(appId) .enqueue( @@ -597,8 +597,7 @@ internal class MessagesActivity : private fun deleteClientAndNavigateToLogin() { val settings = viewModel.settings - val api = ClientFactory.clientToken(settings.url, settings.sslSettings(), settings.token) - .createService(ClientApi::class.java) + val api = ClientFactory.clientToken(settings).createService(ClientApi::class.java) stopService(Intent(this@MessagesActivity, WebSocketService::class.java)) try { val clients = Api.execute(api.clients) diff --git a/app/src/main/kotlin/com/github/gotify/messages/MessagesModel.kt b/app/src/main/kotlin/com/github/gotify/messages/MessagesModel.kt index bcd7c470..77d0d460 100644 --- a/app/src/main/kotlin/com/github/gotify/messages/MessagesModel.kt +++ b/app/src/main/kotlin/com/github/gotify/messages/MessagesModel.kt @@ -14,7 +14,7 @@ import com.squareup.picasso.Target internal class MessagesModel(parentView: Activity) : ViewModel() { val settings = Settings(parentView) val picassoHandler = PicassoHandler(parentView, settings) - val client = ClientFactory.clientToken(settings.url, settings.sslSettings(), settings.token) + val client = ClientFactory.clientToken(settings) val appsHolder = ApplicationHolder(parentView, client) val messages = MessageFacade(client.createService(MessageApi::class.java), appsHolder) diff --git a/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt b/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt index e2fc90d0..af7c68d7 100644 --- a/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt +++ b/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt @@ -68,11 +68,7 @@ internal class WebSocketService : Service() { override fun onCreate() { super.onCreate() settings = Settings(this) - val client = ClientFactory.clientToken( - settings.url, - settings.sslSettings(), - settings.token - ) + val client = ClientFactory.clientToken(settings) missingMessageUtil = MissedMessageUtil(client.createService(MessageApi::class.java)) Logger.info("Create ${javaClass.simpleName}") picassoHandler = PicassoHandler(this, settings) @@ -129,7 +125,7 @@ internal class WebSocketService : Service() { } private fun fetchApps() { - ClientFactory.clientToken(settings.url, settings.sslSettings(), settings.token) + ClientFactory.clientToken(settings) .createService(ApplicationApi::class.java) .apps .enqueue( diff --git a/app/src/main/kotlin/com/github/gotify/sharing/ShareActivity.kt b/app/src/main/kotlin/com/github/gotify/sharing/ShareActivity.kt index 6cd769de..e7e8c70d 100644 --- a/app/src/main/kotlin/com/github/gotify/sharing/ShareActivity.kt +++ b/app/src/main/kotlin/com/github/gotify/sharing/ShareActivity.kt @@ -61,11 +61,7 @@ internal class ShareActivity : AppCompatActivity() { return } - val client = ClientFactory.clientToken( - settings.url, - settings.sslSettings(), - settings.token - ) + val client = ClientFactory.clientToken(settings) appsHolder = ApplicationHolder(this, client) appsHolder.onUpdate { val apps = appsHolder.get() @@ -136,11 +132,7 @@ internal class ShareActivity : AppCompatActivity() { } private fun executeMessageCall(appIndex: Int, message: Message): Boolean { - val pushClient = ClientFactory.clientToken( - settings.url, - settings.sslSettings(), - appsHolder.get()[appIndex].token - ) + val pushClient = ClientFactory.clientToken(settings, appsHolder.get()[appIndex].token) return try { val messageApi = pushClient.createService(MessageApi::class.java) Api.execute(messageApi.createMessage(message))