view src/main/kotlin/name/blackcap/passman/PasswordSubcommand.kt @ 15:0fc90892a3ae

Add password subcommand.
author David Barts <n5jrn@me.com>
date Fri, 03 Feb 2023 18:48:13 -0800
parents
children ea65ab890f66
line wrap: on
line source

package name.blackcap.passman

import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardCopyOption
import kotlin.system.exitProcess

class PasswordSubcommand : Subcommand() {
    override fun run(args: Array<String>) {
        // Parse arguments
        if (args.size > 0 && (args[0] == "-h" || args[0].startsWith("--h"))) {
            println("usage: passman password")
            exitProcess(0)
        }
        if (!args.isEmpty()) {
            die("unexpected arguments", 2)
        }

        // Open databases
        println("Changing database encryption key...")
        val oldPath = Path.of(DB_FILE)
        val oldDb = Database.open(fileName = DB_FILE, passwordPrompt = "Old database key: ")
        val newPath = Path.of(NEW_DB_FILE)
        if (Files.exists(newPath)) {
            println("WARNING: deleting ${see(NEW_DB_FILE)}")
            Files.delete(newPath)
        }
        val newDb = Database.open(fileName = NEW_DB_FILE, passwordPrompt = "New database key: ", create = true)

        // Copy old to new
        println("WARNING: do not interrupt this process or data may be lost!")
        copyRecords(oldDb, newDb)

        // Wrap up
        oldDb.connection.close()
        newDb.connection.close()
        Files.move(newPath, oldPath, StandardCopyOption.REPLACE_EXISTING)
        println("Database key changed.")
    }

    private fun copyRecords(oldDb: Database, newDb: Database) {
        oldDb.connection.prepareStatement("select name, username, password, notes, created, modified, accessed from passwords").use { old ->
            val results = old.executeQuery()
            while (results.next()) {
                newDb.connection.prepareStatement("insert into passwords (id, name, username, password, notes, created, modified, accessed) values (?, ?, ?, ?, ?, ?, ?, ?)").use { new ->
                    // id and name
                    val name = results.getDecryptedString(1, oldDb.encryption)!!
                    new.setLong(1, newDb.makeKey(name))
                    new.setEncryptedString(2, name, newDb.encryption)
                    // username
                    new.setEncryptedString(3,
                        results.getDecryptedString(2, oldDb.encryption)!!, newDb.encryption)
                    // password
                    val password = results.getDecrypted(3, oldDb.encryption)!!
                    new.setEncrypted(4, password, newDb.encryption)
                    password.clear()
                    // notes
                    new.setEncryptedString(5,
                        results.getDecryptedString(4, oldDb.encryption), newDb.encryption)
                    // created, modified, accessed
                    new.setDateOrNull(6, results.getLong(5))
                    new.setDateOrNull(7, results.getLong(6))
                    new.setDateOrNull(8, results.getLong(7))
                    new.executeUpdate()
                }
            }
        }
    }
}