Files
KeePassDX/app/src/main/java/com/kunzisoft/keepass/model/SearchInfo.kt
2020-11-12 15:08:27 +01:00

155 lines
5.5 KiB
Kotlin

package com.kunzisoft.keepass.model
import android.content.Context
import android.content.res.Resources
import android.net.Uri
import android.os.Parcel
import android.os.Parcelable
import com.kunzisoft.keepass.otp.OtpEntryFields
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.ObjectNameResource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
class SearchInfo : ObjectNameResource, Parcelable {
var applicationId: String? = null
set(value) {
field = when {
value == null -> null
Regex(APPLICATION_ID_REGEX).matches(value) -> value
else -> null
}
}
// A web domain can also containing an IP
var webDomain: String? = null
set(value) {
field = when {
value == null -> null
Regex(WEB_DOMAIN_REGEX).matches(value) -> value
Regex(WEB_IP_REGEX).matches(value) -> value
else -> null
}
}
var webScheme: String? = null
get() {
return if (webDomain == null) null else field
}
var otpString: String? = null
constructor()
constructor(toCopy: SearchInfo?) {
applicationId = toCopy?.applicationId
webDomain = toCopy?.webDomain
webScheme = toCopy?.webScheme
otpString = toCopy?.otpString
}
private constructor(parcel: Parcel) {
val readAppId = parcel.readString()
applicationId = if (readAppId.isNullOrEmpty()) null else readAppId
val readDomain = parcel.readString()
webDomain = if (readDomain.isNullOrEmpty()) null else readDomain
val readScheme = parcel.readString()
webScheme = if (readScheme.isNullOrEmpty()) null else readScheme
val readOtp = parcel.readString()
otpString = if (readOtp.isNullOrEmpty()) null else readOtp
}
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(applicationId ?: "")
parcel.writeString(webDomain ?: "")
parcel.writeString(webScheme ?: "")
parcel.writeString(otpString ?: "")
}
override fun getName(resources: Resources): String {
otpString?.let { otpString ->
OtpEntryFields.parseOTPUri(otpString)?.let { otpElement ->
return "${otpElement.type} (${Uri.decode(otpElement.issuer)}:${Uri.decode(otpElement.name)})"
}
}
return toString()
}
fun containsOnlyNullValues(): Boolean {
return applicationId == null
&& webDomain == null
&& webScheme == null
&& otpString == null
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as SearchInfo
if (applicationId != other.applicationId) return false
if (webDomain != other.webDomain) return false
if (webScheme != other.webScheme) return false
if (otpString != other.otpString) return false
return true
}
override fun hashCode(): Int {
var result = applicationId?.hashCode() ?: 0
result = 31 * result + (webDomain?.hashCode() ?: 0)
result = 31 * result + (webScheme?.hashCode() ?: 0)
result = 31 * result + (otpString?.hashCode() ?: 0)
return result
}
override fun toString(): String {
return otpString ?: webDomain ?: applicationId ?: ""
}
companion object {
// https://gist.github.com/rishabhmhjn/8663966
const val APPLICATION_ID_REGEX = "^(?:[a-zA-Z]+(?:\\d*[a-zA-Z_]*)*)(?:\\.[a-zA-Z]+(?:\\d*[a-zA-Z_]*)*)+\$"
const val WEB_DOMAIN_REGEX = "^(?!://)([a-zA-Z0-9-_]+\\.)*[a-zA-Z0-9][a-zA-Z0-9-_]+\\.[a-zA-Z]{2,11}?\$"
const val WEB_IP_REGEX = "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\$"
@JvmField
val CREATOR: Parcelable.Creator<SearchInfo> = object : Parcelable.Creator<SearchInfo> {
override fun createFromParcel(parcel: Parcel): SearchInfo {
return SearchInfo(parcel)
}
override fun newArray(size: Int): Array<SearchInfo?> {
return arrayOfNulls(size)
}
}
/**
* Get the concrete web domain AKA without sub domain if needed
*/
fun getConcreteWebDomain(context: Context,
webDomain: String?,
concreteWebDomain: (String?) -> Unit) {
CoroutineScope(Dispatchers.Main).launch {
if (webDomain != null) {
// Warning, web domain can contains IP, don't crop in this case
if (PreferencesUtil.searchSubdomains(context)
|| Regex(WEB_IP_REGEX).matches(webDomain)) {
concreteWebDomain.invoke(webDomain)
} else {
val publicSuffixList = PublicSuffixList(context)
concreteWebDomain.invoke(publicSuffixList
.getPublicSuffixPlusOne(webDomain).await())
}
} else {
concreteWebDomain.invoke(null)
}
}
}
}
}