view src/main/kotlin/name/blackcap/passman/Entry.kt @ 28:287eadf5ab30 default tip

Check for timeouts inside subcommands while in interactive mode as well.
author David Barts <n5jrn@me.com>
date Wed, 31 Jul 2024 11:21:18 -0700
parents 3a3067ba673b
children
line wrap: on
line source

package name.blackcap.passman

import java.util.*
import kotlin.reflect.KProperty
import kotlin.reflect.full.declaredMemberProperties

class Entry(val name: String, val username: String, val password: CharArray, val notes: String?,
            val created: Date? = null, val modified: Date? = null, val accessed: Date? = null) {

    companion object {
        private val FIELD_MAP = mutableMapOf<String, KProperty.Getter<*>>().apply {
            Entry::class.declaredMemberProperties.forEach{
                this[it.name] = it.getter
            }
        }

        fun withPromptedPassword() = Entry(
                name = _getName(),
                username = _getUsername(),
                password = _getPassword(),
                notes = _getNotes()
            )

        fun withGeneratedPassword(length: Int, allowSymbols: Boolean, verbose: Boolean): Entry {
            return Entry(
                name = _getName(),
                username = _getUsername(),
                password = _genPassword(length, allowSymbols, verbose),
                notes = _getNotes()
            )
        }

        fun fromDatabase(db: Database, name: String): Entry? {
            db.connection.prepareStatement("select name, username, password, notes, created, modified, accessed from passwords where id = ?").use {
                it.setLong(1, db.makeKey(name))
                val results = it.executeQuery()
                if (!results.next()) {
                    return null
                }
                return Entry(
                    name = results.getDecryptedString(1, db.encryption)!!,
                    username = results.getDecryptedString(2, db.encryption)!!,
                    password = results.getDecrypted(3, db.encryption)!!,
                    notes = results.getDecryptedString(4, db.encryption),
                    created = results.getDate(5),
                    modified = results.getDate(6),
                    accessed = results.getDate(7)
                )
            }
        }

        private fun _genPassword(length: Int, allowSymbols: Boolean, verbose: Boolean): CharArray {
            val generated = generate(length, allowSymbols)
            if (verbose) {
                printPassword(generated)
            }
            return generated
        }

        private fun _getName() = mustReadLine("Name of site: ")

        private fun _getUsername() = mustReadLine("Username: ")

        private fun _getPassword() = mustGetPassword("Password: ", verify = true)

        private fun _getNotes() = readLine("Notes: ")
    }

    fun insert(db: Database) {
        db.connection.prepareStatement("insert into passwords (id, name, username, password, notes, created, modified, accessed) values (?, ?, ?, ?, ?, ?, ?, ?)")
            .use {
                it.setLong(1, db.makeKey(name))
                it.setEncryptedString(2, name, db.encryption)
                it.setEncryptedString(3, username, db.encryption)
                it.setEncrypted(4, password, db.encryption)
                it.setEncryptedString(5, notes, db.encryption)
                it.setLongOrNull(6, created?.time)
                it.setLongOrNull(7, modified?.time)
                it.setLongOrNull(8, accessed?.time)
                it.executeUpdate()
            }
    }

    fun update(db: Database) {
        db.connection.prepareStatement("update passwords set name = ?, username = ?, password = ?, notes = ?, created = ?, modified = ?, accessed = ? where id = ?").use {
            it.setEncryptedString(1, name, db.encryption)
            it.setEncryptedString(2, username, db.encryption)
            it.setEncrypted(3, password, db.encryption)
            it.setEncryptedString(4, notes, db.encryption)
            it.setLongOrNull(5, created?.time)
            it.setLongOrNull(6, modified?.time)
            it.setLongOrNull(7, accessed?.time)
            it.setLong(8, db.makeKey(name))
            it.executeUpdate()
        }
    }

    val modifiedOrCreated get() = modified ?: created!!

    fun print(redactPassword: String? = null) {
        println("Name of site: $name")
        println("Username: $username")
        if (redactPassword == null) {
            printPassword(password)
        } else {
            println("Password: $redactPassword")
        }
    }

    fun printLong(redactPassword: String? = null) {
        this.print(redactPassword)
        println("Notes: ${notes ?: "(none)"}")
        printDate("Created", created)
        printDate("Modified", modified)
        printDate("Accessed", accessed)
    }

    fun getField(name: String): Any? {
        return FIELD_MAP[name]!!.call(this)
    }

    private fun printDate(tag: String, date: Date?) {
        kotlin.io.print("${tag}: ")
        if (date == null) {
            println("never")
        } else {
            println(ISO8601.format(date))
        }
    }
}