|
|
@ -3,6 +3,7 @@ import 'package:kdbx/src/kdbx_consts.dart'; |
|
|
|
import 'package:kdbx/src/kdbx_format.dart'; |
|
|
|
import 'package:kdbx/src/kdbx_format.dart'; |
|
|
|
import 'package:kdbx/src/kdbx_group.dart'; |
|
|
|
import 'package:kdbx/src/kdbx_group.dart'; |
|
|
|
import 'package:kdbx/src/kdbx_object.dart'; |
|
|
|
import 'package:kdbx/src/kdbx_object.dart'; |
|
|
|
|
|
|
|
import 'package:kdbx/src/kdbx_xml.dart'; |
|
|
|
import 'package:xml/xml.dart'; |
|
|
|
import 'package:xml/xml.dart'; |
|
|
|
|
|
|
|
|
|
|
|
/// Represents a case insensitive (but case preserving) key. |
|
|
|
/// Represents a case insensitive (but case preserving) key. |
|
|
@ -21,15 +22,16 @@ class KdbxKey { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
class KdbxEntry extends KdbxObject { |
|
|
|
class KdbxEntry extends KdbxObject { |
|
|
|
KdbxEntry.create(this.parent) : super.create('Entry') { |
|
|
|
KdbxEntry.create(KdbxFile file, this.parent) : super.create(file, 'Entry') { |
|
|
|
icon.set(KdbxIcon.Key); |
|
|
|
icon.set(KdbxIcon.Key); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
KdbxEntry.read(this.parent, XmlElement node) : super.read(node) { |
|
|
|
KdbxEntry.read(this.parent, XmlElement node) : super.read(node) { |
|
|
|
_strings.addEntries(node.findElements('String').map((el) { |
|
|
|
_strings.addEntries(node.findElements(KdbxXml.NODE_STRING).map((el) { |
|
|
|
final key = KdbxKey(el.findElements('Key').single.text); |
|
|
|
final key = KdbxKey(el.findElements(KdbxXml.NODE_KEY).single.text); |
|
|
|
final valueNode = el.findElements('Value').single; |
|
|
|
final valueNode = el.findElements(KdbxXml.NODE_VALUE).single; |
|
|
|
if (valueNode.getAttribute('Protected')?.toLowerCase() == 'true') { |
|
|
|
if (valueNode.getAttribute(KdbxXml.ATTR_PROTECTED)?.toLowerCase() == |
|
|
|
|
|
|
|
'true') { |
|
|
|
return MapEntry(key, KdbxFile.protectedValueForNode(valueNode)); |
|
|
|
return MapEntry(key, KdbxFile.protectedValueForNode(valueNode)); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
return MapEntry(key, PlainValue(valueNode.text)); |
|
|
|
return MapEntry(key, PlainValue(valueNode.text)); |
|
|
@ -37,22 +39,52 @@ class KdbxEntry extends KdbxObject { |
|
|
|
})); |
|
|
|
})); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<KdbxEntry> _history; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<KdbxEntry> get history => |
|
|
|
|
|
|
|
_history ?? |
|
|
|
|
|
|
|
(() { |
|
|
|
|
|
|
|
return _historyElement |
|
|
|
|
|
|
|
.findElements('Entry') |
|
|
|
|
|
|
|
.map((entry) => KdbxEntry.read(parent, entry)) |
|
|
|
|
|
|
|
.toList(); |
|
|
|
|
|
|
|
})(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
XmlElement get _historyElement => node |
|
|
|
|
|
|
|
.findElements(KdbxXml.NODE_HISTORY) |
|
|
|
|
|
|
|
.singleWhere((_) => true, orElse: () { |
|
|
|
|
|
|
|
final el = XmlElement(XmlName(KdbxXml.NODE_HISTORY)); |
|
|
|
|
|
|
|
node.children.add(el); |
|
|
|
|
|
|
|
return el; |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
|
|
|
set isDirty(bool newDirty) { |
|
|
|
|
|
|
|
if (!isDirty && newDirty) { |
|
|
|
|
|
|
|
final history = _historyElement; |
|
|
|
|
|
|
|
history.children.add(toXml()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
super.isDirty = newDirty; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
@override |
|
|
|
XmlElement toXml() { |
|
|
|
XmlElement toXml() { |
|
|
|
final el = super.toXml(); |
|
|
|
final el = super.toXml(); |
|
|
|
el.children.removeWhere((e) => e is XmlElement && e.name.local == 'String'); |
|
|
|
el.children.removeWhere( |
|
|
|
|
|
|
|
(e) => e is XmlElement && e.name.local == KdbxXml.NODE_STRING); |
|
|
|
el.children.addAll(stringEntries.map((stringEntry) { |
|
|
|
el.children.addAll(stringEntries.map((stringEntry) { |
|
|
|
final value = XmlElement(XmlName('Value')); |
|
|
|
final value = XmlElement(XmlName(KdbxXml.NODE_VALUE)); |
|
|
|
if (stringEntry.value is ProtectedValue) { |
|
|
|
if (stringEntry.value is ProtectedValue) { |
|
|
|
value.attributes.add(XmlAttribute(XmlName('Protected'), 'true')); |
|
|
|
value.attributes |
|
|
|
|
|
|
|
.add(XmlAttribute(XmlName(KdbxXml.ATTR_PROTECTED), 'true')); |
|
|
|
KdbxFile.setProtectedValueForNode( |
|
|
|
KdbxFile.setProtectedValueForNode( |
|
|
|
value, stringEntry.value as ProtectedValue); |
|
|
|
value, stringEntry.value as ProtectedValue); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
value.children.add(XmlText(stringEntry.value.getText())); |
|
|
|
value.children.add(XmlText(stringEntry.value.getText())); |
|
|
|
} |
|
|
|
} |
|
|
|
return XmlElement(XmlName('String')) |
|
|
|
return XmlElement(XmlName(KdbxXml.NODE_STRING)) |
|
|
|
..children.addAll([ |
|
|
|
..children.addAll([ |
|
|
|
XmlElement(XmlName('Key')) |
|
|
|
XmlElement(XmlName(KdbxXml.ATTR_PROTECTED)) |
|
|
|
..children.add(XmlText(stringEntry.key.key)), |
|
|
|
..children.add(XmlText(stringEntry.key.key)), |
|
|
|
value, |
|
|
|
value, |
|
|
|
]); |
|
|
|
]); |
|
|
@ -65,11 +97,13 @@ class KdbxEntry extends KdbxObject { |
|
|
|
|
|
|
|
|
|
|
|
// Map<KdbxKey, StringValue> get strings => UnmodifiableMapView(_strings); |
|
|
|
// Map<KdbxKey, StringValue> get strings => UnmodifiableMapView(_strings); |
|
|
|
|
|
|
|
|
|
|
|
Iterable<MapEntry<KdbxKey, StringValue>> get stringEntries => _strings.entries; |
|
|
|
Iterable<MapEntry<KdbxKey, StringValue>> get stringEntries => |
|
|
|
|
|
|
|
_strings.entries; |
|
|
|
|
|
|
|
|
|
|
|
StringValue getString(KdbxKey key) => _strings[key]; |
|
|
|
StringValue getString(KdbxKey key) => _strings[key]; |
|
|
|
|
|
|
|
|
|
|
|
void setString(KdbxKey key, StringValue value) { |
|
|
|
void setString(KdbxKey key, StringValue value) { |
|
|
|
|
|
|
|
isDirty = true; |
|
|
|
_strings[key] = value; |
|
|
|
_strings[key] = value; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|