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 val password = parsed.passwordValue?.trim().orEmpty()23 if (password.isBlank()) return null24 val origin = parsed.origin?.trim().orEmpty()25 if (origin.isBlank()) return null26 val identity = (parsed.emailValue ?: parsed.usernameValue)?.trim()?.takeIf { it.isNotBlank() }27 return AutofillSaveDraft(28 origin = origin,29 isWebOrigin = parsed.webDomain != null,30 identity = identity,31 identityIsEmail = parsed.emailValue?.isNotBlank() == true || identity?.contains("@") == true,32 password = password,33 totp = parsed.otpValue?.trim()?.takeIf { it.isNotBlank() },34 )35}3637internal fun planAutofillSave(entries: List<VaultEntry>, draft: AutofillSaveDraft): AutofillSavePlan {38 val target = findAutofillUpdateTarget(entries, draft)39 return if (target == null) {40 AutofillSavePlan.Add(draft.toEntry())41 } else {42 AutofillSavePlan.Update(43 target.copy(44 username = if (!draft.identityIsEmail) draft.identity ?: target.username else target.username,45 email = if (draft.identityIsEmail) draft.identity ?: target.email else target.email,46 password = draft.password,47 totp = draft.totp ?: target.totp,48 ),49 )50 }51}5253private fun findAutofillUpdateTarget(entries: List<VaultEntry>, draft: AutofillSaveDraft): VaultEntry? {54 val candidates = if (draft.isWebOrigin) {55 AutofillResponseBuilder.matchEntries(entries, draft.origin).hits56 } else {57 entries.filter { it.url == draft.origin }58 }.filter { it.deletedAt == null && it.isLogin() }5960 val identity = draft.identity?.lowercase()61 if (identity != null) {62 candidates.firstOrNull { entry ->63 entry.username?.lowercase() == identity || entry.email?.lowercase() == identity64 }?.let { return it }65 }66 return candidates.singleOrNull()67}6869private fun AutofillSaveDraft.toEntry(): VaultEntry {70 val title = if (isWebOrigin) getDisplayDomain(origin) else origin71 return VaultEntry(72 id = "",73 type = "login",74 title = title,75 username = if (!identityIsEmail) identity else null,76 email = if (identityIsEmail) identity else null,77 password = password,78 url = if (isWebOrigin && !origin.contains("://")) "https://$origin" else origin,79 totp = totp,80 createdAt = 0,81 modifiedAt = 0,82 )83}