diff 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 diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/kotlin/name/blackcap/passman/PasswordSubcommand.kt	Fri Feb 03 18:48:13 2023 -0800
@@ -0,0 +1,69 @@
+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()
+                }
+            }
+        }
+    }
+}
\ No newline at end of file