Mercurial > cgi-bin > hgweb.cgi > PassMan
annotate src/main/kotlin/name/blackcap/passman/ImportSubcommand.kt @ 19:7d80cbcb67bb
add shlex-style splitter and tests
author | David Barts <n5jrn@me.com> |
---|---|
date | Sun, 30 Jun 2024 20:37:36 -0700 |
parents | 7a74ae668665 |
children | ea65ab890f66 |
rev | line source |
---|---|
12 | 1 package name.blackcap.passman |
2 | |
3 import com.opencsv.CSVParserBuilder | |
4 import com.opencsv.CSVReaderBuilder | |
13
302d224bbd57
Improve help messages and csv error reportage.
David Barts <n5jrn@me.com>
parents:
12
diff
changeset
|
5 import com.opencsv.exceptions.CsvException |
16 | 6 import org.apache.commons.cli.ParseException |
7 import java.io.FileInputStream | |
12 | 8 import java.io.IOException |
16 | 9 import java.io.InputStreamReader |
12 | 10 import java.text.SimpleDateFormat |
11 import java.util.* | |
12 | |
13 class ImportSubcommand(): Subcommand() { | |
14 private companion object { | |
15 const val NFIELDS = 7 | |
16 } | |
16 | 17 private lateinit var csvDateFormat: SimpleDateFormat |
12 | 18 private lateinit var db: Database |
16 | 19 private val options = ImportExportArguments() |
20 private lateinit var csvFile: String | |
13
302d224bbd57
Improve help messages and csv error reportage.
David Barts <n5jrn@me.com>
parents:
12
diff
changeset
|
21 private var line = 0 |
302d224bbd57
Improve help messages and csv error reportage.
David Barts <n5jrn@me.com>
parents:
12
diff
changeset
|
22 |
12 | 23 override fun run(args: Array<String>) { |
24 parseArguments(args) | |
25 db = Database.open() | |
26 try { | |
27 doImport() | |
28 } catch (e: IOException) { | |
29 die(e.message ?: "I/O error") | |
13
302d224bbd57
Improve help messages and csv error reportage.
David Barts <n5jrn@me.com>
parents:
12
diff
changeset
|
30 } catch (e: CsvException) { |
302d224bbd57
Improve help messages and csv error reportage.
David Barts <n5jrn@me.com>
parents:
12
diff
changeset
|
31 val message = e.message ?: "CSV error" |
302d224bbd57
Improve help messages and csv error reportage.
David Barts <n5jrn@me.com>
parents:
12
diff
changeset
|
32 die("line $line, $message") |
12 | 33 } |
34 } | |
35 | |
36 private fun parseArguments(args: Array<String>) { | |
16 | 37 val params = parseInto("import", args, options) |
38 when (params.size) { | |
39 0 -> die("expecting CSV file name", 2) | |
40 1 -> csvFile = params[0] | |
41 else -> die("unexpected trailing arguments", 2) | |
12 | 42 } |
16 | 43 csvDateFormat = SimpleDateFormat(options.format).apply { |
44 timeZone = TimeZone.getTimeZone(options.zone) | |
45 isLenient = false | |
12 | 46 } |
47 } | |
48 | |
49 private fun doImport() { | |
50 val csvParser = CSVParserBuilder() | |
16 | 51 .withEscapeChar(options.escape) |
52 .withQuoteChar(options.quote) | |
53 .withSeparator(options.separator) | |
54 .withIgnoreLeadingWhiteSpace(options.ignore) | |
12 | 55 .build() |
56 | |
16 | 57 val csvReader = CSVReaderBuilder(InputStreamReader(FileInputStream(csvFile), options.charset)) |
12 | 58 .withCSVParser(csvParser) |
59 .build() | |
60 | |
61 csvReader.use { | |
16 | 62 if (options.skip) { |
13
302d224bbd57
Improve help messages and csv error reportage.
David Barts <n5jrn@me.com>
parents:
12
diff
changeset
|
63 line++ |
12 | 64 it.skip(1) |
65 } | |
13
302d224bbd57
Improve help messages and csv error reportage.
David Barts <n5jrn@me.com>
parents:
12
diff
changeset
|
66 |
12 | 67 it.iterator().forEach { fields -> |
13
302d224bbd57
Improve help messages and csv error reportage.
David Barts <n5jrn@me.com>
parents:
12
diff
changeset
|
68 line++ |
12 | 69 val importedEntry = fromCsv(fields) |
70 val thisEntry = Entry.fromDatabase(db, importedEntry.name) | |
71 try { | |
72 if (okToChange(thisEntry, importedEntry)) { | |
73 if (thisEntry == null) { | |
74 importedEntry.insert(db) | |
75 } else { | |
76 importedEntry.update(db) | |
77 } | |
78 } | |
79 } finally { | |
80 thisEntry?.password?.clear() | |
81 } | |
82 } | |
83 } | |
84 } | |
85 | |
86 private fun fromCsv(fields: Array<String>): Entry { | |
87 if (fields.size != NFIELDS) { | |
13
302d224bbd57
Improve help messages and csv error reportage.
David Barts <n5jrn@me.com>
parents:
12
diff
changeset
|
88 die("line $line, expected $NFIELDS fields but got ${fields.size}") |
12 | 89 } |
90 return Entry( | |
91 name = fields[0], | |
92 username = fields[1], | |
93 password = fields[2].toCharArray(), | |
94 notes = if (saysNull(fields[3])) null else fields[3], | |
95 created = parseCsvTime(fields[4]) ?: Date(), | |
96 modified = parseCsvTime(fields[5]), | |
97 accessed = parseCsvTime(fields[6]) | |
98 ) | |
99 } | |
100 | |
101 private fun parseCsvTime(unparsed: String): Date? { | |
102 if (saysNull(unparsed)) { | |
103 return null | |
104 } | |
105 try { | |
16 | 106 return csvDateFormat.parse(unparsed) |
12 | 107 } catch (e: ParseException) { |
108 die("${see(unparsed)} - invalid date/time string") | |
109 throw e /* kotlin is too stupid to realize this never happens */ | |
110 } | |
111 } | |
112 | |
113 private fun okToChange(thisEntry: Entry?, otherEntry: Entry): Boolean = | |
16 | 114 thisEntry == null || options.force || askUserIfOkToOverwrite(thisEntry, otherEntry) |
12 | 115 |
116 private fun saysNull(string: String) = string.lowercase() == "null" | |
117 | |
13
302d224bbd57
Improve help messages and csv error reportage.
David Barts <n5jrn@me.com>
parents:
12
diff
changeset
|
118 } |