KeepassX format implementation in pure dart.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

142 lines
4.0 KiB

import 'dart:typed_data';
import 'package:kdbx/src/internal/byte_utils.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
final _logger = Logger('kdbx_var_dictionary');
typedef Decoder<T> = T Function(ReaderHelper reader, int length);
typedef Encoder<T> = void Function(WriterHelper writer, T value);
extension on WriterHelper {
LengthWriter _lengthWriter() => (int length) => writeInt32(length);
}
@immutable
class ValueType<T> {
const ValueType(this.code, this.decoder, [this.encoder]);
final int code;
final Decoder<T> decoder;
final Encoder<T> encoder;
static final typeUInt32 = ValueType(
0x04,
(reader, _) => reader.readUint32(),
(writer, value) => writer.writeUint32(value, writer._lengthWriter()),
);
static final typeUInt64 = ValueType(
0x05,
(reader, _) => reader.readUint64(),
(writer, value) => writer.writeUint64(value, writer._lengthWriter()),
);
static final typeBool = ValueType(
0x08,
(reader, _) => reader.readUint8() != 0,
(writer, value) => writer.writeUint8(value ? 1 : 0, writer._lengthWriter()),
);
static final typeInt32 = ValueType(
0x0C,
(reader, _) => reader.readInt32(),
(writer, value) => writer.writeInt32(value, writer._lengthWriter()),
);
static final typeInt64 = ValueType(
0x0D,
(reader, _) => reader.readInt64(),
(writer, value) => writer.writeInt64(value, writer._lengthWriter()),
);
static final typeString = ValueType(
0x18,
(reader, length) => reader.readString(length),
(writer, value) => writer.writeString(value, writer._lengthWriter()),
);
static final typeBytes = ValueType(
0x42,
(reader, length) => reader.readBytes(length),
(writer, value) => writer.writeBytes(value, writer._lengthWriter()),
);
static ValueType typeByCode(int code) =>
values.firstWhere((t) => t.code == code);
static final values = [
typeUInt32,
typeUInt64,
typeBool,
typeInt32,
typeInt64,
typeString,
typeBytes,
];
}
class VarDictionaryItem<T> {
VarDictionaryItem(this._key, this._valueType, this._value);
final String _key;
final ValueType<T> _valueType;
final T _value;
String toDebugString() {
return 'VarDictionaryItem{key=$_key, valueType=$_valueType, value=${_value.runtimeType}}';
}
}
class VarDictionary {
VarDictionary(List<VarDictionaryItem<dynamic>> items)
: assert(items != null),
_items = items,
_dict = Map.fromEntries(items.map((item) => MapEntry(item._key, item)));
factory VarDictionary.read(ReaderHelper reader) {
final items = <VarDictionaryItem>[];
final versionMinor = reader.readUint8();
final versionMajor = reader.readUint8();
_logger.finest('Reading VarDictionary $versionMajor.$versionMinor');
assert(versionMajor == 1);
while (true) {
final item = _readItem(reader);
if (item == null) {
break;
}
items.add(item);
}
return VarDictionary(items);
}
static const DEFAULT_VERSION = 0x0100;
final List<VarDictionaryItem<dynamic>> _items;
final Map<String, VarDictionaryItem<dynamic>> _dict;
Uint8List write() {
final writer = WriterHelper();
writer.writeUint16(DEFAULT_VERSION);
for (final item in _items) {
item._valueType.encoder(writer, item._value);
}
writer.writeUint8(0);
return writer.output.toBytes();
}
T get<T>(ValueType<T> type, String key) => _dict[key]?._value as T;
void set<T>(ValueType<T> type, String key, T value) =>
_dict[key] = VarDictionaryItem<T>(key, type, value);
static VarDictionaryItem<dynamic> _readItem(ReaderHelper reader) {
final type = reader.readUint8();
if (type == 0) {
return null;
}
final keyLength = reader.readUint32();
final key = reader.readString(keyLength);
final valueLength = reader.readInt32();
final valueType = ValueType.typeByCode(type);
return VarDictionaryItem<dynamic>(
key, valueType, valueType.decoder(reader, valueLength));
}
String toDebugString() {
return 'VarDictionary{${_items.map((item) => item.toDebugString())}';
}
}