From 6182f6a568cb208003e0b3b660ef7a0a4160fec1 Mon Sep 17 00:00:00 2001 From: gleam <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 15 Jan 2023 12:26:10 +0000 Subject: [PATCH] Account version 2.0.0 Entry files now contain entries separated by lines and indexed by their ids. --- lib/passy_data/common.dart | 23 ++++++++--- .../convert_1d1d0_account_to_2d0d0.dart | 40 +++++++++++++++++++ lib/passy_data/legacy/legacy.dart | 5 +++ .../passy_entries_encrypted_csv_file.dart | 29 +++++++++----- lib/passy_data/passy_entry.dart | 2 +- 5 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 lib/passy_data/legacy/convert_1d1d0_account_to_2d0d0.dart diff --git a/lib/passy_data/common.dart b/lib/passy_data/common.dart index 249d9a90..357dbe0b 100644 --- a/lib/passy_data/common.dart +++ b/lib/passy_data/common.dart @@ -8,14 +8,13 @@ import 'dart:io'; const String passyVersion = '1.2.0'; const String syncVersion = '1.0.0'; -const String accountVersion = '1.1.0'; +const String accountVersion = '2.0.0'; bool isLineDelimiter(String priorChar, String char, String lineDelimiter) { if (lineDelimiter.length == 1) { return char == lineDelimiter; - } else { - return priorChar + char == lineDelimiter; } + return '$priorChar$char' == lineDelimiter; } String? readLine(RandomAccessFile raf, @@ -25,17 +24,31 @@ String? readLine(RandomAccessFile raf, String priorChar = ''; byte = raf.readByteSync(); while (byte != -1) { - var char = utf8.decode([byte]); + String char = utf8.decode([byte]); if (isLineDelimiter(priorChar, char, lineDelimiter)) return line; line += char; priorChar = char; byte = raf.readByteSync(); } - if (byte == -1) onEOF?.call(); + onEOF?.call(); if (line.isEmpty) return null; return line; } +void skipLine(RandomAccessFile raf, + {String lineDelimiter = '\n', void Function()? onEOF}) { + int byte; + String priorChar = ''; + byte = raf.readByteSync(); + while (byte != -1) { + String char = utf8.decode([byte]); + if (isLineDelimiter(priorChar, char, lineDelimiter)) return; + priorChar = char; + byte = raf.readByteSync(); + } + onEOF?.call(); +} + void copyDirectorySync(Directory source, Directory destination) { destination.createSync(recursive: true); source.listSync(recursive: false).forEach((var entity) { diff --git a/lib/passy_data/legacy/convert_1d1d0_account_to_2d0d0.dart b/lib/passy_data/legacy/convert_1d1d0_account_to_2d0d0.dart new file mode 100644 index 00000000..f102a926 --- /dev/null +++ b/lib/passy_data/legacy/convert_1d1d0_account_to_2d0d0.dart @@ -0,0 +1,40 @@ +import 'dart:io'; + +import 'package:encrypt/encrypt.dart'; +import '../common.dart'; + +void convert1_1_0AccountTo2_2_0({ + required String path, + required Encrypter encrypter, +}) { + // Apply the following changes to id_cards.enc, identities.enc, notes.enc, passwords.enc, payment_cards.enc: + // 1. Read the file and clear it. + // 2. Decrypt entries. + // 3. Split the decrypted entries by newlines. + // 4. Per entry, do the following: + // - 4.1. Retrieve the entry key from the first CSV value. + // - 4.2. Encrypt the entry. + // - 4.3. Join, separating with a comma (,), the entry key from 3.1 and the encrypted entry data from 3.2, adding a newline at the end. + // - 4.4. Append the result of 3.3 to the entries file. + void _convert(File file) { + String _decrypted = decrypt(file.readAsStringSync(), encrypter: encrypter); + List _split = _decrypted.split('\n'); + String _result = ''; + for (String s in _split) { + // 4 + if (s == '') continue; + String _key = s.split(',')[0]; + _result += '$_key,${encrypt(s, encrypter: encrypter)}\n'; + } + file.writeAsStringSync(_result); + } + + _convert(File('$path${Platform.pathSeparator}id_cards.enc')); + _convert(File('$path${Platform.pathSeparator}identities.enc')); + _convert(File('$path${Platform.pathSeparator}notes.enc')); + _convert(File('$path${Platform.pathSeparator}passwords.enc')); + _convert(File('$path${Platform.pathSeparator}payment_cards.enc')); + + File(path + Platform.pathSeparator + 'version.txt') + .writeAsStringSync('2.0.0'); +} diff --git a/lib/passy_data/legacy/legacy.dart b/lib/passy_data/legacy/legacy.dart index ec880a0d..006373ab 100644 --- a/lib/passy_data/legacy/legacy.dart +++ b/lib/passy_data/legacy/legacy.dart @@ -1,4 +1,5 @@ import 'package:encrypt/encrypt.dart'; +import 'package:passy/passy_data/legacy/convert_1d1d0_account_to_2d0d0.dart'; import 'dart:io'; import '../account_credentials.dart'; @@ -65,6 +66,10 @@ void convertLegacyAccount({ // Pre 1.1.0 conversion convertPre1_1_0AccountTo1_1_0(path: path, encrypter: encrypter); } + if (_accountVersion[1] == 1) { + // 1.1.0 conversion + convert1_1_0AccountTo2_2_0(path: path, encrypter: encrypter); + } } // No conversion _versionFile.writeAsStringSync(accountVersion); diff --git a/lib/passy_data/passy_entries_encrypted_csv_file.dart b/lib/passy_data/passy_entries_encrypted_csv_file.dart index 9368c683..e143c4c1 100644 --- a/lib/passy_data/passy_entries_encrypted_csv_file.dart +++ b/lib/passy_data/passy_entries_encrypted_csv_file.dart @@ -1,5 +1,6 @@ import 'package:encrypt/encrypt.dart'; import 'package:passy/passy_data/common.dart'; +import 'package:passy/passy_data/entry_type.dart'; import 'package:passy/passy_data/passy_entry.dart'; import 'package:passy/passy_data/saveable_file_base.dart'; import 'dart:io'; @@ -24,18 +25,23 @@ class PassyEntriesEncryptedCSVFile> required Encrypter encrypter, }) { if (file.existsSync()) { - String _decrypted = - decrypt(file.readAsStringSync(), encrypter: encrypter); - List _split = _decrypted.split('\n'); - List _decoded = []; - for (String s in _split) { - if (s == '') continue; - _decoded.add(csvDecode(s, recursive: true)); - } + Map _entries = {}; + RandomAccessFile _file = file.openSync(); + bool eofReached = false; + do { + String line = readLine(_file, onEOF: () => eofReached = true) ?? ''; + if (line == '') continue; + List _decoded1 = csvDecode(line); + List _decoded2 = csvDecode( + decrypt(_decoded1[1], encrypter: encrypter), + recursive: true); + _entries[_decoded1[0]] = + (PassyEntry.fromCSV(entryTypeFromType(T)!)(_decoded2) as T); + } while (eofReached == false); return PassyEntriesEncryptedCSVFile( file, encrypter: encrypter, - value: PassyEntries.fromCSV(_decoded), + value: PassyEntries(entries: _entries), ); } file.createSync(recursive: true); @@ -50,9 +56,10 @@ class PassyEntriesEncryptedCSVFile> String _save() { String _result = ''; for (List _value in value.toCSV()) { - _result += csvEncode(_value) + '\n'; + String _key = _value[0]; + _result += '$_key,${encrypt(csvEncode(_value), encrypter: _encrypter)}\n'; } - return _result == '' ? '' : encrypt(_result, encrypter: _encrypter); + return _result; } @override diff --git a/lib/passy_data/passy_entry.dart b/lib/passy_data/passy_entry.dart index 9d398151..15fc92ab 100644 --- a/lib/passy_data/passy_entry.dart +++ b/lib/passy_data/passy_entry.dart @@ -33,7 +33,7 @@ abstract class PassyEntry with JsonConvertable, CSVConvertable { } } - static PassyEntry Function(List csv) fromCSV(EntryType entryType) { + static PassyEntry Function(List csv) fromCSV(entryType) { switch (entryType) { case EntryType.password: return Password.fromCSV;