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.
111 lines
3.0 KiB
111 lines
3.0 KiB
import 'dart:async'; |
|
import 'dart:typed_data'; |
|
|
|
import 'package:kdbx/src/crypto/protected_value.dart'; |
|
import 'package:kdbx/src/kdbx_dao.dart'; |
|
import 'package:kdbx/src/kdbx_format.dart'; |
|
import 'package:kdbx/src/kdbx_group.dart'; |
|
import 'package:kdbx/src/kdbx_header.dart'; |
|
import 'package:kdbx/src/kdbx_object.dart'; |
|
import 'package:logging/logging.dart'; |
|
import 'package:xml/xml.dart' as xml; |
|
|
|
final _logger = Logger('kdbx_file'); |
|
|
|
class KdbxFile { |
|
KdbxFile(this.kdbxFormat, this.credentials, this.header, this.body) { |
|
for (final obj in _allObjects) { |
|
obj.file = this; |
|
} |
|
} |
|
|
|
static final protectedValues = Expando<ProtectedValue>(); |
|
|
|
static ProtectedValue protectedValueForNode(xml.XmlElement node) { |
|
return protectedValues[node]; |
|
} |
|
|
|
static void setProtectedValueForNode( |
|
xml.XmlElement node, ProtectedValue value) { |
|
protectedValues[node] = value; |
|
} |
|
|
|
final KdbxFormat kdbxFormat; |
|
final Credentials credentials; |
|
final KdbxHeader header; |
|
final KdbxBody body; |
|
final Set<KdbxObject> dirtyObjects = {}; |
|
final StreamController<Set<KdbxObject>> _dirtyObjectsChanged = |
|
StreamController<Set<KdbxObject>>.broadcast(); |
|
|
|
Stream<Set<KdbxObject>> get dirtyObjectsChanged => |
|
_dirtyObjectsChanged.stream; |
|
|
|
Future<Uint8List> save() async { |
|
return kdbxFormat.save(this); |
|
} |
|
|
|
/// Marks all dirty objects as clean. Called by [KdbxFormat.save]. |
|
void onSaved() { |
|
dirtyObjects.clear(); |
|
_dirtyObjectsChanged.add(dirtyObjects); |
|
} |
|
|
|
Iterable<KdbxObject> get _allObjects => body.rootGroup |
|
.getAllGroups() |
|
.cast<KdbxObject>() |
|
.followedBy(body.rootGroup.getAllEntries()); |
|
|
|
void dirtyObject(KdbxObject kdbxObject) { |
|
dirtyObjects.add(kdbxObject); |
|
_dirtyObjectsChanged.add(dirtyObjects); |
|
} |
|
|
|
void dispose() { |
|
_dirtyObjectsChanged.close(); |
|
} |
|
|
|
KdbxGroup _recycleBin; |
|
|
|
/// Returns the recycle bin, if it exists, null otherwise. |
|
KdbxGroup get recycleBin => _recycleBin ??= _findRecycleBin(); |
|
|
|
KdbxGroup _findRecycleBin() { |
|
final uuid = body.meta.recycleBinUUID.get(); |
|
if (uuid?.isNil != false) { |
|
return null; |
|
} |
|
try { |
|
return findGroupByUuid(uuid); |
|
} catch (e, stackTrace) { |
|
_logger.warning(() { |
|
final groupDebug = body.rootGroup |
|
.getAllGroups() |
|
.map((g) => '${g.uuid}: ${g.name}') |
|
.join('\n'); |
|
return 'All Groups: $groupDebug'; |
|
}); |
|
_logger.severe('Inconsistency error, uuid $uuid not found in groups.', e, |
|
stackTrace); |
|
rethrow; |
|
} |
|
} |
|
|
|
// void _subscribeToChildren() { |
|
// final allObjects = _allObjects; |
|
// for (final obj in allObjects) { |
|
// _subscriptions.handle(obj.changes.listen((event) { |
|
// if (event.isDirty) { |
|
// isDirty = true; |
|
// if (event.object is KdbxGroup) { |
|
// Future(() { |
|
// // resubscribe, just in case some child groups/entries have changed. |
|
// _subscriptions.cancelSubscriptions(); |
|
// _subscribeToChildren(); |
|
// }); |
|
// } |
|
// } |
|
// })); |
|
// } |
|
// } |
|
}
|
|
|