Mercurial > cgi-bin > hgweb.cgi > PassMan
annotate src/main/kotlin/name/blackcap/passman/ImportSubcommand.kt @ 25:131e39d96862
Fix erroneous help message in read subcommand.
author | David Barts <n5jrn@me.com> |
---|---|
date | Thu, 04 Jul 2024 09:49:36 -0700 |
parents | ea65ab890f66 |
children |
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) | |
21
ea65ab890f66
More work to support interactive feature.
David Barts <n5jrn@me.com>
parents:
16
diff
changeset
|
25 db = Database.default |
12 | 26 try { |
27 doImport() | |
28 } catch (e: IOException) { | |
21
ea65ab890f66
More work to support interactive feature.
David Barts <n5jrn@me.com>
parents:
16
diff
changeset
|
29 throw SubcommandException(message = e.message ?: "I/O error", cause = e) |
13
302d224bbd57
Improve help messages and csv error reportage.
David Barts <n5jrn@me.com>
parents:
12
diff
changeset
|
30 } catch (e: CsvException) { |
21
ea65ab890f66
More work to support interactive feature.
David Barts <n5jrn@me.com>
parents:
16
diff
changeset
|
31 val baseMessage = e.message ?: "CSV error" |
ea65ab890f66
More work to support interactive feature.
David Barts <n5jrn@me.com>
parents:
16
diff
changeset
|
32 throw SubcommandException(message = "line $line, $baseMessage", cause = e) |
12 | 33 } |
34 } | |
35 | |
36 private fun parseArguments(args: Array<String>) { | |
16 | 37 val params = parseInto("import", args, options) |
38 when (params.size) { | |
21
ea65ab890f66
More work to support interactive feature.
David Barts <n5jrn@me.com>
parents:
16
diff
changeset
|
39 0 -> throw SubcommandException(message = "expecting CSV file name", status = 2) |
16 | 40 1 -> csvFile = params[0] |
21
ea65ab890f66
More work to support interactive feature.
David Barts <n5jrn@me.com>
parents:
16
diff
changeset
|
41 else -> throw SubcommandException(message = "unexpected trailing arguments", status = 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) { | |
21
ea65ab890f66
More work to support interactive feature.
David Barts <n5jrn@me.com>
parents:
16
diff
changeset
|
88 throw SubcommandException(message = "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) { |
21
ea65ab890f66
More work to support interactive feature.
David Barts <n5jrn@me.com>
parents:
16
diff
changeset
|
108 throw SubcommandException(message = "${see(unparsed)} - invalid date/time string", cause = e) |
12 | 109 } |
110 } | |
111 | |
112 private fun okToChange(thisEntry: Entry?, otherEntry: Entry): Boolean = | |
16 | 113 thisEntry == null || options.force || askUserIfOkToOverwrite(thisEntry, otherEntry) |
12 | 114 |
115 private fun saysNull(string: String) = string.lowercase() == "null" | |
116 | |
13
302d224bbd57
Improve help messages and csv error reportage.
David Barts <n5jrn@me.com>
parents:
12
diff
changeset
|
117 } |