view src/main/kotlin/name/blackcap/passman/CreateSubcommand.kt @ 9:72619175004e

Fix issues found in testing.
author David Barts <n5jrn@me.com>
date Sat, 01 Oct 2022 09:57:23 -0700
parents 698c4a3d758d
children cbe4c797c9a6
line wrap: on
line source

package name.blackcap.passman

import org.apache.commons.cli.*
import kotlin.system.exitProcess

class CreateSubcommand(): Subcommand() {
    private companion object {
        const val GENERATE = "generate"
        const val HELP = "help"
        const val LENGTH = "length"
        const val SYMBOLS = "symbols"
        const val VERBOSE = "verbose"
    }
    private lateinit var commandLine: CommandLine

    override fun run(args: Array<String>) {
        val options = Options().apply {
            addOption("g", GENERATE, false, "Use password generator.")
            addOption("h", HELP, false, "Print this help message.")
            addOption("l", LENGTH, true, "Length of generated password.")
            addOption("s", SYMBOLS, false, "Use symbol characters in generated password.")
            addOption("v", VERBOSE, false, "Print the generated password.")
        }
        try {
            commandLine = DefaultParser().parse(options, args)
        } catch (e: ParseException) {
            die(e.message ?: "syntax error", 2)
        }
        if (commandLine.hasOption(HELP)) {
            HelpFormatter().printHelp("$SHORTNAME create [options]", options)
            exitProcess(0)
        }
        checkArguments()
        val db = Database.open()

        val entry = if (commandLine.hasOption(GENERATE)) {
            val rawLength = commandLine.getOptionValue(LENGTH)
            val length = try {
                rawLength?.toInt() ?: DEFAULT_GENERATED_LENGTH
            } catch (e: NumberFormatException) {
                die("${see(rawLength)} - invalid length")
                -1  /* will never happen */
            }
            Entry.withGeneratedPassword(length,
                commandLine.hasOption(SYMBOLS),
                commandLine.hasOption(VERBOSE))
        } else {
            Entry.withPromptedPassword()
        }
        val id = db.makeKey(entry.name)

        db.connection.prepareStatement("select count(*) from passwords where id = ?").use {
            it.setLong(1, id)
            val result = it.executeQuery()
            result.next()
            val count = result.getInt(1)
            if (count > 0) {
                die("record matching ${see(entry.name)} already exists")
            }
        }

        try {
            if (entry.notes.isNullOrBlank()) {
                db.connection.prepareStatement("insert into passwords (id, name, username, password, created) values (?, ?, ?, ?, ?)")
                    .use {
                        it.setLong(1, id)
                        it.setEncryptedString(2, entry.name, db.encryption)
                        it.setEncryptedString(3, entry.username, db.encryption)
                        it.setEncrypted(4, entry.password, db.encryption)
                        it.setLong(5, System.currentTimeMillis())
                        it.execute()
                    }
            } else {
                db.connection.prepareStatement("insert into passwords (id, name, username, password, notes, created) values (?, ?, ?, ?, ?, ?)")
                    .use {
                        it.setLong(1, db.makeKey(entry.name))
                        it.setEncryptedString(2, entry.name, db.encryption)
                        it.setEncryptedString(3, entry.username, db.encryption)
                        it.setEncrypted(4, entry.password, db.encryption)
                        it.setEncryptedString(5, entry.notes, db.encryption)
                        it.setLong(6, System.currentTimeMillis())
                        it.execute()
                    }
            }
        } finally {
            entry.password.clear()
        }
    }

    private fun checkArguments(): Unit {
        var bad = false
        if (!commandLine.hasOption(GENERATE)) {
            for (option in listOf<String>(LENGTH, SYMBOLS, VERBOSE)) {
                if (commandLine.hasOption(option)) {
                    error("--$option requires --$GENERATE")
                    bad = true
                }
            }
        }
        if (bad) {
            exitProcess(2);
        }
        if (commandLine.args.isNotEmpty()) {
            die("unexpected trailing arguments", 2)
        }
    }
}