diff --git a/lib/src/dbus_client.dart b/lib/src/dbus_client.dart index fe3ca35..ebfc13c 100644 --- a/lib/src/dbus_client.dart +++ b/lib/src/dbus_client.dart @@ -226,6 +226,9 @@ class DBusClient { // Names owned by this client. e.g. [ 'com.example.Foo', 'com.example.Bar' ]. final _ownedNames = {}; + // True if this client registers a name on this bus. + final bool _acquireName; + // Unique name of this client, e.g. ':42'. DBusBusName? _uniqueName; @@ -233,8 +236,14 @@ class DBusClient { final bool introspectable; /// Creates a new DBus client to connect on [address]. - DBusClient(DBusAddress address, {this.introspectable = true}) - : _address = address; + /// If [acquireName] is false, then no name is acquired from the server. + /// This is useful if the server is not a standard bus with multiple clients + /// but rather using D-Bus for client to server communication and not client + /// to client. + DBusClient(DBusAddress address, + {this.introspectable = true, bool acquireName = true}) + : _address = address, + _acquireName = acquireName; /// Creates a new DBus client to communicate with the system bus. factory DBusClient.system({bool introspectable = true}) { @@ -797,13 +806,15 @@ class DBusClient { // The first message to the bus must be this call, note requireConnect is // false as the _connect call hasn't yet completed and would otherwise have // been called again. - var result = await _callMethod( - destination: DBusBusName('org.freedesktop.DBus'), - path: DBusObjectPath('/org/freedesktop/DBus'), - interface: DBusInterfaceName('org.freedesktop.DBus'), - name: DBusMemberName('Hello'), - replySignature: DBusSignature('s')); - _uniqueName = DBusBusName(result.returnValues[0].asString()); + if (_acquireName) { + var result = await _callMethod( + destination: DBusBusName('org.freedesktop.DBus'), + path: DBusObjectPath('/org/freedesktop/DBus'), + interface: DBusInterfaceName('org.freedesktop.DBus'), + name: DBusMemberName('Hello'), + replySignature: DBusSignature('s')); + _uniqueName = DBusBusName(result.returnValues[0].asString()); + } // Notify anyone else awaiting connection. _connectCompleter?.complete(); @@ -811,7 +822,9 @@ class DBusClient { // Monitor name ownership so we know what names we have, and can match incoming signals from other clients. _nameAcquiredSubscription = nameAcquired.listen((name) { var busName = DBusBusName(name); - _nameOwners[busName] = _uniqueName!; + if (_acquireName) { + _nameOwners[busName] = _uniqueName!; + } _ownedNames.add(busName); }); _nameLostSubscription = nameLost.listen((name) { diff --git a/lib/src/dbus_method_call.dart b/lib/src/dbus_method_call.dart index 4c8c426..c9ff332 100644 --- a/lib/src/dbus_method_call.dart +++ b/lib/src/dbus_method_call.dart @@ -3,7 +3,7 @@ import 'dbus_value.dart'; /// A D-Bus method call. class DBusMethodCall { /// Client that called the method. - final String sender; + final String? sender; /// Interface method is on. final String? interface; @@ -29,7 +29,7 @@ class DBusMethodCall { .fold(DBusSignature(''), (a, b) => a + b); const DBusMethodCall( - {required this.sender, + {this.sender, this.interface, required this.name, this.values = const [], diff --git a/lib/src/dbus_server.dart b/lib/src/dbus_server.dart index db3ce6b..02dd4fa 100644 --- a/lib/src/dbus_server.dart +++ b/lib/src/dbus_server.dart @@ -13,7 +13,10 @@ import 'dbus_introspectable.dart'; import 'dbus_match_rule.dart'; import 'dbus_member_name.dart'; import 'dbus_message.dart'; +import 'dbus_method_call.dart'; import 'dbus_method_response.dart'; +import 'dbus_object.dart'; +import 'dbus_object_tree.dart'; import 'dbus_peer.dart'; import 'dbus_properties.dart'; import 'dbus_read_buffer.dart'; @@ -326,6 +329,9 @@ class DBusServer { /// Next serial number to use for messages from the server. int _nextSerial = 1; + /// True if clients are required to use names on this server. + bool _requireNames; + /// Queues for name ownership. final _nameQueues = {}; @@ -335,8 +341,15 @@ class DBusServer { /// Interfaces supported by the server. final _interfaces = []; + /// Objects provided by the server. + final _objectTree = DBusObjectTree(); + /// Creates a new DBus server. - DBusServer(); + /// If [requireNames] is false, clients don't need to acquire a names. + /// This is useful if the server is not a standard bus with multiple clients + /// but rather using D-Bus for client to server communication and not client + /// to client. + DBusServer({bool requireNames = true}) : _requireNames = requireNames; /// Start a service that uses [name]. /// Override this method to enable this feature. @@ -486,6 +499,29 @@ class DBusServer { } } + /// Registers a server [object] on the bus. + /// This is useful if this server is being used for . + void registerObject(DBusObject object) async { + if (object.client != null) { + throw Exception('Object already registered'); + } + + _objectTree.add(object.path, object); + } + + /// Unregisters an [object] on the bus. + void unregisterObject(DBusObject object) async { + if (object.client != null) { + throw 'Object registered on other client'; + } + + var node = _objectTree.lookup(object.path); + if (node == null) { + return; + } + _objectTree.remove(object.path); + } + /// Get the client that is currently owning [name]. _DBusRemoteClient? _getClientByName(DBusBusName name) { for (var client in _clients) { @@ -511,7 +547,9 @@ class DBusServer { // Process requests for the server. DBusMethodResponse? response; - if (client != null && + _DBusRemoteClient? responseClient; + if (_requireNames && + client != null && !client.receivedHello && !(message.destination?.value == 'org.freedesktop.DBus' && message.interface?.value == 'org.freedesktop.DBus' && @@ -523,6 +561,11 @@ class DBusServer { if (client != null && message.type == DBusMessageType.methodCall) { response = await _processServerMethodCall(client, message); } + } else if (message.destination == null) { + if (client != null && message.type == DBusMessageType.methodCall) { + response = await _processServerObjectMethodCall(message); + responseClient = client; + } } else { // No-one is going to handle this message. if (message.destination != null && @@ -555,7 +598,7 @@ class DBusServer { values: values); _nextSerial++; // ignore: unawaited_futures - _processMessage(null, responseMessage); + _processMessage(responseClient, responseMessage); } } @@ -1230,6 +1273,32 @@ class DBusServer { return DBusGetAllPropertiesResponse(properties); } + /// Process a method call requested on the D-Bus server. + Future _processServerObjectMethodCall( + DBusMessage message) async { + if (message.path == null || message.member == null) { + return DBusMethodErrorResponse.failed(); + } + + var node = _objectTree.lookup(message.path!); + if (node == null || node.object == null) { + return DBusMethodErrorResponse.unknownObject(); + } + var object = node.object!; + + var methodCall = DBusMethodCall( + sender: message.sender?.value, + interface: message.interface?.value, + name: message.member!.value, + values: message.values, + noReplyExpected: + message.flags.contains(DBusMessageFlag.noReplyExpected), + noAutoStart: message.flags.contains(DBusMessageFlag.noAutoStart), + allowInteractiveAuthorization: message.flags + .contains(DBusMessageFlag.allowInteractiveAuthorization)); + return await object.handleMethodCall(methodCall); + } + /// Emits org.freedesktop.DBus.NameOwnerChanged. void _emitNameOwnerChanged( DBusBusName name, DBusBusName? oldOwner, DBusBusName? newOwner) { diff --git a/test/dbus_test.dart b/test/dbus_test.dart index 6079e2e..26ab1f8 100644 --- a/test/dbus_test.dart +++ b/test/dbus_test.dart @@ -1512,6 +1512,29 @@ void main() { () => client2.ping(name1), throwsA(isA())); }); + test('no acquire name', () async { + var server = DBusServer(requireNames: false); + var address = + await server.listenAddress(DBusAddress.unix(abstract: 'abstract')); + var client = DBusClient(address, acquireName: false); + addTearDown(() async { + await client.close(); + await server.close(); + }); + + var object = TestObject( + path: DBusObjectPath('/com/example/Object1'), + expectedMethodName: 'Foo', + methodResponses: { + 'Foo': DBusMethodSuccessResponse([DBusString('Hello World')]) + }); + server.registerObject(object); + + var response = await client.callMethod( + path: DBusObjectPath('/com/example/Object1'), name: 'Foo'); + expect(response.values, equals([DBusString('Hello World')])); + }); + test('list names', () async { var server = DBusServer(); var address =