AutofillSave.kt

3.3 KB · raw

1package com.peachpasswords.autofill23import android.content.Context4import com.peachpasswords.passkeys.PasskeyCallerOrigin5import com.peachpasswords.shared.getDisplayDomain6import com.peachpasswords.vault.VaultEntry7import com.peachpasswords.vault.isLogin89private fun nativeSignerDigests(context: Context, packageName: String?): Set<String>? =10    packageName?.let { PasskeyCallerOrigin.signerFingerprints(context, it) }1112internal data class AutofillSaveDraft(13    val origin: String,14    val isWebOrigin: Boolean,15    val identity: String?,16    val identityIsEmail: Boolean,17    val password: String,18    val nativeSignerDigests: Set<String>? = null,19)2021internal sealed interface AutofillSavePlan {22    data class Add(val entry: VaultEntry) : AutofillSavePlan23    data class Update(val entry: VaultEntry) : AutofillSavePlan24}2526internal fun autofillSaveDraft(parsed: ParsedStructure, nativeSignerDigests: Set<String>? = null): AutofillSaveDraft? {27    if (!parsed.credentialReleaseAllowed) return null28    val password = parsed.passwordValue?.trim().orEmpty()29    if (password.isBlank()) return null30    val origin = parsed.origin?.trim().orEmpty()31    if (origin.isBlank()) return null32    val identity = (parsed.emailValue ?: parsed.usernameValue)?.trim()?.takeIf { it.isNotBlank() }33    return AutofillSaveDraft(34        origin = origin,35        isWebOrigin = parsed.webDomain != null,36        identity = identity,37        identityIsEmail = parsed.emailValue?.isNotBlank() == true || identity?.contains("@") == true,38        password = password,39        nativeSignerDigests = nativeSignerDigests,40    )41}4243internal fun planAutofillSave(entries: List<VaultEntry>, draft: AutofillSaveDraft): AutofillSavePlan {44    val target = findAutofillUpdateTarget(entries, draft)45    return if (target == null) {46        AutofillSavePlan.Add(draft.toEntry())47    } else {48        AutofillSavePlan.Update(49            target.copy(50                username = if (!draft.identityIsEmail) draft.identity ?: target.username else target.username,51                email = if (draft.identityIsEmail) draft.identity ?: target.email else target.email,52                password = draft.password,53            ),54        )55    }56}5758private fun findAutofillUpdateTarget(entries: List<VaultEntry>, draft: AutofillSaveDraft): VaultEntry? {59    val candidates = if (draft.isWebOrigin) {60        AutofillResponseBuilder.matchEntries(entries, draft.origin).hits61    } else {62        entries.filter { it.url == draft.origin }63    }.filter { it.deletedAt == null && it.isLogin() }6465    val identity = draft.identity?.lowercase()66    if (identity != null) {67        candidates.firstOrNull { entry ->68            entry.username?.lowercase() == identity || entry.email?.lowercase() == identity69        }?.let { return it }70    }71    return candidates.singleOrNull()72}7374private fun AutofillSaveDraft.toEntry(): VaultEntry {75    val title = if (isWebOrigin) getDisplayDomain(origin) else origin76    return VaultEntry(77        id = "",78        type = "login",79        title = title,80        username = if (!identityIsEmail) identity else null,81        email = if (identityIsEmail) identity else null,82        password = password,83        url = if (isWebOrigin && !origin.contains("://")) "https://$origin" else origin,84        nativeSignerDigests = nativeSignerDigests,85        createdAt = 0,86        modifiedAt = 0,87    )88}