view src/main/kotlin/name/blackcap/passman/ExportSubcommand.kt @ 22:07406c4af4a5

More interactive mode stuff.
author David Barts <n5jrn@me.com>
date Tue, 02 Jul 2024 17:34:52 -0700
parents ea65ab890f66
children
line wrap: on
line source

package name.blackcap.passman

import com.opencsv.CSVWriterBuilder
import com.opencsv.ICSVWriter
import java.io.FileOutputStream
import java.io.IOException
import java.io.OutputStreamWriter
import java.sql.ResultSet
import java.text.SimpleDateFormat
import java.util.*

class ExportSubcommand() : Subcommand() {
    private companion object {
        const val NULL = "null"
    }
    private lateinit var csvDateFormat: SimpleDateFormat
    private lateinit var db: Database
    private val options = ImportExportArguments()
    private lateinit var csvFile: String

    override fun run(args: Array<String>) {
        parseArguments(args)
        db = Database.default
        try {
            doExport()
        } catch (e: IOException) {
            throw SubcommandException(message = e.message ?: "I/O error", cause = e)
        }
    }

    private fun parseArguments(args: Array<String>) {
        val params = parseInto("export", args, options)
        when (params.size) {
            0 -> throw SubcommandException(message = "expecting CSV file name", status = 2)
            1 -> csvFile = params[0]
            else -> throw SubcommandException(message = "unexpected trailing arguments", status = 2)
        }
        csvDateFormat = SimpleDateFormat(options.format).apply {
            timeZone = TimeZone.getTimeZone(options.zone)
            isLenient = false
        }
    }

    private fun doExport() {
        val csvWriter = CSVWriterBuilder(OutputStreamWriter(FileOutputStream(csvFile), options.charset))
            .withEscapeChar(options.escape)
            .withQuoteChar(options.quote)
            .withSeparator(options.separator)
            .withLineEnd(ICSVWriter.RFC4180_LINE_END)
            .build()

        try {
            db.connection.prepareStatement("select name, username, password, notes, created, modified, accessed from passwords").use {
                val results = it.executeQuery()
                while (results.next()) {
                    val entry = arrayOf<String>(
                        results.getDecryptedString(1, db.encryption)!!,
                        results.getDecryptedString(2, db.encryption)!!,
                        results.getDecryptedString(3, db.encryption)!!,
                        results.getDecryptedString(4, db.encryption) ?: NULL,
                        results.getTimeString(5),
                        results.getTimeString(6),
                        results.getTimeString(7)
                        )
                    csvWriter.writeNext(entry)
                }
            }
        } finally {
            csvWriter.close()
        }
    }

    private fun ResultSet.getTimeString(columnIndex: Int): String {
        val value = getDate(columnIndex)
        return if (value == null) NULL else csvDateFormat.format(value)
    }
}