AutofillSave.kt

3.0 KB · raw

1package com.peachpasswords.autofill23import com.peachpasswords.shared.getDisplayDomain4import com.peachpasswords.vault.VaultEntry5import com.peachpasswords.vault.isLogin67internal data class AutofillSaveDraft(8    val origin: String,9    val isWebOrigin: Boolean,10    val identity: String?,11    val identityIsEmail: Boolean,12    val password: String,13    val totp: String?,14)1516internal sealed interface AutofillSavePlan {17    data class Add(val entry: VaultEntry) : AutofillSavePlan18    data class Update(val entry: VaultEntry) : AutofillSavePlan19}2021internal fun autofillSaveDraft(parsed: ParsedStructure): AutofillSaveDraft? {22    if (!parsed.credentialReleaseAllowed) return null23    val password = parsed.passwordValue?.trim().orEmpty()24    if (password.isBlank()) return null25    val origin = parsed.origin?.trim().orEmpty()26    if (origin.isBlank()) return null27    val identity = (parsed.emailValue ?: parsed.usernameValue)?.trim()?.takeIf { it.isNotBlank() }28    return AutofillSaveDraft(29        origin = origin,30        isWebOrigin = parsed.webDomain != null,31        identity = identity,32        identityIsEmail = parsed.emailValue?.isNotBlank() == true || identity?.contains("@") == true,33        password = password,34        totp = parsed.otpValue?.trim()?.takeIf { it.isNotBlank() },35    )36}3738internal fun planAutofillSave(entries: List<VaultEntry>, draft: AutofillSaveDraft): AutofillSavePlan {39    val target = findAutofillUpdateTarget(entries, draft)40    return if (target == null) {41        AutofillSavePlan.Add(draft.toEntry())42    } else {43        AutofillSavePlan.Update(44            target.copy(45                username = if (!draft.identityIsEmail) draft.identity ?: target.username else target.username,46                email = if (draft.identityIsEmail) draft.identity ?: target.email else target.email,47                password = draft.password,48                totp = draft.totp ?: target.totp,49            ),50        )51    }52}5354private fun findAutofillUpdateTarget(entries: List<VaultEntry>, draft: AutofillSaveDraft): VaultEntry? {55    val candidates = if (draft.isWebOrigin) {56        AutofillResponseBuilder.matchEntries(entries, draft.origin).hits57    } else {58        entries.filter { it.url == draft.origin }59    }.filter { it.deletedAt == null && it.isLogin() }6061    val identity = draft.identity?.lowercase()62    if (identity != null) {63        candidates.firstOrNull { entry ->64            entry.username?.lowercase() == identity || entry.email?.lowercase() == identity65        }?.let { return it }66    }67    return candidates.singleOrNull()68}6970private fun AutofillSaveDraft.toEntry(): VaultEntry {71    val title = if (isWebOrigin) getDisplayDomain(origin) else origin72    return VaultEntry(73        id = "",74        type = "login",75        title = title,76        username = if (!identityIsEmail) identity else null,77        email = if (identityIsEmail) identity else null,78        password = password,79        url = if (isWebOrigin && !origin.contains("://")) "https://$origin" else origin,80        totp = totp,81        createdAt = 0,82        modifiedAt = 0,83    )84}