From 87905c186d77d927f511a6535ccc649067e701ea Mon Sep 17 00:00:00 2001 From: rmartinc Date: Fri, 5 May 2023 09:00:00 +0200 Subject: [PATCH] Upgrade dbus-java to 4.3.0. Incorporated: dbus-java-core and dbus-java-transport-native-unixsocket --- .../java/cx/ath/matthew/LibraryLoader.java | 53 - .../main/java/cx/ath/matthew/debug/Debug.java | 673 ------- .../matthew/unix/NotConnectedException.java | 35 - .../cx/ath/matthew/unix/USInputStream.java | 94 - .../cx/ath/matthew/unix/USOutputStream.java | 78 - .../cx/ath/matthew/unix/UnixIOException.java | 43 - .../java/cx/ath/matthew/unix/UnixSocket.java | 346 ---- .../ath/matthew/unix/UnixSocketAddress.java | 86 - .../java/cx/ath/matthew/utils/Hexdump.java | 147 -- .../src/main/java/org/freedesktop/DBus.java | 534 ------ .../freedesktop/dbus/AbstractConnection.java | 1059 ---------- .../java/org/freedesktop/dbus/ArrayFrob.java | 233 +-- .../java/org/freedesktop/dbus/BusAddress.java | 52 - .../org/freedesktop/dbus/CallbackHandler.java | 22 - .../java/org/freedesktop/dbus/Container.java | 110 +- .../org/freedesktop/dbus/DBusAsyncReply.java | 145 +- .../org/freedesktop/dbus/DBusCallInfo.java | 68 +- .../org/freedesktop/dbus/DBusConnection.java | 794 -------- .../org/freedesktop/dbus/DBusInterface.java | 31 - .../freedesktop/dbus/DBusInterfaceName.java | 28 - .../java/org/freedesktop/dbus/DBusMap.java | 258 +-- .../org/freedesktop/dbus/DBusMatchRule.java | 333 +++- .../org/freedesktop/dbus/DBusMemberName.java | 29 - .../java/org/freedesktop/dbus/DBusPath.java | 37 + .../freedesktop/dbus/DBusSerializable.java | 39 - .../org/freedesktop/dbus/DBusSigHandler.java | 27 - .../java/org/freedesktop/dbus/DBusSignal.java | 259 --- .../org/freedesktop/dbus/EfficientMap.java | 122 -- .../org/freedesktop/dbus/EfficientQueue.java | 109 -- .../main/java/org/freedesktop/dbus/Error.java | 144 -- .../org/freedesktop/dbus/ExportedObject.java | 165 -- .../org/freedesktop/dbus/FileDescriptor.java | 64 + .../java/org/freedesktop/dbus/Gettext.java | 30 - .../org/freedesktop/dbus/InternalSignal.java | 20 - .../org/freedesktop/dbus/Marshalling.java | 940 +++++---- .../java/org/freedesktop/dbus/Message.java | 1216 ------------ .../org/freedesktop/dbus/MessageReader.java | 194 -- .../org/freedesktop/dbus/MessageWriter.java | 67 - .../java/org/freedesktop/dbus/MethodCall.java | 137 -- .../org/freedesktop/dbus/MethodReturn.java | 77 - .../org/freedesktop/dbus/MethodTuple.java | 71 +- .../java/org/freedesktop/dbus/ObjectPath.java | 31 +- .../java/org/freedesktop/dbus/ObjectTree.java | 158 -- .../main/java/org/freedesktop/dbus/Path.java | 39 - .../java/org/freedesktop/dbus/Position.java | 29 - .../dbus/RemoteInvocationHandler.java | 303 +-- .../org/freedesktop/dbus/RemoteObject.java | 72 +- .../org/freedesktop/dbus/SignalTuple.java | 127 +- .../org/freedesktop/dbus/StrongReference.java | 26 +- .../java/org/freedesktop/dbus/Struct.java | 10 - .../org/freedesktop/dbus/StructHelper.java | 96 + .../java/org/freedesktop/dbus/Transport.java | 835 -------- .../main/java/org/freedesktop/dbus/Tuple.java | 12 +- .../java/org/freedesktop/dbus/TypeRef.java | 15 + .../org/freedesktop/dbus/TypeSignature.java | 24 +- .../java/org/freedesktop/dbus/UInt16.java | 122 -- .../java/org/freedesktop/dbus/UInt32.java | 122 -- .../java/org/freedesktop/dbus/UInt64.java | 203 -- .../java/org/freedesktop/dbus/Variant.java | 136 -- .../dbus/annotations/DBusIgnore.java | 28 + .../dbus/annotations/DBusInterfaceName.java | 18 + .../dbus/annotations/DBusMemberName.java | 20 + .../dbus/annotations/DBusProperties.java | 24 + .../dbus/annotations/DBusProperty.java | 74 + .../dbus/annotations/DeprecatedOnDBus.java | 19 + .../dbus/annotations/GlibCSymbol.java | 16 + .../annotations/IntrospectionDescription.java | 13 + .../dbus/annotations/MethodError.java | 16 + .../dbus/annotations/MethodNoReply.java | 22 + .../dbus/annotations/Position.java | 19 + .../PropertiesEmitsChangedSignal.java | 40 + .../org/freedesktop/dbus/bin/DBusDaemon.java | 875 +++++++++ .../dbus/bin/EmbeddedDBusDaemon.java | 224 +++ .../freedesktop/dbus/config/DBusSysProps.java | 21 + .../dbus/connections/AbstractConnection.java | 1308 +++++++++++++ .../dbus/connections/BusAddress.java | 284 +++ .../dbus/connections/FallbackContainer.java | 58 + .../dbus/connections/GlobalHandler.java | 59 + .../dbus/connections/IDisconnectAction.java | 5 + .../dbus/connections/IDisconnectCallback.java | 39 + .../connections/IncomingMessageThread.java | 71 + .../freedesktop/dbus/connections/PeerSet.java | 137 ++ .../connections/PendingCallbackManager.java | 39 + .../dbus/connections/ReceivingService.java | 249 +++ .../freedesktop/dbus/connections/SASL.java | 923 +++++++++ .../dbus/connections/SenderThread.java | 62 + .../config/ReceivingServiceConfig.java | 98 + .../config/ReceivingServiceConfigBuilder.java | 207 ++ .../dbus/connections/config/SaslConfig.java | 114 ++ .../connections/config/SaslConfigBuilder.java | 84 + .../connections/config/TransportConfig.java | 157 ++ .../config/TransportConfigBuilder.java | 275 +++ .../impl/BaseConnectionBuilder.java | 211 ++ .../dbus/connections/impl/DBusConnection.java | 999 ++++++++++ .../impl/DBusConnectionBuilder.java | 222 +++ .../connections/impl/DirectConnection.java | 300 +++ .../impl/DirectConnectionBuilder.java | 64 + .../transports/AbstractTransport.java | 342 ++++ .../transports/AbstractUnixTransport.java | 17 + .../transports/IFileBasedBusAddress.java | 18 + .../transports/TransportBuilder.java | 405 ++++ .../transports/TransportConnection.java | 73 + .../freedesktop/dbus/errors/AccessDenied.java | 14 + .../org/freedesktop/dbus/errors/Error.java | 145 ++ .../dbus/errors/InvalidMethodArgument.java | 14 + .../dbus/errors/MatchRuleInvalid.java | 14 + .../org/freedesktop/dbus/errors/NoReply.java | 14 + .../freedesktop/dbus/errors/NotSupported.java | 14 + .../dbus/errors/PropertyReadOnly.java | 14 + .../dbus/errors/ServiceUnknown.java | 14 + .../org/freedesktop/dbus/errors/Timeout.java | 14 + .../dbus/errors/UnknownInterface.java | 14 + .../dbus/errors/UnknownMethod.java | 14 + .../dbus/errors/UnknownObject.java | 14 + .../dbus/errors/UnknownProperty.java | 14 + .../exceptions/AddressResolvingException.java | 11 + .../exceptions/AuthenticationException.java | 16 + .../exceptions/DBusConnectionException.java | 34 + .../dbus/exceptions/DBusException.java | 38 +- .../exceptions/DBusExecutionException.java | 42 +- .../dbus/exceptions/FatalDBusException.java | 28 +- .../dbus/exceptions/FatalException.java | 14 - .../IllegalThreadPoolStateException.java | 29 + .../exceptions/InternalMessageException.java | 19 +- .../InvalidBusAddressException.java | 29 + .../dbus/exceptions/MarshallingException.java | 24 +- .../exceptions/MessageFormatException.java | 19 +- .../MessageProtocolVersionException.java | 20 +- .../dbus/exceptions/MessageTypeException.java | 19 +- .../dbus/exceptions/NonFatalException.java | 14 - .../dbus/exceptions/NotConnected.java | 19 +- .../exceptions/SocketClosedException.java | 30 + .../TransportConfigurationException.java | 14 + .../TransportRegistrationException.java | 20 + .../exceptions/UnknownTypeCodeException.java | 19 +- .../AbstractInterfacesAddedHandler.java | 21 + .../AbstractInterfacesRemovedHandler.java | 21 + .../AbstractPropertiesChangedHandler.java | 21 + .../handlers/AbstractSignalHandlerBase.java | 18 + .../dbus/interfaces/CallbackHandler.java | 12 + .../org/freedesktop/dbus/interfaces/DBus.java | 250 +++ .../dbus/interfaces/DBusInterface.java | 32 + .../dbus/interfaces/DBusSerializable.java | 23 + .../dbus/interfaces/DBusSigHandler.java | 17 + .../freedesktop/dbus/interfaces/Error.java | 10 + .../dbus/interfaces/FatalException.java | 4 + .../freedesktop/dbus/interfaces/Features.java | 8 + .../dbus/interfaces/Introspectable.java | 17 + .../dbus/interfaces/Monitoring.java | 47 + .../dbus/interfaces/NonFatalException.java | 4 + .../dbus/interfaces/ObjectManager.java | 123 ++ .../org/freedesktop/dbus/interfaces/Peer.java | 14 + .../dbus/interfaces/Properties.java | 99 + .../freedesktop/dbus/messages/DBusSignal.java | 345 ++++ .../dbus/messages/EmptyCollectionHelper.java | 125 ++ .../dbus/messages/ExportedObject.java | 341 ++++ .../freedesktop/dbus/messages/Message.java | 1708 +++++++++++++++++ .../dbus/messages/MessageFactory.java | 48 + .../freedesktop/dbus/messages/MethodBase.java | 38 + .../freedesktop/dbus/messages/MethodCall.java | 110 ++ .../dbus/messages/MethodReturn.java | 59 + .../freedesktop/dbus/messages/ObjectTree.java | 188 ++ .../dbus/spi/message/IMessageReader.java | 17 + .../dbus/spi/message/IMessageWriter.java | 22 + .../dbus/spi/message/ISocketProvider.java | 42 + .../spi/message/InputStreamMessageReader.java | 193 ++ .../message/OutputStreamMessageWriter.java | 59 + .../spi/transport/ITransportProvider.java | 79 + .../freedesktop/dbus/types/DBusListType.java | 30 +- .../freedesktop/dbus/types/DBusMapType.java | 36 +- .../dbus/types/DBusStructType.java | 26 +- .../org/freedesktop/dbus/types/UInt16.java | 93 + .../org/freedesktop/dbus/types/UInt32.java | 93 + .../org/freedesktop/dbus/types/UInt64.java | 174 ++ .../org/freedesktop/dbus/types/Variant.java | 146 ++ .../dbus/utils/AddressBuilder.java | 163 ++ .../dbus/utils/CommonRegexPattern.java | 23 + .../dbus/utils/DBusNamingUtil.java | 83 + .../org/freedesktop/dbus/utils/Hexdump.java | 145 ++ .../dbus/utils/IThrowingSupplier.java | 21 + .../freedesktop/dbus/utils/LoggingHelper.java | 80 + .../dbus/utils/NameableThreadFactory.java | 63 + .../freedesktop/dbus/utils/TimeMeasure.java | 136 ++ .../java/org/freedesktop/dbus/utils/Util.java | 678 +++++++ .../dbus/utils/XmlErrorHandlers.java | 60 + .../org/freedesktop/dbus/utils/XmlUtil.java | 228 +++ .../org/freedesktop/sssd/infopipe/Cache.java | 33 - .../freedesktop/sssd/infopipe/InfoPipe.java | 9 +- .../jre/NativeTransportProvider.java | 38 + .../transport/jre/NativeUnixSocketHelper.java | 33 + .../jre/NativeUnixSocketTransport.java | 100 + .../dbus/transport/jre/UnixBusAddress.java | 30 + ...ktop.dbus.spi.transport.ITransportProvider | 1 + 193 files changed, 17165 insertions(+), 9821 deletions(-) delete mode 100644 federation/sssd/src/main/java/cx/ath/matthew/LibraryLoader.java delete mode 100644 federation/sssd/src/main/java/cx/ath/matthew/debug/Debug.java delete mode 100644 federation/sssd/src/main/java/cx/ath/matthew/unix/NotConnectedException.java delete mode 100644 federation/sssd/src/main/java/cx/ath/matthew/unix/USInputStream.java delete mode 100644 federation/sssd/src/main/java/cx/ath/matthew/unix/USOutputStream.java delete mode 100644 federation/sssd/src/main/java/cx/ath/matthew/unix/UnixIOException.java delete mode 100644 federation/sssd/src/main/java/cx/ath/matthew/unix/UnixSocket.java delete mode 100644 federation/sssd/src/main/java/cx/ath/matthew/unix/UnixSocketAddress.java delete mode 100644 federation/sssd/src/main/java/cx/ath/matthew/utils/Hexdump.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/DBus.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/AbstractConnection.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/BusAddress.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/CallbackHandler.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/DBusConnection.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/DBusInterface.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/DBusInterfaceName.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/DBusMemberName.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/DBusPath.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/DBusSerializable.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/DBusSigHandler.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/DBusSignal.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/EfficientMap.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/EfficientQueue.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/Error.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/ExportedObject.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/FileDescriptor.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/Gettext.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/InternalSignal.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/Message.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/MessageReader.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/MessageWriter.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/MethodCall.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/MethodReturn.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/ObjectTree.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/Path.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/Position.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/StructHelper.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/Transport.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/TypeRef.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/UInt16.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/UInt32.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/UInt64.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/Variant.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusIgnore.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusInterfaceName.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusMemberName.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusProperties.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusProperty.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DeprecatedOnDBus.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/annotations/GlibCSymbol.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/annotations/IntrospectionDescription.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/annotations/MethodError.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/annotations/MethodNoReply.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/annotations/Position.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/annotations/PropertiesEmitsChangedSignal.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/bin/DBusDaemon.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/bin/EmbeddedDBusDaemon.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/config/DBusSysProps.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/AbstractConnection.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/BusAddress.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/FallbackContainer.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/GlobalHandler.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/IDisconnectAction.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/IDisconnectCallback.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/IncomingMessageThread.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/PeerSet.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/PendingCallbackManager.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/ReceivingService.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/SASL.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/SenderThread.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/ReceivingServiceConfig.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/ReceivingServiceConfigBuilder.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/SaslConfig.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/SaslConfigBuilder.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/TransportConfig.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/TransportConfigBuilder.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/BaseConnectionBuilder.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnection.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnectionBuilder.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DirectConnection.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DirectConnectionBuilder.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/AbstractTransport.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/AbstractUnixTransport.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/IFileBasedBusAddress.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/TransportBuilder.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/TransportConnection.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/errors/AccessDenied.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/errors/Error.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/errors/InvalidMethodArgument.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/errors/MatchRuleInvalid.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/errors/NoReply.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/errors/NotSupported.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/errors/PropertyReadOnly.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/errors/ServiceUnknown.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/errors/Timeout.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownInterface.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownMethod.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownObject.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownProperty.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/AddressResolvingException.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/AuthenticationException.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusConnectionException.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/FatalException.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/IllegalThreadPoolStateException.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/InvalidBusAddressException.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/NonFatalException.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/SocketClosedException.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/TransportConfigurationException.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/TransportRegistrationException.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractInterfacesAddedHandler.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractInterfacesRemovedHandler.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractPropertiesChangedHandler.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractSignalHandlerBase.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/CallbackHandler.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBus.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBusInterface.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBusSerializable.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBusSigHandler.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Error.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/FatalException.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Features.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Introspectable.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Monitoring.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/NonFatalException.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/ObjectManager.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Peer.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Properties.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/messages/DBusSignal.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/messages/EmptyCollectionHelper.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/messages/ExportedObject.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/messages/Message.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/messages/MessageFactory.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/messages/MethodBase.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/messages/MethodCall.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/messages/MethodReturn.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/messages/ObjectTree.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/IMessageReader.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/IMessageWriter.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/ISocketProvider.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/InputStreamMessageReader.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/OutputStreamMessageWriter.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/spi/transport/ITransportProvider.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/types/UInt16.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/types/UInt32.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/types/UInt64.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/types/Variant.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/utils/AddressBuilder.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/utils/CommonRegexPattern.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/utils/DBusNamingUtil.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/utils/Hexdump.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/utils/IThrowingSupplier.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/utils/LoggingHelper.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/utils/NameableThreadFactory.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/utils/TimeMeasure.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/utils/Util.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/utils/XmlErrorHandlers.java create mode 100644 federation/sssd/src/main/java/org/freedesktop/dbus/utils/XmlUtil.java delete mode 100644 federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/Cache.java create mode 100644 federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/NativeTransportProvider.java create mode 100644 federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/NativeUnixSocketHelper.java create mode 100644 federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/NativeUnixSocketTransport.java create mode 100644 federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/UnixBusAddress.java create mode 100644 federation/sssd/src/main/resources/META-INF/services/org.freedesktop.dbus.spi.transport.ITransportProvider diff --git a/federation/sssd/src/main/java/cx/ath/matthew/LibraryLoader.java b/federation/sssd/src/main/java/cx/ath/matthew/LibraryLoader.java deleted file mode 100644 index 7279a36682..0000000000 --- a/federation/sssd/src/main/java/cx/ath/matthew/LibraryLoader.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package cx.ath.matthew; - -/** - * @author Bruno Oliveira. - */ -public class LibraryLoader { - - private static final String[] PATHS = { - "/opt/rh/rh-sso7/root/lib/", - "/opt/rh/rh-sso7/root/lib64/", - "/usr/lib/", - "/usr/lib64/", - "/usr/local/lib/", - "/opt/local/lib/" - }; - private static final String LIBRARY_NAME = "libunix_dbus_java"; - private static final String VERSION = "0.0.8"; - private static boolean loadSucceeded; - - public static LibraryLoader load() { - for (String path : PATHS) { - try { - System.load(String.format("%s/%s.so.%s", path, LIBRARY_NAME, VERSION)); - loadSucceeded = true; - break; - } catch (UnsatisfiedLinkError e) { - loadSucceeded = false; - } - } - - return new LibraryLoader(); - } - - public boolean succeed() { - return loadSucceeded; - } -} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/debug/Debug.java b/federation/sssd/src/main/java/cx/ath/matthew/debug/Debug.java deleted file mode 100644 index b81bf9c23d..0000000000 --- a/federation/sssd/src/main/java/cx/ath/matthew/debug/Debug.java +++ /dev/null @@ -1,673 +0,0 @@ -/* Copyright (C) 1991-2015 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - . */ -/* This header is separate from features.h so that the compiler can - include it implicitly at the start of every compilation. It must - not itself include or any other header that includes - because the implicit include comes before any feature - test macros that may be defined in a source file before it first - explicitly includes a system header. GCC knows the name of this - header in order to preinclude it. */ -/* glibc's intent is to support the IEC 559 math functionality, real - and complex. If the GCC (4.9 and later) predefined macros - specifying compiler intent are available, use them to determine - whether the overall intent is to support these features; otherwise, - presume an older compiler has intent to support these features and - define these macros by default. */ -/* wchar_t uses Unicode 7.0.0. Version 7.0 of the Unicode Standard is - synchronized with ISO/IEC 10646:2012, plus Amendments 1 (published - on April, 2013) and 2 (not yet published as of February, 2015). - Additionally, it includes the accelerated publication of U+20BD - RUBLE SIGN. Therefore Unicode 7.0.0 is between 10646:2012 and - 10646:2014, and so we use the date ISO/IEC 10646:2012 Amd.1 was - published. */ -/* We do not support C11 . */ -/* - * Java Debug Library - * - * Copyright (c) Matthew Johnson 2005 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * To Contact the author, please email src@matthew.ath.cx - * - */ -package cx.ath.matthew.debug; - -import cx.ath.matthew.utils.Hexdump; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Properties; - -/** - * Add debugging to your program, has support for large projects with multiple - * classes and debug levels per class. Supports optional enabling of debug - * per-level per-class and debug targets of files, Streams or stderr. - * Also supports timing between debug outputs, printing of stack traces for Throwables - * and files/line numbers on each message. - *

- * Debug now automatically figures out which class it was called from, so all - * methods passing in the calling class are deprecated. - *

- *

- * The defaults are to print all messages to stderr with class and method name. - *

- *

- * Should be called like this: - *

- *
- * if (Debug.debug) Debug.print(Debug.INFO, "Debug Message");
- * 
- */ -public class Debug { - /** - * This interface can be used to provide custom printing filters - * for certain classes. - */ - public static interface FilterCommand { - /** - * Called to print debug messages with a custom filter. - * - * @param output The PrintStream to output to. - * @param level The debug level of this message. - * @param location The textual location of the message. - * @param extra Extra information such as timing details. - * @param message The debug message. - * @param lines Other lines of a multiple-line debug message. - */ - public void filter(PrintStream output, int level, String location, String extra, String message, String[] lines); - } - - /** - * Highest priority messages - */ - public static final int CRIT = 1; - /** - * Error messages - */ - public static final int ERR = 2; - /** - * Warnings - */ - public static final int WARN = 3; - /** - * Information - */ - public static final int INFO = 4; - /** - * Debug messages - */ - public static final int DEBUG = 5; - /** - * Verbose debug messages - */ - public static final int VERBOSE = 6; - /** - * Set this to false to disable compilation of Debug statements - */ - public static final boolean debug = false; - /** - * The current output stream (defaults to System.err) - */ - public static PrintStream debugout = System.err; - private static Properties prop = null; - private static boolean timing = false; - private static boolean ttrace = false; - private static boolean lines = false; - private static boolean hexdump = false; - private static long last = 0; - private static int balen = 36; - private static int bawidth = 80; - private static Class saveclass = null; - //TODO: 1.5 private static Map, FilterCommand> filterMap = new HashMap, FilterCommand>(); - private static Map filterMap = new HashMap(); - - /** - * Set properties to configure debugging. - * Format of properties is class => level, e.g. - *
-     * cx.ath.matthew.io.TeeOutputStream = INFO
-     * cx.ath.matthew.io.DOMPrinter = DEBUG
-     * 
- * The debug level can be one of CRIT, ERR, WARN, INFO, DEBUG or VERBOSE which - * correspond to all messages up to that level. The special words YES, ALL and TRUE - * cause all messages to be printed regardless of level. All other terms disable - * messages for that class. CRIT and ERR messages are always printed if debugging is enabled - * unless explicitly disabled. - * The special class name ALL can be used to set the default level for all classes. - * - * @param prop Properties object to use. - */ - public static void setProperties(Properties prop) { - Debug.prop = prop; - } - - /** - * Read which class to debug on at which level from the given File. - * Syntax the same as Java Properties files: - *
-     * <class> = <debuglevel>
-     * 
- * E.G. - *
-     * cx.ath.matthew.io.TeeOutputStream = INFO
-     * cx.ath.matthew.io.DOMPrinter = DEBUG
-     * 
- * The debug level can be one of CRIT, ERR, WARN, INFO, DEBUG or VERBOSE which - * correspond to all messages up to that level. The special words YES, ALL and TRUE - * cause all messages to be printed regardless of level. All other terms disable - * messages for that class. CRIT and ERR messages are always printed if debugging is enabled - * unless explicitly disabled. - * The special class name ALL can be used to set the default level for all classes. - * - * @param f File to read from. - */ - public static void loadConfig(File f) throws IOException { - prop = new Properties(); - try (FileInputStream is = new FileInputStream((f))) { - prop.load(is); - } - } - - /** - * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. - */ - //TODO: 1.5 @Deprecated() - public static boolean debugging(Class c, int loglevel) { - if (debug) { - if (null == c) return true; - return debugging(c.getName(), loglevel); - } - return false; - } - - public static boolean debugging(String s, int loglevel) { - if (debug) { - try { - if (null == s) return true; - if (null == prop) return loglevel <= DEBUG; - String d = prop.getProperty(s); - if (null == d || "".equals(d)) d = prop.getProperty("ALL"); - if (null == d) return loglevel <= ERR; - if ("".equals(d)) return loglevel <= ERR; - d = d.toLowerCase(); - if ("true".equals(d)) return true; - if ("yes".equals(d)) return true; - if ("all".equals(d)) return true; - if ("verbose".equals(d)) return loglevel <= VERBOSE; - if ("debug".equals(d)) return loglevel <= DEBUG; - if ("info".equals(d)) return loglevel <= INFO; - if ("warn".equals(d)) return loglevel <= WARN; - if ("err".equals(d)) return loglevel <= ERR; - if ("crit".equals(d)) return loglevel <= CRIT; - int i = Integer.parseInt(d); - return i >= loglevel; - } catch (Exception e) { - return false; - } - } - return false; - } - - /** - * Output to the given Stream - */ - public static void setOutput(PrintStream p) throws IOException { - debugout = p; - } - - /** - * Output to the given file - */ - public static void setOutput(String filename) throws IOException { - debugout = new PrintStream(new FileOutputStream(filename, true)); - } - - /** - * Output to the default debug.log - */ - public static void setOutput() throws IOException { - setOutput("./debug.log"); - } - - /** - * Log at DEBUG - * - * @param d The object to log - */ - public static void print(Object d) { - if (debug) { - if (d instanceof String) - print(DEBUG, (String) d); - else if (d instanceof Throwable) - print(DEBUG, (Throwable) d); - else if (d instanceof byte[]) - print(DEBUG, (byte[]) d); - else if (d instanceof Map) - printMap(DEBUG, (Map) d); - else print(DEBUG, d); - } - } - - /** - * Log at DEBUG - * - * @param o The object doing the logging - * @param d The object to log - * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. - */ - //TODO: 1.5 @Deprecated() - public static void print(Object o, Object d) { - if (debug) { - if (o instanceof Class) - saveclass = (Class) o; - else - saveclass = o.getClass(); - print(d); - } - } - - /** - * Log an Object - * - * @param o The object doing the logging - * @param loglevel The level to log at (DEBUG, WARN, etc) - * @param d The object to log with d.toString() - * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. - */ - //TODO: 1.5 @Deprecated() - public static void print(Object o, int loglevel, Object d) { - if (debug) { - if (o instanceof Class) - saveclass = (Class) o; - else - saveclass = o.getClass(); - print(loglevel, d); - } - } - - /** - * Log a String - * - * @param o The object doing the logging - * @param loglevel The level to log at (DEBUG, WARN, etc) - * @param s The log message - * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. - */ - //TODO: 1.5 @Deprecated() - public static void print(Object o, int loglevel, String s) { - if (debug) { - if (o instanceof Class) - saveclass = (Class) o; - else - saveclass = o.getClass(); - print(loglevel, s); - } - } - - /** - * Log a Throwable - * - * @param o The object doing the logging - * @param loglevel The level to log at (DEBUG, WARN, etc) - * @param t The throwable to log with .toString and .printStackTrace - * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. - */ - //TODO: 1.5 @Deprecated() - public static void print(Object o, int loglevel, Throwable t) { - if (debug) { - if (o instanceof Class) - saveclass = (Class) o; - else - saveclass = o.getClass(); - print(loglevel, t); - } - } - - /** - * Log a Throwable - * - * @param c The class doing the logging - * @param loglevel The level to log at (DEBUG, WARN, etc) - * @param t The throwable to log with .toString and .printStackTrace - * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. - */ - //TODO: 1.5 @Deprecated() - public static void print(Class c, int loglevel, Throwable t) { - if (debug) { - saveclass = c; - print(loglevel, t); - } - } - - /** - * Log a Throwable - * - * @param loglevel The level to log at (DEBUG, WARN, etc) - * @param t The throwable to log with .toString and .printStackTrace - * @see #setThrowableTraces to turn on stack traces. - */ - public static void print(int loglevel, Throwable t) { - if (debug) { - String timestr = ""; - String[] data = getTraceElements(); - if (debugging(data[0], loglevel)) { - if (timing) { - long now = System.currentTimeMillis(); - timestr = "{" + (now - last) + "} "; - last = now; - } - String[] lines = null; - if (ttrace) { - StackTraceElement[] ste = t.getStackTrace(); - lines = new String[ste.length]; - for (int i = 0; i < ste.length; i++) - lines[i] = "\tat " + ste[i].toString(); - } - _print(t.getClass(), loglevel, data[0] + "." + data[1] + "()" + data[2], timestr, t.toString(), lines); - } - } - } - - /** - * Log a byte array - * - * @param loglevel The level to log at (DEBUG, WARN, etc) - * @param b The byte array to print. - * @see #setHexDump to enable hex dumping. - * @see #setByteArrayCount to change how many bytes are printed. - * @see #setByteArrayWidth to change the formatting width of hex. - */ - public static void print(int loglevel, byte[] b) { - if (debug) { - String timestr = ""; - String[] data = getTraceElements(); - if (debugging(data[0], loglevel)) { - if (timing) { - long now = System.currentTimeMillis(); - timestr = "{" + (now - last) + "} "; - last = now; - } - String[] lines = null; - if (hexdump) { - if (balen >= b.length) - lines = Hexdump.format(b, bawidth).split("\n"); - else { - byte[] buf = new byte[balen]; - System.arraycopy(b, 0, buf, 0, balen); - lines = Hexdump.format(buf, bawidth).split("\n"); - } - } - _print(b.getClass(), loglevel, data[0] + "." + data[1] + "()" + data[2], timestr, b.length + " bytes", lines); - } - } - } - - /** - * Log a String - * - * @param loglevel The level to log at (DEBUG, WARN, etc) - * @param s The string to log with d.toString() - */ - public static void print(int loglevel, String s) { - if (debug) - print(loglevel, (Object) s); - } - - /** - * Log an Object - * - * @param c The class doing the logging - * @param loglevel The level to log at (DEBUG, WARN, etc) - * @param d The object to log with d.toString() - * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. - */ - //TODO: 1.5 @Deprecated() - public static void print(Class c, int loglevel, Object d) { - if (debug) { - saveclass = c; - print(loglevel, d); - } - } - - /** - * Log a String - * - * @param c The class doing the logging - * @param loglevel The level to log at (DEBUG, WARN, etc) - * @param s The log message - * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. - */ - //TODO: 1.5 @Deprecated() - public static void print(Class c, int loglevel, String s) { - if (debug) { - saveclass = c; - print(loglevel, s); - } - } - - private static String[] getTraceElements() { - String[] data = new String[]{"", "", ""}; - try { - Method m = Thread.class.getMethod("getStackTrace", new Class[0]); - StackTraceElement[] stes = (StackTraceElement[]) m.invoke(Thread.currentThread(), new Object[0]); - for (StackTraceElement ste : stes) { - if (Debug.class.getName().equals(ste.getClassName())) continue; - if (Thread.class.getName().equals(ste.getClassName())) continue; - if (Method.class.getName().equals(ste.getClassName())) continue; - if (ste.getClassName().startsWith("sun.reflect")) continue; - data[0] = ste.getClassName(); - data[1] = ste.getMethodName(); - if (lines) - data[2] = " " + ste.getFileName() + ":" + ste.getLineNumber(); - break; - } - } catch (NoSuchMethodException NSMe) { - if (null != saveclass) - data[0] = saveclass.getName(); - } catch (IllegalAccessException IAe) { - } catch (InvocationTargetException ITe) { - } - return data; - } - - /** - * Log an Object - * - * @param loglevel The level to log at (DEBUG, WARN, etc) - * @param o The object to log - */ - public static void print(int loglevel, Object o) { - if (debug) { - String timestr = ""; - String[] data = getTraceElements(); - if (debugging(data[0], loglevel)) { - if (timing) { - long now = System.currentTimeMillis(); - timestr = "{" + (now - last) + "} "; - last = now; - } - _print(o.getClass(), loglevel, data[0] + "." + data[1] + "()" + data[2], timestr, o.toString(), null); - } - } - } - - /** - * Log a Map - * - * @param o The object doing the logging - * @param loglevel The level to log at (DEBUG, WARN, etc) - * @param m The Map to print out - * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. - */ - //TODO: 1.5 @Deprecated() - public static void printMap(Object o, int loglevel, Map m) { - if (debug) { - if (o instanceof Class) - saveclass = (Class) o; - else - saveclass = o.getClass(); - printMap(loglevel, m); - } - } - - /** - * Log a Map - * - * @param c The class doing the logging - * @param loglevel The level to log at (DEBUG, WARN, etc) - * @param m The Map to print out - * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. - */ - //TODO: 1.5 @Deprecated() - public static void printMap(Class c, int loglevel, Map m) { - if (debug) { - saveclass = c; - printMap(loglevel, m); - } - } - - /** - * Log a Map at DEBUG log level - * - * @param m The Map to print out - */ - public static void printMap(Map m) { - printMap(DEBUG, m); - } - - /** - * Log a Map - * - * @param loglevel The level to log at (DEBUG, WARN, etc) - * @param m The Map to print out - */ - public static void printMap(int loglevel, Map m) { - if (debug) { - String timestr = ""; - String[] data = getTraceElements(); - if (debugging(data[0], loglevel)) { - if (timing) { - long now = System.currentTimeMillis(); - timestr = "{" + (now - last) + "} "; - last = now; - } - Iterator i = m.keySet().iterator(); - String[] lines = new String[m.size()]; - int j = 0; - while (i.hasNext()) { - Object key = i.next(); - lines[j++] = "\t\t- " + key + " => " + m.get(key); - } - _print(m.getClass(), loglevel, data[0] + "." + data[1] + "()" + data[2], timestr, "Map:", lines); - } - } - } - - /** - * Enable or disable stack traces in Debuging throwables. - */ - public static void setThrowableTraces(boolean ttrace) { - Debug.ttrace = ttrace; - } - - /** - * Enable or disable timing in Debug messages. - */ - public static void setTiming(boolean timing) { - Debug.timing = timing; - } - - /** - * Enable or disable line numbers. - */ - public static void setLineNos(boolean lines) { - Debug.lines = lines; - } - - /** - * Enable or disable hexdumps. - */ - public static void setHexDump(boolean hexdump) { - Debug.hexdump = hexdump; - } - - /** - * Set the size of hexdumps. - * (Default: 36) - */ - public static void setByteArrayCount(int count) { - Debug.balen = count; - } - - /** - * Set the formatted width of hexdumps. - * (Default: 80 chars) - */ - public static void setByteArrayWidth(int width) { - Debug.bawidth = width; - } - - /** - * Add a filter command for a specific type. - * This command will be called with the output stream - * and the text to be sent. It should perform any - * changes necessary to the text and then print the - * result to the output stream. - */ - public static void addFilterCommand(Class c, FilterCommand f) - //TODO 1.5: public static void addFilterCommand(Class c, FilterCommand f) - { - filterMap.put(c, f); - } - - private static void _print(Class c, int level, String loc, String extra, String message, String[] lines) { - //TODO 1.5: FilterCommand f = filterMap.get(c); - FilterCommand f = (FilterCommand) filterMap.get(c); - if (null == f) { - debugout.println("[" + loc + "] " + extra + message); - if (null != lines) - for (String s : lines) - debugout.println(s); - } else - f.filter(debugout, level, loc, extra, message, lines); - } -} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/unix/NotConnectedException.java b/federation/sssd/src/main/java/cx/ath/matthew/unix/NotConnectedException.java deleted file mode 100644 index 836f5d6229..0000000000 --- a/federation/sssd/src/main/java/cx/ath/matthew/unix/NotConnectedException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Java Unix Sockets Library - * - * Copyright (c) Matthew Johnson 2004 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * To Contact the author, please email src@matthew.ath.cx - * - */ -package cx.ath.matthew.unix; - -import java.net.SocketException; - -public class NotConnectedException extends SocketException { - public NotConnectedException() { - super("The Socket is Not Connected"); - } -} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/unix/USInputStream.java b/federation/sssd/src/main/java/cx/ath/matthew/unix/USInputStream.java deleted file mode 100644 index eb143fe6a9..0000000000 --- a/federation/sssd/src/main/java/cx/ath/matthew/unix/USInputStream.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Java Unix Sockets Library - * - * Copyright (c) Matthew Johnson 2004 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * To Contact the author, please email src@matthew.ath.cx - * - */ -package cx.ath.matthew.unix; - -import java.io.IOException; -import java.io.InputStream; - -public class USInputStream extends InputStream { - public static final int MSG_DONTWAIT = 0x40; - - private native int native_recv(int sock, byte[] b, int off, int len, int flags, int timeout) throws IOException; - - private int sock; - boolean closed = false; - private byte[] onebuf = new byte[1]; - private UnixSocket us; - private boolean blocking = true; - private int flags = 0; - private int timeout = 0; - - public USInputStream(int sock, UnixSocket us) { - this.sock = sock; - this.us = us; - } - - public void close() throws IOException { - closed = true; - us.close(); - } - - public boolean markSupported() { - return false; - } - - public int read() throws IOException { - int rv = 0; - while (0 >= rv) rv = read(onebuf); - if (-1 == rv) return -1; - return 0 > onebuf[0] ? -onebuf[0] : onebuf[0]; - } - - public int read(byte[] b, int off, int len) throws IOException { - if (closed) throw new NotConnectedException(); - int count = native_recv(sock, b, off, len, flags, timeout); - /* Yes, I really want to do this. Recv returns 0 for 'connection shut down'. - * read() returns -1 for 'end of stream. - * Recv returns -1 for 'EAGAIN' (all other errors cause an exception to be raised) - * whereas read() returns 0 for '0 bytes read', so yes, I really want to swap them here. - */ - if (0 == count) return -1; - else if (-1 == count) return 0; - else return count; - } - - public boolean isClosed() { - return closed; - } - - public UnixSocket getSocket() { - return us; - } - - public void setBlocking(boolean enable) { - flags = enable ? 0 : MSG_DONTWAIT; - } - - public void setSoTimeout(int timeout) { - this.timeout = timeout; - } -} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/unix/USOutputStream.java b/federation/sssd/src/main/java/cx/ath/matthew/unix/USOutputStream.java deleted file mode 100644 index d8c85a7718..0000000000 --- a/federation/sssd/src/main/java/cx/ath/matthew/unix/USOutputStream.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Java Unix Sockets Library - * - * Copyright (c) Matthew Johnson 2004 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * To Contact the author, please email src@matthew.ath.cx - * - */ -package cx.ath.matthew.unix; - -import java.io.IOException; -import java.io.OutputStream; - -public class USOutputStream extends OutputStream { - private native int native_send(int sock, byte[] b, int off, int len) throws IOException; - - private native int native_send(int sock, byte[][] b) throws IOException; - - private int sock; - boolean closed = false; - private byte[] onebuf = new byte[1]; - private UnixSocket us; - - public USOutputStream(int sock, UnixSocket us) { - this.sock = sock; - this.us = us; - } - - public void close() throws IOException { - closed = true; - us.close(); - } - - public void flush() { - } // no-op, we do not buffer - - public void write(byte[][] b) throws IOException { - if (closed) throw new NotConnectedException(); - native_send(sock, b); - } - - public void write(byte[] b, int off, int len) throws IOException { - if (closed) throw new NotConnectedException(); - native_send(sock, b, off, len); - } - - public void write(int b) throws IOException { - onebuf[0] = (byte) (b % 0x7F); - if (1 == (b % 0x80)) onebuf[0] = (byte) -onebuf[0]; - write(onebuf); - } - - public boolean isClosed() { - return closed; - } - - public UnixSocket getSocket() { - return us; - } -} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixIOException.java b/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixIOException.java deleted file mode 100644 index 24fd20cd1f..0000000000 --- a/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixIOException.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Java Unix Sockets Library - * - * Copyright (c) Matthew Johnson 2004 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * To Contact the author, please email src@matthew.ath.cx - * - */ -package cx.ath.matthew.unix; - -import java.io.IOException; - -/** - * An IO Exception which occurred during UNIX Socket IO - */ -public class UnixIOException extends IOException { - private int no; - private String message; - - public UnixIOException(int no, String message) { - super(message); - this.message = message; - this.no = no; - } -} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixSocket.java b/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixSocket.java deleted file mode 100644 index 1c59dd7fd3..0000000000 --- a/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixSocket.java +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Java Unix Sockets Library - * - * Copyright (c) Matthew Johnson 2004 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * To Contact the author, please email src@matthew.ath.cx - * - */ -package cx.ath.matthew.unix; - -import cx.ath.matthew.debug.Debug; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Represents a UnixSocket. - */ -public class UnixSocket { - - private native void native_set_pass_cred(int sock, boolean passcred) throws IOException; - - private native int native_connect(String address, boolean abs) throws IOException; - - private native void native_close(int sock) throws IOException; - - private native int native_getPID(int sock); - - private native int native_getUID(int sock); - - private native int native_getGID(int sock); - - private native void native_send_creds(int sock, byte data) throws IOException; - - private native byte native_recv_creds(int sock, int[] creds) throws IOException; - - private UnixSocketAddress address = null; - private USOutputStream os = null; - private USInputStream is = null; - private boolean closed = false; - private boolean connected = false; - private boolean passcred = false; - private int sock = 0; - private boolean blocking = true; - private int uid = -1; - private int pid = -1; - private int gid = -1; - - UnixSocket(int sock, UnixSocketAddress address) { - this.sock = sock; - this.address = address; - this.connected = true; - this.os = new USOutputStream(sock, this); - this.is = new USInputStream(sock, this); - } - - /** - * Create an unconnected socket. - */ - public UnixSocket() { - } - - /** - * Create a socket connected to the given address. - * - * @param address The Unix Socket address to connect to - */ - public UnixSocket(UnixSocketAddress address) throws IOException { - connect(address); - } - - /** - * Create a socket connected to the given address. - * - * @param address The Unix Socket address to connect to - */ - public UnixSocket(String address) throws IOException { - this(new UnixSocketAddress(address)); - } - - /** - * Connect the socket to this address. - * - * @param address The Unix Socket address to connect to - */ - public void connect(UnixSocketAddress address) throws IOException { - if (connected) close(); - this.sock = native_connect(address.path, address.abs); - this.os = new USOutputStream(this.sock, this); - this.is = new USInputStream(this.sock, this); - this.address = address; - this.connected = true; - this.closed = false; - this.is.setBlocking(blocking); - } - - /** - * Connect the socket to this address. - * - * @param address The Unix Socket address to connect to - */ - public void connect(String address) throws IOException { - connect(new UnixSocketAddress(address)); - } - - public void finalize() { - try { - close(); - } catch (IOException IOe) { - } - } - - /** - * Closes the connection. - */ - public synchronized void close() throws IOException { - if (Debug.debug) Debug.print(Debug.INFO, "Closing socket"); - native_close(sock); - sock = 0; - this.closed = true; - this.connected = false; - os = null; - is = null; - } - - /** - * Returns an InputStream for reading from the socket. - * - * @return An InputStream connected to this socket. - */ - public InputStream getInputStream() { - return is; - } - - /** - * Returns an OutputStream for writing to the socket. - * - * @return An OutputStream connected to this socket. - */ - public OutputStream getOutputStream() { - return os; - } - - /** - * Returns the address this socket is connected to. - * Returns null if the socket is unconnected. - * - * @return The UnixSocketAddress the socket is connected to - */ - public UnixSocketAddress getAddress() { - return address; - } - - /** - * Send a single byte of data with credentials. - * (Works on BSDs) - * - * @param data The byte of data to send. - */ - public void sendCredentialByte(byte data) throws IOException { - if (!connected) throw new NotConnectedException(); - native_send_creds(sock, data); - } - - /** - * Receive a single byte of data, with credentials. - * (Works on BSDs) - * - * @param data The byte of data to send. - * @see getPeerUID - * @see getPeerPID - * @see getPeerGID - */ - public byte recvCredentialByte() throws IOException { - if (!connected) throw new NotConnectedException(); - int[] creds = new int[]{-1, -1, -1}; - byte data = native_recv_creds(sock, creds); - pid = creds[0]; - uid = creds[1]; - gid = creds[2]; - return data; - } - - /** - * Get the credential passing status. - * (only effective on linux) - * - * @return The current status of credential passing. - * @see setPassCred - */ - public boolean getPassCred() { - return passcred; - } - - /** - * Return the uid of the remote process. - * Some data must have been received on the socket to do this. - * Either setPassCred must be called on Linux first, or recvCredentialByte - * on BSD. - * - * @return the UID or -1 if it is not available - */ - public int getPeerUID() { - if (-1 == uid) - uid = native_getUID(sock); - return uid; - } - - /** - * Return the gid of the remote process. - * Some data must have been received on the socket to do this. - * Either setPassCred must be called on Linux first, or recvCredentialByte - * on BSD. - * - * @return the GID or -1 if it is not available - */ - public int getPeerGID() { - if (-1 == gid) - gid = native_getGID(sock); - return gid; - } - - /** - * Return the pid of the remote process. - * Some data must have been received on the socket to do this. - * Either setPassCred must be called on Linux first, or recvCredentialByte - * on BSD. - * - * @return the PID or -1 if it is not available - */ - public int getPeerPID() { - if (-1 == pid) - pid = native_getPID(sock); - return pid; - } - - /** - * Set the credential passing status. - * (Only does anything on linux, for other OS, you need - * to use send/recv credentials) - * - * @param enable Set to true for credentials to be passed. - */ - public void setPassCred(boolean enable) throws IOException { - native_set_pass_cred(sock, enable); - passcred = enable; - } - - /** - * Get the blocking mode. - * - * @return true if reads are blocking. - * @see setBlocking - */ - public boolean getBlocking() { - return blocking; - } - - /** - * Set the blocking mode. - * - * @param enable Set to false for non-blocking reads. - */ - public void setBlocking(boolean enable) { - blocking = enable; - if (null != is) is.setBlocking(enable); - } - - /** - * Check the socket status. - * - * @return true if closed. - */ - public boolean isClosed() { - return closed; - } - - /** - * Check the socket status. - * - * @return true if connected. - */ - public boolean isConnected() { - return connected; - } - - /** - * Check the socket status. - * - * @return true if the input stream has been shutdown - */ - public boolean isInputShutdown() { - return is.isClosed(); - } - - /** - * Check the socket status. - * - * @return true if the output stream has been shutdown - */ - public boolean isOutputShutdown() { - return os.isClosed(); - } - - /** - * Shuts down the input stream. - * Subsequent reads on the associated InputStream will fail. - */ - public void shutdownInput() { - is.closed = true; - } - - /** - * Shuts down the output stream. - * Subsequent writes to the associated OutputStream will fail. - */ - public void shutdownOutput() { - os.closed = true; - } - - /** - * Set timeout of read requests. - */ - public void setSoTimeout(int timeout) { - is.setSoTimeout(timeout); - } -} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixSocketAddress.java b/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixSocketAddress.java deleted file mode 100644 index 0baba479bb..0000000000 --- a/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixSocketAddress.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Java Unix Sockets Library - * - * Copyright (c) Matthew Johnson 2004 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * To Contact the author, please email src@matthew.ath.cx - * - */ -package cx.ath.matthew.unix; - -/** - * Represents an address for a Unix Socket - */ -public class UnixSocketAddress { - String path; - boolean abs; - - /** - * Create the address. - * - * @param path The path to the Unix Socket. - * @param abs True if this should be an abstract socket. - */ - public UnixSocketAddress(String path, boolean abs) { - this.path = path; - this.abs = abs; - } - - /** - * Create the address. - * - * @param path The path to the Unix Socket. - */ - public UnixSocketAddress(String path) { - this.path = path; - this.abs = false; - } - - /** - * Return the path. - */ - public String getPath() { - return path; - } - - /** - * Returns true if this an address for an abstract socket. - */ - public boolean isAbstract() { - return abs; - } - - /** - * Return the Address as a String. - */ - public String toString() { - return "unix" + (abs ? ":abstract" : "") + ":path=" + path; - } - - public boolean equals(Object o) { - if (!(o instanceof UnixSocketAddress)) return false; - return ((UnixSocketAddress) o).path.equals(this.path); - } - - public int hashCode() { - return path.hashCode(); - } -} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/utils/Hexdump.java b/federation/sssd/src/main/java/cx/ath/matthew/utils/Hexdump.java deleted file mode 100644 index 63f37194db..0000000000 --- a/federation/sssd/src/main/java/cx/ath/matthew/utils/Hexdump.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Java Hexdump Library - * - * Copyright (c) Matthew Johnson 2005 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * To Contact the author, please email src@matthew.ath.cx - * - */ - -package cx.ath.matthew.utils; - -import java.io.PrintStream; - -public class Hexdump { - public static final char[] hexchars = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - - public static String toHex(byte[] buf) { - return toHex(buf, 0, buf.length); - } - - public static String toHex(byte[] buf, int ofs, int len) { - StringBuffer sb = new StringBuffer(); - int j = ofs + len; - for (int i = ofs; i < j; i++) { - if (i < buf.length) { - sb.append(hexchars[(buf[i] & 0xF0) >> 4]); - sb.append(hexchars[buf[i] & 0x0F]); - sb.append(' '); - } else { - sb.append(' '); - sb.append(' '); - sb.append(' '); - } - } - return sb.toString(); - } - - public static String toAscii(byte[] buf) { - return toAscii(buf, 0, buf.length); - } - - public static String toAscii(byte[] buf, int ofs, int len) { - StringBuffer sb = new StringBuffer(); - int j = ofs + len; - for (int i = ofs; i < j; i++) { - if (i < buf.length) { - if (20 <= buf[i] && 126 >= buf[i]) - sb.append((char) buf[i]); - else - sb.append('.'); - } else - sb.append(' '); - } - return sb.toString(); - } - - public static String format(byte[] buf) { - return format(buf, 80); - } - - public static String format(byte[] buf, int width) { - int bs = (width - 8) / 4; - int i = 0; - StringBuffer sb = new StringBuffer(); - do { - for (int j = 0; j < 6; j++) { - sb.append(hexchars[(i << (j * 4) & 0xF00000) >> 20]); - } - sb.append('\t'); - sb.append(toHex(buf, i, bs)); - sb.append(' '); - sb.append(toAscii(buf, i, bs)); - sb.append('\n'); - i += bs; - } while (i < buf.length); - return sb.toString(); - } - - public static void print(byte[] buf) { - print(buf, System.err); - } - - public static void print(byte[] buf, int width) { - print(buf, width, System.err); - } - - public static void print(byte[] buf, int width, PrintStream out) { - out.print(format(buf, width)); - } - - public static void print(byte[] buf, PrintStream out) { - out.print(format(buf)); - } - - /** - * Returns a string which can be written to a Java source file as part - * of a static initializer for a byte array. - * Returns data in the format 0xAB, 0xCD, .... - * use like: - * javafile.print("byte[] data = {") - * javafile.print(Hexdump.toByteArray(data)); - * javafile.println("};"); - */ - public static String toByteArray(byte[] buf) { - return toByteArray(buf, 0, buf.length); - } - - /** - * Returns a string which can be written to a Java source file as part - * of a static initializer for a byte array. - * Returns data in the format 0xAB, 0xCD, .... - * use like: - * javafile.print("byte[] data = {") - * javafile.print(Hexdump.toByteArray(data)); - * javafile.println("};"); - */ - public static String toByteArray(byte[] buf, int ofs, int len) { - StringBuffer sb = new StringBuffer(); - for (int i = ofs; i < len && i < buf.length; i++) { - sb.append('0'); - sb.append('x'); - sb.append(hexchars[(buf[i] & 0xF0) >> 4]); - sb.append(hexchars[buf[i] & 0x0F]); - if ((i + 1) < len && (i + 1) < buf.length) - sb.append(','); - } - return sb.toString(); - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/DBus.java b/federation/sssd/src/main/java/org/freedesktop/DBus.java deleted file mode 100644 index b7a16877ae..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/DBus.java +++ /dev/null @@ -1,534 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop; - -import org.freedesktop.dbus.DBusInterface; -import org.freedesktop.dbus.DBusSignal; -import org.freedesktop.dbus.Position; -import org.freedesktop.dbus.Struct; -import org.freedesktop.dbus.Tuple; -import org.freedesktop.dbus.UInt16; -import org.freedesktop.dbus.UInt32; -import org.freedesktop.dbus.UInt64; -import org.freedesktop.dbus.Variant; -import org.freedesktop.dbus.exceptions.DBusException; -import org.freedesktop.dbus.exceptions.DBusExecutionException; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.List; -import java.util.Map; - -public interface DBus extends DBusInterface { - - String BUSNAME = "org.freedesktop.DBus"; - String OBJECTPATH = "/org/freedesktop/DBus"; - - int DBUS_NAME_FLAG_ALLOW_REPLACEMENT = 0x01; - int DBUS_NAME_FLAG_REPLACE_EXISTING = 0x02; - int DBUS_NAME_FLAG_DO_NOT_QUEUE = 0x04; - int DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = 1; - int DBUS_REQUEST_NAME_REPLY_IN_QUEUE = 2; - int DBUS_REQUEST_NAME_REPLY_EXISTS = 3; - int DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER = 4; - int DBUS_RELEASEME_REPLY_RELEASED = 1; - int DBUS_RELEASE_NAME_REPLY_NON_EXISTANT = 2; - int DBUS_RELEASE_NAME_REPLY_NOT_OWNER = 3; - int DBUS_START_REPLY_SUCCESS = 1; - int DBUS_START_REPLY_ALREADY_RUNNING = 2; - - /** - * All DBus Applications should respond to the Ping method on this interface - */ - public interface Peer extends DBusInterface { - public void Ping(); - } - - /** - * Objects can provide introspection data via this interface and method. - * See the Introspection Format. - */ - public interface Introspectable extends DBusInterface { - /** - * @return The XML introspection data for this object - */ - public String Introspect(); - } - - /** - * A standard properties interface. - */ - public interface Properties extends DBusInterface { - /** - * Get the value for the given property. - * - * @param interface_name The interface this property is associated with. - * @param property_name The name of the property. - * @return The value of the property (may be any valid DBus type). - */ - public A Get(String interface_name, String property_name); - - /** - * Set the value for the given property. - * - * @param interface_name The interface this property is associated with. - * @param property_name The name of the property. - * @param value The new value of the property (may be any valid DBus type). - */ - public void Set(String interface_name, String property_name, A value); - - /** - * Get all properties and values. - * - * @param interface_name The interface the properties is associated with. - * @return The properties mapped to their values. - */ - public Map GetAll(String interface_name); - } - - /** - * Messages generated locally in the application. - */ - public interface Local extends DBusInterface { - public class Disconnected extends DBusSignal { - public Disconnected(String path) throws DBusException { - super(path); - } - } - } - - /** - * Initial message to register ourselves on the Bus. - * - * @return The unique name of this connection to the Bus. - */ - public String Hello(); - - /** - * Lists all connected names on the Bus. - * - * @return An array of all connected names. - */ - public String[] ListNames(); - - /** - * Determine if a name has an owner. - * - * @param name The name to query. - * @return true if the name has an owner. - */ - public boolean NameHasOwner(String name); - - /** - * Get the connection unique name that owns the given name. - * - * @param name The name to query. - * @return The connection which owns the name. - */ - public String GetNameOwner(String name); - - /** - * Get the Unix UID that owns a connection name. - * - * @param connection_name The connection name. - * @return The Unix UID that owns it. - */ - public UInt32 GetConnectionUnixUser(String connection_name); - - /** - * Start a service. If the given service is not provided - * by any application, it will be started according to the .service file - * for that service. - * - * @param name The service name to start. - * @param flags Unused. - * @return DBUS_START_REPLY constants. - */ - public UInt32 StartServiceByName(String name, UInt32 flags); - - /** - * Request a name on the bus. - * - * @param name The name to request. - * @param flags DBUS_NAME flags. - * @return DBUS_REQUEST_NAME_REPLY constants. - */ - public UInt32 RequestName(String name, UInt32 flags); - - /** - * Release a name on the bus. - * - * @param name The name to release. - * @return DBUS_RELEASE_NAME_REPLY constants. - */ - public UInt32 ReleaseName(String name); - - /** - * Add a match rule. - * Will cause you to receive messages that aren't directed to you which - * match this rule. - * - * @param matchrule The Match rule as a string. Format Undocumented. - */ - public void AddMatch(String matchrule) throws Error.MatchRuleInvalid; - - /** - * Remove a match rule. - * Will cause you to stop receiving messages that aren't directed to you which - * match this rule. - * - * @param matchrule The Match rule as a string. Format Undocumented. - */ - public void RemoveMatch(String matchrule) throws Error.MatchRuleInvalid; - - /** - * List the connections currently queued for a name. - * - * @param name The name to query - * @return A list of unique connection IDs. - */ - public String[] ListQueuedOwners(String name); - - /** - * Returns the proccess ID associated with a connection. - * - * @param connection_name The name of the connection - * @return The PID of the connection. - */ - public UInt32 GetConnectionUnixProcessID(String connection_name); - - /** - * Does something undocumented. - */ - public Byte[] GetConnectionSELinuxSecurityContext(String a); - - /** - * Does something undocumented. - */ - public void ReloadConfig(); - - /** - * Signal sent when the owner of a name changes - */ - public class NameOwnerChanged extends DBusSignal { - public final String name; - public final String old_owner; - public final String new_owner; - - public NameOwnerChanged(String path, String name, String old_owner, String new_owner) throws DBusException { - super(path, new Object[]{name, old_owner, new_owner}); - this.name = name; - this.old_owner = old_owner; - this.new_owner = new_owner; - } - } - - /** - * Signal sent to a connection when it loses a name - */ - public class NameLost extends DBusSignal { - public final String name; - - public NameLost(String path, String name) throws DBusException { - super(path, name); - this.name = name; - } - } - - /** - * Signal sent to a connection when it aquires a name - */ - public class NameAcquired extends DBusSignal { - public final String name; - - public NameAcquired(String path, String name) throws DBusException { - super(path, name); - this.name = name; - } - } - - /** - * Contains standard errors that can be thrown from methods. - */ - public interface Error { - /** - * Thrown if the method called was unknown on the remote object - */ - @SuppressWarnings("serial") - public class UnknownMethod extends DBusExecutionException { - public UnknownMethod(String message) { - super(message); - } - } - - /** - * Thrown if the object was unknown on a remote connection - */ - @SuppressWarnings("serial") - public class UnknownObject extends DBusExecutionException { - public UnknownObject(String message) { - super(message); - } - } - - /** - * Thrown if the requested service was not available - */ - @SuppressWarnings("serial") - public class ServiceUnknown extends DBusExecutionException { - public ServiceUnknown(String message) { - super(message); - } - } - - /** - * Thrown if the match rule is invalid - */ - @SuppressWarnings("serial") - public class MatchRuleInvalid extends DBusExecutionException { - public MatchRuleInvalid(String message) { - super(message); - } - } - - /** - * Thrown if there is no reply to a method call - */ - @SuppressWarnings("serial") - public class NoReply extends DBusExecutionException { - public NoReply(String message) { - super(message); - } - } - - /** - * Thrown if a message is denied due to a security policy - */ - @SuppressWarnings("serial") - public class AccessDenied extends DBusExecutionException { - public AccessDenied(String message) { - super(message); - } - } - } - - /** - * Description of the interface or method, returned in the introspection data - */ - @Retention(RetentionPolicy.RUNTIME) - public @interface Description { - String value(); - } - - /** - * Indicates that a DBus interface or method is deprecated - */ - @Retention(RetentionPolicy.RUNTIME) - public @interface Deprecated { - } - - /** - * Contains method-specific annotations - */ - public interface Method { - /** - * Methods annotated with this do not send a reply - */ - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) - public @interface NoReply { - } - - /** - * Give an error that the method can return - */ - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) - public @interface Error { - String value(); - } - } - - /** - * Contains GLib-specific annotations - */ - public interface GLib { - /** - * Define a C symbol to map to this method. Used by GLib only - */ - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) - public @interface CSymbol { - String value(); - } - } - - /** - * Contains Binding-test interfaces - */ - public interface Binding { - public interface SingleTests extends DBusInterface { - @Description("Returns the sum of the values in the input list") - public UInt32 Sum(byte[] a); - } - - public interface TestClient extends DBusInterface { - @Description("when the trigger signal is received, this method should be called on the sending process/object.") - public void Response(UInt16 a, double b); - - @Description("Causes a callback") - public static class Trigger extends DBusSignal { - public final UInt16 a; - public final double b; - - public Trigger(String path, UInt16 a, double b) throws DBusException { - super(path, a, b); - this.a = a; - this.b = b; - } - } - - } - - public interface Tests extends DBusInterface { - @Description("Returns whatever it is passed") - public Variant Identity(Variant input); - - @Description("Returns whatever it is passed") - public byte IdentityByte(byte input); - - @Description("Returns whatever it is passed") - public boolean IdentityBool(boolean input); - - @Description("Returns whatever it is passed") - public short IdentityInt16(short input); - - @Description("Returns whatever it is passed") - public UInt16 IdentityUInt16(UInt16 input); - - @Description("Returns whatever it is passed") - public int IdentityInt32(int input); - - @Description("Returns whatever it is passed") - public UInt32 IdentityUInt32(UInt32 input); - - @Description("Returns whatever it is passed") - public long IdentityInt64(long input); - - @Description("Returns whatever it is passed") - public UInt64 IdentityUInt64(UInt64 input); - - @Description("Returns whatever it is passed") - public double IdentityDouble(double input); - - @Description("Returns whatever it is passed") - public String IdentityString(String input); - - @Description("Returns whatever it is passed") - public Variant[] IdentityArray(Variant[] input); - - @Description("Returns whatever it is passed") - public byte[] IdentityByteArray(byte[] input); - - @Description("Returns whatever it is passed") - public boolean[] IdentityBoolArray(boolean[] input); - - @Description("Returns whatever it is passed") - public short[] IdentityInt16Array(short[] input); - - @Description("Returns whatever it is passed") - public UInt16[] IdentityUInt16Array(UInt16[] input); - - @Description("Returns whatever it is passed") - public int[] IdentityInt32Array(int[] input); - - @Description("Returns whatever it is passed") - public UInt32[] IdentityUInt32Array(UInt32[] input); - - @Description("Returns whatever it is passed") - public long[] IdentityInt64Array(long[] input); - - @Description("Returns whatever it is passed") - public UInt64[] IdentityUInt64Array(UInt64[] input); - - @Description("Returns whatever it is passed") - public double[] IdentityDoubleArray(double[] input); - - @Description("Returns whatever it is passed") - public String[] IdentityStringArray(String[] input); - - @Description("Returns the sum of the values in the input list") - public long Sum(int[] a); - - @Description("Given a map of A => B, should return a map of B => a list of all the As which mapped to B") - public Map> InvertMapping(Map a); - - @Description("This method returns the contents of a struct as separate values") - public Triplet DeStruct(TestStruct a); - - @Description("Given any compound type as a variant, return all the primitive types recursively contained within as an array of variants") - public List> Primitize(Variant a); - - @Description("inverts it's input") - public boolean Invert(boolean a); - - @Description("triggers sending of a signal from the supplied object with the given parameter") - public void Trigger(String a, UInt64 b); - - @Description("Causes the server to exit") - public void Exit(); - } - - public interface TestSignals extends DBusInterface { - @Description("Sent in response to a method call") - public static class Triggered extends DBusSignal { - public final UInt64 a; - - public Triggered(String path, UInt64 a) throws DBusException { - super(path, a); - this.a = a; - } - } - } - - public final class Triplet extends Tuple { - @Position(0) - public final A a; - @Position(1) - public final B b; - @Position(2) - public final C c; - - public Triplet(A a, B b, C c) { - this.a = a; - this.b = b; - this.c = c; - } - } - - public final class TestStruct extends Struct { - @Position(0) - public final String a; - @Position(1) - public final UInt32 b; - @Position(2) - public final Short c; - - public TestStruct(String a, UInt32 b, Short c) { - this.a = a; - this.b = b; - this.c = c; - } - } - } -} \ No newline at end of file diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/AbstractConnection.java b/federation/sssd/src/main/java/org/freedesktop/dbus/AbstractConnection.java deleted file mode 100644 index d1d7f51fb5..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/AbstractConnection.java +++ /dev/null @@ -1,1059 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import cx.ath.matthew.debug.Debug; -import org.freedesktop.DBus; -import org.freedesktop.dbus.exceptions.DBusException; -import org.freedesktop.dbus.exceptions.DBusExecutionException; -import org.freedesktop.dbus.exceptions.FatalDBusException; -import org.freedesktop.dbus.exceptions.FatalException; -import org.freedesktop.dbus.exceptions.NotConnected; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.text.MessageFormat; -import java.text.ParseException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; -import java.util.Properties; -import java.util.Vector; -import java.util.regex.Pattern; - -import static org.freedesktop.dbus.Gettext.getString; - - -/** - * Handles a connection to DBus. - */ -public abstract class AbstractConnection { - protected class FallbackContainer { - private Map fallbacks = new HashMap(); - - public synchronized void add(String path, ExportedObject eo) { - if (Debug.debug) Debug.print(Debug.DEBUG, "Adding fallback on " + path + " of " + eo); - fallbacks.put(path.split("/"), eo); - } - - public synchronized void remove(String path) { - if (Debug.debug) Debug.print(Debug.DEBUG, "Removing fallback on " + path); - fallbacks.remove(path.split("/")); - } - - public synchronized ExportedObject get(String path) { - int best = 0; - int i = 0; - ExportedObject bestobject = null; - String[] pathel = path.split("/"); - for (String[] fbpath : fallbacks.keySet()) { - if (Debug.debug) - Debug.print(Debug.VERBOSE, "Trying fallback path " + Arrays.deepToString(fbpath) + " to match " + Arrays.deepToString(pathel)); - for (i = 0; i < pathel.length && i < fbpath.length; i++) - if (!pathel[i].equals(fbpath[i])) break; - if (i > 0 && i == fbpath.length && i > best) - bestobject = fallbacks.get(fbpath); - if (Debug.debug) Debug.print(Debug.VERBOSE, "Matches " + i + " bestobject now " + bestobject); - } - if (Debug.debug) Debug.print(Debug.DEBUG, "Found fallback for " + path + " of " + bestobject); - return bestobject; - } - } - - protected class _thread extends Thread { - public _thread() { - setName("DBusConnection"); - } - - public void run() { - try { - Message m = null; - while (_run) { - m = null; - - // read from the wire - try { - // this blocks on outgoing being non-empty or a message being available. - m = readIncoming(); - if (m != null) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Got Incoming Message: " + m); - synchronized (this) { - notifyAll(); - } - - if (m instanceof DBusSignal) - handleMessage((DBusSignal) m); - else if (m instanceof MethodCall) - handleMessage((MethodCall) m); - else if (m instanceof MethodReturn) - handleMessage((MethodReturn) m); - else if (m instanceof Error) - handleMessage((Error) m); - - m = null; - } - } catch (Exception e) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - if (e instanceof FatalException) { - disconnect(); - } - } - - } - synchronized (this) { - notifyAll(); - } - } catch (Exception e) { - if (Debug.debug && EXCEPTION_DEBUG) Debug.print(Debug.ERR, e); - } - } - } - - private class _globalhandler implements org.freedesktop.DBus.Peer, org.freedesktop.DBus.Introspectable { - private String objectpath; - - public _globalhandler() { - this.objectpath = null; - } - - public _globalhandler(String objectpath) { - this.objectpath = objectpath; - } - - public boolean isRemote() { - return false; - } - - public void Ping() { - return; - } - - public String Introspect() { - String intro = objectTree.Introspect(objectpath); - if (null == intro) { - ExportedObject eo = fallbackcontainer.get(objectpath); - if (null != eo) intro = eo.introspectiondata; - } - if (null == intro) - throw new DBus.Error.UnknownObject("Introspecting on non-existant object"); - else return - "\n" + intro; - } - } - - protected class _workerthread extends Thread { - private boolean _run = true; - - public void halt() { - _run = false; - } - - public void run() { - while (_run) { - Runnable r = null; - synchronized (runnables) { - while (runnables.size() == 0 && _run) - try { - runnables.wait(); - } catch (InterruptedException Ie) { - } - if (runnables.size() > 0) - r = runnables.removeFirst(); - } - if (null != r) r.run(); - } - } - } - - private class _sender extends Thread { - public _sender() { - setName("Sender"); - } - - public void run() { - Message m = null; - - if (Debug.debug) Debug.print(Debug.INFO, "Monitoring outbound queue"); - // block on the outbound queue and send from it - while (_run) { - if (null != outgoing) synchronized (outgoing) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Blocking"); - while (outgoing.size() == 0 && _run) - try { - outgoing.wait(); - } catch (InterruptedException Ie) { - } - if (Debug.debug) Debug.print(Debug.VERBOSE, "Notified"); - if (outgoing.size() > 0) - m = outgoing.remove(); - if (Debug.debug) Debug.print(Debug.DEBUG, "Got message: " + m); - } - if (null != m) - sendMessage(m); - m = null; - } - - if (Debug.debug) Debug.print(Debug.INFO, "Flushing outbound queue and quitting"); - // flush the outbound queue before disconnect. - if (null != outgoing) do { - EfficientQueue ogq = outgoing; - synchronized (ogq) { - outgoing = null; - } - if (!ogq.isEmpty()) - m = ogq.remove(); - else m = null; - sendMessage(m); - } while (null != m); - - // close the underlying streams - } - } - - /** - * Timeout in us on checking the BUS for incoming messages and sending outgoing messages - */ - protected static final int TIMEOUT = 100000; - /** - * Initial size of the pending calls map - */ - private static final int PENDING_MAP_INITIAL_SIZE = 10; - static final String BUSNAME_REGEX = "^[-_a-zA-Z][-_a-zA-Z0-9]*(\\.[-_a-zA-Z][-_a-zA-Z0-9]*)*$"; - static final String CONNID_REGEX = "^:[0-9]*\\.[0-9]*$"; - static final String OBJECT_REGEX = "^/([-_a-zA-Z0-9]+(/[-_a-zA-Z0-9]+)*)?$"; - static final byte THREADCOUNT = 4; - static final int MAX_ARRAY_LENGTH = 67108864; - static final int MAX_NAME_LENGTH = 255; - protected Map exportedObjects; - private ObjectTree objectTree; - private _globalhandler _globalhandlerreference; - protected Map importedObjects; - protected Map>> handledSignals; - protected EfficientMap pendingCalls; - protected Map> pendingCallbacks; - protected Map> pendingCallbackReplys; - protected LinkedList runnables; - protected LinkedList<_workerthread> workers; - protected FallbackContainer fallbackcontainer; - protected boolean _run; - EfficientQueue outgoing; - LinkedList pendingErrors; - private static final Map infomap = new HashMap(); - protected _thread thread; - protected _sender sender; - protected Transport transport; - protected String addr; - protected boolean weakreferences = false; - static final Pattern dollar_pattern = Pattern.compile("[$]"); - public static final boolean EXCEPTION_DEBUG; - static final boolean FLOAT_SUPPORT; - protected boolean connected = false; - - static { - FLOAT_SUPPORT = (null != System.getenv("DBUS_JAVA_FLOATS")); - EXCEPTION_DEBUG = (null != System.getenv("DBUS_JAVA_EXCEPTION_DEBUG")); - if (EXCEPTION_DEBUG) { - Debug.print("Debugging of internal exceptions enabled"); - Debug.setThrowableTraces(true); - } - if (Debug.debug) { - File f = new File("debug.conf"); - if (f.exists()) { - Debug.print("Loading debug config file: " + f); - try { - Debug.loadConfig(f); - } catch (IOException IOe) { - } - } else { - Properties p = new Properties(); - p.setProperty("ALL", "INFO"); - Debug.print("debug config file " + f + " does not exist, not loading."); - } - Debug.setHexDump(true); - } - } - - protected AbstractConnection(String address) throws DBusException { - exportedObjects = new HashMap(); - importedObjects = new HashMap(); - _globalhandlerreference = new _globalhandler(); - synchronized (exportedObjects) { - exportedObjects.put(null, new ExportedObject(_globalhandlerreference, weakreferences)); - } - handledSignals = new HashMap>>(); - pendingCalls = new EfficientMap(PENDING_MAP_INITIAL_SIZE); - outgoing = new EfficientQueue(PENDING_MAP_INITIAL_SIZE); - pendingCallbacks = new HashMap>(); - pendingCallbackReplys = new HashMap>(); - pendingErrors = new LinkedList(); - runnables = new LinkedList(); - workers = new LinkedList<_workerthread>(); - objectTree = new ObjectTree(); - fallbackcontainer = new FallbackContainer(); - synchronized (workers) { - for (int i = 0; i < THREADCOUNT; i++) { - _workerthread t = new _workerthread(); - t.start(); - workers.add(t); - } - } - _run = true; - addr = address; - } - - protected void listen() { - // start listening - thread = new _thread(); - thread.start(); - sender = new _sender(); - sender.start(); - } - - /** - * Change the number of worker threads to receive method calls and handle signals. - * Default is 4 threads - * - * @param newcount The new number of worker Threads to use. - */ - public void changeThreadCount(byte newcount) { - synchronized (workers) { - if (workers.size() > newcount) { - int n = workers.size() - newcount; - for (int i = 0; i < n; i++) { - _workerthread t = workers.removeFirst(); - t.halt(); - } - } else if (workers.size() < newcount) { - int n = newcount - workers.size(); - for (int i = 0; i < n; i++) { - _workerthread t = new _workerthread(); - t.start(); - workers.add(t); - } - } - } - } - - private void addRunnable(Runnable r) { - synchronized (runnables) { - runnables.add(r); - runnables.notifyAll(); - } - } - - String getExportedObject(DBusInterface i) throws DBusException { - synchronized (exportedObjects) { - for (String s : exportedObjects.keySet()) - if (i.equals(exportedObjects.get(s).object.get())) - return s; - } - - String s = importedObjects.get(i).objectpath; - if (null != s) return s; - - throw new DBusException("Not an object exported or imported by this connection"); - } - - abstract DBusInterface getExportedObject(String source, String path) throws DBusException; - - /** - * Returns a structure with information on the current method call. - * - * @return the DBusCallInfo for this method call, or null if we are not in a method call. - */ - public static DBusCallInfo getCallInfo() { - DBusCallInfo info; - synchronized (infomap) { - info = infomap.get(Thread.currentThread()); - } - return info; - } - - /** - * If set to true the bus will not hold a strong reference to exported objects. - * If they go out of scope they will automatically be unexported from the bus. - * The default is to hold a strong reference, which means objects must be - * explicitly unexported before they will be garbage collected. - */ - public void setWeakReferences(boolean weakreferences) { - this.weakreferences = weakreferences; - } - - /** - * Export an object so that its methods can be called on DBus. - * - * @param objectpath The path to the object we are exposing. MUST be in slash-notation, like "/org/freedesktop/Local", - * and SHOULD end with a capitalised term. Only one object may be exposed on each path at any one time, but an object - * may be exposed on several paths at once. - * @param object The object to export. - * @throws DBusException If the objectpath is already exporting an object. - * or if objectpath is incorrectly formatted, - */ - public void exportObject(String objectpath, DBusInterface object) throws DBusException { - if (null == objectpath || "".equals(objectpath)) - throw new DBusException(getString("missingObjectPath")); - if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidObjectPath") + objectpath); - synchronized (exportedObjects) { - if (null != exportedObjects.get(objectpath)) - throw new DBusException(getString("objectAlreadyExported")); - ExportedObject eo = new ExportedObject(object, weakreferences); - exportedObjects.put(objectpath, eo); - objectTree.add(objectpath, eo, eo.introspectiondata); - } - } - - /** - * Export an object as a fallback object. - * This object will have it's methods invoked for all paths starting - * with this object path. - * - * @param objectprefix The path below which the fallback handles calls. - * MUST be in slash-notation, like "/org/freedesktop/Local", - * @param object The object to export. - * @throws DBusException If the objectpath is incorrectly formatted, - */ - public void addFallback(String objectprefix, DBusInterface object) throws DBusException { - if (null == objectprefix || "".equals(objectprefix)) - throw new DBusException(getString("missingObjectPath")); - if (!objectprefix.matches(OBJECT_REGEX) || objectprefix.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidObjectPath") + objectprefix); - ExportedObject eo = new ExportedObject(object, weakreferences); - fallbackcontainer.add(objectprefix, eo); - } - - /** - * Remove a fallback - * - * @param objectprefix The prefix to remove the fallback for. - */ - public void removeFallback(String objectprefix) { - fallbackcontainer.remove(objectprefix); - } - - /** - * Stop Exporting an object - * - * @param objectpath The objectpath to stop exporting. - */ - public void unExportObject(String objectpath) { - synchronized (exportedObjects) { - exportedObjects.remove(objectpath); - objectTree.remove(objectpath); - } - } - /** - * Return a reference to a remote object. - * This method will resolve the well known name (if given) to a unique bus name when you call it. - * This means that if a well known name is released by one process and acquired by another calls to - * objects gained from this method will continue to operate on the original process. - * @param busname The bus name to connect to. Usually a well known bus name in dot-notation (such as "org.freedesktop.local") - * or may be a DBus address such as ":1-16". - * @param objectpath The path on which the process is exporting the object.$ - * @param type The interface they are exporting it on. This type must have the same full class name and exposed method signatures - * as the interface the remote object is exporting. - * @return A reference to a remote object. - * @throws ClassCastException If type is not a sub-type of DBusInterface - * @throws DBusException If busname or objectpath are incorrectly formatted or type is not in a package. - */ - /** - * Send a signal. - * - * @param signal The signal to send. - */ - public void sendSignal(DBusSignal signal) { - queueOutgoing(signal); - } - - void queueOutgoing(Message m) { - synchronized (outgoing) { - if (null == outgoing) return; - outgoing.add(m); - if (Debug.debug) Debug.print(Debug.DEBUG, "Notifying outgoing thread"); - outgoing.notifyAll(); - } - } - - /** - * Remove a Signal Handler. - * Stops listening for this signal. - * - * @param type The signal to watch for. - * @throws DBusException If listening for the signal on the bus failed. - * @throws ClassCastException If type is not a sub-type of DBusSignal. - */ - public void removeSigHandler(Class type, DBusSigHandler handler) throws DBusException { - if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); - removeSigHandler(new DBusMatchRule(type), handler); - } - - /** - * Remove a Signal Handler. - * Stops listening for this signal. - * - * @param type The signal to watch for. - * @param object The object emitting the signal. - * @throws DBusException If listening for the signal on the bus failed. - * @throws ClassCastException If type is not a sub-type of DBusSignal. - */ - public void removeSigHandler(Class type, DBusInterface object, DBusSigHandler handler) throws DBusException { - if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); - String objectpath = importedObjects.get(object).objectpath; - if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidObjectPath") + objectpath); - removeSigHandler(new DBusMatchRule(type, null, objectpath), handler); - } - - protected abstract void removeSigHandler(DBusMatchRule rule, DBusSigHandler handler) throws DBusException; - - /** - * Add a Signal Handler. - * Adds a signal handler to call when a signal is received which matches the specified type and name. - * - * @param type The signal to watch for. - * @param handler The handler to call when a signal is received. - * @throws DBusException If listening for the signal on the bus failed. - * @throws ClassCastException If type is not a sub-type of DBusSignal. - */ - @SuppressWarnings("unchecked") - public void addSigHandler(Class type, DBusSigHandler handler) throws DBusException { - if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); - addSigHandler(new DBusMatchRule(type), (DBusSigHandler) handler); - } - - /** - * Add a Signal Handler. - * Adds a signal handler to call when a signal is received which matches the specified type, name and object. - * - * @param type The signal to watch for. - * @param object The object from which the signal will be emitted - * @param handler The handler to call when a signal is received. - * @throws DBusException If listening for the signal on the bus failed. - * @throws ClassCastException If type is not a sub-type of DBusSignal. - */ - @SuppressWarnings("unchecked") - public void addSigHandler(Class type, DBusInterface object, DBusSigHandler handler) throws DBusException { - if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); - String objectpath = importedObjects.get(object).objectpath; - if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidObjectPath") + objectpath); - addSigHandler(new DBusMatchRule(type, null, objectpath), (DBusSigHandler) handler); - } - - protected abstract void addSigHandler(DBusMatchRule rule, DBusSigHandler handler) throws DBusException; - - protected void addSigHandlerWithoutMatch(Class signal, DBusSigHandler handler) throws DBusException { - DBusMatchRule rule = new DBusMatchRule(signal); - SignalTuple key = new SignalTuple(rule.getInterface(), rule.getMember(), rule.getObject(), rule.getSource()); - synchronized (handledSignals) { - Vector> v = handledSignals.get(key); - if (null == v) { - v = new Vector>(); - v.add(handler); - handledSignals.put(key, v); - } else - v.add(handler); - } - } - - /** - * Disconnect from the Bus. - */ - public void disconnect() { - connected = false; - if (Debug.debug) Debug.print(Debug.INFO, "Sending disconnected signal"); - try { - handleMessage(new org.freedesktop.DBus.Local.Disconnected("/")); - } catch (Exception ee) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, ee); - } - - if (Debug.debug) Debug.print(Debug.INFO, "Disconnecting Abstract Connection"); - // run all pending tasks. - while (runnables.size() > 0) - synchronized (runnables) { - runnables.notifyAll(); - } - - // stop the main thread - _run = false; - - // unblock the sending thread. - synchronized (outgoing) { - outgoing.notifyAll(); - } - - // disconnect from the trasport layer - try { - if (null != transport) { - transport.disconnect(); - transport = null; - } - } catch (IOException IOe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOe); - } - - // stop all the workers - synchronized (workers) { - for (_workerthread t : workers) - t.halt(); - } - - // make sure none are blocking on the runnables queue still - synchronized (runnables) { - runnables.notifyAll(); - } - } - - public void finalize() { - disconnect(); - } - - /** - * Return any DBus error which has been received. - * - * @return A DBusExecutionException, or null if no error is pending. - */ - public DBusExecutionException getError() { - synchronized (pendingErrors) { - if (pendingErrors.size() == 0) return null; - else - return pendingErrors.removeFirst().getException(); - } - } - - /** - * Call a method asynchronously and set a callback. - * This handler will be called in a separate thread. - * - * @param object The remote object on which to call the method. - * @param m The name of the method on the interface to call. - * @param callback The callback handler. - * @param parameters The parameters to call the method with. - */ - @SuppressWarnings("unchecked") - public void callWithCallback(DBusInterface object, String m, CallbackHandler callback, Object... parameters) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "callWithCallback(" + object + "," + m + ", " + callback); - Class[] types = new Class[parameters.length]; - for (int i = 0; i < parameters.length; i++) - types[i] = parameters[i].getClass(); - RemoteObject ro = importedObjects.get(object); - - try { - Method me; - if (null == ro.iface) - me = object.getClass().getMethod(m, types); - else - me = ro.iface.getMethod(m, types); - RemoteInvocationHandler.executeRemoteMethod(ro, me, this, RemoteInvocationHandler.CALL_TYPE_CALLBACK, callback, parameters); - } catch (DBusExecutionException DBEe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); - throw DBEe; - } catch (Exception e) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - throw new DBusExecutionException(e.getMessage()); - } - } - - /** - * Call a method asynchronously and get a handle with which to get the reply. - * - * @param object The remote object on which to call the method. - * @param m The name of the method on the interface to call. - * @param parameters The parameters to call the method with. - * @return A handle to the call. - */ - @SuppressWarnings("unchecked") - public DBusAsyncReply callMethodAsync(DBusInterface object, String m, Object... parameters) { - Class[] types = new Class[parameters.length]; - for (int i = 0; i < parameters.length; i++) - types[i] = parameters[i].getClass(); - RemoteObject ro = importedObjects.get(object); - - try { - Method me; - if (null == ro.iface) - me = object.getClass().getMethod(m, types); - else - me = ro.iface.getMethod(m, types); - return (DBusAsyncReply) RemoteInvocationHandler.executeRemoteMethod(ro, me, this, RemoteInvocationHandler.CALL_TYPE_ASYNC, null, parameters); - } catch (DBusExecutionException DBEe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); - throw DBEe; - } catch (Exception e) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - throw new DBusExecutionException(e.getMessage()); - } - } - - private void handleMessage(final MethodCall m) throws DBusException { - if (Debug.debug) Debug.print(Debug.DEBUG, "Handling incoming method call: " + m); - - ExportedObject eo = null; - Method meth = null; - Object o = null; - - if (null == m.getInterface() || - m.getInterface().equals("org.freedesktop.DBus.Peer") || - m.getInterface().equals("org.freedesktop.DBus.Introspectable")) { - synchronized (exportedObjects) { - eo = exportedObjects.get(null); - } - if (null != eo && null == eo.object.get()) { - unExportObject(null); - eo = null; - } - if (null != eo) { - meth = eo.methods.get(new MethodTuple(m.getName(), m.getSig())); - } - if (null != meth) - o = new _globalhandler(m.getPath()); - else - eo = null; - } - if (null == o) { - // now check for specific exported functions - - synchronized (exportedObjects) { - eo = exportedObjects.get(m.getPath()); - } - if (null != eo && null == eo.object.get()) { - if (Debug.debug) Debug.print(Debug.INFO, "Unexporting " + m.getPath() + " implicitly"); - unExportObject(m.getPath()); - eo = null; - } - - if (null == eo) { - eo = fallbackcontainer.get(m.getPath()); - } - - if (null == eo) { - try { - queueOutgoing(new Error(m, new DBus.Error.UnknownObject(m.getPath() + getString("notObjectProvidedByProcess")))); - } catch (DBusException DBe) { - } - return; - } - if (Debug.debug) { - Debug.print(Debug.VERBOSE, "Searching for method " + m.getName() + " with signature " + m.getSig()); - Debug.print(Debug.VERBOSE, "List of methods on " + eo + ":"); - for (MethodTuple mt : eo.methods.keySet()) - Debug.print(Debug.VERBOSE, " " + mt + " => " + eo.methods.get(mt)); - } - meth = eo.methods.get(new MethodTuple(m.getName(), m.getSig())); - if (null == meth) { - try { - queueOutgoing(new Error(m, new DBus.Error.UnknownMethod(MessageFormat.format(getString("methodDoesNotExist"), new Object[]{m.getInterface(), m.getName()})))); - } catch (DBusException DBe) { - } - return; - } - o = eo.object.get(); - } - - // now execute it - final Method me = meth; - final Object ob = o; - final boolean noreply = (1 == (m.getFlags() & Message.Flags.NO_REPLY_EXPECTED)); - final DBusCallInfo info = new DBusCallInfo(m); - final AbstractConnection conn = this; - if (Debug.debug) Debug.print(Debug.VERBOSE, "Adding Runnable for method " + meth); - addRunnable(new Runnable() { - private boolean run = false; - - public synchronized void run() { - if (run) return; - run = true; - if (Debug.debug) Debug.print(Debug.DEBUG, "Running method " + me + " for remote call"); - try { - Type[] ts = me.getGenericParameterTypes(); - m.setArgs(Marshalling.deSerializeParameters(m.getParameters(), ts, conn)); - if (Debug.debug) - Debug.print(Debug.VERBOSE, "Deserialised " + Arrays.deepToString(m.getParameters()) + " to types " + Arrays.deepToString(ts)); - } catch (Exception e) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - try { - conn.queueOutgoing(new Error(m, new DBus.Error.UnknownMethod(getString("deSerializationFailure") + e))); - } catch (DBusException DBe) { - } - return; - } - - try { - synchronized (infomap) { - infomap.put(Thread.currentThread(), info); - } - Object result; - try { - if (Debug.debug) - Debug.print(Debug.VERBOSE, "Invoking Method: " + me + " on " + ob + " with parameters " + Arrays.deepToString(m.getParameters())); - result = me.invoke(ob, m.getParameters()); - } catch (InvocationTargetException ITe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, ITe.getCause()); - throw ITe.getCause(); - } - synchronized (infomap) { - infomap.remove(Thread.currentThread()); - } - if (!noreply) { - MethodReturn reply; - if (Void.TYPE.equals(me.getReturnType())) - reply = new MethodReturn(m, null); - else { - StringBuffer sb = new StringBuffer(); - for (String s : Marshalling.getDBusType(me.getGenericReturnType())) - sb.append(s); - Object[] nr = Marshalling.convertParameters(new Object[]{result}, new Type[]{me.getGenericReturnType()}, conn); - - reply = new MethodReturn(m, sb.toString(), nr); - } - conn.queueOutgoing(reply); - } - } catch (DBusExecutionException DBEe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); - try { - conn.queueOutgoing(new Error(m, DBEe)); - } catch (DBusException DBe) { - } - } catch (Throwable e) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - try { - conn.queueOutgoing(new Error(m, new DBusExecutionException(MessageFormat.format(getString("errorExecutingMethod"), new Object[]{m.getInterface(), m.getName(), e.getMessage()})))); - } catch (DBusException DBe) { - } - } - } - }); - } - - @SuppressWarnings({"unchecked", "deprecation"}) - private void handleMessage(final DBusSignal s) { - if (Debug.debug) Debug.print(Debug.DEBUG, "Handling incoming signal: " + s); - Vector> v = new Vector>(); - synchronized (handledSignals) { - Vector> t; - t = handledSignals.get(new SignalTuple(s.getInterface(), s.getName(), null, null)); - if (null != t) v.addAll(t); - t = handledSignals.get(new SignalTuple(s.getInterface(), s.getName(), s.getPath(), null)); - if (null != t) v.addAll(t); - t = handledSignals.get(new SignalTuple(s.getInterface(), s.getName(), null, s.getSource())); - if (null != t) v.addAll(t); - t = handledSignals.get(new SignalTuple(s.getInterface(), s.getName(), s.getPath(), s.getSource())); - if (null != t) v.addAll(t); - } - if (0 == v.size()) return; - final AbstractConnection conn = this; - for (final DBusSigHandler h : v) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Adding Runnable for signal " + s + " with handler " + h); - addRunnable(new Runnable() { - private boolean run = false; - - public synchronized void run() { - if (run) return; - run = true; - try { - DBusSignal rs; - if (s instanceof DBusSignal.internalsig || s.getClass().equals(DBusSignal.class)) - rs = s.createReal(conn); - else - rs = s; - ((DBusSigHandler) h).handle(rs); - } catch (DBusException DBe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); - try { - conn.queueOutgoing(new Error(s, new DBusExecutionException("Error handling signal " + s.getInterface() + "." + s.getName() + ": " + DBe.getMessage()))); - } catch (DBusException DBe2) { - } - } - } - }); - } - } - - private void handleMessage(final Error err) { - if (Debug.debug) Debug.print(Debug.DEBUG, "Handling incoming error: " + err); - MethodCall m = null; - if (null == pendingCalls) return; - synchronized (pendingCalls) { - if (pendingCalls.contains(err.getReplySerial())) - m = pendingCalls.remove(err.getReplySerial()); - } - if (null != m) { - m.setReply(err); - CallbackHandler cbh = null; - DBusAsyncReply asr = null; - synchronized (pendingCallbacks) { - cbh = pendingCallbacks.remove(m); - if (Debug.debug) Debug.print(Debug.VERBOSE, cbh + " = pendingCallbacks.remove(" + m + ")"); - asr = pendingCallbackReplys.remove(m); - } - // queue callback for execution - if (null != cbh) { - final CallbackHandler fcbh = cbh; - if (Debug.debug) Debug.print(Debug.VERBOSE, "Adding Error Runnable with callback handler " + fcbh); - addRunnable(new Runnable() { - private boolean run = false; - - public synchronized void run() { - if (run) return; - run = true; - try { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Running Error Callback for " + err); - DBusCallInfo info = new DBusCallInfo(err); - synchronized (infomap) { - infomap.put(Thread.currentThread(), info); - } - - fcbh.handleError(err.getException()); - synchronized (infomap) { - infomap.remove(Thread.currentThread()); - } - - } catch (Exception e) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - } - } - }); - } - - } else - synchronized (pendingErrors) { - pendingErrors.addLast(err); - } - } - - @SuppressWarnings("unchecked") - private void handleMessage(final MethodReturn mr) { - if (Debug.debug) Debug.print(Debug.DEBUG, "Handling incoming method return: " + mr); - MethodCall m = null; - if (null == pendingCalls) return; - synchronized (pendingCalls) { - if (pendingCalls.contains(mr.getReplySerial())) - m = pendingCalls.remove(mr.getReplySerial()); - } - if (null != m) { - m.setReply(mr); - mr.setCall(m); - CallbackHandler cbh = null; - DBusAsyncReply asr = null; - synchronized (pendingCallbacks) { - cbh = pendingCallbacks.remove(m); - if (Debug.debug) Debug.print(Debug.VERBOSE, cbh + " = pendingCallbacks.remove(" + m + ")"); - asr = pendingCallbackReplys.remove(m); - } - // queue callback for execution - if (null != cbh) { - final CallbackHandler fcbh = cbh; - final DBusAsyncReply fasr = asr; - if (Debug.debug) - Debug.print(Debug.VERBOSE, "Adding Runnable for method " + fasr.getMethod() + " with callback handler " + fcbh); - addRunnable(new Runnable() { - private boolean run = false; - - public synchronized void run() { - if (run) return; - run = true; - try { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Running Callback for " + mr); - DBusCallInfo info = new DBusCallInfo(mr); - synchronized (infomap) { - infomap.put(Thread.currentThread(), info); - } - - fcbh.handle(RemoteInvocationHandler.convertRV(mr.getSig(), mr.getParameters(), fasr.getMethod(), fasr.getConnection())); - synchronized (infomap) { - infomap.remove(Thread.currentThread()); - } - - } catch (Exception e) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - } - } - }); - } - - } else - try { - queueOutgoing(new Error(mr, new DBusExecutionException(getString("spuriousReply")))); - } catch (DBusException DBe) { - } - } - - protected void sendMessage(Message m) { - try { - if (!connected) throw new NotConnected(getString("disconnected")); - if (m instanceof DBusSignal) - ((DBusSignal) m).appendbody(this); - - if (m instanceof MethodCall) { - if (0 == (m.getFlags() & Message.Flags.NO_REPLY_EXPECTED)) - if (null == pendingCalls) - ((MethodCall) m).setReply(new Error("org.freedesktop.DBus.Local", "org.freedesktop.DBus.Local.disconnected", 0, "s", new Object[]{getString("disconnected")})); - else synchronized (pendingCalls) { - pendingCalls.put(m.getSerial(), (MethodCall) m); - } - } - - transport.mout.writeMessage(m); - - } catch (Exception e) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - if (m instanceof MethodCall && e instanceof NotConnected) - try { - ((MethodCall) m).setReply(new Error("org.freedesktop.DBus.Local", "org.freedesktop.DBus.Local.disconnected", 0, "s", new Object[]{getString("disconnected")})); - } catch (DBusException DBe) { - } - if (m instanceof MethodCall && e instanceof DBusExecutionException) - try { - ((MethodCall) m).setReply(new Error(m, e)); - } catch (DBusException DBe) { - } - else if (m instanceof MethodCall) - try { - if (Debug.debug) Debug.print(Debug.INFO, "Setting reply to " + m + " as an error"); - ((MethodCall) m).setReply(new Error(m, new DBusExecutionException(getString("messageFailedSend") + e.getMessage()))); - } catch (DBusException DBe) { - } - else if (m instanceof MethodReturn) - try { - transport.mout.writeMessage(new Error(m, e)); - } catch (IOException IOe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOe); - } catch (DBusException IOe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - } - if (e instanceof IOException) disconnect(); - } - } - - private Message readIncoming() throws DBusException { - if (!connected) throw new NotConnected(getString("missingTransport")); - Message m = null; - try { - m = transport.min.readMessage(); - } catch (IOException IOe) { - throw new FatalDBusException(IOe.getMessage()); - } - return m; - } - - /** - * Returns the address this connection is connected to. - */ - public BusAddress getAddress() throws ParseException { - return new BusAddress(addr); - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/ArrayFrob.java b/federation/sssd/src/main/java/org/freedesktop/dbus/ArrayFrob.java index 86dfa5282c..38a2d467f9 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/ArrayFrob.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/ArrayFrob.java @@ -1,173 +1,180 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; -import cx.ath.matthew.debug.Debug; +import org.slf4j.LoggerFactory; import java.lang.reflect.Array; -import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; -import java.util.Hashtable; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; -import static org.freedesktop.dbus.Gettext.getString; - -class ArrayFrob { - static Hashtable, Class> primitiveToWrapper = new Hashtable, Class>(); - static Hashtable, Class> wrapperToPrimitive = new Hashtable, Class>(); - +public final class ArrayFrob { + private static final Map, Class> PRIMITIVE_TO_WRAPPER = new ConcurrentHashMap<>(); + private static final Map, Class> WRAPPER_TO_PRIMITIVE = new ConcurrentHashMap<>(); static { - primitiveToWrapper.put(Boolean.TYPE, Boolean.class); - primitiveToWrapper.put(Byte.TYPE, Byte.class); - primitiveToWrapper.put(Short.TYPE, Short.class); - primitiveToWrapper.put(Character.TYPE, Character.class); - primitiveToWrapper.put(Integer.TYPE, Integer.class); - primitiveToWrapper.put(Long.TYPE, Long.class); - primitiveToWrapper.put(Float.TYPE, Float.class); - primitiveToWrapper.put(Double.TYPE, Double.class); - wrapperToPrimitive.put(Boolean.class, Boolean.TYPE); - wrapperToPrimitive.put(Byte.class, Byte.TYPE); - wrapperToPrimitive.put(Short.class, Short.TYPE); - wrapperToPrimitive.put(Character.class, Character.TYPE); - wrapperToPrimitive.put(Integer.class, Integer.TYPE); - wrapperToPrimitive.put(Long.class, Long.TYPE); - wrapperToPrimitive.put(Float.class, Float.TYPE); - wrapperToPrimitive.put(Double.class, Double.TYPE); + PRIMITIVE_TO_WRAPPER.put(Boolean.TYPE, Boolean.class); + PRIMITIVE_TO_WRAPPER.put(Byte.TYPE, Byte.class); + PRIMITIVE_TO_WRAPPER.put(Short.TYPE, Short.class); + PRIMITIVE_TO_WRAPPER.put(Character.TYPE, Character.class); + PRIMITIVE_TO_WRAPPER.put(Integer.TYPE, Integer.class); + PRIMITIVE_TO_WRAPPER.put(Long.TYPE, Long.class); + PRIMITIVE_TO_WRAPPER.put(Float.TYPE, Float.class); + PRIMITIVE_TO_WRAPPER.put(Double.TYPE, Double.class); + WRAPPER_TO_PRIMITIVE.put(Boolean.class, Boolean.TYPE); + WRAPPER_TO_PRIMITIVE.put(Byte.class, Byte.TYPE); + WRAPPER_TO_PRIMITIVE.put(Short.class, Short.TYPE); + WRAPPER_TO_PRIMITIVE.put(Character.class, Character.TYPE); + WRAPPER_TO_PRIMITIVE.put(Integer.class, Integer.TYPE); + WRAPPER_TO_PRIMITIVE.put(Long.class, Long.TYPE); + WRAPPER_TO_PRIMITIVE.put(Float.class, Float.TYPE); + WRAPPER_TO_PRIMITIVE.put(Double.class, Double.TYPE); + } + private ArrayFrob() { + } + + public static Map, Class> getPrimitiveToWrapperTypes() { + return Collections.unmodifiableMap(PRIMITIVE_TO_WRAPPER); + } + + public static Map, Class> getWrapperToPrimitiveTypes() { + return Collections.unmodifiableMap(WRAPPER_TO_PRIMITIVE); } @SuppressWarnings("unchecked") - public static T[] wrap(Object o) throws IllegalArgumentException { - Class ac = o.getClass(); - if (!ac.isArray()) throw new IllegalArgumentException(getString("invalidArray")); + public static T[] wrap(Object _o) throws IllegalArgumentException { + Class ac = _o.getClass(); + if (!ac.isArray()) { + throw new IllegalArgumentException("Not an array"); + } Class cc = ac.getComponentType(); - Class ncc = primitiveToWrapper.get(cc); - if (null == ncc) throw new IllegalArgumentException(getString("notPrimitiveType")); - T[] ns = (T[]) Array.newInstance(ncc, Array.getLength(o)); - for (int i = 0; i < ns.length; i++) - ns[i] = (T) Array.get(o, i); + Class ncc = PRIMITIVE_TO_WRAPPER.get(cc); + if (null == ncc) { + throw new IllegalArgumentException("Not a primitive type"); + } + T[] ns = (T[]) Array.newInstance(ncc, Array.getLength(_o)); + for (int i = 0; i < ns.length; i++) { + ns[i] = (T) Array.get(_o, i); + } return ns; } @SuppressWarnings("unchecked") - public static Object unwrap(T[] ns) throws IllegalArgumentException { - Class ac = (Class) ns.getClass(); + public static Object unwrap(T[] _ns) throws IllegalArgumentException { + Class ac = (Class) _ns.getClass(); Class cc = (Class) ac.getComponentType(); - Class ncc = wrapperToPrimitive.get(cc); - if (null == ncc) throw new IllegalArgumentException(getString("invalidWrapperType")); - Object o = Array.newInstance(ncc, ns.length); - for (int i = 0; i < ns.length; i++) - Array.set(o, i, ns[i]); + Class ncc = WRAPPER_TO_PRIMITIVE.get(cc); + if (null == ncc) { + throw new IllegalArgumentException("Not a wrapper type"); + } + Object o = Array.newInstance(ncc, _ns.length); + for (int i = 0; i < _ns.length; i++) { + Array.set(o, i, _ns[i]); + } return o; } - public static List listify(T[] ns) throws IllegalArgumentException { - return Arrays.asList(ns); + public static List listify(T[] _ns) throws IllegalArgumentException { + return Arrays.asList(_ns); } @SuppressWarnings("unchecked") - public static List listify(Object o) throws IllegalArgumentException { - if (o instanceof Object[]) return listify((T[]) o); - if (!o.getClass().isArray()) throw new IllegalArgumentException(getString("invalidArray")); - List l = new ArrayList(Array.getLength(o)); - for (int i = 0; i < Array.getLength(o); i++) - l.add((T) Array.get(o, i)); + public static List listify(Object _o) throws IllegalArgumentException { + if (_o instanceof Object[]) { + return listify((T[]) _o); + } + if (!_o.getClass().isArray()) { + throw new IllegalArgumentException("Not an array"); + } + List l = new ArrayList<>(Array.getLength(_o)); + for (int i = 0; i < Array.getLength(_o); i++) { + l.add((T) Array.get(_o, i)); + } return l; } @SuppressWarnings("unchecked") - public static T[] delist(List l, Class c) throws IllegalArgumentException { - return l.toArray((T[]) Array.newInstance(c, 0)); + public static T[] delist(List _l, Class _c) throws IllegalArgumentException { + return _l.toArray((T[]) Array.newInstance(_c, 0)); } - public static Object delistprimitive(List l, Class c) throws IllegalArgumentException { - Object o = Array.newInstance(c, l.size()); - for (int i = 0; i < l.size(); i++) - Array.set(o, i, l.get(i)); + public static Object delistprimitive(List _l, Class _c) throws IllegalArgumentException { + Object o = Array.newInstance(_c, _l.size()); + for (int i = 0; i < _l.size(); i++) { + Array.set(o, i, _l.get(i)); + } return o; } @SuppressWarnings("unchecked") - public static Object convert(Object o, Class c) throws IllegalArgumentException { - /* Possible Conversions: - * - ** List -> List - ** List -> int[] - ** List -> Integer[] - ** int[] -> int[] - ** int[] -> List - ** int[] -> Integer[] - ** Integer[] -> Integer[] - ** Integer[] -> int[] - ** Integer[] -> List - */ + public static Object convert(Object _o, Class _c) throws IllegalArgumentException { + /* Possible Conversions: + * + ** List -> List + ** List -> int[] + ** List -> Integer[] + ** int[] -> int[] + ** int[] -> List + ** int[] -> Integer[] + ** Integer[] -> Integer[] + ** Integer[] -> int[] + ** Integer[] -> List + */ try { // List -> List - if (List.class.equals(c) - && o instanceof List) - return o; + if (List.class.equals(_c) && _o instanceof List) { + return _o; + } // int[] -> List // Integer[] -> List - if (List.class.equals(c) - && o.getClass().isArray()) - return listify(o); + if (List.class.equals(_c) && _o.getClass().isArray()) { + return listify(_o); + } // int[] -> int[] // Integer[] -> Integer[] - if (o.getClass().isArray() - && c.isArray() - && o.getClass().getComponentType().equals(c.getComponentType())) - return o; + if (_o.getClass().isArray() && _c.isArray() && _o.getClass().getComponentType().equals(_c.getComponentType())) { + return _o; + } // int[] -> Integer[] - if (o.getClass().isArray() - && c.isArray() - && o.getClass().getComponentType().isPrimitive()) - return wrap(o); + if (_o.getClass().isArray() && _c.isArray() && _o.getClass().getComponentType().isPrimitive()) { + return wrap(_o); + } // Integer[] -> int[] - if (o.getClass().isArray() - && c.isArray() - && c.getComponentType().isPrimitive()) - return unwrap((Object[]) o); + if (_o.getClass().isArray() && _c.isArray() && _c.getComponentType().isPrimitive()) { + return unwrap((Object[]) _o); + } // List -> int[] - if (o instanceof List - && c.isArray() - && c.getComponentType().isPrimitive()) - return delistprimitive((List) o, (Class) c.getComponentType()); + if (_o instanceof List && _c.isArray() && _c.getComponentType().isPrimitive()) { + return delistprimitive((List) _o, (Class) _c.getComponentType()); + } // List -> Integer[] - if (o instanceof List - && c.isArray()) - return delist((List) o, (Class) c.getComponentType()); + if (_o instanceof List && _c.isArray()) { + return delist((List) _o, (Class) _c.getComponentType()); + } - if (o.getClass().isArray() - && c.isArray()) - return type((Object[]) o, (Class) c.getComponentType()); + if (_o.getClass().isArray() && _c.isArray()) { + return type((Object[]) _o, (Class) _c.getComponentType()); + } - } catch (Exception e) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - throw new IllegalArgumentException(e); + } catch (Exception _ex) { + LoggerFactory.getLogger(ArrayFrob.class).debug("Cannot convert object.", _ex); + throw new IllegalArgumentException(_ex); } - throw new IllegalArgumentException(MessageFormat.format(getString("convertionTypeNotExpected"), new Object[]{o.getClass(), c})); + throw new IllegalArgumentException(String.format("Not An Expected Convertion type from %s to %s", _o.getClass(), _c)); } - public static Object[] type(Object[] old, Class c) { - Object[] ns = (Object[]) Array.newInstance(c, old.length); - for (int i = 0; i < ns.length; i++) - ns[i] = old[i]; + public static Object[] type(Object[] _old, Class _c) { + Object[] ns = (Object[]) Array.newInstance(_c, _old.length); + System.arraycopy(_old, 0, ns, 0, ns.length); return ns; } } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/BusAddress.java b/federation/sssd/src/main/java/org/freedesktop/dbus/BusAddress.java deleted file mode 100644 index 3848951f39..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/BusAddress.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import cx.ath.matthew.debug.Debug; - -import java.text.ParseException; -import java.util.HashMap; -import java.util.Map; - -import static org.freedesktop.dbus.Gettext.getString; - -public class BusAddress { - private String type; - private Map parameters; - - public BusAddress(String address) throws ParseException { - if (null == address || "".equals(address)) throw new ParseException(getString("busAddressBlank"), 0); - if (Debug.debug) Debug.print(Debug.VERBOSE, "Parsing bus address: " + address); - String[] ss = address.split(":", 2); - if (ss.length < 2) throw new ParseException(getString("busAddressInvalid") + address, 0); - type = ss[0]; - if (Debug.debug) Debug.print(Debug.VERBOSE, "Transport type: " + type); - String[] ps = ss[1].split(","); - parameters = new HashMap(); - for (String p : ps) { - String[] kv = p.split("=", 2); - parameters.put(kv[0], kv[1]); - } - if (Debug.debug) Debug.print(Debug.VERBOSE, "Transport options: " + parameters); - } - - public String getType() { - return type; - } - - public String getParameter(String key) { - return parameters.get(key); - } - - public String toString() { - return type + ": " + parameters; - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/CallbackHandler.java b/federation/sssd/src/main/java/org/freedesktop/dbus/CallbackHandler.java deleted file mode 100644 index 0e2a81e77a..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/CallbackHandler.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import org.freedesktop.dbus.exceptions.DBusExecutionException; - -/** - * Interface for callbacks in async mode - */ -public interface CallbackHandler { - public void handle(ReturnType r); - - public void handleError(DBusExecutionException e); -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Container.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Container.java index 39fd245527..1663c029d7 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/Container.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/Container.java @@ -1,39 +1,25 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; +import org.freedesktop.dbus.annotations.Position; +import org.slf4j.LoggerFactory; + import java.lang.reflect.Field; import java.lang.reflect.Type; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; /** * This class is the super class of both Structs and Tuples * and holds common methods. */ -abstract class Container { - private static Map typecache = new HashMap(); +public abstract class Container { + private static final Map TYPE_CACHE = new HashMap<>(); + private Object[] parameters = null; - static void putTypeCache(Type k, Type[] v) { - typecache.put(k, v); - } - - static Type[] getTypeCache(Type k) { - return typecache.get(k); - } - - private Object[] parameters = null; - - public Container() { + Container() { } private void setup() { @@ -43,13 +29,16 @@ abstract class Container { int diff = 0; for (Field f : fs) { Position p = f.getAnnotation(Position.class); + f.setAccessible(true); + if (null == p) { diff++; continue; } try { args[p.value()] = f.get(this); - } catch (IllegalAccessException IAe) { + } catch (IllegalAccessException _exIa) { + LoggerFactory.getLogger(getClass()).trace("Could not set value", _exIa); } } @@ -58,36 +47,67 @@ abstract class Container { } /** - * Returns the struct contents in order. - * - * @throws DBusException If there is a problem doing this. - */ + * Returns the struct contents in order. + * @return object array + */ public final Object[] getParameters() { - if (null != parameters) return parameters; + if (null != parameters) { + return parameters; + } setup(); return parameters; } - /** - * Returns this struct as a string. - */ + /** Returns this struct as a string. */ + @Override public final String toString() { - String s = getClass().getName() + "<"; - if (null == parameters) + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getName()).append("<"); + if (null == parameters) { setup(); - if (0 == parameters.length) - return s + ">"; - for (Object o : parameters) - s += o + ", "; - return s.replaceAll(", $", ">"); + } + if (0 == parameters.length) { + return sb.append(">").toString(); + } + sb.append(Arrays.stream(parameters).map(o -> Objects.toString(o)).collect(Collectors.joining(", "))); + return sb.append(">").toString(); } - public final boolean equals(Object other) { - if (other instanceof Container) { - Container that = (Container) other; - if (this.getClass().equals(that.getClass())) + @Override + public final boolean equals(Object _other) { + if (this == _other) { + return true; + } + if (_other == null) { + return false; + } + + if (_other instanceof Container) { + Container that = (Container) _other; + if (this.getClass().equals(that.getClass())) { return Arrays.equals(this.getParameters(), that.getParameters()); - else return false; - } else return false; + } else { + return false; + } + } else { + return false; + } } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.deepHashCode(parameters); + return result; + } + + static void putTypeCache(Type _k, Type[] _v) { + TYPE_CACHE.put(_k, _v); + } + + static Type[] getTypeCache(Type _k) { + return TYPE_CACHE.get(_k); + } + } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusAsyncReply.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusAsyncReply.java index 51c2bfd831..653bab10b9 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusAsyncReply.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusAsyncReply.java @@ -1,117 +1,122 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; -import cx.ath.matthew.debug.Debug; -import org.freedesktop.DBus.Error.NoReply; +import org.freedesktop.dbus.connections.AbstractConnection; +import org.freedesktop.dbus.errors.Error; +import org.freedesktop.dbus.errors.NoReply; import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.freedesktop.dbus.messages.Message; +import org.freedesktop.dbus.messages.MethodCall; +import org.freedesktop.dbus.messages.MethodReturn; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; - -import static org.freedesktop.dbus.Gettext.getString; /** * A handle to an asynchronous method call. */ -public class DBusAsyncReply { - /** - * Check if any of a set of asynchronous calls have had a reply. - * - * @param replies A Collection of handles to replies to check. - * @return A Collection only containing those calls which have had replies. - */ - public static Collection> hasReply(Collection> replies) { - Collection> c = new ArrayList>(replies); - Iterator> i = c.iterator(); - while (i.hasNext()) - if (!i.next().hasReply()) i.remove(); - return c; - } +public class DBusAsyncReply { - private ReturnType rval = null; - private DBusExecutionException error = null; - private MethodCall mc; - private Method me; - private AbstractConnection conn; + private final Logger logger = LoggerFactory.getLogger(getClass()); - DBusAsyncReply(MethodCall mc, Method me, AbstractConnection conn) { - this.mc = mc; - this.me = me; - this.conn = conn; + private T rval = null; + private DBusExecutionException error = null; + private final MethodCall mc; + private final Method me; + private final AbstractConnection conn; + + public DBusAsyncReply(MethodCall _mc, Method _me, AbstractConnection _conn) { + this.mc = _mc; + this.me = _me; + this.conn = _conn; } @SuppressWarnings("unchecked") private synchronized void checkReply() { if (mc.hasReply()) { Message m = mc.getReply(); - if (m instanceof Error) + if (m instanceof Error) { error = ((Error) m).getException(); - else if (m instanceof MethodReturn) { + } else if (m instanceof MethodReturn) { try { - rval = (ReturnType) RemoteInvocationHandler.convertRV(m.getSig(), m.getParameters(), me, conn); - } catch (DBusExecutionException DBEe) { - error = DBEe; - } catch (DBusException DBe) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); - error = new DBusExecutionException(DBe.getMessage()); + Object obj = RemoteInvocationHandler.convertRV(m.getSig(), m.getParameters(), me, conn); + + rval = (T) obj; + } catch (DBusExecutionException _ex) { + error = _ex; + } catch (DBusException _ex) { + logger.debug("", _ex); + error = new DBusExecutionException(_ex.getMessage()); } } } } /** - * Check if we've had a reply. - * - * @return True if we have a reply - */ + * Check if we've had a reply. + * @return true if we have a reply + */ public boolean hasReply() { - if (null != rval || null != error) return true; + if (null != rval || null != error) { + return true; + } checkReply(); return null != rval || null != error; } /** - * Get the reply. - * - * @return The return value from the method. - * @throws DBusExecutionException if the reply to the method was an error. - * @throws NoReply if the method hasn't had a reply yet - */ - public ReturnType getReply() throws DBusExecutionException { - if (null != rval) return rval; - else if (null != error) throw error; + * Get the reply. + * @return The return value from the method. + * @throws DBusException if the reply to the method was an error. + * @throws NoReply if the method hasn't had a reply yet + */ + public T getReply() throws DBusException { + if (null != rval) { + return rval; + } else if (null != error) { + throw error; + } checkReply(); - if (null != rval) return rval; - else if (null != error) throw error; - else throw new NoReply(getString("asyncCallNoReply")); + if (null != rval) { + return rval; + } else if (null != error) { + throw error; + } else { + throw new NoReply("Async call has not had a reply"); + } } + @Override public String toString() { - return getString("waitingFor") + mc; + return "Waiting for: " + mc; } - Method getMethod() { + public Method getMethod() { return me; } - AbstractConnection getConnection() { + public AbstractConnection getConnection() { return conn; } - MethodCall getCall() { + public MethodCall getCall() { return mc; } -} + /** + * Check if any of a set of asynchronous calls have had a reply. + * @param _replies A Collection of handles to replies to check. + * @return A Collection only containing those calls which have had replies. + */ +// public static Collection> hasReply(Collection> _replies) { +// Collection> c = new ArrayList<>(_replies); +// Iterator> i = c.iterator(); +// while (i.hasNext()) { +// if (!i.next().hasReply()) { +// i.remove(); +// } +// } +// return c; +// } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusCallInfo.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusCallInfo.java index 1c9d143154..c01c074c51 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusCallInfo.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusCallInfo.java @@ -1,77 +1,69 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; +import org.freedesktop.dbus.messages.Message; + /** * Holds information on a method call */ public class DBusCallInfo { /** - * Indicates the caller won't wait for a reply (and we won't send one). - */ + * Indicates the caller won't wait for a reply (and we won't send one). + */ public static final int NO_REPLY = Message.Flags.NO_REPLY_EXPECTED; - public static final int ASYNC = 0x100; - private String source; - private String destination; - private String objectpath; - private String iface; - private String method; - private int flags; + public static final int ASYNC = 0x100; + private final String source; + private final String destination; + private final String objectpath; + private final String iface; + private final String method; + private final int flags; - DBusCallInfo(Message m) { - this.source = m.getSource(); - this.destination = m.getDestination(); - this.objectpath = m.getPath(); - this.iface = m.getInterface(); - this.method = m.getName(); - this.flags = m.getFlags(); + public DBusCallInfo(Message _m) { + source = _m.getSource(); + destination = _m.getDestination(); + objectpath = _m.getPath(); + iface = _m.getInterface(); + method = _m.getName(); + flags = _m.getFlags(); } - /** - * Returns the BusID which called the method + /** Returns the BusID which called the method. + * @return source */ public String getSource() { return source; } - /** - * Returns the name with which we were addressed on the Bus + /** Returns the name with which we were addressed on the Bus. + * @return destination */ public String getDestination() { return destination; } - /** - * Returns the object path used to call this method + /** Returns the object path used to call this method. + * @return objectpath */ public String getObjectPath() { return objectpath; } - /** - * Returns the interface this method was called with + /** Returns the interface this method was called with. + * @return interface */ public String getInterface() { return iface; } - /** - * Returns the method name used to call this method + /** Returns the method name used to call this method. + * @return method */ public String getMethod() { return method; } - /** - * Returns any flags set on this method call + /** Returns any flags set on this method call. + * @return flags */ public int getFlags() { return flags; diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusConnection.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusConnection.java deleted file mode 100644 index 6f3adc3115..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusConnection.java +++ /dev/null @@ -1,794 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import cx.ath.matthew.debug.Debug; -import org.freedesktop.DBus; -import org.freedesktop.dbus.exceptions.DBusException; -import org.freedesktop.dbus.exceptions.DBusExecutionException; -import org.freedesktop.dbus.exceptions.NotConnected; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.lang.reflect.Proxy; -import java.text.MessageFormat; -import java.text.ParseException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; -import java.util.Vector; - -import static org.freedesktop.dbus.Gettext.getString; - -/** - * Handles a connection to DBus. - *

- * This is a Singleton class, only 1 connection to the SYSTEM or SESSION busses can be made. - * Repeated calls to getConnection will return the same reference. - *

- *

- * Signal Handlers and method calls from remote objects are run in their own threads, you MUST handle the concurrency issues. - *

- */ -public class DBusConnection extends AbstractConnection { - /** - * Add addresses of peers to a set which will watch for them to - * disappear and automatically remove them from the set. - */ - public class PeerSet implements Set, DBusSigHandler { - private Set addresses; - - public PeerSet() { - addresses = new TreeSet(); - try { - addSigHandler(new DBusMatchRule(DBus.NameOwnerChanged.class, null, null), this); - } catch (DBusException DBe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); - } - } - - public void handle(DBus.NameOwnerChanged noc) { - if (Debug.debug) - Debug.print(Debug.DEBUG, "Received NameOwnerChanged(" + noc.name + "," + noc.old_owner + "," + noc.new_owner + ")"); - if ("".equals(noc.new_owner) && addresses.contains(noc.name)) - remove(noc.name); - } - - public boolean add(String address) { - if (Debug.debug) - Debug.print(Debug.DEBUG, "Adding " + address); - synchronized (addresses) { - return addresses.add(address); - } - } - - public boolean addAll(Collection addresses) { - synchronized (this.addresses) { - return this.addresses.addAll(addresses); - } - } - - public void clear() { - synchronized (addresses) { - addresses.clear(); - } - } - - public boolean contains(Object o) { - return addresses.contains(o); - } - - public boolean containsAll(Collection os) { - return addresses.containsAll(os); - } - - public boolean equals(Object o) { - if (o instanceof PeerSet) - return ((PeerSet) o).addresses.equals(addresses); - else return false; - } - - public int hashCode() { - return addresses.hashCode(); - } - - public boolean isEmpty() { - return addresses.isEmpty(); - } - - public Iterator iterator() { - return addresses.iterator(); - } - - public boolean remove(Object o) { - if (Debug.debug) - Debug.print(Debug.DEBUG, "Removing " + o); - synchronized (addresses) { - return addresses.remove(o); - } - } - - public boolean removeAll(Collection os) { - synchronized (addresses) { - return addresses.removeAll(os); - } - } - - public boolean retainAll(Collection os) { - synchronized (addresses) { - return addresses.retainAll(os); - } - } - - public int size() { - return addresses.size(); - } - - public Object[] toArray() { - synchronized (addresses) { - return addresses.toArray(); - } - } - - public T[] toArray(T[] a) { - synchronized (addresses) { - return addresses.toArray(a); - } - } - } - - private class _sighandler implements DBusSigHandler { - public void handle(DBusSignal s) { - if (s instanceof org.freedesktop.DBus.Local.Disconnected) { - if (Debug.debug) Debug.print(Debug.WARN, "Handling disconnected signal from bus"); - try { - Error err = new Error( - "org.freedesktop.DBus.Local", "org.freedesktop.DBus.Local.disconnected", 0, "s", new Object[]{getString("disconnected")}); - if (null != pendingCalls) synchronized (pendingCalls) { - long[] set = pendingCalls.getKeys(); - for (long l : set) - if (-1 != l) { - MethodCall m = pendingCalls.remove(l); - if (null != m) - m.setReply(err); - } - } - synchronized (pendingErrors) { - pendingErrors.add(err); - } - } catch (DBusException DBe) { - } - } else if (s instanceof org.freedesktop.DBus.NameAcquired) { - busnames.add(((org.freedesktop.DBus.NameAcquired) s).name); - } - } - } - - /** - * System Bus - */ - public static final int SYSTEM = 0; - /** - * Session Bus - */ - public static final int SESSION = 1; - - public static final String DEFAULT_SYSTEM_BUS_ADDRESS = "unix:path=/var/run/dbus/system_bus_socket"; - - private List busnames; - - private static final Map conn = new HashMap(); - private int _refcount = 0; - private Object _reflock = new Object(); - private DBus _dbus; - - /** - * Connect to the BUS. If a connection already exists to the specified Bus, a reference to it is returned. - * - * @param address The address of the bus to connect to - * @throws DBusException If there is a problem connecting to the Bus. - */ - public static DBusConnection getConnection(String address) throws DBusException { - synchronized (conn) { - DBusConnection c = conn.get(address); - if (null != c) { - synchronized (c._reflock) { - c._refcount++; - } - return c; - } else { - c = new DBusConnection(address); - conn.put(address, c); - return c; - } - } - } - - /** - * Connect to the BUS. If a connection already exists to the specified Bus, a reference to it is returned. - * - * @param bustype The Bus to connect to. - * @throws DBusException If there is a problem connecting to the Bus. - * @see #SYSTEM - * @see #SESSION - */ - public static DBusConnection getConnection(int bustype) throws DBusException { - synchronized (conn) { - String s = null; - switch (bustype) { - case SYSTEM: - s = System.getenv("DBUS_SYSTEM_BUS_ADDRESS"); - if (null == s) s = DEFAULT_SYSTEM_BUS_ADDRESS; - break; - case SESSION: - s = System.getenv("DBUS_SESSION_BUS_ADDRESS"); - if (null == s) { - // address gets stashed in $HOME/.dbus/session-bus/`dbus-uuidgen --get`-`sed 's/:\(.\)\..*/\1/' <<< $DISPLAY` - String display = System.getenv("DISPLAY"); - if (null == display) throw new DBusException(getString("cannotResolveSessionBusAddress")); - File uuidfile = new File("/var/lib/dbus/machine-id"); - if (!uuidfile.exists()) throw new DBusException(getString("cannotResolveSessionBusAddress")); - try (BufferedReader r = new BufferedReader(new FileReader(uuidfile))) { - String uuid = r.readLine(); - String homedir = System.getProperty("user.home"); - File addressfile = new File(homedir + "/.dbus/session-bus", - uuid + "-" + display.replaceAll(":([0-9]*)\\..*", "$1")); - if (!addressfile.exists()) - throw new DBusException(getString("cannotResolveSessionBusAddress")); - try (BufferedReader r2 = new BufferedReader(new FileReader(addressfile))) { - String l; - while (null != (l = r2.readLine())) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Reading D-Bus session data: " + l); - if (l.matches("DBUS_SESSION_BUS_ADDRESS.*")) { - s = l.replaceAll("^[^=]*=", ""); - if (Debug.debug) Debug.print(Debug.VERBOSE, "Parsing " + l + " to " + s); - } - } - } - if (null == s || "".equals(s)) - throw new DBusException(getString("cannotResolveSessionBusAddress")); - if (Debug.debug) - Debug.print(Debug.INFO, "Read bus address " + s + " from file " + addressfile.toString()); - } catch (Exception e) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - throw new DBusException(getString("cannotResolveSessionBusAddress")); - } - } - break; - default: - throw new DBusException(getString("invalidBusType") + bustype); - } - DBusConnection c = conn.get(s); - if (Debug.debug) Debug.print(Debug.VERBOSE, "Getting bus connection for " + s + ": " + c); - if (null != c) { - synchronized (c._reflock) { - c._refcount++; - } - return c; - } else { - if (Debug.debug) Debug.print(Debug.DEBUG, "Creating new bus connection to: " + s); - c = new DBusConnection(s); - conn.put(s, c); - return c; - } - } - } - - @SuppressWarnings("unchecked") - private DBusConnection(String address) throws DBusException { - super(address); - busnames = new Vector(); - - synchronized (_reflock) { - _refcount = 1; - } - - try { - transport = new Transport(addr, AbstractConnection.TIMEOUT); - connected = true; - } catch (IOException IOe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOe); - disconnect(); - throw new DBusException(getString("connectionFailure") + IOe.getMessage()); - } catch (ParseException Pe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, Pe); - disconnect(); - throw new DBusException(getString("connectionFailure") + Pe.getMessage()); - } - - // start listening for calls - listen(); - - // register disconnect handlers - DBusSigHandler h = new _sighandler(); - addSigHandlerWithoutMatch(org.freedesktop.DBus.Local.Disconnected.class, h); - addSigHandlerWithoutMatch(org.freedesktop.DBus.NameAcquired.class, h); - - // register ourselves - _dbus = getRemoteObject("org.freedesktop.DBus", "/org/freedesktop/DBus", DBus.class); - try { - busnames.add(_dbus.Hello()); - } catch (DBusExecutionException DBEe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); - throw new DBusException(DBEe.getMessage()); - } - } - - @SuppressWarnings("unchecked") - DBusInterface dynamicProxy(String source, String path) throws DBusException { - if (Debug.debug) - Debug.print(Debug.INFO, "Introspecting " + path + " on " + source + " for dynamic proxy creation"); - try { - DBus.Introspectable intro = getRemoteObject(source, path, DBus.Introspectable.class); - String data = intro.Introspect(); - if (Debug.debug) Debug.print(Debug.VERBOSE, "Got introspection data: " + data); - String[] tags = data.split("[<>]"); - Vector ifaces = new Vector(); - for (String tag : tags) { - if (tag.startsWith("interface")) { - ifaces.add(tag.replaceAll("^interface *name *= *['\"]([^'\"]*)['\"].*$", "$1")); - } - } - Vector> ifcs = new Vector>(); - for (String iface : ifaces) { - if (Debug.debug) Debug.print(Debug.DEBUG, "Trying interface " + iface); - int j = 0; - while (j >= 0) { - try { - Class ifclass = Class.forName(iface); - if (!ifcs.contains(ifclass)) - ifcs.add(ifclass); - break; - } catch (Exception e) { - } - j = iface.lastIndexOf("."); - char[] cs = iface.toCharArray(); - if (j >= 0) { - cs[j] = '$'; - iface = String.valueOf(cs); - } - } - } - - if (ifcs.size() == 0) throw new DBusException(getString("interfaceToCastNotFound")); - - RemoteObject ro = new RemoteObject(source, path, null, false); - DBusInterface newi = (DBusInterface) - Proxy.newProxyInstance(ifcs.get(0).getClassLoader(), - ifcs.toArray(new Class[0]), - new RemoteInvocationHandler(this, ro)); - importedObjects.put(newi, ro); - return newi; - } catch (Exception e) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - throw new DBusException(MessageFormat.format(getString("createProxyExportFailure"), new Object[]{path, source, e.getMessage()})); - } - } - - DBusInterface getExportedObject(String source, String path) throws DBusException { - ExportedObject o = null; - synchronized (exportedObjects) { - o = exportedObjects.get(path); - } - if (null != o && null == o.object.get()) { - unExportObject(path); - o = null; - } - if (null != o) return o.object.get(); - if (null == source) throw new DBusException(getString("objectNotExportedNoRemoteSpecified")); - return dynamicProxy(source, path); - } - - /** - * Release a bus name. - * Releases the name so that other people can use it - * - * @param busname The name to release. MUST be in dot-notation like "org.freedesktop.local" - * @throws DBusException If the busname is incorrectly formatted. - */ - public void releaseBusName(String busname) throws DBusException { - if (!busname.matches(BUSNAME_REGEX) || busname.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidBusName")); - synchronized (this.busnames) { - UInt32 rv; - try { - rv = _dbus.ReleaseName(busname); - } catch (DBusExecutionException DBEe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); - throw new DBusException(DBEe.getMessage()); - } - this.busnames.remove(busname); - } - } - - /** - * Request a bus name. - * Request the well known name that this should respond to on the Bus. - * - * @param busname The name to respond to. MUST be in dot-notation like "org.freedesktop.local" - * @throws DBusException If the register name failed, or our name already exists on the bus. - * or if busname is incorrectly formatted. - */ - public void requestBusName(String busname) throws DBusException { - if (!busname.matches(BUSNAME_REGEX) || busname.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidBusName")); - synchronized (this.busnames) { - UInt32 rv; - try { - rv = _dbus.RequestName(busname, - new UInt32(DBus.DBUS_NAME_FLAG_REPLACE_EXISTING | - DBus.DBUS_NAME_FLAG_DO_NOT_QUEUE)); - } catch (DBusExecutionException DBEe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); - throw new DBusException(DBEe.getMessage()); - } - switch (rv.intValue()) { - case DBus.DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: - break; - case DBus.DBUS_REQUEST_NAME_REPLY_IN_QUEUE: - throw new DBusException(getString("dbusRegistrationFailure")); - case DBus.DBUS_REQUEST_NAME_REPLY_EXISTS: - throw new DBusException(getString("dbusRegistrationFailure")); - case DBus.DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: - break; - default: - break; - } - this.busnames.add(busname); - } - } - - /** - * Returns the unique name of this connection. - */ - public String getUniqueName() { - return busnames.get(0); - } - - /** - * Returns all the names owned by this connection. - */ - public String[] getNames() { - Set names = new TreeSet(); - names.addAll(busnames); - return names.toArray(new String[0]); - } - - public I getPeerRemoteObject(String busname, String objectpath, Class type) throws DBusException { - return getPeerRemoteObject(busname, objectpath, type, true); - } - - /** - * Return a reference to a remote object. - * This method will resolve the well known name (if given) to a unique bus name when you call it. - * This means that if a well known name is released by one process and acquired by another calls to - * objects gained from this method will continue to operate on the original process. - *

- * This method will use bus introspection to determine the interfaces on a remote object and so - * may block and may fail. The resulting proxy object will, however, be castable - * to any interface it implements. It will also autostart the process if applicable. Also note - * that the resulting proxy may fail to execute the correct method with overloaded methods - * and that complex types may fail in interesting ways. Basically, if something odd happens, - * try specifying the interface explicitly. - * - * @param busname The bus name to connect to. Usually a well known bus name in dot-notation (such as "org.freedesktop.local") - * or may be a DBus address such as ":1-16". - * @param objectpath The path on which the process is exporting the object.$ - * @return A reference to a remote object. - * @throws ClassCastException If type is not a sub-type of DBusInterface - * @throws DBusException If busname or objectpath are incorrectly formatted. - */ - public DBusInterface getPeerRemoteObject(String busname, String objectpath) throws DBusException { - if (null == busname) throw new DBusException(getString("nullBusName")); - - if ((!busname.matches(BUSNAME_REGEX) && !busname.matches(CONNID_REGEX)) - || busname.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidBusName") + busname); - - String unique = _dbus.GetNameOwner(busname); - - return dynamicProxy(unique, objectpath); - } - - /** - * Return a reference to a remote object. - * This method will always refer to the well known name (if given) rather than resolving it to a unique bus name. - * In particular this means that if a process providing the well known name disappears and is taken over by another process - * proxy objects gained by this method will make calls on the new proccess. - *

- * This method will use bus introspection to determine the interfaces on a remote object and so - * may block and may fail. The resulting proxy object will, however, be castable - * to any interface it implements. It will also autostart the process if applicable. Also note - * that the resulting proxy may fail to execute the correct method with overloaded methods - * and that complex types may fail in interesting ways. Basically, if something odd happens, - * try specifying the interface explicitly. - * - * @param busname The bus name to connect to. Usually a well known bus name name in dot-notation (such as "org.freedesktop.local") - * or may be a DBus address such as ":1-16". - * @param objectpath The path on which the process is exporting the object. - * @return A reference to a remote object. - * @throws ClassCastException If type is not a sub-type of DBusInterface - * @throws DBusException If busname or objectpath are incorrectly formatted. - */ - public DBusInterface getRemoteObject(String busname, String objectpath) throws DBusException { - if (null == busname) throw new DBusException(getString("nullBusName")); - if (null == objectpath) throw new DBusException(getString("nullObjectPath")); - - if ((!busname.matches(BUSNAME_REGEX) && !busname.matches(CONNID_REGEX)) - || busname.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidBusName") + busname); - - if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidObjectPath") + objectpath); - - return dynamicProxy(busname, objectpath); - } - - /** - * Return a reference to a remote object. - * This method will resolve the well known name (if given) to a unique bus name when you call it. - * This means that if a well known name is released by one process and acquired by another calls to - * objects gained from this method will continue to operate on the original process. - * - * @param busname The bus name to connect to. Usually a well known bus name in dot-notation (such as "org.freedesktop.local") - * or may be a DBus address such as ":1-16". - * @param objectpath The path on which the process is exporting the object.$ - * @param type The interface they are exporting it on. This type must have the same full class name and exposed method signatures - * as the interface the remote object is exporting. - * @param autostart Disable/Enable auto-starting of services in response to calls on this object. - * Default is enabled; when calling a method with auto-start enabled, if the destination is a well-known name - * and is not owned the bus will attempt to start a process to take the name. When disabled an error is - * returned immediately. - * @return A reference to a remote object. - * @throws ClassCastException If type is not a sub-type of DBusInterface - * @throws DBusException If busname or objectpath are incorrectly formatted or type is not in a package. - */ - public I getPeerRemoteObject(String busname, String objectpath, Class type, boolean autostart) throws DBusException { - if (null == busname) throw new DBusException(getString("nullBusName")); - - if ((!busname.matches(BUSNAME_REGEX) && !busname.matches(CONNID_REGEX)) - || busname.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidBusName") + busname); - - String unique = _dbus.GetNameOwner(busname); - - return getRemoteObject(unique, objectpath, type, autostart); - } - - /** - * Return a reference to a remote object. - * This method will always refer to the well known name (if given) rather than resolving it to a unique bus name. - * In particular this means that if a process providing the well known name disappears and is taken over by another process - * proxy objects gained by this method will make calls on the new proccess. - * - * @param busname The bus name to connect to. Usually a well known bus name name in dot-notation (such as "org.freedesktop.local") - * or may be a DBus address such as ":1-16". - * @param objectpath The path on which the process is exporting the object. - * @param type The interface they are exporting it on. This type must have the same full class name and exposed method signatures - * as the interface the remote object is exporting. - * @return A reference to a remote object. - * @throws ClassCastException If type is not a sub-type of DBusInterface - * @throws DBusException If busname or objectpath are incorrectly formatted or type is not in a package. - */ - public I getRemoteObject(String busname, String objectpath, Class type) throws DBusException { - return getRemoteObject(busname, objectpath, type, true); - } - - /** - * Return a reference to a remote object. - * This method will always refer to the well known name (if given) rather than resolving it to a unique bus name. - * In particular this means that if a process providing the well known name disappears and is taken over by another process - * proxy objects gained by this method will make calls on the new proccess. - * - * @param busname The bus name to connect to. Usually a well known bus name name in dot-notation (such as "org.freedesktop.local") - * or may be a DBus address such as ":1-16". - * @param objectpath The path on which the process is exporting the object. - * @param type The interface they are exporting it on. This type must have the same full class name and exposed method signatures - * as the interface the remote object is exporting. - * @param autostart Disable/Enable auto-starting of services in response to calls on this object. - * Default is enabled; when calling a method with auto-start enabled, if the destination is a well-known name - * and is not owned the bus will attempt to start a process to take the name. When disabled an error is - * returned immediately. - * @return A reference to a remote object. - * @throws ClassCastException If type is not a sub-type of DBusInterface - * @throws DBusException If busname or objectpath are incorrectly formatted or type is not in a package. - */ - @SuppressWarnings("unchecked") - public I getRemoteObject(String busname, String objectpath, Class type, boolean autostart) throws DBusException { - if (null == busname) throw new DBusException(getString("nullBusName")); - if (null == objectpath) throw new DBusException(getString("nullObjectPath")); - if (null == type) throw new ClassCastException(getString("notDBusInterface")); - - if ((!busname.matches(BUSNAME_REGEX) && !busname.matches(CONNID_REGEX)) - || busname.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidBusName") + busname); - - if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidObjectPath") + objectpath); - - if (!DBusInterface.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusInterface")); - - // don't let people import things which don't have a - // valid D-Bus interface name - if (type.getName().equals(type.getSimpleName())) - throw new DBusException(getString("interfaceNotAllowedOutsidePackage")); - - RemoteObject ro = new RemoteObject(busname, objectpath, type, autostart); - I i = (I) Proxy.newProxyInstance(type.getClassLoader(), - new Class[]{type}, new RemoteInvocationHandler(this, ro)); - importedObjects.put(i, ro); - return i; - } - - /** - * Remove a Signal Handler. - * Stops listening for this signal. - * - * @param type The signal to watch for. - * @param source The source of the signal. - * @throws DBusException If listening for the signal on the bus failed. - * @throws ClassCastException If type is not a sub-type of DBusSignal. - */ - public void removeSigHandler(Class type, String source, DBusSigHandler handler) throws DBusException { - if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); - if (source.matches(BUSNAME_REGEX)) throw new DBusException(getString("cannotWatchSignalsWellKnownBussName")); - if (!source.matches(CONNID_REGEX) || source.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidBusName") + source); - removeSigHandler(new DBusMatchRule(type, source, null), handler); - } - - /** - * Remove a Signal Handler. - * Stops listening for this signal. - * - * @param type The signal to watch for. - * @param source The source of the signal. - * @param object The object emitting the signal. - * @throws DBusException If listening for the signal on the bus failed. - * @throws ClassCastException If type is not a sub-type of DBusSignal. - */ - public void removeSigHandler(Class type, String source, DBusInterface object, DBusSigHandler handler) throws DBusException { - if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); - if (source.matches(BUSNAME_REGEX)) throw new DBusException(getString("cannotWatchSignalsWellKnownBussName")); - if (!source.matches(CONNID_REGEX) || source.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidBusName") + source); - String objectpath = importedObjects.get(object).objectpath; - if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidObjectPath") + objectpath); - removeSigHandler(new DBusMatchRule(type, source, objectpath), handler); - } - - protected void removeSigHandler(DBusMatchRule rule, DBusSigHandler handler) throws DBusException { - - SignalTuple key = new SignalTuple(rule.getInterface(), rule.getMember(), rule.getObject(), rule.getSource()); - synchronized (handledSignals) { - Vector> v = handledSignals.get(key); - if (null != v) { - v.remove(handler); - if (0 == v.size()) { - handledSignals.remove(key); - try { - _dbus.RemoveMatch(rule.toString()); - } catch (NotConnected NC) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, NC); - } catch (DBusExecutionException DBEe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); - throw new DBusException(DBEe.getMessage()); - } - } - } - } - } - - /** - * Add a Signal Handler. - * Adds a signal handler to call when a signal is received which matches the specified type, name and source. - * - * @param type The signal to watch for. - * @param source The process which will send the signal. This MUST be a unique bus name and not a well known name. - * @param handler The handler to call when a signal is received. - * @throws DBusException If listening for the signal on the bus failed. - * @throws ClassCastException If type is not a sub-type of DBusSignal. - */ - @SuppressWarnings("unchecked") - public void addSigHandler(Class type, String source, DBusSigHandler handler) throws DBusException { - if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); - if (source.matches(BUSNAME_REGEX)) throw new DBusException(getString("cannotWatchSignalsWellKnownBussName")); - if (!source.matches(CONNID_REGEX) || source.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidBusName") + source); - addSigHandler(new DBusMatchRule(type, source, null), (DBusSigHandler) handler); - } - - /** - * Add a Signal Handler. - * Adds a signal handler to call when a signal is received which matches the specified type, name, source and object. - * - * @param type The signal to watch for. - * @param source The process which will send the signal. This MUST be a unique bus name and not a well known name. - * @param object The object from which the signal will be emitted - * @param handler The handler to call when a signal is received. - * @throws DBusException If listening for the signal on the bus failed. - * @throws ClassCastException If type is not a sub-type of DBusSignal. - */ - @SuppressWarnings("unchecked") - public void addSigHandler(Class type, String source, DBusInterface object, DBusSigHandler handler) throws DBusException { - if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); - if (source.matches(BUSNAME_REGEX)) throw new DBusException(getString("cannotWatchSignalsWellKnownBussName")); - if (!source.matches(CONNID_REGEX) || source.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidBusName") + source); - String objectpath = importedObjects.get(object).objectpath; - if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) - throw new DBusException(getString("invalidObjectPath") + objectpath); - addSigHandler(new DBusMatchRule(type, source, objectpath), (DBusSigHandler) handler); - } - - protected void addSigHandler(DBusMatchRule rule, DBusSigHandler handler) throws DBusException { - try { - _dbus.AddMatch(rule.toString()); - } catch (DBusExecutionException DBEe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); - throw new DBusException(DBEe.getMessage()); - } - SignalTuple key = new SignalTuple(rule.getInterface(), rule.getMember(), rule.getObject(), rule.getSource()); - synchronized (handledSignals) { - Vector> v = handledSignals.get(key); - if (null == v) { - v = new Vector>(); - v.add(handler); - handledSignals.put(key, v); - } else - v.add(handler); - } - } - - /** - * Disconnect from the Bus. - * This only disconnects when the last reference to the bus has disconnect called on it - * or has been destroyed. - */ - public void disconnect() { - synchronized (conn) { - synchronized (_reflock) { - if (0 == --_refcount) { - if (Debug.debug) Debug.print(Debug.INFO, "Disconnecting DBusConnection"); - // Set all pending messages to have an error. - try { - Error err = new Error( - "org.freedesktop.DBus.Local", "org.freedesktop.DBus.Local.disconnected", 0, "s", new Object[]{getString("disconnected")}); - synchronized (pendingCalls) { - long[] set = pendingCalls.getKeys(); - for (long l : set) - if (-1 != l) { - MethodCall m = pendingCalls.remove(l); - if (null != m) - m.setReply(err); - } - pendingCalls = null; - } - synchronized (pendingErrors) { - pendingErrors.add(err); - } - } catch (DBusException DBe) { - } - - conn.remove(addr); - super.disconnect(); - } - } - } - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusInterface.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusInterface.java deleted file mode 100644 index b9e404c100..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusInterface.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -/** - * Denotes a class as exportable or a remote interface which can be called. - *

- * Any interface which should be exported or imported should extend this - * interface. All public methods from that interface are exported/imported - * with the given method signatures. - *

- *

- * All method calls on exported objects are run in their own threads. - * Application writers are responsible for any concurrency issues. - *

- */ -public interface DBusInterface { - /** - * Returns true on remote objects. - * Local objects implementing this interface MUST return false. - */ - public boolean isRemote(); -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusInterfaceName.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusInterfaceName.java deleted file mode 100644 index 0fc705685a..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusInterfaceName.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Force the interface name to be different to the Java class name. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface DBusInterfaceName { - /** - * The replacement interface name. - */ - String value(); -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMap.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMap.java index 6590e62448..627064b264 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMap.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMap.java @@ -1,150 +1,184 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; -import java.util.TreeSet; -import java.util.Vector; +import java.util.stream.Collectors; -class DBusMap implements Map { +public class DBusMap implements Map { + // CHECKSTYLE:OFF Object[][] entries; + // CHECKSTYLE:ON + public DBusMap(Object[][] _entries) { + this.entries = _entries; + } - public DBusMap(Object[][] entries) { - this.entries = entries; + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsKey(Object _key) { + for (Object[] entry : entries) { + if (Objects.equals(_key, entry[0])) { + return true; + } + } + return false; + } + + @Override + public boolean containsValue(Object _value) { + for (Object[] entry : entries) { + if (Objects.equals(_value, entry[1])) { + return true; + } + } + return false; + } + + @Override + public Set> entrySet() { + Set> s = new LinkedHashSet<>(); + for (int i = 0; i < entries.length; i++) { + s.add(new Entry(i)); + } + return s; + } + + @Override + @SuppressWarnings("unchecked") + public V get(Object _key) { + for (Object[] entry : entries) { + if (_key == entry[0] || _key != null && _key.equals(entry[0])) { + return (V) entry[1]; + } + } + return null; + } + + @Override + public boolean isEmpty() { + return entries.length == 0; + } + + @Override + @SuppressWarnings("unchecked") + public Set keySet() { + Set s = new LinkedHashSet<>(); + for (Object[] entry : entries) { + s.add((K) entry[0]); + } + return s; + } + + @Override + public V put(K _key, V _value) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(Map _t) { + throw new UnsupportedOperationException(); + } + + @Override + public V remove(Object _key) { + throw new UnsupportedOperationException(); + } + + @Override + public int size() { + return entries.length; + } + + @Override + @SuppressWarnings("unchecked") + public Collection values() { + List l = new ArrayList<>(); + for (Object[] entry : entries) { + l.add((V) entry[1]); + } + return l; + } + + @Override + public int hashCode() { + return Arrays.deepHashCode(entries); + } + + @Override + @SuppressWarnings("unchecked") + public boolean equals(Object _o) { + if (null == _o) { + return false; + } + if (!(_o instanceof Map)) { + return false; + } + return ((Map) _o).entrySet().equals(entrySet()); + } + + @Override + public String toString() { + String sb = "{" + + Arrays.stream(entries).map(e -> e[0] + " => " + e[1]) + .collect(Collectors.joining(",")) + + "}"; + + return sb; } class Entry implements Map.Entry, Comparable { - private int entry; + private final int entry; - public Entry(int i) { - this.entry = i; + Entry(int _i) { + this.entry = _i; } - public boolean equals(Object o) { - if (null == o) return false; - if (!(o instanceof DBusMap.Entry)) return false; - return this.entry == ((Entry) o).entry; + @SuppressWarnings("unchecked") + @Override + public boolean equals(Object _o) { + if (null == _o) { + return false; + } + if (!(_o instanceof DBusMap.Entry)) { + return false; + } + return this.entry == ((Entry) _o).entry; } + @Override @SuppressWarnings("unchecked") public K getKey() { return (K) entries[entry][0]; } + @Override @SuppressWarnings("unchecked") public V getValue() { return (V) entries[entry][1]; } + @Override public int hashCode() { return entries[entry][0].hashCode(); } - public V setValue(V value) { + @Override + public V setValue(V _value) { throw new UnsupportedOperationException(); } - public int compareTo(Entry e) { - return entry - e.entry; + @Override + public int compareTo(Entry _e) { + return entry - _e.entry; } } - - public void clear() { - throw new UnsupportedOperationException(); - } - - public boolean containsKey(Object key) { - for (int i = 0; i < entries.length; i++) - if (key == entries[i][0] || (key != null && key.equals(entries[i][0]))) - return true; - return false; - } - - public boolean containsValue(Object value) { - for (int i = 0; i < entries.length; i++) - if (value == entries[i][1] || (value != null && value.equals(entries[i][1]))) - return true; - return false; - } - - public Set> entrySet() { - Set> s = new TreeSet>(); - for (int i = 0; i < entries.length; i++) - s.add(new Entry(i)); - return s; - } - - @SuppressWarnings("unchecked") - public V get(Object key) { - for (int i = 0; i < entries.length; i++) - if (key == entries[i][0] || (key != null && key.equals(entries[i][0]))) - return (V) entries[i][1]; - return null; - } - - public boolean isEmpty() { - return entries.length == 0; - } - - @SuppressWarnings("unchecked") - public Set keySet() { - Set s = new TreeSet(); - for (Object[] entry : entries) - s.add((K) entry[0]); - return s; - } - - public V put(K key, V value) { - throw new UnsupportedOperationException(); - } - - public void putAll(Map t) { - throw new UnsupportedOperationException(); - } - - public V remove(Object key) { - throw new UnsupportedOperationException(); - } - - public int size() { - return entries.length; - } - - @SuppressWarnings("unchecked") - public Collection values() { - List l = new Vector(); - for (Object[] entry : entries) - l.add((V) entry[1]); - return l; - } - - public int hashCode() { - return Arrays.deepHashCode(entries); - } - - @SuppressWarnings("unchecked") - public boolean equals(Object o) { - if (null == o) return false; - if (!(o instanceof Map)) return false; - return ((Map) o).entrySet().equals(entrySet()); - } - - public String toString() { - String s = "{ "; - for (int i = 0; i < entries.length; i++) - s += entries[i][0] + " => " + entries[i][1] + ","; - return s.replaceAll(".$", " }"); - } } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMatchRule.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMatchRule.java index fa886c0cf9..1d29e27b0e 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMatchRule.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMatchRule.java @@ -1,133 +1,270 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; +import org.freedesktop.dbus.errors.Error; import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.freedesktop.dbus.interfaces.DBusInterface; +import org.freedesktop.dbus.messages.DBusSignal; +import org.freedesktop.dbus.messages.Message; +import org.freedesktop.dbus.messages.MethodCall; +import org.freedesktop.dbus.messages.MethodReturn; +import org.freedesktop.dbus.utils.DBusNamingUtil; +import org.freedesktop.dbus.utils.Util; -import java.util.HashMap; - -import static org.freedesktop.dbus.Gettext.getString; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.regex.Pattern; +/** + * Defined a rule to match a message.
+ * This is mainly used to handle / take actions when signals arrive. + */ public class DBusMatchRule { + private static final Pattern IFACE_PATTERN = Pattern.compile(".*\\..*"); + private static final Map> SIGNALTYPEMAP = new ConcurrentHashMap<>(); + + /** Equals operations used in {@link #matches(DBusMatchRule, boolean)} - do not change order! */ + private static final List> MATCHRULE_EQUALS_OPERATIONS = List.of( + x -> x.getInterface(), + x -> x.getMember(), + x -> x.getObject(), + x -> x.getSource() + ); + + /** Equals operations used in {@link #matches(DBusSignal, boolean)} - do not change order! */ + private static final List> SIGNAL_EQUALS_OPERATIONS = List.of( + x -> x.getInterface(), + x -> x.getName(), + x -> x.getPath(), + x -> x.getSource() + ); + /* signal, error, method_call, method_reply */ - private String type; - private String iface; - private String member; - private String object; - private String source; - private static HashMap> signalTypeMap = - new HashMap>(); + private final String type; + private final String iface; + private final String member; + private final String object; + private final String source; - static Class getCachedSignalType(String type) { - return signalTypeMap.get(type); + public DBusMatchRule(String _type, String _iface, String _member) { + this(_type, _iface, _member, null); } - public DBusMatchRule(String type, String iface, String member) { - this.type = type; - this.iface = iface; - this.member = member; + public DBusMatchRule(String _type, String _iface, String _member, String _object) { + type = _type; + iface = _iface; + member = _member; + object = _object; + source = null; } - public DBusMatchRule(DBusExecutionException e) throws DBusException { - this(e.getClass()); - member = null; - type = "error"; + public DBusMatchRule(DBusExecutionException _e) throws DBusException { + this(_e.getClass()); } - public DBusMatchRule(Message m) { - iface = m.getInterface(); - member = m.getName(); - if (m instanceof DBusSignal) + public DBusMatchRule(Message _m) { + iface = _m.getInterface(); + source = null; + object = null; + member = _m instanceof Error ? null : _m.getName(); + if (_m instanceof DBusSignal) { type = "signal"; - else if (m instanceof Error) { + } else if (_m instanceof Error) { type = "error"; - member = null; - } else if (m instanceof MethodCall) + } else if (_m instanceof MethodCall) { type = "method_call"; - else if (m instanceof MethodReturn) + } else if (_m instanceof MethodReturn) { type = "method_reply"; + } else { + type = null; + } } - public DBusMatchRule(Class c, String method) throws DBusException { - this(c); - member = method; - type = "method_call"; - } - - public DBusMatchRule(Class c, String source, String object) throws DBusException { - this(c); - this.source = source; - this.object = object; + public DBusMatchRule(Class _c, String _method) throws DBusException { + this(_c, null, null, "method_call", _method); } @SuppressWarnings("unchecked") - public DBusMatchRule(Class c) throws DBusException { - if (DBusInterface.class.isAssignableFrom(c)) { - if (null != c.getAnnotation(DBusInterfaceName.class)) - iface = c.getAnnotation(DBusInterfaceName.class).value(); - else - iface = AbstractConnection.dollar_pattern.matcher(c.getName()).replaceAll("."); - if (!iface.matches(".*\\..*")) - throw new DBusException(getString("interfaceMustBeDefinedPackage")); - member = null; - type = null; - } else if (DBusSignal.class.isAssignableFrom(c)) { - if (null == c.getEnclosingClass()) - throw new DBusException(getString("signalsMustBeMemberOfClass")); - else if (null != c.getEnclosingClass().getAnnotation(DBusInterfaceName.class)) - iface = c.getEnclosingClass().getAnnotation(DBusInterfaceName.class).value(); - else - iface = AbstractConnection.dollar_pattern.matcher(c.getEnclosingClass().getName()).replaceAll("."); + DBusMatchRule(Class _c, String _source, String _object, String _type, String _member) throws DBusException { + if (DBusInterface.class.isAssignableFrom(_c)) { + iface = DBusNamingUtil.getInterfaceName(_c); + assertDBusInterface(iface); + + member = _member != null ? _member : null; + type = _type != null ? _type : null; + } else if (DBusSignal.class.isAssignableFrom(_c)) { + if (null == _c.getEnclosingClass()) { + throw new DBusException("Signals must be declared as a member of a class implementing DBusInterface which is the member of a package."); + } + iface = DBusNamingUtil.getInterfaceName(_c.getEnclosingClass()); // Don't export things which are invalid D-Bus interfaces - if (!iface.matches(".*\\..*")) - throw new DBusException(getString("interfaceMustBeDefinedPackage")); - if (c.isAnnotationPresent(DBusMemberName.class)) - member = c.getAnnotation(DBusMemberName.class).value(); - else - member = c.getSimpleName(); - signalTypeMap.put(iface + '$' + member, (Class) c); - type = "signal"; - } else if (Error.class.isAssignableFrom(c)) { - if (null != c.getAnnotation(DBusInterfaceName.class)) - iface = c.getAnnotation(DBusInterfaceName.class).value(); - else - iface = AbstractConnection.dollar_pattern.matcher(c.getName()).replaceAll("."); - if (!iface.matches(".*\\..*")) - throw new DBusException(getString("interfaceMustBeDefinedPackage")); - member = null; - type = "error"; - } else if (DBusExecutionException.class.isAssignableFrom(c)) { - if (null != c.getClass().getAnnotation(DBusInterfaceName.class)) - iface = c.getClass().getAnnotation(DBusInterfaceName.class).value(); - else - iface = AbstractConnection.dollar_pattern.matcher(c.getClass().getName()).replaceAll("."); - if (!iface.matches(".*\\..*")) - throw new DBusException(getString("interfaceMustBeDefinedPackage")); - member = null; - type = "error"; - } else - throw new DBusException(getString("invalidTypeMatchRule") + c); + assertDBusInterface(iface); + + member = _member != null ? _member : DBusNamingUtil.getSignalName(_c); + SIGNALTYPEMAP.put(iface + '$' + member, (Class) _c); + type = _type != null ? _type : "signal"; + } else if (Error.class.isAssignableFrom(_c)) { + iface = DBusNamingUtil.getInterfaceName(_c); + assertDBusInterface(iface); + member = _member != null ? _member : null; + type = _type != null ? _type : "error"; + } else if (DBusExecutionException.class.isAssignableFrom(_c)) { + iface = DBusNamingUtil.getInterfaceName(_c); + assertDBusInterface(iface); + member = _member != null ? _member : null; + type = _type != null ? _type : "error"; + } else { + throw new DBusException("Invalid type for match rule: " + _c); + } + + source = _source; + object = _object; } + public DBusMatchRule(Class _c, String _source, String _object) throws DBusException { + this(_c, _source, _object, null, null); + } + + public DBusMatchRule(Class _c) throws DBusException { + this(_c, null, null); + } + + public static Class getCachedSignalType(String _type) { + return SIGNALTYPEMAP.get(_type); + } + + void assertDBusInterface(String _str) throws DBusException { + if (!IFACE_PATTERN.matcher(_str).matches()) { + throw new DBusException("DBusInterfaces must be defined in a package."); + } + } + + /** + * Checks if the given rule matches with our rule. + *

+ * Method supports partial matching by setting strict to false. + * Partial means that only the parts of this object are compared to the given + * object which were set (non-null) on ourselves. + * Fields set on the given object but not on ourselves will be ignored. + *

+ * + * @param _rule rule to compare + * @param _strict true to get an exact match, false to allow partial matches + * + * @return true if matching + */ + public boolean matches(DBusMatchRule _rule, boolean _strict) { + if (_rule == null) { + return false; + } + + if (_strict) { + return Util.strEquals(_rule.getInterface(), getInterface()) + && Util.strEquals(_rule.getMember(), getMember()) + && Util.strEquals(_rule.getObject(), getObject()) + && Util.strEquals(_rule.getSource(), getSource()); + } + + String[] compareVals = new String[] {getInterface(), getMember(), getObject(), getSource()}; + + for (int i = 0; i < compareVals.length; i++) { + if (compareVals[i] == null) { + continue; + } + Function function = MATCHRULE_EQUALS_OPERATIONS.get(i); + if (!Util.strEquals(compareVals[i], function.apply(_rule))) { + return false; + } + } + + return true; + } + + /** + * Checks if the given signal matches with our rule. + *

+ * Method supports partial matching by setting strict to false. + * Partial means that only the parts of this rule are compared to the given + * signal which were set (non-null) on ourselves. + * Fields set on the given signal but not on ourselves will be ignored. + *

+ * + * @param _signal signal to compare + * @param _strict true to get an exact match, false to allow partial matches + * + * @return true if matching + */ + public boolean matches(DBusSignal _signal, boolean _strict) { + if (_signal == null) { + return false; + } + + // _signal.getInterface(), _signal.getName(), _signal.getPath(), _signal.getSource() + if (_strict) { + return Util.strEquals(_signal.getInterface(), getInterface()) + && Util.strEquals(_signal.getName(), getMember()) + && Util.strEquals(_signal.getPath(), getObject()) + && Util.strEquals(_signal.getSource(), getSource()); + } + + String[] compareVals = new String[] {getInterface(), getMember(), getObject(), getSource()}; + + for (int i = 0; i < compareVals.length; i++) { + if (compareVals[i] == null) { + continue; + } + Function function = SIGNAL_EQUALS_OPERATIONS.get(i); + if (!Util.strEquals(compareVals[i], function.apply(_signal))) { + return false; + } + } + + return true; + } + + @Override public String toString() { String s = null; - if (null != type) s = null == s ? "type='" + type + "'" : s + ",type='" + type + "'"; - if (null != member) s = null == s ? "member='" + member + "'" : s + ",member='" + member + "'"; - if (null != iface) s = null == s ? "interface='" + iface + "'" : s + ",interface='" + iface + "'"; - if (null != source) s = null == s ? "sender='" + source + "'" : s + ",sender='" + source + "'"; - if (null != object) s = null == s ? "path='" + object + "'" : s + ",path='" + object + "'"; + if (null != type) { + s = null == s ? "type='" + type + "'" : s + ",type='" + type + "'"; + } + if (null != member) { + s = null == s ? "member='" + member + "'" : s + ",member='" + member + "'"; + } + if (null != iface) { + s = null == s ? "interface='" + iface + "'" : s + ",interface='" + iface + "'"; + } + if (null != source) { + s = null == s ? "sender='" + source + "'" : s + ",sender='" + source + "'"; + } + if (null != object) { + s = null == s ? "path='" + object + "'" : s + ",path='" + object + "'"; + } return s; } + @Override + public int hashCode() { + return Objects.hash(iface, member, object, source, type); + } + + @Override + public boolean equals(Object _obj) { + if (this == _obj) { + return true; + } + if (!(_obj instanceof DBusMatchRule)) { + return false; + } + DBusMatchRule other = (DBusMatchRule) _obj; + return Objects.equals(iface, other.iface) && Objects.equals(member, other.member) + && Objects.equals(object, other.object) && Objects.equals(source, other.source) + && Objects.equals(type, other.type); + } + public String getType() { return type; } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMemberName.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMemberName.java deleted file mode 100644 index 25d30d7e37..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMemberName.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Force the member (method/signal) name on the bus to be different to the Java name. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.METHOD}) -public @interface DBusMemberName { - /** - * The replacement member name. - */ - String value(); -} - diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusPath.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusPath.java new file mode 100644 index 0000000000..accc69a59b --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusPath.java @@ -0,0 +1,37 @@ +package org.freedesktop.dbus; + +public class DBusPath implements Comparable { + private String path; + + public DBusPath(String _path) { + this.setPath(_path); + } + + public String getPath() { + return path; + } + + @Override + public String toString() { + return getPath(); + } + + @Override + public boolean equals(Object _other) { + return _other instanceof DBusPath && getPath().equals(((DBusPath) _other).getPath()); + } + + @Override + public int hashCode() { + return getPath().hashCode(); + } + + @Override + public int compareTo(DBusPath _that) { + return getPath().compareTo(_that.getPath()); + } + + public void setPath(String _path) { + path = _path; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSerializable.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSerializable.java deleted file mode 100644 index 8d9fea58ae..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSerializable.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import org.freedesktop.dbus.exceptions.DBusException; - -/** - * Custom classes may be sent over DBus if they implement this interface. - *

- * In addition to the serialize method, classes MUST implement - * a deserialize method which returns null and takes as it's arguments - * all the DBus types the class will be serialied to in order and - * with type parameterisation. They MUST also provide a - * zero-argument constructor. - *

- *

- * The serialize method should return the class properties you wish to - * serialize, correctly formatted for the wire - * (DBusConnection.convertParameters() can help with this), in order in an - * Object array. - *

- *

- * The deserialize method will be called once after the zero-argument - * constructor. This should contain all the code to initialise the object - * from the types. - *

- */ -public interface DBusSerializable { - public Object[] serialize() throws DBusException; -} - diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSigHandler.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSigHandler.java deleted file mode 100644 index 6e40a82059..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSigHandler.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -/** - * Handle a signal on DBus. - * All Signal handlers are run in their own Thread. - * Application writers are responsible for managing any concurrency issues. - */ -public interface DBusSigHandler { - /** - * Handle a signal. - * - * @param s The signal to handle. If such a class exists, the - * signal will be an instance of the class with the correct type signature. - * Otherwise it will be an instance of DBusSignal - */ - public void handle(T s); -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSignal.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSignal.java deleted file mode 100644 index 96ba6b3e70..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSignal.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import cx.ath.matthew.debug.Debug; -import org.freedesktop.dbus.exceptions.DBusException; -import org.freedesktop.dbus.exceptions.MessageFormatException; - -import java.lang.reflect.Constructor; -import java.lang.reflect.GenericDeclaration; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Vector; - -import static org.freedesktop.dbus.Gettext.getString; - -public class DBusSignal extends Message { - DBusSignal() { - } - - public DBusSignal(String source, String path, String iface, String member, String sig, Object... args) throws DBusException { - super(Message.Endian.BIG, Message.MessageType.SIGNAL, (byte) 0); - - if (null == path || null == member || null == iface) - throw new MessageFormatException(getString("missingPathInterfaceSignal")); - headers.put(Message.HeaderField.PATH, path); - headers.put(Message.HeaderField.MEMBER, member); - headers.put(Message.HeaderField.INTERFACE, iface); - - Vector hargs = new Vector(); - hargs.add(new Object[]{Message.HeaderField.PATH, new Object[]{ArgumentType.OBJECT_PATH_STRING, path}}); - hargs.add(new Object[]{Message.HeaderField.INTERFACE, new Object[]{ArgumentType.STRING_STRING, iface}}); - hargs.add(new Object[]{Message.HeaderField.MEMBER, new Object[]{ArgumentType.STRING_STRING, member}}); - - if (null != source) { - headers.put(Message.HeaderField.SENDER, source); - hargs.add(new Object[]{Message.HeaderField.SENDER, new Object[]{ArgumentType.STRING_STRING, source}}); - } - - if (null != sig) { - hargs.add(new Object[]{Message.HeaderField.SIGNATURE, new Object[]{ArgumentType.SIGNATURE_STRING, sig}}); - headers.put(Message.HeaderField.SIGNATURE, sig); - setArgs(args); - } - - blen = new byte[4]; - appendBytes(blen); - append("ua(yv)", ++serial, hargs.toArray()); - pad((byte) 8); - - long c = bytecounter; - if (null != sig) append(sig, args); - marshallint(bytecounter - c, blen, 0, 4); - bodydone = true; - } - - static class internalsig extends DBusSignal { - public internalsig(String source, String objectpath, String type, String name, String sig, Object[] parameters, long serial) throws DBusException { - super(source, objectpath, type, name, sig, parameters, serial); - } - } - - private static Map, Type[]> typeCache = new HashMap, Type[]>(); - private static Map> classCache = new HashMap>(); - private static Map, Constructor> conCache = new HashMap, Constructor>(); - private static Map signames = new HashMap(); - private static Map intnames = new HashMap(); - private Class c; - private boolean bodydone = false; - private byte[] blen; - - static void addInterfaceMap(String java, String dbus) { - intnames.put(dbus, java); - } - - static void addSignalMap(String java, String dbus) { - signames.put(dbus, java); - } - - static DBusSignal createSignal(Class c, String source, String objectpath, String sig, long serial, Object... parameters) throws DBusException { - String type = ""; - if (null != c.getEnclosingClass()) { - if (null != c.getEnclosingClass().getAnnotation(DBusInterfaceName.class)) - type = c.getEnclosingClass().getAnnotation(DBusInterfaceName.class).value(); - else - type = AbstractConnection.dollar_pattern.matcher(c.getEnclosingClass().getName()).replaceAll("."); - - } else - throw new DBusException(getString("signalsMustBeMemberOfClass")); - DBusSignal s = new internalsig(source, objectpath, type, c.getSimpleName(), sig, parameters, serial); - s.c = c; - return s; - } - - @SuppressWarnings("unchecked") - private static Class createSignalClass(String intname, String signame) throws DBusException { - String name = intname + '$' + signame; - Class c = classCache.get(name); - if (null == c) c = DBusMatchRule.getCachedSignalType(name); - if (null != c) return c; - do { - try { - c = (Class) Class.forName(name); - } catch (ClassNotFoundException CNFe) { - } - name = name.replaceAll("\\.([^\\.]*)$", "\\$$1"); - } while (null == c && name.matches(".*\\..*")); - if (null == c) - throw new DBusException(getString("cannotCreateClassFromSignal") + intname + '.' + signame); - classCache.put(name, c); - return c; - } - - @SuppressWarnings("unchecked") - DBusSignal createReal(AbstractConnection conn) throws DBusException { - String intname = intnames.get(getInterface()); - String signame = signames.get(getName()); - if (null == intname) intname = getInterface(); - if (null == signame) signame = getName(); - if (null == c) - c = createSignalClass(intname, signame); - if (Debug.debug) Debug.print(Debug.DEBUG, "Converting signal to type: " + c); - Type[] types = typeCache.get(c); - Constructor con = conCache.get(c); - if (null == types) { - con = (Constructor) c.getDeclaredConstructors()[0]; - conCache.put(c, con); - Type[] ts = con.getGenericParameterTypes(); - types = new Type[ts.length - 1]; - for (int i = 1; i < ts.length; i++) - if (ts[i] instanceof TypeVariable) - for (Type b : ((TypeVariable) ts[i]).getBounds()) - types[i - 1] = b; - else - types[i - 1] = ts[i]; - typeCache.put(c, types); - } - - try { - DBusSignal s; - Object[] args = Marshalling.deSerializeParameters(getParameters(), types, conn); - if (null == args) s = (DBusSignal) con.newInstance(getPath()); - else { - Object[] params = new Object[args.length + 1]; - params[0] = getPath(); - System.arraycopy(args, 0, params, 1, args.length); - - if (Debug.debug) - Debug.print(Debug.DEBUG, "Creating signal of type " + c + " with parameters " + Arrays.deepToString(params)); - s = (DBusSignal) con.newInstance(params); - } - s.headers = headers; - s.wiredata = wiredata; - s.bytecounter = wiredata.length; - return s; - } catch (Exception e) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - throw new DBusException(e.getMessage()); - } - } - - /** - * Create a new signal. - * This contructor MUST be called by all sub classes. - * - * @param objectpath The path to the object this is emitted from. - * @param args The parameters of the signal. - * @throws DBusException This is thrown if the subclass is incorrectly defined. - */ - @SuppressWarnings("unchecked") - protected DBusSignal(String objectpath, Object... args) throws DBusException { - super(Message.Endian.BIG, Message.MessageType.SIGNAL, (byte) 0); - - if (!objectpath.matches(AbstractConnection.OBJECT_REGEX)) - throw new DBusException(getString("invalidObjectPath") + objectpath); - - Class tc = getClass(); - String member; - if (tc.isAnnotationPresent(DBusMemberName.class)) - member = tc.getAnnotation(DBusMemberName.class).value(); - else - member = tc.getSimpleName(); - String iface = null; - Class enc = tc.getEnclosingClass(); - if (null == enc || - !DBusInterface.class.isAssignableFrom(enc) || - enc.getName().equals(enc.getSimpleName())) - throw new DBusException(getString("signalsMustBeMemberOfClass")); - else if (null != enc.getAnnotation(DBusInterfaceName.class)) - iface = enc.getAnnotation(DBusInterfaceName.class).value(); - else - iface = AbstractConnection.dollar_pattern.matcher(enc.getName()).replaceAll("."); - - headers.put(Message.HeaderField.PATH, objectpath); - headers.put(Message.HeaderField.MEMBER, member); - headers.put(Message.HeaderField.INTERFACE, iface); - - Vector hargs = new Vector(); - hargs.add(new Object[]{Message.HeaderField.PATH, new Object[]{ArgumentType.OBJECT_PATH_STRING, objectpath}}); - hargs.add(new Object[]{Message.HeaderField.INTERFACE, new Object[]{ArgumentType.STRING_STRING, iface}}); - hargs.add(new Object[]{Message.HeaderField.MEMBER, new Object[]{ArgumentType.STRING_STRING, member}}); - - String sig = null; - if (0 < args.length) { - try { - Type[] types = typeCache.get(tc); - if (null == types) { - Constructor con = (Constructor) tc.getDeclaredConstructors()[0]; - conCache.put(tc, con); - Type[] ts = con.getGenericParameterTypes(); - types = new Type[ts.length - 1]; - for (int i = 1; i <= types.length; i++) - if (ts[i] instanceof TypeVariable) - types[i - 1] = ((TypeVariable) ts[i]).getBounds()[0]; - else - types[i - 1] = ts[i]; - typeCache.put(tc, types); - } - sig = Marshalling.getDBusType(types); - hargs.add(new Object[]{Message.HeaderField.SIGNATURE, new Object[]{ArgumentType.SIGNATURE_STRING, sig}}); - headers.put(Message.HeaderField.SIGNATURE, sig); - setArgs(args); - } catch (Exception e) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - throw new DBusException(getString("errorAddSignalParameters") + e.getMessage()); - } - } - - blen = new byte[4]; - appendBytes(blen); - append("ua(yv)", ++serial, hargs.toArray()); - pad((byte) 8); - } - - void appendbody(AbstractConnection conn) throws DBusException { - if (bodydone) return; - - Type[] types = typeCache.get(getClass()); - Object[] args = Marshalling.convertParameters(getParameters(), types, conn); - setArgs(args); - String sig = getSig(); - - long c = bytecounter; - if (null != args && 0 < args.length) append(sig, args); - marshallint(bytecounter - c, blen, 0, 4); - bodydone = true; - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/EfficientMap.java b/federation/sssd/src/main/java/org/freedesktop/dbus/EfficientMap.java deleted file mode 100644 index 32cc7bb5e2..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/EfficientMap.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -/** - * Provides a long => MethodCall map which doesn't allocate objects - * on insertion/removal. Keys must be inserted in ascending order. - */ -class EfficientMap { - private long[] kv; - private MethodCall[] vv; - private int start; - private int end; - private int init_size; - - public EfficientMap(int initial_size) { - init_size = initial_size; - shrink(); - } - - private void grow() { - // create new vectors twice as long - long[] oldkv = kv; - kv = new long[oldkv.length * 2]; - MethodCall[] oldvv = vv; - vv = new MethodCall[oldvv.length * 2]; - - // copy start->length to the start of the new vector - System.arraycopy(oldkv, start, kv, 0, oldkv.length - start); - System.arraycopy(oldvv, start, vv, 0, oldvv.length - start); - // copy 0->end to the next part of the new vector - if (end != (oldkv.length - 1)) { - System.arraycopy(oldkv, 0, kv, oldkv.length - start, end + 1); - System.arraycopy(oldvv, 0, vv, oldvv.length - start, end + 1); - } - // reposition pointers - start = 0; - end = oldkv.length; - } - - // create a new vector with just the valid keys in and return it - public long[] getKeys() { - int size; - if (start < end) size = end - start; - else size = kv.length - (start - end); - long[] lv = new long[size]; - int copya; - if (size > kv.length - start) copya = kv.length - start; - else copya = size; - System.arraycopy(kv, start, lv, 0, copya); - if (copya < size) { - System.arraycopy(kv, 0, lv, copya, size - copya); - } - return lv; - } - - private void shrink() { - if (null != kv && kv.length == init_size) return; - // reset to original size - kv = new long[init_size]; - vv = new MethodCall[init_size]; - start = 0; - end = 0; - } - - public void put(long l, MethodCall m) { - // put this at the end - kv[end] = l; - vv[end] = m; - // move the end - if (end == (kv.length - 1)) end = 0; - else end++; - // if we are out of space, grow. - if (end == start) grow(); - } - - public MethodCall remove(long l) { - // find the item - int pos = find(l); - // if we don't have it return null - if (-1 == pos) return null; - // get the value - MethodCall m = vv[pos]; - // set it as unused - vv[pos] = null; - kv[pos] = -1; - // move the pointer to the first full element - while (-1 == kv[start]) { - if (start == (kv.length - 1)) start = 0; - else start++; - // if we have emptied the list, shrink it - if (start == end) { - shrink(); - break; - } - } - return m; - } - - public boolean contains(long l) { - // check if find succeeds - return -1 != find(l); - } - - /* could binary search, but it's probably the first one */ - private int find(long l) { - int i = start; - while (i != end && kv[i] != l) - if (i == (kv.length - 1)) i = 0; - else i++; - if (i == end) return -1; - return i; - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/EfficientQueue.java b/federation/sssd/src/main/java/org/freedesktop/dbus/EfficientQueue.java deleted file mode 100644 index a60c88735c..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/EfficientQueue.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import cx.ath.matthew.debug.Debug; - -/** - * Provides a Message queue which doesn't allocate objects - * on insertion/removal. - */ -class EfficientQueue { - private Message[] mv; - private int start; - private int end; - private int init_size; - - public EfficientQueue(int initial_size) { - init_size = initial_size; - shrink(); - } - - private void grow() { - if (Debug.debug) Debug.print(Debug.DEBUG, "Growing"); - // create new vectors twice as long - Message[] oldmv = mv; - mv = new Message[oldmv.length * 2]; - - // copy start->length to the start of the new vector - System.arraycopy(oldmv, start, mv, 0, oldmv.length - start); - // copy 0->end to the next part of the new vector - if (end != (oldmv.length - 1)) { - System.arraycopy(oldmv, 0, mv, oldmv.length - start, end + 1); - } - // reposition pointers - start = 0; - end = oldmv.length; - } - - // create a new vector with just the valid keys in and return it - public Message[] getKeys() { - if (start == end) return new Message[0]; - Message[] lv; - if (start < end) { - int size = end - start; - lv = new Message[size]; - System.arraycopy(mv, start, lv, 0, size); - } else { - int size = mv.length - start + end; - lv = new Message[size]; - System.arraycopy(mv, start, lv, 0, mv.length - start); - System.arraycopy(mv, 0, lv, mv.length - start, end); - } - return lv; - } - - private void shrink() { - if (Debug.debug) Debug.print(Debug.DEBUG, "Shrinking"); - if (null != mv && mv.length == init_size) return; - // reset to original size - mv = new Message[init_size]; - start = 0; - end = 0; - } - - public void add(Message m) { - if (Debug.debug) Debug.print(Debug.DEBUG, "Enqueueing Message " + m); - // put this at the end - mv[end] = m; - // move the end - if (end == (mv.length - 1)) end = 0; - else end++; - // if we are out of space, grow. - if (end == start) grow(); - } - - public Message remove() { - if (start == end) return null; - // find the item - int pos = start; - // get the value - Message m = mv[pos]; - // set it as unused - mv[pos] = null; - if (start == (mv.length - 1)) start = 0; - else start++; - if (Debug.debug) Debug.print(Debug.DEBUG, "Dequeueing " + m); - return m; - } - - public boolean isEmpty() { - // check if find succeeds - return start == end; - } - - public int size() { - if (end >= start) - return end - start; - else - return mv.length - start + end; - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Error.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Error.java deleted file mode 100644 index 3350e9584a..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/Error.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import cx.ath.matthew.debug.Debug; -import org.freedesktop.dbus.exceptions.DBusException; -import org.freedesktop.dbus.exceptions.DBusExecutionException; -import org.freedesktop.dbus.exceptions.MessageFormatException; -import org.freedesktop.dbus.exceptions.NotConnected; - -import java.lang.reflect.Constructor; -import java.util.Vector; - -import static org.freedesktop.dbus.Gettext.getString; - -/** - * Error messages which can be sent over the bus. - */ -public class Error extends Message { - Error() { - } - - public Error(String dest, String errorName, long replyserial, String sig, Object... args) throws DBusException { - this(null, dest, errorName, replyserial, sig, args); - } - - public Error(String source, String dest, String errorName, long replyserial, String sig, Object... args) throws DBusException { - super(Message.Endian.BIG, Message.MessageType.ERROR, (byte) 0); - - if (null == errorName) - throw new MessageFormatException(getString("missingErrorName")); - headers.put(Message.HeaderField.REPLY_SERIAL, replyserial); - headers.put(Message.HeaderField.ERROR_NAME, errorName); - - Vector hargs = new Vector(); - hargs.add(new Object[]{Message.HeaderField.ERROR_NAME, new Object[]{ArgumentType.STRING_STRING, errorName}}); - hargs.add(new Object[]{Message.HeaderField.REPLY_SERIAL, new Object[]{ArgumentType.UINT32_STRING, replyserial}}); - - if (null != source) { - headers.put(Message.HeaderField.SENDER, source); - hargs.add(new Object[]{Message.HeaderField.SENDER, new Object[]{ArgumentType.STRING_STRING, source}}); - } - - if (null != dest) { - headers.put(Message.HeaderField.DESTINATION, dest); - hargs.add(new Object[]{Message.HeaderField.DESTINATION, new Object[]{ArgumentType.STRING_STRING, dest}}); - } - - if (null != sig) { - hargs.add(new Object[]{Message.HeaderField.SIGNATURE, new Object[]{ArgumentType.SIGNATURE_STRING, sig}}); - headers.put(Message.HeaderField.SIGNATURE, sig); - setArgs(args); - } - - byte[] blen = new byte[4]; - appendBytes(blen); - append("ua(yv)", serial, hargs.toArray()); - pad((byte) 8); - - long c = bytecounter; - if (null != sig) append(sig, args); - marshallint(bytecounter - c, blen, 0, 4); - } - - public Error(String source, Message m, Throwable e) throws DBusException { - this(source, m.getSource(), AbstractConnection.dollar_pattern.matcher(e.getClass().getName()).replaceAll("."), m.getSerial(), "s", e.getMessage()); - } - - public Error(Message m, Throwable e) throws DBusException { - this(m.getSource(), AbstractConnection.dollar_pattern.matcher(e.getClass().getName()).replaceAll("."), m.getSerial(), "s", e.getMessage()); - } - - @SuppressWarnings("unchecked") - private static Class createExceptionClass(String name) { - if (name == "org.freedesktop.DBus.Local.disconnected") return NotConnected.class; - Class c = null; - do { - try { - c = (Class) Class.forName(name); - } catch (ClassNotFoundException CNFe) { - } - name = name.replaceAll("\\.([^\\.]*)$", "\\$$1"); - } while (null == c && name.matches(".*\\..*")); - return c; - } - - /** - * Turns this into an exception of the correct type - */ - public DBusExecutionException getException() { - try { - Class c = createExceptionClass(getName()); - if (null == c || !DBusExecutionException.class.isAssignableFrom(c)) c = DBusExecutionException.class; - Constructor con = c.getConstructor(String.class); - DBusExecutionException ex; - Object[] args = getParameters(); - if (null == args || 0 == args.length) - ex = con.newInstance(""); - else { - String s = ""; - for (Object o : args) - s += o + " "; - ex = con.newInstance(s.trim()); - } - ex.setType(getName()); - return ex; - } catch (Exception e) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug && null != e.getCause()) - Debug.print(Debug.ERR, e.getCause()); - DBusExecutionException ex; - Object[] args = null; - try { - args = getParameters(); - } catch (Exception ee) { - } - if (null == args || 0 == args.length) - ex = new DBusExecutionException(""); - else { - String s = ""; - for (Object o : args) - s += o + " "; - ex = new DBusExecutionException(s.trim()); - } - ex.setType(getName()); - return ex; - } - } - - /** - * Throw this as an exception of the correct type - */ - public void throwException() throws DBusExecutionException { - throw getException(); - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/ExportedObject.java b/federation/sssd/src/main/java/org/freedesktop/dbus/ExportedObject.java deleted file mode 100644 index b9719a1515..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/ExportedObject.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import org.freedesktop.dbus.exceptions.DBusException; -import org.freedesktop.dbus.exceptions.DBusExecutionException; - -import java.lang.annotation.Annotation; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.Map; - -import static org.freedesktop.dbus.Gettext.getString; - -class ExportedObject { - @SuppressWarnings("unchecked") - private String getAnnotations(AnnotatedElement c) { - String ans = ""; - for (Annotation a : c.getDeclaredAnnotations()) { - Class t = a.annotationType(); - String value = ""; - try { - Method m = t.getMethod("value"); - value = m.invoke(a).toString(); - } catch (NoSuchMethodException NSMe) { - } catch (InvocationTargetException ITe) { - } catch (IllegalAccessException IAe) { - } - - ans += " \n"; - } - return ans; - } - - @SuppressWarnings("unchecked") - private Map getExportedMethods(Class c) throws DBusException { - if (DBusInterface.class.equals(c)) return new HashMap(); - Map m = new HashMap(); - for (Class i : c.getInterfaces()) - if (DBusInterface.class.equals(i)) { - // add this class's public methods - if (null != c.getAnnotation(DBusInterfaceName.class)) { - String name = ((DBusInterfaceName) c.getAnnotation(DBusInterfaceName.class)).value(); - introspectiondata += " \n"; - DBusSignal.addInterfaceMap(c.getName(), name); - } else { - // don't let people export things which don't have a - // valid D-Bus interface name - if (c.getName().equals(c.getSimpleName())) - throw new DBusException(getString("interfaceNotAllowedOutsidePackage")); - if (c.getName().length() > DBusConnection.MAX_NAME_LENGTH) - throw new DBusException(getString("introspectInterfaceExceedCharacters") + c.getName()); - else - introspectiondata += " \n"; - } - introspectiondata += getAnnotations(c); - for (Method meth : c.getDeclaredMethods()) - if (Modifier.isPublic(meth.getModifiers())) { - String ms = ""; - String name; - if (meth.isAnnotationPresent(DBusMemberName.class)) - name = meth.getAnnotation(DBusMemberName.class).value(); - else - name = meth.getName(); - if (name.length() > DBusConnection.MAX_NAME_LENGTH) - throw new DBusException(getString("introspectMethodExceedCharacters") + name); - introspectiondata += " \n"; - introspectiondata += getAnnotations(meth); - for (Class ex : meth.getExceptionTypes()) - if (DBusExecutionException.class.isAssignableFrom(ex)) - introspectiondata += - " \n"; - for (Type pt : meth.getGenericParameterTypes()) - for (String s : Marshalling.getDBusType(pt)) { - introspectiondata += " \n"; - ms += s; - } - if (!Void.TYPE.equals(meth.getGenericReturnType())) { - if (Tuple.class.isAssignableFrom((Class) meth.getReturnType())) { - ParameterizedType tc = (ParameterizedType) meth.getGenericReturnType(); - Type[] ts = tc.getActualTypeArguments(); - - for (Type t : ts) - if (t != null) - for (String s : Marshalling.getDBusType(t)) - introspectiondata += " \n"; - } else if (Object[].class.equals(meth.getGenericReturnType())) { - throw new DBusException(getString("cannotIntrospectReturnType")); - } else - for (String s : Marshalling.getDBusType(meth.getGenericReturnType())) - introspectiondata += " \n"; - } - introspectiondata += " \n"; - m.put(new MethodTuple(name, ms), meth); - } - for (Class sig : c.getDeclaredClasses()) - if (DBusSignal.class.isAssignableFrom(sig)) { - String name; - if (sig.isAnnotationPresent(DBusMemberName.class)) { - name = ((DBusMemberName) sig.getAnnotation(DBusMemberName.class)).value(); - DBusSignal.addSignalMap(sig.getSimpleName(), name); - } else - name = sig.getSimpleName(); - if (name.length() > DBusConnection.MAX_NAME_LENGTH) - throw new DBusException(getString("introspectSignalExceedCharacters") + name); - introspectiondata += " \n"; - Constructor con = sig.getConstructors()[0]; - Type[] ts = con.getGenericParameterTypes(); - for (int j = 1; j < ts.length; j++) - for (String s : Marshalling.getDBusType(ts[j])) - introspectiondata += " \n"; - introspectiondata += getAnnotations(sig); - introspectiondata += " \n"; - - } - introspectiondata += " \n"; - } else { - // recurse - m.putAll(getExportedMethods(i)); - } - return m; - } - - Map methods; - Reference object; - String introspectiondata; - - public ExportedObject(DBusInterface object, boolean weakreferences) throws DBusException { - if (weakreferences) - this.object = new WeakReference(object); - else - this.object = new StrongReference(object); - introspectiondata = ""; - methods = getExportedMethods(object.getClass()); - introspectiondata += - " \n" + - " \n" + - " \n" + - " \n" + - " \n"; - introspectiondata += - " \n" + - " \n" + - " \n" + - " \n"; - } -} - - diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/FileDescriptor.java b/federation/sssd/src/main/java/org/freedesktop/dbus/FileDescriptor.java new file mode 100644 index 0000000000..80997a10cc --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/FileDescriptor.java @@ -0,0 +1,64 @@ +package org.freedesktop.dbus; + +import org.freedesktop.dbus.exceptions.MarshallingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +/** + * Represents a FileDescriptor to be passed over the bus. Can be created from + * either an integer(gotten through some JNI/JNA/JNR call) or from a + * java.io.FileDescriptor. + * + */ +public class FileDescriptor { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final int fd; + + public FileDescriptor(int _fd) { + fd = _fd; + } + + // TODO this should have a better exception? + public FileDescriptor(java.io.FileDescriptor _data) throws MarshallingException { + fd = getFileDescriptor(_data); + } + + // TODO this should have a better exception? + public java.io.FileDescriptor toJavaFileDescriptor() throws MarshallingException { + return createFileDescriptorByReflection(fd); + } + + public int getIntFileDescriptor() { + return fd; + } + + private int getFileDescriptor(java.io.FileDescriptor _data) throws MarshallingException { + Field declaredField; + try { + declaredField = _data.getClass().getDeclaredField("fd"); + declaredField.setAccessible(true); + return declaredField.getInt(_data); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException _ex) { + logger.error("Could not get filedescriptor by reflection.", _ex); + throw new MarshallingException("Could not get member 'fd' of FileDescriptor by reflection!", _ex); + } + } + + private java.io.FileDescriptor createFileDescriptorByReflection(long _demarshallint) throws MarshallingException { + try { + Constructor constructor = java.io.FileDescriptor.class.getDeclaredConstructor(int.class); + constructor.setAccessible(true); + return constructor.newInstance((int) _demarshallint); + } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException _ex) { + logger.error("Could not create new FileDescriptor instance by reflection.", _ex); + throw new MarshallingException("Could not create new FileDescriptor instance by reflection", _ex); + } + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Gettext.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Gettext.java deleted file mode 100644 index a519a6f9a3..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/Gettext.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Pescetti Pseudo-Duplimate Generator - * - * Copyright (C) 2007 Matthew Johnson - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License Version 2 as published by - * the Free Software Foundation. This program is distributed in the hope that - * it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. You should have received a - * copy of the GNU Lesser General Public License along with this program; if not, - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * - * To Contact me, please email src@matthew.ath.cx - * - */ -package org.freedesktop.dbus; - -import java.util.ResourceBundle; - -public class Gettext { - private static ResourceBundle myResources = - ResourceBundle.getBundle("en_US"); - - public static String getString(String s) { - return myResources.getString(s); - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/InternalSignal.java b/federation/sssd/src/main/java/org/freedesktop/dbus/InternalSignal.java deleted file mode 100644 index 0a3072d477..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/InternalSignal.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import org.freedesktop.dbus.exceptions.DBusException; - -class InternalSignal extends DBusSignal { - public InternalSignal(String source, String objectpath, String name, String iface, String sig, long serial, Object... parameters) throws DBusException { - super(objectpath, iface, name, sig, parameters); - this.serial = serial; - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Marshalling.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Marshalling.java index 9e88309b10..5bfb1f06b6 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/Marshalling.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/Marshalling.java @@ -1,20 +1,21 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; -import cx.ath.matthew.debug.Debug; +import org.freedesktop.dbus.annotations.Position; +import org.freedesktop.dbus.connections.AbstractConnection; import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.interfaces.DBusInterface; +import org.freedesktop.dbus.interfaces.DBusSerializable; +import org.freedesktop.dbus.messages.Message; import org.freedesktop.dbus.types.DBusListType; import org.freedesktop.dbus.types.DBusMapType; import org.freedesktop.dbus.types.DBusStructType; +import org.freedesktop.dbus.types.UInt16; +import org.freedesktop.dbus.types.UInt32; +import org.freedesktop.dbus.types.UInt64; +import org.freedesktop.dbus.types.Variant; +import org.freedesktop.dbus.utils.LoggingHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.lang.reflect.Array; import java.lang.reflect.Constructor; @@ -24,342 +25,421 @@ import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; -import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Vector; - -import static org.freedesktop.dbus.Gettext.getString; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; /** * Contains static methods for marshalling values. */ -public class Marshalling { - private static Map typeCache = new HashMap(); +public final class Marshalling { + private static final Logger LOGGER = LoggerFactory.getLogger(Marshalling.class); + + private static final Map TYPE_CACHE = new ConcurrentHashMap<>(); + + private static final Map, Byte> CLASS_TO_ARGUMENTTYPE = new LinkedHashMap<>(); + static { + CLASS_TO_ARGUMENTTYPE.put(Boolean.class, Message.ArgumentType.BOOLEAN); // class + CLASS_TO_ARGUMENTTYPE.put(Boolean.TYPE, Message.ArgumentType.BOOLEAN); // primitive type + + CLASS_TO_ARGUMENTTYPE.put(Byte.class, Message.ArgumentType.BYTE); + CLASS_TO_ARGUMENTTYPE.put(Byte.TYPE, Message.ArgumentType.BYTE); + + CLASS_TO_ARGUMENTTYPE.put(Short.class, Message.ArgumentType.INT16); + CLASS_TO_ARGUMENTTYPE.put(Short.TYPE, Message.ArgumentType.INT16); + + CLASS_TO_ARGUMENTTYPE.put(Integer.class, Message.ArgumentType.INT32); + CLASS_TO_ARGUMENTTYPE.put(Integer.TYPE, Message.ArgumentType.INT32); + + CLASS_TO_ARGUMENTTYPE.put(Long.class, Message.ArgumentType.INT64); + CLASS_TO_ARGUMENTTYPE.put(Long.TYPE, Message.ArgumentType.INT64); + + CLASS_TO_ARGUMENTTYPE.put(Double.class, Message.ArgumentType.DOUBLE); + CLASS_TO_ARGUMENTTYPE.put(Double.TYPE, Message.ArgumentType.DOUBLE); + + if (AbstractConnection.FLOAT_SUPPORT) { + CLASS_TO_ARGUMENTTYPE.put(Float.class, Message.ArgumentType.FLOAT); + CLASS_TO_ARGUMENTTYPE.put(Float.TYPE, Message.ArgumentType.FLOAT); + } else { + CLASS_TO_ARGUMENTTYPE.put(Float.class, Message.ArgumentType.DOUBLE); + CLASS_TO_ARGUMENTTYPE.put(Float.TYPE, Message.ArgumentType.DOUBLE); + } + + CLASS_TO_ARGUMENTTYPE.put(UInt16.class, Message.ArgumentType.UINT16); + CLASS_TO_ARGUMENTTYPE.put(UInt32.class, Message.ArgumentType.UINT32); + CLASS_TO_ARGUMENTTYPE.put(UInt64.class, Message.ArgumentType.UINT64); + + CLASS_TO_ARGUMENTTYPE.put(CharSequence.class, Message.ArgumentType.STRING); + CLASS_TO_ARGUMENTTYPE.put(Variant.class, Message.ArgumentType.VARIANT); + + CLASS_TO_ARGUMENTTYPE.put(FileDescriptor.class, Message.ArgumentType.FILEDESCRIPTOR); + + CLASS_TO_ARGUMENTTYPE.put(DBusInterface.class, Message.ArgumentType.OBJECT_PATH); + CLASS_TO_ARGUMENTTYPE.put(DBusPath.class, Message.ArgumentType.OBJECT_PATH); + CLASS_TO_ARGUMENTTYPE.put(ObjectPath.class, Message.ArgumentType.OBJECT_PATH); + } + + private Marshalling() { + } /** - * Will return the DBus type corresponding to the given Java type. - * Note, container type should have their ParameterizedType not their - * Class passed in here. - * - * @param c The Java types. - * @return The DBus types. - * @throws DBusException If the given type cannot be converted to a DBus type. - */ - public static String getDBusType(Type[] c) throws DBusException { - StringBuffer sb = new StringBuffer(); - for (Type t : c) - for (String s : getDBusType(t)) + * Will return the DBus type corresponding to the given Java type. + * Note, container type should have their ParameterizedType not their + * Class passed in here. + * @param _javaType The Java types. + * @return The DBus types. + * @throws DBusException If the given type cannot be converted to a DBus type. + */ + public static String getDBusType(Type[] _javaType) throws DBusException { + StringBuilder sb = new StringBuilder(); + for (Type t : _javaType) { + for (String s : getDBusType(t)) { sb.append(s); + } + } return sb.toString(); } /** - * Will return the DBus type corresponding to the given Java type. - * Note, container type should have their ParameterizedType not their - * Class passed in here. - * - * @param c The Java type. - * @return The DBus type. - * @throws DBusException If the given type cannot be converted to a DBus type. - */ - public static String[] getDBusType(Type c) throws DBusException { - String[] cached = typeCache.get(c); - if (null != cached) return cached; - cached = getDBusType(c, false); - typeCache.put(c, cached); + * Will return the DBus type corresponding to the given Java type. + * Note, container type should have their ParameterizedType not their + * Class passed in here. + * @param _javaType The Java type. + * @return The DBus type. + * @throws DBusException If the given type cannot be converted to a DBus type. + */ + public static String[] getDBusType(Type _javaType) throws DBusException { + String[] cached = TYPE_CACHE.get(_javaType); + if (null != cached) { + return cached; + } + cached = getDBusType(_javaType, false); + TYPE_CACHE.put(_javaType, cached); return cached; } /** - * Will return the DBus type corresponding to the given Java type. - * Note, container type should have their ParameterizedType not their - * Class passed in here. - * - * @param c The Java type. - * @param basic If true enforces this to be a non-compound type. (compound types are Maps, Structs and Lists/arrays). - * @return The DBus type. - * @throws DBusException If the given type cannot be converted to a DBus type. - */ - public static String[] getDBusType(Type c, boolean basic) throws DBusException { - return recursiveGetDBusType(c, basic, 0); + * Will return the DBus type corresponding to the given Java type. + * Note, container type should have their ParameterizedType not their + * Class passed in here. + * @param _dataType The Java type. + * @param _basic If true enforces this to be a non-compound type. (compound types are Maps, Structs and Lists/arrays). + * @return The DBus type. + * @throws DBusException If the given type cannot be converted to a DBus type. + */ + public static String[] getDBusType(Type _dataType, boolean _basic) throws DBusException { + return recursiveGetDBusType(new StringBuffer[10], _dataType, _basic, 0); } - private static StringBuffer[] out = new StringBuffer[10]; - - @SuppressWarnings("unchecked") - public static String[] recursiveGetDBusType(Type c, boolean basic, int level) throws DBusException { - if (out.length <= level) { - StringBuffer[] newout = new StringBuffer[out.length]; - System.arraycopy(out, 0, newout, 0, out.length); - out = newout; + @SuppressWarnings("checkstyle:parameterassignment") + private static String[] recursiveGetDBusType(StringBuffer[] _out, Type _dataType, boolean _basic, int _level) throws DBusException { + if (_out.length <= _level) { + StringBuffer[] newout = new StringBuffer[_out.length]; + System.arraycopy(_out, 0, newout, 0, _out.length); + _out = newout; + } + if (null == _out[_level]) { + _out[_level] = new StringBuffer(); + } else { + _out[_level].delete(0, _out[_level].length()); } - if (null == out[level]) out[level] = new StringBuffer(); - else out[level].delete(0, out[level].length()); - if (basic && !(c instanceof Class)) - throw new DBusException(c + getString("notBasicType")); + if (_basic && !(_dataType instanceof Class)) { + throw new DBusException(_dataType + " is not a basic type"); + } - if (c instanceof TypeVariable) out[level].append((char) Message.ArgumentType.VARIANT); - else if (c instanceof GenericArrayType) { - out[level].append((char) Message.ArgumentType.ARRAY); - String[] s = recursiveGetDBusType(((GenericArrayType) c).getGenericComponentType(), false, level + 1); - if (s.length != 1) throw new DBusException(getString("multiValuedArrayNotPermitted")); - out[level].append(s[0]); - } else if ((c instanceof Class && - DBusSerializable.class.isAssignableFrom((Class) c)) || - (c instanceof ParameterizedType && - DBusSerializable.class.isAssignableFrom((Class) ((ParameterizedType) c).getRawType()))) { + if (_dataType instanceof TypeVariable) { + _out[_level].append((char) Message.ArgumentType.VARIANT); + } else if (_dataType instanceof GenericArrayType) { + _out[_level].append((char) Message.ArgumentType.ARRAY); + String[] s = recursiveGetDBusType(_out, ((GenericArrayType) _dataType).getGenericComponentType(), false, _level + 1); + if (s.length != 1) { + throw new DBusException("Multi-valued array types not permitted"); + } + _out[_level].append(s[0]); + } else if (_dataType instanceof Class && DBusSerializable.class.isAssignableFrom((Class) _dataType) + || _dataType instanceof ParameterizedType + && DBusSerializable.class.isAssignableFrom((Class) ((ParameterizedType) _dataType).getRawType())) { // it's a custom serializable type Type[] newtypes = null; - if (c instanceof Class) { - for (Method m : ((Class) c).getDeclaredMethods()) - if (m.getName().equals("deserialize")) + if (_dataType instanceof Class) { + for (Method m : ((Class) _dataType).getDeclaredMethods()) { + if (m.getName().equals("deserialize")) { newtypes = m.getGenericParameterTypes(); - } else - for (Method m : ((Class) ((ParameterizedType) c).getRawType()).getDeclaredMethods()) - if (m.getName().equals("deserialize")) + } + } + } else { + for (Method m : ((Class) ((ParameterizedType) _dataType).getRawType()).getDeclaredMethods()) { + if (m.getName().equals("deserialize")) { newtypes = m.getGenericParameterTypes(); + } + } + } - if (null == newtypes) throw new DBusException(getString("mustImplementDeserializeMethod")); + if (null == newtypes) { + throw new DBusException("Serializable classes must implement a deserialize method"); + } String[] sigs = new String[newtypes.length]; for (int j = 0; j < sigs.length; j++) { - String[] ss = recursiveGetDBusType(newtypes[j], false, level + 1); - if (1 != ss.length) throw new DBusException(getString("mustSerializeNativeDBusTypes")); + String[] ss = recursiveGetDBusType(_out, newtypes[j], false, _level + 1); + if (1 != ss.length) { + throw new DBusException("Serializable classes must serialize to native DBus types"); + } sigs[j] = ss[0]; } return sigs; - } else if (c instanceof ParameterizedType) { - ParameterizedType p = (ParameterizedType) c; + } else if (_dataType instanceof ParameterizedType) { + ParameterizedType p = (ParameterizedType) _dataType; if (p.getRawType().equals(Map.class)) { - out[level].append("a{"); + _out[_level].append("a{"); Type[] t = p.getActualTypeArguments(); try { - String[] s = recursiveGetDBusType(t[0], true, level + 1); - if (s.length != 1) throw new DBusException(getString("multiValuedArrayNotPermitted")); - out[level].append(s[0]); - s = recursiveGetDBusType(t[1], false, level + 1); - if (s.length != 1) throw new DBusException(getString("multiValuedArrayNotPermitted")); - out[level].append(s[0]); - } catch (ArrayIndexOutOfBoundsException AIOOBe) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, AIOOBe); - throw new DBusException(getString("mapParameters")); + String[] s = recursiveGetDBusType(_out, t[0], true, _level + 1); + if (s.length != 1) { + throw new DBusException("Multi-valued array types not permitted"); + } + _out[_level].append(s[0]); + s = recursiveGetDBusType(_out, t[1], false, _level + 1); + if (s.length != 1) { + throw new DBusException("Multi-valued array types not permitted"); + } + _out[_level].append(s[0]); + } catch (ArrayIndexOutOfBoundsException _ex) { + LOGGER.debug("", _ex); + throw new DBusException("Map must have 2 parameters"); } - out[level].append('}'); - } else if (List.class.isAssignableFrom((Class) p.getRawType())) { + _out[_level].append('}'); + } else if (List.class.isAssignableFrom((Class) p.getRawType())) { for (Type t : p.getActualTypeArguments()) { - if (Type.class.equals(t)) - out[level].append((char) Message.ArgumentType.SIGNATURE); - else { - String[] s = recursiveGetDBusType(t, false, level + 1); - if (s.length != 1) throw new DBusException(getString("multiValuedArrayNotPermitted")); - out[level].append((char) Message.ArgumentType.ARRAY); - out[level].append(s[0]); + if (Type.class.equals(t)) { + _out[_level].append((char) Message.ArgumentType.SIGNATURE); + } else { + String[] s = recursiveGetDBusType(_out, t, false, _level + 1); + if (s.length != 1) { + throw new DBusException("Multi-valued array types not permitted"); + } + _out[_level].append((char) Message.ArgumentType.ARRAY); + _out[_level].append(s[0]); } } } else if (p.getRawType().equals(Variant.class)) { - out[level].append((char) Message.ArgumentType.VARIANT); - } else if (DBusInterface.class.isAssignableFrom((Class) p.getRawType())) { - out[level].append((char) Message.ArgumentType.OBJECT_PATH); - } else if (Tuple.class.isAssignableFrom((Class) p.getRawType())) { + _out[_level].append((char) Message.ArgumentType.VARIANT); + } else if (DBusInterface.class.isAssignableFrom((Class) p.getRawType())) { + _out[_level].append((char) Message.ArgumentType.OBJECT_PATH); + } else if (Struct.class.isAssignableFrom((Class) p.getRawType())) { + _out[_level].append((char) Message.ArgumentType.STRUCT1); + } else if (Tuple.class.isAssignableFrom((Class) p.getRawType())) { Type[] ts = p.getActualTypeArguments(); - Vector vs = new Vector(); - for (Type t : ts) - for (String s : recursiveGetDBusType(t, false, level + 1)) + List vs = new ArrayList<>(); + for (Type t : ts) { + for (String s : recursiveGetDBusType(_out, t, false, _level + 1)) { vs.add(s); - return vs.toArray(new String[0]); - } else - throw new DBusException(getString("nonExportableParameterizedType") + c); - } else if (c.equals(Byte.class)) out[level].append((char) Message.ArgumentType.BYTE); - else if (c.equals(Byte.TYPE)) out[level].append((char) Message.ArgumentType.BYTE); - else if (c.equals(Boolean.class)) out[level].append((char) Message.ArgumentType.BOOLEAN); - else if (c.equals(Boolean.TYPE)) out[level].append((char) Message.ArgumentType.BOOLEAN); - else if (c.equals(Short.class)) out[level].append((char) Message.ArgumentType.INT16); - else if (c.equals(Short.TYPE)) out[level].append((char) Message.ArgumentType.INT16); - else if (c.equals(UInt16.class)) out[level].append((char) Message.ArgumentType.UINT16); - else if (c.equals(Integer.class)) out[level].append((char) Message.ArgumentType.INT32); - else if (c.equals(Integer.TYPE)) out[level].append((char) Message.ArgumentType.INT32); - else if (c.equals(UInt32.class)) out[level].append((char) Message.ArgumentType.UINT32); - else if (c.equals(Long.class)) out[level].append((char) Message.ArgumentType.INT64); - else if (c.equals(Long.TYPE)) out[level].append((char) Message.ArgumentType.INT64); - else if (c.equals(UInt64.class)) out[level].append((char) Message.ArgumentType.UINT64); - else if (c.equals(Double.class)) out[level].append((char) Message.ArgumentType.DOUBLE); - else if (c.equals(Double.TYPE)) out[level].append((char) Message.ArgumentType.DOUBLE); - else if (c.equals(Float.class) && AbstractConnection.FLOAT_SUPPORT) - out[level].append((char) Message.ArgumentType.FLOAT); - else if (c.equals(Float.class)) out[level].append((char) Message.ArgumentType.DOUBLE); - else if (c.equals(Float.TYPE) && AbstractConnection.FLOAT_SUPPORT) - out[level].append((char) Message.ArgumentType.FLOAT); - else if (c.equals(Float.TYPE)) out[level].append((char) Message.ArgumentType.DOUBLE); - else if (c.equals(String.class)) out[level].append((char) Message.ArgumentType.STRING); - else if (c.equals(Variant.class)) out[level].append((char) Message.ArgumentType.VARIANT); - else if (c instanceof Class && - DBusInterface.class.isAssignableFrom((Class) c)) - out[level].append((char) Message.ArgumentType.OBJECT_PATH); - else if (c instanceof Class && - Path.class.equals((Class) c)) - out[level].append((char) Message.ArgumentType.OBJECT_PATH); - else if (c instanceof Class && - ObjectPath.class.equals((Class) c)) - out[level].append((char) Message.ArgumentType.OBJECT_PATH); - else if (c instanceof Class && - ((Class) c).isArray()) { - if (Type.class.equals(((Class) c).getComponentType())) - out[level].append((char) Message.ArgumentType.SIGNATURE); - else { - out[level].append((char) Message.ArgumentType.ARRAY); - String[] s = recursiveGetDBusType(((Class) c).getComponentType(), false, level + 1); - if (s.length != 1) throw new DBusException(getString("multiValuedArrayNotPermitted")); - out[level].append(s[0]); - } - } else if (c instanceof Class && - Struct.class.isAssignableFrom((Class) c)) { - out[level].append((char) Message.ArgumentType.STRUCT1); - Type[] ts = Container.getTypeCache(c); - if (null == ts) { - Field[] fs = ((Class) c).getDeclaredFields(); - ts = new Type[fs.length]; - for (Field f : fs) { - Position p = f.getAnnotation(Position.class); - if (null == p) continue; - ts[p.value()] = f.getGenericType(); + } } - Container.putTypeCache(c, ts); + return vs.toArray(new String[0]); + } else { + throw new DBusException("Exporting non-exportable parameterized type " + _dataType); } + } else if (_dataType instanceof Class) { + Class dataTypeClazz = (Class) _dataType; - for (Type t : ts) - if (t != null) - for (String s : recursiveGetDBusType(t, false, level + 1)) - out[level].append(s); - out[level].append(')'); - } else { - throw new DBusException(getString("nonExportableType") + c); + if (dataTypeClazz.isArray()) { + if (Type.class.equals(((Class) _dataType).getComponentType())) { + _out[_level].append((char) Message.ArgumentType.SIGNATURE); + } else { + _out[_level].append((char) Message.ArgumentType.ARRAY); + String[] s = recursiveGetDBusType(_out, ((Class) _dataType).getComponentType(), false, _level + 1); + if (s.length != 1) { + throw new DBusException("Multi-valued array types not permitted"); + } + _out[_level].append(s[0]); + } + } else if (Struct.class.isAssignableFrom((Class) _dataType)) { + _out[_level].append((char) Message.ArgumentType.STRUCT1); + Type[] ts = Container.getTypeCache(_dataType); + if (null == ts) { + Field[] fs = ((Class) _dataType).getDeclaredFields(); + ts = new Type[fs.length]; + for (Field f : fs) { + Position p = f.getAnnotation(Position.class); + if (null == p) { + continue; + } + ts[p.value()] = f.getGenericType(); + } + Container.putTypeCache(_dataType, ts); + } + + for (Type t : ts) { + if (t != null) { + for (String s : recursiveGetDBusType(_out, t, false, _level + 1)) { + _out[_level].append(s); + } + } + } + _out[_level].append(')'); + + } else if (Enum.class.isAssignableFrom(dataTypeClazz)) { + _out[_level].append((char) Message.ArgumentType.STRING); + } else { + boolean found = false; + + for (Entry, Byte> entry : CLASS_TO_ARGUMENTTYPE.entrySet()) { + if (entry.getKey().isAssignableFrom(dataTypeClazz)) { + _out[_level].append((char) entry.getValue().byteValue()); + found = true; + break; + } + } + if (!found) { + throw new DBusException("Exporting non-exportable type: " + _dataType); + } + } } - if (Debug.debug) Debug.print(Debug.VERBOSE, "Converted Java type: " + c + " to D-Bus Type: " + out[level]); + LOGGER.trace("Converted Java type: {} to D-Bus Type: {}", _dataType, _out[_level]); - return new String[]{out[level].toString()}; + return new String[] { + _out[_level].toString() + }; } /** - * Converts a dbus type string into Java Type objects, - * - * @param dbus The DBus type or types. - * @param rv Vector to return the types in. - * @param limit Maximum number of types to parse (-1 == nolimit). - * @return number of characters parsed from the type string. - */ - public static int getJavaType(String dbus, List rv, int limit) throws DBusException { - if (null == dbus || "".equals(dbus) || 0 == limit) return 0; + * Converts a dbus type string into Java Type objects, + * @param _dbusType The DBus type or types. + * @param _resultValue List to return the types in. + * @param _limit Maximum number of types to parse (-1 == nolimit). + * @return number of characters parsed from the type string. + * @throws DBusException on error + */ + public static int getJavaType(String _dbusType, List _resultValue, int _limit) throws DBusException { + if (null == _dbusType || _dbusType.isEmpty() || 0 == _limit) { + return 0; + } try { - int i = 0; - for (; i < dbus.length() && (-1 == limit || limit > rv.size()); i++) - switch (dbus.charAt(i)) { - case Message.ArgumentType.STRUCT1: - int j = i + 1; - for (int c = 1; c > 0; j++) { - if (')' == dbus.charAt(j)) c--; - else if (Message.ArgumentType.STRUCT1 == dbus.charAt(j)) c++; + int idx = 0; + for (; idx < _dbusType.length() && (-1 == _limit || _limit > _resultValue.size()); idx++) { + switch (_dbusType.charAt(idx)) { + case Message.ArgumentType.STRUCT1: + int structIdx = idx + 1; + for (int structLen = 1; structLen > 0; structIdx++) { + if (Message.ArgumentType.STRUCT2 == _dbusType.charAt(structIdx)) { + structLen--; + } else if (Message.ArgumentType.STRUCT1 == _dbusType.charAt(structIdx)) { + structLen++; } + } - Vector contained = new Vector(); - int c = getJavaType(dbus.substring(i + 1, j - 1), contained, -1); - rv.add(new DBusStructType(contained.toArray(new Type[0]))); - i = j; - break; - case Message.ArgumentType.ARRAY: - if (Message.ArgumentType.DICT_ENTRY1 == dbus.charAt(i + 1)) { - contained = new Vector(); - c = getJavaType(dbus.substring(i + 2), contained, 2); - rv.add(new DBusMapType(contained.get(0), contained.get(1))); - i += (c + 2); - } else { - contained = new Vector(); - c = getJavaType(dbus.substring(i + 1), contained, 1); - rv.add(new DBusListType(contained.get(0))); - i += c; - } - break; - case Message.ArgumentType.VARIANT: - rv.add(Variant.class); - break; - case Message.ArgumentType.BOOLEAN: - rv.add(Boolean.class); - break; - case Message.ArgumentType.INT16: - rv.add(Short.class); - break; - case Message.ArgumentType.BYTE: - rv.add(Byte.class); - break; - case Message.ArgumentType.OBJECT_PATH: - rv.add(DBusInterface.class); - break; - case Message.ArgumentType.UINT16: - rv.add(UInt16.class); - break; - case Message.ArgumentType.INT32: - rv.add(Integer.class); - break; - case Message.ArgumentType.UINT32: - rv.add(UInt32.class); - break; - case Message.ArgumentType.INT64: - rv.add(Long.class); - break; - case Message.ArgumentType.UINT64: - rv.add(UInt64.class); - break; - case Message.ArgumentType.DOUBLE: - rv.add(Double.class); - break; - case Message.ArgumentType.FLOAT: - rv.add(Float.class); - break; - case Message.ArgumentType.STRING: - rv.add(String.class); - break; - case Message.ArgumentType.SIGNATURE: - rv.add(Type[].class); - break; - case Message.ArgumentType.DICT_ENTRY1: - rv.add(Map.Entry.class); - contained = new Vector(); - c = getJavaType(dbus.substring(i + 1), contained, 2); - i += c + 1; - break; - default: - throw new DBusException(MessageFormat.format(getString("parseDBusSignatureFailure"), new Object[]{dbus, dbus.charAt(i)})); + List contained = new ArrayList<>(); + int javaType = getJavaType(_dbusType.substring(idx + 1, structIdx - 1), contained, -1); + _resultValue.add(new DBusStructType(contained.toArray(new Type[0]))); + idx = structIdx - 1; //-1 because j already points to the next signature char + break; + case Message.ArgumentType.ARRAY: + if (Message.ArgumentType.DICT_ENTRY1 == _dbusType.charAt(idx + 1)) { + contained = new ArrayList<>(); + javaType = getJavaType(_dbusType.substring(idx + 2), contained, 2); + _resultValue.add(new DBusMapType(contained.get(0), contained.get(1))); + idx += javaType + 2; + } else { + contained = new ArrayList<>(); + javaType = getJavaType(_dbusType.substring(idx + 1), contained, 1); + _resultValue.add(new DBusListType(contained.get(0))); + idx += javaType; + } + break; + case Message.ArgumentType.VARIANT: + _resultValue.add(Variant.class); + break; + case Message.ArgumentType.BOOLEAN: + _resultValue.add(Boolean.class); + break; + case Message.ArgumentType.INT16: + _resultValue.add(Short.class); + break; + case Message.ArgumentType.BYTE: + _resultValue.add(Byte.class); + break; + case Message.ArgumentType.OBJECT_PATH: + _resultValue.add(DBusPath.class); + break; + case Message.ArgumentType.UINT16: + _resultValue.add(UInt16.class); + break; + case Message.ArgumentType.INT32: + _resultValue.add(Integer.class); + break; + case Message.ArgumentType.UINT32: + _resultValue.add(UInt32.class); + break; + case Message.ArgumentType.INT64: + _resultValue.add(Long.class); + break; + case Message.ArgumentType.UINT64: + _resultValue.add(UInt64.class); + break; + case Message.ArgumentType.DOUBLE: + _resultValue.add(Double.class); + break; + case Message.ArgumentType.FLOAT: + _resultValue.add(Float.class); + break; + case Message.ArgumentType.STRING: + _resultValue.add(CharSequence.class); + break; + case Message.ArgumentType.FILEDESCRIPTOR: + _resultValue.add(FileDescriptor.class); + break; + case Message.ArgumentType.SIGNATURE: + _resultValue.add(Type[].class); + break; + case Message.ArgumentType.DICT_ENTRY1: + _resultValue.add(Map.Entry.class); + contained = new ArrayList<>(); + javaType = getJavaType(_dbusType.substring(idx + 1), contained, 2); + idx += javaType + 1; + break; + default: + throw new DBusException(String.format("Failed to parse DBus type signature: %s (%s).", _dbusType, _dbusType.charAt(idx))); } - return i; - } catch (IndexOutOfBoundsException IOOBe) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOOBe); - throw new DBusException(getString("parseDBusTypeSignatureFailure") + dbus); + } + return idx; + } catch (IndexOutOfBoundsException _ex) { + LOGGER.debug("Failed to parse DBus type signature.", _ex); + throw new DBusException("Failed to parse DBus type signature: " + _dbusType); } } /** - * Recursively converts types for serialization onto DBus. - * - * @param parameters The parameters to convert. - * @param types The (possibly generic) types of the parameters. - * @return The converted parameters. - * @throws DBusException Thrown if there is an error in converting the objects. - */ - @SuppressWarnings("unchecked") - public static Object[] convertParameters(Object[] parameters, Type[] types, AbstractConnection conn) throws DBusException { - if (null == parameters) return null; + * Recursively converts types for serialization onto DBus. + * @param _parameters The parameters to convert. + * @param _types The (possibly generic) types of the parameters. + * @param _conn the connection + * @return The converted parameters. + * @throws DBusException Thrown if there is an error in converting the objects. + */ + public static Object[] convertParameters(Object[] _parameters, Type[] _types, AbstractConnection _conn) throws DBusException { + if (null == _parameters) { + return null; + } + + Object[] parameters = _parameters; + Type[] types = _types; + for (int i = 0; i < parameters.length; i++) { - if (Debug.debug) - Debug.print(Debug.VERBOSE, "Converting " + i + " from " + parameters[i] + " to " + types[i]); - if (null == parameters[i]) continue; + if (null == parameters[i]) { + continue; + } + LOGGER.trace("Converting {} from '{}' to {}", i, parameters[i], types[i]); if (parameters[i] instanceof DBusSerializable) { - for (Method m : parameters[i].getClass().getDeclaredMethods()) + for (Method m : parameters[i].getClass().getDeclaredMethods()) { if (m.getName().equals("deserialize")) { Type[] newtypes = m.getParameterTypes(); Type[] expand = new Type[types.length + newtypes.length - 1]; @@ -374,6 +454,7 @@ public class Marshalling { System.arraycopy(parameters, i + 1, exparams, i + newparams.length, parameters.length - i - 1); parameters = exparams; } + } i--; } else if (parameters[i] instanceof Tuple) { Type[] newtypes = ((ParameterizedType) types[i]).getActualTypeArguments(); @@ -388,242 +469,261 @@ public class Marshalling { System.arraycopy(newparams, 0, exparams, i, newparams.length); System.arraycopy(parameters, i + 1, exparams, i + newparams.length, parameters.length - i - 1); parameters = exparams; - if (Debug.debug) - Debug.print(Debug.VERBOSE, "New params: " + Arrays.deepToString(parameters) + " new types: " + Arrays.deepToString(types)); + + LoggingHelper.logIf(LOGGER.isTraceEnabled(), () -> { + LOGGER.trace("New params: {}, new types: {}", Arrays.deepToString(exparams), Arrays.deepToString(expand)); + }); + i--; - } else if (types[i] instanceof TypeVariable && - !(parameters[i] instanceof Variant)) + } else if (types[i] instanceof TypeVariable && !(parameters[i] instanceof Variant)) { // its an unwrapped variant, wrap it - parameters[i] = new Variant(parameters[i]); - else if (parameters[i] instanceof DBusInterface) - parameters[i] = conn.getExportedObject((DBusInterface) parameters[i]); + parameters[i] = new Variant<>(parameters[i]); + } else if (parameters[i] instanceof DBusInterface) { + parameters[i] = _conn.getExportedObject((DBusInterface) parameters[i]); + } } return parameters; } - @SuppressWarnings("unchecked") - static Object deSerializeParameter(Object parameter, Type type, AbstractConnection conn) throws Exception { - if (Debug.debug) - Debug.print(Debug.VERBOSE, "Deserializing from " + parameter.getClass() + " to " + type.getClass()); - if (null == parameter) - return null; + @SuppressWarnings({ "unchecked", "rawtypes" }) + static Object deSerializeParameter(Object _parameter, Type _type, AbstractConnection _conn) throws Exception { + LOGGER.trace("Deserializing from {} to {}", _parameter.getClass(), _type); + Object parameter = _parameter; // its a wrapped variant, unwrap it - if (type instanceof TypeVariable - && parameter instanceof Variant) { - parameter = ((Variant) parameter).getValue(); + if (_type instanceof TypeVariable && parameter instanceof Variant) { + parameter = ((Variant) parameter).getValue(); + LOGGER.trace("Type is variant, unwrapping to {}", parameter); } // Turn a signature into a Type[] - if (type instanceof Class - && ((Class) type).isArray() - && ((Class) type).getComponentType().equals(Type.class) - && parameter instanceof String) { - Vector rv = new Vector(); + if (_type instanceof Class && ((Class) _type).isArray() && ((Class) _type).getComponentType().equals(Type.class) && parameter instanceof String) { + List rv = new ArrayList<>(); getJavaType((String) parameter, rv, -1); parameter = rv.toArray(new Type[0]); } // its an object path, get/create the proxy if (parameter instanceof ObjectPath) { - if (type instanceof Class && DBusInterface.class.isAssignableFrom((Class) type)) - parameter = conn.getExportedObject( - ((ObjectPath) parameter).source, - ((ObjectPath) parameter).path); - else - parameter = new Path(((ObjectPath) parameter).path); + LOGGER.trace("Parameter is ObjectPath"); + if (_type instanceof Class && DBusInterface.class.isAssignableFrom((Class) _type)) { + parameter = _conn.getExportedObject(((ObjectPath) parameter).getSource(), ((ObjectPath) parameter).getPath(), (Class) _type); + } else { + parameter = new DBusPath(((ObjectPath) parameter).getPath()); + } + } + + // its an enum, parse either as the string name or the ordinal + if (parameter instanceof String && _type instanceof Class && Enum.class.isAssignableFrom((Class) _type)) { + LOGGER.trace("Type seems to be an enum"); + parameter = Enum.valueOf((Class) _type, (String) parameter); } // it should be a struct. create it - if (parameter instanceof Object[] && - type instanceof Class && - Struct.class.isAssignableFrom((Class) type)) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Creating Struct " + type + " from " + parameter); - Type[] ts = Container.getTypeCache(type); + if (parameter instanceof Object[] && _type instanceof Class && Struct.class.isAssignableFrom((Class) _type)) { + LOGGER.trace("Creating Struct {} from {}", _type, parameter); + Type[] ts = Container.getTypeCache(_type); if (null == ts) { - Field[] fs = ((Class) type).getDeclaredFields(); + Field[] fs = ((Class) _type).getDeclaredFields(); ts = new Type[fs.length]; for (Field f : fs) { Position p = f.getAnnotation(Position.class); - if (null == p) continue; + if (null == p) { + continue; + } ts[p.value()] = f.getGenericType(); } - Container.putTypeCache(type, ts); + Container.putTypeCache(_type, ts); } // recurse over struct contents - parameter = deSerializeParameters((Object[]) parameter, ts, conn); - for (Constructor con : ((Class) type).getDeclaredConstructors()) { + parameter = deSerializeParameters((Object[]) parameter, ts, _conn); + for (Constructor con : ((Class) _type).getDeclaredConstructors()) { try { parameter = con.newInstance((Object[]) parameter); break; - } catch (IllegalArgumentException IAe) { + } catch (IllegalArgumentException _exIa) { + LOGGER.trace("Could not create new instance", _exIa); } } } // recurse over arrays if (parameter instanceof Object[]) { + LOGGER.trace("Parameter is object array"); Type[] ts = new Type[((Object[]) parameter).length]; Arrays.fill(ts, parameter.getClass().getComponentType()); - parameter = deSerializeParameters((Object[]) parameter, - ts, conn); + parameter = deSerializeParameters((Object[]) parameter, ts, _conn); } if (parameter instanceof List) { + LOGGER.trace("Parameter is List"); Type type2; - if (type instanceof ParameterizedType) - type2 = ((ParameterizedType) type).getActualTypeArguments()[0]; - else if (type instanceof GenericArrayType) - type2 = ((GenericArrayType) type).getGenericComponentType(); - else if (type instanceof Class && ((Class) type).isArray()) - type2 = ((Class) type).getComponentType(); - else + if (_type instanceof ParameterizedType) { + type2 = ((ParameterizedType) _type).getActualTypeArguments()[0]; + } else if (_type instanceof GenericArrayType) { + type2 = ((GenericArrayType) _type).getGenericComponentType(); + } else if (_type instanceof Class && ((Class) _type).isArray()) { + type2 = ((Class) _type).getComponentType(); + } else { type2 = null; - if (null != type2) - parameter = deSerializeParameters((List) parameter, type2, conn); + } + if (null != type2) { + parameter = deSerializeParameters((List) parameter, type2, _conn); + } } // correct floats if appropriate - if (type.equals(Float.class) || type.equals(Float.TYPE)) - if (!(parameter instanceof Float)) - parameter = ((Number) parameter).floatValue(); + if ((_type.equals(Float.class) || _type.equals(Float.TYPE)) && !(parameter instanceof Float)) { + parameter = ((Number) parameter).floatValue(); + LOGGER.trace("Parameter is float of value: {}", parameter); + } // make sure arrays are in the correct format - if (parameter instanceof Object[] || - parameter instanceof List || - parameter.getClass().isArray()) { - if (type instanceof ParameterizedType) - parameter = ArrayFrob.convert(parameter, - (Class) ((ParameterizedType) type).getRawType()); - else if (type instanceof GenericArrayType) { - Type ct = ((GenericArrayType) type).getGenericComponentType(); - Class cc = null; - if (ct instanceof Class) - cc = (Class) ct; - if (ct instanceof ParameterizedType) - cc = (Class) ((ParameterizedType) ct).getRawType(); + if (parameter instanceof Object[] || parameter instanceof List || parameter.getClass().isArray()) { + if (_type instanceof ParameterizedType) { + parameter = ArrayFrob.convert(parameter, (Class) ((ParameterizedType) _type).getRawType()); + } else if (_type instanceof GenericArrayType) { + Type ct = ((GenericArrayType) _type).getGenericComponentType(); + Class cc = null; + if (ct instanceof Class) { + cc = (Class) ct; + } + if (ct instanceof ParameterizedType) { + cc = (Class) ((ParameterizedType) ct).getRawType(); + } Object o = Array.newInstance(cc, 0); - parameter = ArrayFrob.convert(parameter, - o.getClass()); - } else if (type instanceof Class && - ((Class) type).isArray()) { - Class cc = ((Class) type).getComponentType(); - if ((cc.equals(Float.class) || cc.equals(Float.TYPE)) - && (parameter instanceof double[])) { + parameter = ArrayFrob.convert(parameter, o.getClass()); + } else if (_type instanceof Class && ((Class) _type).isArray()) { + Class cc = ((Class) _type).getComponentType(); + if ((cc.equals(Float.class) || cc.equals(Float.TYPE)) && parameter instanceof double[]) { double[] tmp1 = (double[]) parameter; float[] tmp2 = new float[tmp1.length]; - for (int i = 0; i < tmp1.length; i++) + for (int i = 0; i < tmp1.length; i++) { tmp2[i] = (float) tmp1[i]; + } parameter = tmp2; } Object o = Array.newInstance(cc, 0); - parameter = ArrayFrob.convert(parameter, - o.getClass()); + parameter = ArrayFrob.convert(parameter, o.getClass()); } } if (parameter instanceof DBusMap) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Deserializing a Map"); - DBusMap dmap = (DBusMap) parameter; - Type[] maptypes = ((ParameterizedType) type).getActualTypeArguments(); + LOGGER.trace("Deserializing a Map"); + DBusMap dmap = (DBusMap) parameter; + + Type[] maptypes; + if (_type instanceof ParameterizedType) { + maptypes = ((ParameterizedType) _type).getActualTypeArguments(); + } else { + maptypes = parameter.getClass().getTypeParameters(); + } + for (int i = 0; i < dmap.entries.length; i++) { - dmap.entries[i][0] = deSerializeParameter(dmap.entries[i][0], maptypes[0], conn); - dmap.entries[i][1] = deSerializeParameter(dmap.entries[i][1], maptypes[1], conn); + dmap.entries[i][0] = deSerializeParameter(dmap.entries[i][0], maptypes[0], _conn); + dmap.entries[i][1] = deSerializeParameter(dmap.entries[i][1], maptypes[1], _conn); } } return parameter; } - static List deSerializeParameters(List parameters, Type type, AbstractConnection conn) throws Exception { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Deserializing from " + parameters + " to " + type); - if (null == parameters) return null; - for (int i = 0; i < parameters.size(); i++) { - if (null == parameters.get(i)) continue; - - /* DO NOT DO THIS! IT'S REALLY NOT SUPPORTED! - * if (type instanceof Class && - DBusSerializable.class.isAssignableFrom((Class) types[i])) { - for (Method m: ((Class) types[i]).getDeclaredMethods()) - if (m.getName().equals("deserialize")) { - Type[] newtypes = m.getGenericParameterTypes(); - try { - Object[] sub = new Object[newtypes.length]; - System.arraycopy(parameters, i, sub, 0, newtypes.length); - sub = deSerializeParameters(sub, newtypes, conn); - DBusSerializable sz = (DBusSerializable) ((Class) types[i]).newInstance(); - m.invoke(sz, sub); - Object[] compress = new Object[parameters.length - newtypes.length + 1]; - System.arraycopy(parameters, 0, compress, 0, i); - compress[i] = sz; - System.arraycopy(parameters, i + newtypes.length, compress, i+1, parameters.length - i - newtypes.length); - parameters = compress; - } catch (ArrayIndexOutOfBoundsException AIOOBe) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, AIOOBe); - throw new DBusException("Not enough elements to create custom object from serialized data ("+(parameters.size()-i)+" < "+(newtypes.length)+")"); - } - } - } else*/ - parameters.set(i, deSerializeParameter(parameters.get(i), type, conn)); + static List deSerializeParameters(List _parameters, Type _type, AbstractConnection _conn) throws Exception { + LOGGER.trace("Deserializing from {} to {}", _parameters, _type); + if (null == _parameters) { + return null; } - return parameters; + for (int i = 0; i < _parameters.size(); i++) { + if (null == _parameters.get(i)) { + continue; + } + + _parameters.set(i, deSerializeParameter(_parameters.get(i), _type, _conn)); + } + return _parameters; } @SuppressWarnings("unchecked") - static Object[] deSerializeParameters(Object[] parameters, Type[] types, AbstractConnection conn) throws Exception { - if (Debug.debug) - Debug.print(Debug.VERBOSE, "Deserializing from " + Arrays.deepToString(parameters) + " to " + Arrays.deepToString(types)); - if (null == parameters) return null; + public static Object[] deSerializeParameters(Object[] _parameters, Type[] _types, AbstractConnection _conn) throws Exception { + LoggingHelper.logIf(LOGGER.isTraceEnabled(), () -> LOGGER.trace("Deserializing from {} to {} ", Arrays.deepToString(_parameters), Arrays.deepToString(_types))); - if (types.length == 1 && types[0] instanceof ParameterizedType - && Tuple.class.isAssignableFrom((Class) ((ParameterizedType) types[0]).getRawType())) { + if (null == _parameters) { + return null; + } + + Object[] parameters = _parameters; + Type[] types = _types; + + if (types.length == 1 && types[0] instanceof ParameterizedType && Tuple.class.isAssignableFrom((Class) ((ParameterizedType) types[0]).getRawType())) { types = ((ParameterizedType) types[0]).getActualTypeArguments(); } + if (types.length == 1 && types[0] instanceof Class && Tuple.class.isAssignableFrom((Class) types[0])) { + String typeName = types[0].getTypeName(); + Constructor[] constructors = Class.forName(typeName).getDeclaredConstructors(); + if (constructors.length != 1) { + throw new DBusException("Error deserializing message: " + + "We had a Tuple type but wrong number of constructors for this Tuple. " + + "There should be exactly one."); + } + + if (constructors[0].getParameterCount() != parameters.length) { + throw new DBusException("Error deserializing message: " + + "We had a Tuple type but it had wrong number of constructor arguments. " + + "The number of constructor arguments should match the number of parameters to deserialize."); + } + + Object o = constructors[0].newInstance(parameters); + return new Object[] {o}; + } + for (int i = 0; i < parameters.length; i++) { // CHECK IF ARRAYS HAVE THE SAME LENGTH <-- has to happen after expanding parameters if (i >= types.length) { - if (Debug.debug) { + if (LOGGER.isDebugEnabled()) { + LOGGER.error("Parameter length differs, expected {} but got {}", parameters.length, types.length); for (int j = 0; j < parameters.length; j++) { - Debug.print(Debug.ERR, String.format("Error, Parameters difference (%1d, '%2s')", j, parameters[j].toString())); + LOGGER.error("Error, Parameters differ: {}, '{}'", j, parameters[j].toString()); } } - throw new DBusException(getString("errorDeserializingMessage")); + throw new DBusException("Error deserializing message: number of parameters didn't match receiving signature"); + } + if (null == parameters[i]) { + continue; } - if (null == parameters[i]) continue; - if ((types[i] instanceof Class && - DBusSerializable.class.isAssignableFrom((Class) types[i])) || - (types[i] instanceof ParameterizedType && - DBusSerializable.class.isAssignableFrom((Class) ((ParameterizedType) types[i]).getRawType()))) { + if (types[i] instanceof Class + && DBusSerializable.class.isAssignableFrom((Class) types[i]) + || types[i] instanceof ParameterizedType + && DBusSerializable.class.isAssignableFrom((Class) ((ParameterizedType) types[i]).getRawType())) { Class dsc; - if (types[i] instanceof Class) + if (types[i] instanceof Class) { dsc = (Class) types[i]; - else + } else { dsc = (Class) ((ParameterizedType) types[i]).getRawType(); - for (Method m : dsc.getDeclaredMethods()) + } + for (Method m : dsc.getDeclaredMethods()) { if (m.getName().equals("deserialize")) { Type[] newtypes = m.getGenericParameterTypes(); try { Object[] sub = new Object[newtypes.length]; System.arraycopy(parameters, i, sub, 0, newtypes.length); - sub = deSerializeParameters(sub, newtypes, conn); - DBusSerializable sz = dsc.newInstance(); + sub = deSerializeParameters(sub, newtypes, _conn); + DBusSerializable sz = dsc.getDeclaredConstructor().newInstance(); m.invoke(sz, sub); Object[] compress = new Object[parameters.length - newtypes.length + 1]; System.arraycopy(parameters, 0, compress, 0, i); compress[i] = sz; System.arraycopy(parameters, i + newtypes.length, compress, i + 1, parameters.length - i - newtypes.length); parameters = compress; - } catch (ArrayIndexOutOfBoundsException AIOOBe) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, AIOOBe); - throw new DBusException(MessageFormat.format(getString("notEnoughElementsToCreateCustomObject"), - new Object[]{parameters.length - i, newtypes.length})); + } catch (ArrayIndexOutOfBoundsException _ex) { + LOGGER.debug("", _ex); + throw new DBusException(String.format("Not enough elements to create custom object from serialized data (%s < %s).", parameters.length - i, newtypes.length)); } } - } else - parameters[i] = deSerializeParameter(parameters[i], types[i], conn); + } + } else { + parameters[i] = deSerializeParameter(parameters[i], types[i], _conn); + } } return parameters; } } - - diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Message.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Message.java deleted file mode 100644 index 01f00e8823..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/Message.java +++ /dev/null @@ -1,1216 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import cx.ath.matthew.debug.Debug; -import cx.ath.matthew.utils.Hexdump; -import org.freedesktop.dbus.exceptions.DBusException; -import org.freedesktop.dbus.exceptions.MarshallingException; -import org.freedesktop.dbus.exceptions.UnknownTypeCodeException; - -import java.io.UnsupportedEncodingException; -import java.lang.reflect.Array; -import java.lang.reflect.Type; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Vector; - -import static org.freedesktop.dbus.Gettext.getString; - -/** - * Superclass of all messages which are sent over the Bus. - * This class deals with all the marshalling to/from the wire format. - */ -public class Message { - /** - * Defines constants representing the endianness of the message. - */ - public static interface Endian { - public static final byte BIG = 'B'; - public static final byte LITTLE = 'l'; - } - - /** - * Defines constants representing the flags which can be set on a message. - */ - public static interface Flags { - public static final byte NO_REPLY_EXPECTED = 0x01; - public static final byte NO_AUTO_START = 0x02; - public static final byte ASYNC = 0x40; - } - - /** - * Defines constants for each message type. - */ - public static interface MessageType { - public static final byte METHOD_CALL = 1; - public static final byte METHOD_RETURN = 2; - public static final byte ERROR = 3; - public static final byte SIGNAL = 4; - } - - /** - * The current protocol major version. - */ - public static final byte PROTOCOL = 1; - - /** - * Defines constants for each valid header field type. - */ - public static interface HeaderField { - public static final byte PATH = 1; - public static final byte INTERFACE = 2; - public static final byte MEMBER = 3; - public static final byte ERROR_NAME = 4; - public static final byte REPLY_SERIAL = 5; - public static final byte DESTINATION = 6; - public static final byte SENDER = 7; - public static final byte SIGNATURE = 8; - } - - /** - * Defines constants for each argument type. - * There are two constants for each argument type, - * as a byte or as a String (the _STRING version) - */ - public static interface ArgumentType { - public static final String BYTE_STRING = "y"; - public static final String BOOLEAN_STRING = "b"; - public static final String INT16_STRING = "n"; - public static final String UINT16_STRING = "q"; - public static final String INT32_STRING = "i"; - public static final String UINT32_STRING = "u"; - public static final String INT64_STRING = "x"; - public static final String UINT64_STRING = "t"; - public static final String DOUBLE_STRING = "d"; - public static final String FLOAT_STRING = "f"; - public static final String STRING_STRING = "s"; - public static final String OBJECT_PATH_STRING = "o"; - public static final String SIGNATURE_STRING = "g"; - public static final String ARRAY_STRING = "a"; - public static final String VARIANT_STRING = "v"; - public static final String STRUCT_STRING = "r"; - public static final String STRUCT1_STRING = "("; - public static final String STRUCT2_STRING = ")"; - public static final String DICT_ENTRY_STRING = "e"; - public static final String DICT_ENTRY1_STRING = "{"; - public static final String DICT_ENTRY2_STRING = "}"; - - public static final byte BYTE = 'y'; - public static final byte BOOLEAN = 'b'; - public static final byte INT16 = 'n'; - public static final byte UINT16 = 'q'; - public static final byte INT32 = 'i'; - public static final byte UINT32 = 'u'; - public static final byte INT64 = 'x'; - public static final byte UINT64 = 't'; - public static final byte DOUBLE = 'd'; - public static final byte FLOAT = 'f'; - public static final byte STRING = 's'; - public static final byte OBJECT_PATH = 'o'; - public static final byte SIGNATURE = 'g'; - public static final byte ARRAY = 'a'; - public static final byte VARIANT = 'v'; - public static final byte STRUCT = 'r'; - public static final byte STRUCT1 = '('; - public static final byte STRUCT2 = ')'; - public static final byte DICT_ENTRY = 'e'; - public static final byte DICT_ENTRY1 = '{'; - public static final byte DICT_ENTRY2 = '}'; - } - - /** - * Keep a static reference to each size of padding array to prevent allocation. - */ - private static byte[][] padding; - - static { - padding = new byte[][]{ - null, - new byte[1], - new byte[2], - new byte[3], - new byte[4], - new byte[5], - new byte[6], - new byte[7]}; - } - - /** - * Steps to increment the buffer array. - */ - private static final int BUFFERINCREMENT = 20; - - private boolean big; - protected byte[][] wiredata; - protected long bytecounter; - protected Map headers; - protected static long globalserial = 0; - protected long serial; - protected byte type; - protected byte flags; - protected byte protover; - private Object[] args; - private byte[] body; - private long bodylen = 0; - private int preallocated = 0; - private int paofs = 0; - private byte[] pabuf; - private int bufferuse = 0; - - /** - * Returns the name of the given header field. - */ - public static String getHeaderFieldName(byte field) { - switch (field) { - case HeaderField.PATH: - return "Path"; - case HeaderField.INTERFACE: - return "Interface"; - case HeaderField.MEMBER: - return "Member"; - case HeaderField.ERROR_NAME: - return "Error Name"; - case HeaderField.REPLY_SERIAL: - return "Reply Serial"; - case HeaderField.DESTINATION: - return "Destination"; - case HeaderField.SENDER: - return "Sender"; - case HeaderField.SIGNATURE: - return "Signature"; - default: - return "Invalid"; - } - } - - /** - * Create a message; only to be called by sub-classes. - * - * @param endian The endianness to create the message. - * @param type The message type. - * @param flags Any message flags. - */ - protected Message(byte endian, byte type, byte flags) throws DBusException { - wiredata = new byte[BUFFERINCREMENT][]; - headers = new HashMap(); - big = (Endian.BIG == endian); - bytecounter = 0; - synchronized (Message.class) { - serial = ++globalserial; - } - if (Debug.debug) Debug.print(Debug.DEBUG, "Creating message with serial " + serial); - this.type = type; - this.flags = flags; - preallocate(4); - append("yyyy", endian, type, flags, Message.PROTOCOL); - } - - /** - * Create a blank message. Only to be used when calling populate. - */ - protected Message() { - wiredata = new byte[BUFFERINCREMENT][]; - headers = new HashMap(); - bytecounter = 0; - } - - /** - * Create a message from wire-format data. - * - * @param msg D-Bus serialized data of type yyyuu - * @param headers D-Bus serialized data of type a(yv) - * @param body D-Bus serialized data of the signature defined in headers. - */ - @SuppressWarnings("unchecked") - void populate(byte[] msg, byte[] headers, byte[] body) throws DBusException { - big = (msg[0] == Endian.BIG); - type = msg[1]; - flags = msg[2]; - protover = msg[3]; - wiredata[0] = msg; - wiredata[1] = headers; - wiredata[2] = body; - this.body = body; - bufferuse = 3; - bodylen = ((Number) extract(Message.ArgumentType.UINT32_STRING, msg, 4)[0]).longValue(); - serial = ((Number) extract(Message.ArgumentType.UINT32_STRING, msg, 8)[0]).longValue(); - bytecounter = msg.length + headers.length + body.length; - if (Debug.debug) Debug.print(Debug.VERBOSE, headers); - Object[] hs = extract("a(yv)", headers, 0); - if (Debug.debug) Debug.print(Debug.VERBOSE, Arrays.deepToString(hs)); - for (Object o : (Vector) hs[0]) { - this.headers.put((Byte) ((Object[]) o)[0], ((Variant) ((Object[]) o)[1]).getValue()); - } - } - - /** - * Create a buffer of num bytes. - * Data is copied to this rather than added to the buffer list. - */ - private void preallocate(int num) { - preallocated = 0; - pabuf = new byte[num]; - appendBytes(pabuf); - preallocated = num; - paofs = 0; - } - - /** - * Ensures there are enough free buffers. - * - * @param num number of free buffers to create. - */ - private void ensureBuffers(int num) { - int increase = num - wiredata.length + bufferuse; - if (increase > 0) { - if (increase < BUFFERINCREMENT) increase = BUFFERINCREMENT; - if (Debug.debug) Debug.print(Debug.VERBOSE, "Resizing " + bufferuse); - byte[][] temp = new byte[wiredata.length + increase][]; - System.arraycopy(wiredata, 0, temp, 0, wiredata.length); - wiredata = temp; - } - } - - /** - * Appends a buffer to the buffer list. - */ - protected void appendBytes(byte[] buf) { - if (null == buf) return; - if (preallocated > 0) { - if (paofs + buf.length > pabuf.length) - throw new ArrayIndexOutOfBoundsException(MessageFormat.format(getString("arrayOutOfBounds"), new Object[]{paofs, pabuf.length, buf.length})); - System.arraycopy(buf, 0, pabuf, paofs, buf.length); - paofs += buf.length; - preallocated -= buf.length; - } else { - if (bufferuse == wiredata.length) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Resizing " + bufferuse); - byte[][] temp = new byte[wiredata.length + BUFFERINCREMENT][]; - System.arraycopy(wiredata, 0, temp, 0, wiredata.length); - wiredata = temp; - } - wiredata[bufferuse++] = buf; - bytecounter += buf.length; - } - } - - /** - * Appends a byte to the buffer list. - */ - protected void appendByte(byte b) { - if (preallocated > 0) { - pabuf[paofs++] = b; - preallocated--; - } else { - if (bufferuse == wiredata.length) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Resizing " + bufferuse); - byte[][] temp = new byte[wiredata.length + BUFFERINCREMENT][]; - System.arraycopy(wiredata, 0, temp, 0, wiredata.length); - wiredata = temp; - } - wiredata[bufferuse++] = new byte[]{b}; - bytecounter++; - } - } - - /** - * Demarshalls an integer of a given width from a buffer. - * Endianness is determined from the format of the message. - * - * @param buf The buffer to demarshall from. - * @param ofs The offset to demarshall from. - * @param width The byte-width of the int. - */ - public long demarshallint(byte[] buf, int ofs, int width) { - return big ? demarshallintBig(buf, ofs, width) : demarshallintLittle(buf, ofs, width); - } - - /** - * Demarshalls an integer of a given width from a buffer. - * - * @param buf The buffer to demarshall from. - * @param ofs The offset to demarshall from. - * @param endian The endianness to use in demarshalling. - * @param width The byte-width of the int. - */ - public static long demarshallint(byte[] buf, int ofs, byte endian, int width) { - return endian == Endian.BIG ? demarshallintBig(buf, ofs, width) : demarshallintLittle(buf, ofs, width); - } - - /** - * Demarshalls an integer of a given width from a buffer using big-endian format. - * - * @param buf The buffer to demarshall from. - * @param ofs The offset to demarshall from. - * @param width The byte-width of the int. - */ - public static long demarshallintBig(byte[] buf, int ofs, int width) { - long l = 0; - for (int i = 0; i < width; i++) { - l <<= 8; - l |= (buf[ofs + i] & 0xFF); - } - return l; - } - - /** - * Demarshalls an integer of a given width from a buffer using little-endian format. - * - * @param buf The buffer to demarshall from. - * @param ofs The offset to demarshall from. - * @param width The byte-width of the int. - */ - public static long demarshallintLittle(byte[] buf, int ofs, int width) { - long l = 0; - for (int i = (width - 1); i >= 0; i--) { - l <<= 8; - l |= (buf[ofs + i] & 0xFF); - } - return l; - } - - /** - * Marshalls an integer of a given width and appends it to the message. - * Endianness is determined from the message. - * - * @param l The integer to marshall. - * @param width The byte-width of the int. - */ - public void appendint(long l, int width) { - byte[] buf = new byte[width]; - marshallint(l, buf, 0, width); - appendBytes(buf); - } - - /** - * Marshalls an integer of a given width into a buffer. - * Endianness is determined from the message. - * - * @param l The integer to marshall. - * @param buf The buffer to marshall to. - * @param ofs The offset to marshall to. - * @param width The byte-width of the int. - */ - public void marshallint(long l, byte[] buf, int ofs, int width) { - if (big) marshallintBig(l, buf, ofs, width); - else marshallintLittle(l, buf, ofs, width); - if (Debug.debug) Debug.print(Debug.VERBOSE, "Marshalled int " + l + " to " + Hexdump.toHex(buf, ofs, width)); - } - - /** - * Marshalls an integer of a given width into a buffer using big-endian format. - * - * @param l The integer to marshall. - * @param buf The buffer to marshall to. - * @param ofs The offset to marshall to. - * @param width The byte-width of the int. - */ - public static void marshallintBig(long l, byte[] buf, int ofs, int width) { - for (int i = (width - 1); i >= 0; i--) { - buf[i + ofs] = (byte) (l & 0xFF); - l >>= 8; - } - } - - /** - * Marshalls an integer of a given width into a buffer using little-endian format. - * - * @param l The integer to marshall. - * @param buf The buffer to demarshall to. - * @param ofs The offset to demarshall to. - * @param width The byte-width of the int. - */ - public static void marshallintLittle(long l, byte[] buf, int ofs, int width) { - for (int i = 0; i < width; i++) { - buf[i + ofs] = (byte) (l & 0xFF); - l >>= 8; - } - } - - public byte[][] getWireData() { - return wiredata; - } - - /** - * Formats the message in a human-readable format. - */ - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append(getClass().getSimpleName()); - sb.append('('); - sb.append(flags); - sb.append(','); - sb.append(serial); - sb.append(')'); - sb.append(' '); - sb.append('{'); - sb.append(' '); - if (headers.size() == 0) - sb.append('}'); - else { - for (Byte field : headers.keySet()) { - sb.append(getHeaderFieldName(field)); - sb.append('='); - sb.append('>'); - sb.append(headers.get(field).toString()); - sb.append(','); - sb.append(' '); - } - sb.setCharAt(sb.length() - 2, ' '); - sb.setCharAt(sb.length() - 1, '}'); - } - sb.append(' '); - sb.append('{'); - sb.append(' '); - Object[] args = null; - try { - args = getParameters(); - } catch (DBusException DBe) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); - } - if (null == args || 0 == args.length) - sb.append('}'); - else { - for (Object o : args) { - if (o instanceof Object[]) - sb.append(Arrays.deepToString((Object[]) o)); - else if (o instanceof byte[]) - sb.append(Arrays.toString((byte[]) o)); - else if (o instanceof int[]) - sb.append(Arrays.toString((int[]) o)); - else if (o instanceof short[]) - sb.append(Arrays.toString((short[]) o)); - else if (o instanceof long[]) - sb.append(Arrays.toString((long[]) o)); - else if (o instanceof boolean[]) - sb.append(Arrays.toString((boolean[]) o)); - else if (o instanceof double[]) - sb.append(Arrays.toString((double[]) o)); - else if (o instanceof float[]) - sb.append(Arrays.toString((float[]) o)); - else - sb.append(o.toString()); - sb.append(','); - sb.append(' '); - } - sb.setCharAt(sb.length() - 2, ' '); - sb.setCharAt(sb.length() - 1, '}'); - } - return sb.toString(); - } - - /** - * Returns the value of the header field of a given field. - * - * @param type The field to return. - * @return The value of the field or null if unset. - */ - public Object getHeader(byte type) { - return headers.get(type); - } - - /** - * Appends a value to the message. - * The type of the value is read from a D-Bus signature and used to marshall - * the value. - * - * @param sigb A buffer of the D-Bus signature. - * @param sigofs The offset into the signature corresponding to this value. - * @param data The value to marshall. - * @return The offset into the signature of the end of this value's type. - */ - @SuppressWarnings("unchecked") - private int appendone(byte[] sigb, int sigofs, Object data) throws DBusException { - try { - int i = sigofs; - if (Debug.debug) Debug.print(Debug.VERBOSE, (Object) bytecounter); - if (Debug.debug) Debug.print(Debug.VERBOSE, "Appending type: " + ((char) sigb[i]) + " value: " + data); - - // pad to the alignment of this type. - pad(sigb[i]); - switch (sigb[i]) { - case ArgumentType.BYTE: - appendByte(((Number) data).byteValue()); - break; - case ArgumentType.BOOLEAN: - appendint(((Boolean) data).booleanValue() ? 1 : 0, 4); - break; - case ArgumentType.DOUBLE: - long l = Double.doubleToLongBits(((Number) data).doubleValue()); - appendint(l, 8); - break; - case ArgumentType.FLOAT: - int rf = Float.floatToIntBits(((Number) data).floatValue()); - appendint(rf, 4); - break; - case ArgumentType.UINT32: - appendint(((Number) data).longValue(), 4); - break; - case ArgumentType.INT64: - appendint(((Number) data).longValue(), 8); - break; - case ArgumentType.UINT64: - if (big) { - appendint(((UInt64) data).top(), 4); - appendint(((UInt64) data).bottom(), 4); - } else { - appendint(((UInt64) data).bottom(), 4); - appendint(((UInt64) data).top(), 4); - } - break; - case ArgumentType.INT32: - appendint(((Number) data).intValue(), 4); - break; - case ArgumentType.UINT16: - appendint(((Number) data).intValue(), 2); - break; - case ArgumentType.INT16: - appendint(((Number) data).shortValue(), 2); - break; - case ArgumentType.STRING: - case ArgumentType.OBJECT_PATH: - // Strings are marshalled as a UInt32 with the length, - // followed by the String, followed by a null byte. - String payload = data.toString(); - byte[] payloadbytes = null; - try { - payloadbytes = payload.getBytes("UTF-8"); - } catch (UnsupportedEncodingException UEe) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(UEe); - throw new DBusException(getString("utf8NotSupported")); - } - if (Debug.debug) Debug.print(Debug.VERBOSE, "Appending String of length " + payloadbytes.length); - appendint(payloadbytes.length, 4); - appendBytes(payloadbytes); - appendBytes(padding[1]); - //pad(ArgumentType.STRING);? do we need this? - break; - case ArgumentType.SIGNATURE: - // Signatures are marshalled as a byte with the length, - // followed by the String, followed by a null byte. - // Signatures are generally short, so preallocate the array - // for the string, length and null byte. - if (data instanceof Type[]) - payload = Marshalling.getDBusType((Type[]) data); - else - payload = (String) data; - byte[] pbytes = payload.getBytes(); - preallocate(2 + pbytes.length); - appendByte((byte) pbytes.length); - appendBytes(pbytes); - appendByte((byte) 0); - break; - case ArgumentType.ARRAY: - // Arrays are given as a UInt32 for the length in bytes, - // padding to the element alignment, then elements in - // order. The length is the length from the end of the - // initial padding to the end of the last element. - if (Debug.debug) { - if (data instanceof Object[]) - Debug.print(Debug.VERBOSE, "Appending array: " + Arrays.deepToString((Object[]) data)); - } - - byte[] alen = new byte[4]; - appendBytes(alen); - pad(sigb[++i]); - long c = bytecounter; - - // optimise primatives - if (data.getClass().isArray() && - data.getClass().getComponentType().isPrimitive()) { - byte[] primbuf; - int algn = getAlignment(sigb[i]); - int len = Array.getLength(data); - switch (sigb[i]) { - case ArgumentType.BYTE: - primbuf = (byte[]) data; - break; - case ArgumentType.INT16: - case ArgumentType.INT32: - case ArgumentType.INT64: - primbuf = new byte[len * algn]; - for (int j = 0, k = 0; j < len; j++, k += algn) - marshallint(Array.getLong(data, j), primbuf, k, algn); - break; - case ArgumentType.BOOLEAN: - primbuf = new byte[len * algn]; - for (int j = 0, k = 0; j < len; j++, k += algn) - marshallint(Array.getBoolean(data, j) ? 1 : 0, primbuf, k, algn); - break; - case ArgumentType.DOUBLE: - primbuf = new byte[len * algn]; - if (data instanceof float[]) - for (int j = 0, k = 0; j < len; j++, k += algn) - marshallint(Double.doubleToRawLongBits(((float[]) data)[j]), - primbuf, k, algn); - else - for (int j = 0, k = 0; j < len; j++, k += algn) - marshallint(Double.doubleToRawLongBits(((double[]) data)[j]), - primbuf, k, algn); - break; - case ArgumentType.FLOAT: - primbuf = new byte[len * algn]; - for (int j = 0, k = 0; j < len; j++, k += algn) - marshallint( - Float.floatToRawIntBits(((float[]) data)[j]), - primbuf, k, algn); - break; - default: - throw new MarshallingException(getString("arraySentAsNonPrimitive")); - } - appendBytes(primbuf); - } else if (data instanceof List) { - Object[] contents = ((List) data).toArray(); - int diff = i; - ensureBuffers(contents.length * 4); - for (Object o : contents) - diff = appendone(sigb, i, o); - i = diff; - } else if (data instanceof Map) { - int diff = i; - ensureBuffers(((Map) data).size() * 6); - for (Map.Entry o : ((Map) data).entrySet()) - diff = appendone(sigb, i, o); - if (i == diff) { - // advance the type parser even on 0-size arrays. - Vector temp = new Vector(); - byte[] temp2 = new byte[sigb.length - diff]; - System.arraycopy(sigb, diff, temp2, 0, temp2.length); - String temp3 = new String(temp2); - int temp4 = Marshalling.getJavaType(temp3, temp, 1); - diff += temp4; - } - i = diff; - } else { - Object[] contents = (Object[]) data; - ensureBuffers(contents.length * 4); - int diff = i; - for (Object o : contents) - diff = appendone(sigb, i, o); - i = diff; - } - if (Debug.debug) - Debug.print(Debug.VERBOSE, "start: " + c + " end: " + bytecounter + " length: " + (bytecounter - c)); - marshallint(bytecounter - c, alen, 0, 4); - break; - case ArgumentType.STRUCT1: - // Structs are aligned to 8 bytes - // and simply contain each element marshalled in order - Object[] contents; - if (data instanceof Container) - contents = ((Container) data).getParameters(); - else - contents = (Object[]) data; - ensureBuffers(contents.length * 4); - int j = 0; - for (i++; sigb[i] != ArgumentType.STRUCT2; i++) - i = appendone(sigb, i, contents[j++]); - break; - case ArgumentType.DICT_ENTRY1: - // Dict entries are the same as structs. - if (data instanceof Map.Entry) { - i++; - i = appendone(sigb, i, ((Map.Entry) data).getKey()); - i++; - i = appendone(sigb, i, ((Map.Entry) data).getValue()); - i++; - } else { - contents = (Object[]) data; - j = 0; - for (i++; sigb[i] != ArgumentType.DICT_ENTRY2; i++) - i = appendone(sigb, i, contents[j++]); - } - break; - case ArgumentType.VARIANT: - // Variants are marshalled as a signature - // followed by the value. - if (data instanceof Variant) { - Variant var = (Variant) data; - appendone(new byte[]{ArgumentType.SIGNATURE}, 0, var.getSig()); - appendone((var.getSig()).getBytes(), 0, var.getValue()); - } else if (data instanceof Object[]) { - contents = (Object[]) data; - appendone(new byte[]{ArgumentType.SIGNATURE}, 0, contents[0]); - appendone(((String) contents[0]).getBytes(), 0, contents[1]); - } else { - String sig = Marshalling.getDBusType(data.getClass())[0]; - appendone(new byte[]{ArgumentType.SIGNATURE}, 0, sig); - appendone((sig).getBytes(), 0, data); - } - break; - } - return i; - } catch (ClassCastException CCe) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, CCe); - throw new MarshallingException(MessageFormat.format(getString("unconvertableType"), new Object[]{data.getClass().getName(), sigb[sigofs]})); - } - } - - /** - * Pad the message to the proper alignment for the given type. - */ - public void pad(byte type) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "padding for " + (char) type); - int a = getAlignment(type); - if (Debug.debug) Debug.print(Debug.VERBOSE, preallocated + " " + paofs + " " + bytecounter + " " + a); - int b = (int) ((bytecounter - preallocated) % a); - if (0 == b) return; - a = (a - b); - if (preallocated > 0) { - paofs += a; - preallocated -= a; - } else - appendBytes(padding[a]); - if (Debug.debug) Debug.print(Debug.VERBOSE, preallocated + " " + paofs + " " + bytecounter + " " + a); - } - - /** - * Return the alignment for a given type. - */ - public static int getAlignment(byte type) { - switch (type) { - case 2: - case ArgumentType.INT16: - case ArgumentType.UINT16: - return 2; - case 4: - case ArgumentType.BOOLEAN: - case ArgumentType.FLOAT: - case ArgumentType.INT32: - case ArgumentType.UINT32: - case ArgumentType.STRING: - case ArgumentType.OBJECT_PATH: - case ArgumentType.ARRAY: - return 4; - case 8: - case ArgumentType.INT64: - case ArgumentType.UINT64: - case ArgumentType.DOUBLE: - case ArgumentType.STRUCT: - case ArgumentType.DICT_ENTRY: - case ArgumentType.STRUCT1: - case ArgumentType.DICT_ENTRY1: - case ArgumentType.STRUCT2: - case ArgumentType.DICT_ENTRY2: - return 8; - case 1: - case ArgumentType.BYTE: - case ArgumentType.SIGNATURE: - case ArgumentType.VARIANT: - default: - return 1; - } - } - - /** - * Append a series of values to the message. - * - * @param sig The signature(s) of the value(s). - * @param data The value(s). - */ - public void append(String sig, Object... data) throws DBusException { - if (Debug.debug) Debug.print(Debug.DEBUG, "Appending sig: " + sig + " data: " + Arrays.deepToString(data)); - byte[] sigb = sig.getBytes(); - int j = 0; - for (int i = 0; i < sigb.length; i++) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Appending item: " + i + " " + ((char) sigb[i]) + " " + j); - i = appendone(sigb, i, data[j++]); - } - } - - /** - * Align a counter to the given type. - * - * @param current The current counter. - * @param type The type to align to. - * @return The new, aligned, counter. - */ - public int align(int current, byte type) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "aligning to " + (char) type); - int a = getAlignment(type); - if (0 == (current % a)) return current; - return current + (a - (current % a)); - } - - /** - * Demarshall one value from a buffer. - * - * @param sigb A buffer of the D-Bus signature. - * @param buf The buffer to demarshall from. - * @param ofs An array of two ints, the offset into the signature buffer - * and the offset into the data buffer. These values will be - * updated to the start of the next value ofter demarshalling. - * @param contained converts nested arrays to Lists - * @return The demarshalled value. - */ - private Object extractone(byte[] sigb, byte[] buf, int[] ofs, boolean contained) throws DBusException { - if (Debug.debug) - Debug.print(Debug.VERBOSE, "Extracting type: " + ((char) sigb[ofs[0]]) + " from offset " + ofs[1]); - Object rv = null; - ofs[1] = align(ofs[1], sigb[ofs[0]]); - switch (sigb[ofs[0]]) { - case ArgumentType.BYTE: - rv = buf[ofs[1]++]; - break; - case ArgumentType.UINT32: - rv = new UInt32(demarshallint(buf, ofs[1], 4)); - ofs[1] += 4; - break; - case ArgumentType.INT32: - rv = (int) demarshallint(buf, ofs[1], 4); - ofs[1] += 4; - break; - case ArgumentType.INT16: - rv = (short) demarshallint(buf, ofs[1], 2); - ofs[1] += 2; - break; - case ArgumentType.UINT16: - rv = new UInt16((int) demarshallint(buf, ofs[1], 2)); - ofs[1] += 2; - break; - case ArgumentType.INT64: - rv = demarshallint(buf, ofs[1], 8); - ofs[1] += 8; - break; - case ArgumentType.UINT64: - long top; - long bottom; - if (big) { - top = demarshallint(buf, ofs[1], 4); - ofs[1] += 4; - bottom = demarshallint(buf, ofs[1], 4); - } else { - bottom = demarshallint(buf, ofs[1], 4); - ofs[1] += 4; - top = demarshallint(buf, ofs[1], 4); - } - rv = new UInt64(top, bottom); - ofs[1] += 4; - break; - case ArgumentType.DOUBLE: - long l = demarshallint(buf, ofs[1], 8); - ofs[1] += 8; - rv = Double.longBitsToDouble(l); - break; - case ArgumentType.FLOAT: - int rf = (int) demarshallint(buf, ofs[1], 4); - ofs[1] += 4; - rv = Float.intBitsToFloat(rf); - break; - case ArgumentType.BOOLEAN: - rf = (int) demarshallint(buf, ofs[1], 4); - ofs[1] += 4; - rv = (1 == rf) ? Boolean.TRUE : Boolean.FALSE; - break; - case ArgumentType.ARRAY: - long size = demarshallint(buf, ofs[1], 4); - if (Debug.debug) Debug.print(Debug.VERBOSE, "Reading array of size: " + size); - ofs[1] += 4; - byte algn = (byte) getAlignment(sigb[++ofs[0]]); - ofs[1] = align(ofs[1], sigb[ofs[0]]); - int length = (int) (size / algn); - if (length > DBusConnection.MAX_ARRAY_LENGTH) - throw new MarshallingException(getString("arrayMustNotExceed") + DBusConnection.MAX_ARRAY_LENGTH); - // optimise primatives - switch (sigb[ofs[0]]) { - case ArgumentType.BYTE: - rv = new byte[length]; - System.arraycopy(buf, ofs[1], rv, 0, length); - ofs[1] += size; - break; - case ArgumentType.INT16: - rv = new short[length]; - for (int j = 0; j < length; j++, ofs[1] += algn) - ((short[]) rv)[j] = (short) demarshallint(buf, ofs[1], algn); - break; - case ArgumentType.INT32: - rv = new int[length]; - for (int j = 0; j < length; j++, ofs[1] += algn) - ((int[]) rv)[j] = (int) demarshallint(buf, ofs[1], algn); - break; - case ArgumentType.INT64: - rv = new long[length]; - for (int j = 0; j < length; j++, ofs[1] += algn) - ((long[]) rv)[j] = demarshallint(buf, ofs[1], algn); - break; - case ArgumentType.BOOLEAN: - rv = new boolean[length]; - for (int j = 0; j < length; j++, ofs[1] += algn) - ((boolean[]) rv)[j] = (1 == demarshallint(buf, ofs[1], algn)); - break; - case ArgumentType.FLOAT: - rv = new float[length]; - for (int j = 0; j < length; j++, ofs[1] += algn) - ((float[]) rv)[j] = - Float.intBitsToFloat((int) demarshallint(buf, ofs[1], algn)); - break; - case ArgumentType.DOUBLE: - rv = new double[length]; - for (int j = 0; j < length; j++, ofs[1] += algn) - ((double[]) rv)[j] = - Double.longBitsToDouble(demarshallint(buf, ofs[1], algn)); - break; - case ArgumentType.DICT_ENTRY1: - if (0 == size) { - // advance the type parser even on 0-size arrays. - Vector temp = new Vector(); - byte[] temp2 = new byte[sigb.length - ofs[0]]; - System.arraycopy(sigb, ofs[0], temp2, 0, temp2.length); - String temp3 = new String(temp2); - // ofs[0] gets incremented anyway. Leave one character on the stack - int temp4 = Marshalling.getJavaType(temp3, temp, 1) - 1; - ofs[0] += temp4; - if (Debug.debug) - Debug.print(Debug.VERBOSE, "Aligned type: " + temp3 + " " + temp4 + " " + ofs[0]); - } - int ofssave = ofs[0]; - long end = ofs[1] + size; - Vector entries = new Vector(); - while (ofs[1] < end) { - ofs[0] = ofssave; - entries.add((Object[]) extractone(sigb, buf, ofs, true)); - } - rv = new DBusMap(entries.toArray(new Object[0][])); - break; - default: - if (0 == size) { - // advance the type parser even on 0-size arrays. - Vector temp = new Vector(); - byte[] temp2 = new byte[sigb.length - ofs[0]]; - System.arraycopy(sigb, ofs[0], temp2, 0, temp2.length); - String temp3 = new String(temp2); - // ofs[0] gets incremented anyway. Leave one character on the stack - int temp4 = Marshalling.getJavaType(temp3, temp, 1) - 1; - ofs[0] += temp4; - if (Debug.debug) - Debug.print(Debug.VERBOSE, "Aligned type: " + temp3 + " " + temp4 + " " + ofs[0]); - } - ofssave = ofs[0]; - end = ofs[1] + size; - Vector contents = new Vector(); - while (ofs[1] < end) { - ofs[0] = ofssave; - contents.add(extractone(sigb, buf, ofs, true)); - } - rv = contents; - } - if (contained && !(rv instanceof List) && !(rv instanceof Map)) - rv = ArrayFrob.listify(rv); - break; - case ArgumentType.STRUCT1: - Vector contents = new Vector(); - while (sigb[++ofs[0]] != ArgumentType.STRUCT2) - contents.add(extractone(sigb, buf, ofs, true)); - rv = contents.toArray(); - break; - case ArgumentType.DICT_ENTRY1: - Object[] decontents = new Object[2]; - if (Debug.debug) - Debug.print(Debug.VERBOSE, "Extracting Dict Entry (" + Hexdump.toAscii(sigb, ofs[0], sigb.length - ofs[0]) + ") from: " + Hexdump.toHex(buf, ofs[1], buf.length - ofs[1])); - ofs[0]++; - decontents[0] = extractone(sigb, buf, ofs, true); - ofs[0]++; - decontents[1] = extractone(sigb, buf, ofs, true); - ofs[0]++; - rv = decontents; - break; - case ArgumentType.VARIANT: - int[] newofs = new int[]{0, ofs[1]}; - String sig = (String) extract(ArgumentType.SIGNATURE_STRING, buf, newofs)[0]; - newofs[0] = 0; - rv = new Variant(extract(sig, buf, newofs)[0], sig); - ofs[1] = newofs[1]; - break; - case ArgumentType.STRING: - length = (int) demarshallint(buf, ofs[1], 4); - ofs[1] += 4; - try { - rv = new String(buf, ofs[1], length, "UTF-8"); - } catch (UnsupportedEncodingException UEe) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(UEe); - throw new DBusException(getString("utf8NotSupported")); - } - ofs[1] += length + 1; - break; - case ArgumentType.OBJECT_PATH: - length = (int) demarshallint(buf, ofs[1], 4); - ofs[1] += 4; - rv = new ObjectPath(getSource(), new String(buf, ofs[1], length)); - ofs[1] += length + 1; - break; - case ArgumentType.SIGNATURE: - length = (buf[ofs[1]++] & 0xFF); - rv = new String(buf, ofs[1], length); - ofs[1] += length + 1; - break; - default: - throw new UnknownTypeCodeException(sigb[ofs[0]]); - } - if (Debug.debug) if (rv instanceof Object[]) - Debug.print(Debug.VERBOSE, "Extracted: " + Arrays.deepToString((Object[]) rv) + " (now at " + ofs[1] + ")"); - else - Debug.print(Debug.VERBOSE, "Extracted: " + rv + " (now at " + ofs[1] + ")"); - return rv; - } - - /** - * Demarshall values from a buffer. - * - * @param sig The D-Bus signature(s) of the value(s). - * @param buf The buffer to demarshall from. - * @param ofs The offset into the data buffer to start. - * @return The demarshalled value(s). - */ - public Object[] extract(String sig, byte[] buf, int ofs) throws DBusException { - return extract(sig, buf, new int[]{0, ofs}); - } - - /** - * Demarshall values from a buffer. - * - * @param sig The D-Bus signature(s) of the value(s). - * @param buf The buffer to demarshall from. - * @param ofs An array of two ints, the offset into the signature - * and the offset into the data buffer. These values will be - * updated to the start of the next value ofter demarshalling. - * @return The demarshalled value(s). - */ - public Object[] extract(String sig, byte[] buf, int[] ofs) throws DBusException { - if (Debug.debug) - Debug.print(Debug.VERBOSE, "extract(" + sig + ",#" + buf.length + ", {" + ofs[0] + "," + ofs[1] + "}"); - Vector rv = new Vector(); - byte[] sigb = sig.getBytes(); - for (int[] i = ofs; i[0] < sigb.length; i[0]++) { - rv.add(extractone(sigb, buf, i, false)); - } - return rv.toArray(); - } - - /** - * Returns the Bus ID that sent the message. - */ - public String getSource() { - return (String) headers.get(HeaderField.SENDER); - } - - /** - * Returns the destination of the message. - */ - public String getDestination() { - return (String) headers.get(HeaderField.DESTINATION); - } - - /** - * Returns the interface of the message. - */ - public String getInterface() { - return (String) headers.get(HeaderField.INTERFACE); - } - - /** - * Returns the object path of the message. - */ - public String getPath() { - Object o = headers.get(HeaderField.PATH); - if (null == o) return null; - return o.toString(); - } - - /** - * Returns the member name or error name this message represents. - */ - public String getName() { - if (this instanceof Error) - return (String) headers.get(HeaderField.ERROR_NAME); - else - return (String) headers.get(HeaderField.MEMBER); - } - - /** - * Returns the dbus signature of the parameters. - */ - public String getSig() { - return (String) headers.get(HeaderField.SIGNATURE); - } - - /** - * Returns the message flags. - */ - public int getFlags() { - return flags; - } - - /** - * Returns the message serial ID (unique for this connection) - * - * @return the message serial. - */ - public long getSerial() { - return serial; - } - - /** - * If this is a reply to a message, this returns its serial. - * - * @return The reply serial, or 0 if it is not a reply. - */ - public long getReplySerial() { - Number l = (Number) headers.get(HeaderField.REPLY_SERIAL); - if (null == l) return 0; - return l.longValue(); - } - - /** - * Parses and returns the parameters to this message as an Object array. - */ - public Object[] getParameters() throws DBusException { - if (null == args && null != body) { - String sig = (String) headers.get(HeaderField.SIGNATURE); - if (null != sig && 0 != body.length) { - args = extract(sig, body, 0); - } else args = new Object[0]; - } - return args; - } - - protected void setArgs(Object[] args) { - this.args = args; - } - - /** - * Warning, do not use this method unless you really know what you are doing. - */ - public void setSource(String source) throws DBusException { - if (null != body) { - wiredata = new byte[BUFFERINCREMENT][]; - bufferuse = 0; - bytecounter = 0; - preallocate(12); - append("yyyyuu", big ? Endian.BIG : Endian.LITTLE, type, flags, protover, bodylen, serial); - headers.put(HeaderField.SENDER, source); - Object[][] newhead = new Object[headers.size()][]; - int i = 0; - for (Byte b : headers.keySet()) { - newhead[i] = new Object[2]; - newhead[i][0] = b; - newhead[i][1] = headers.get(b); - i++; - } - append("a(yv)", (Object) newhead); - pad((byte) 8); - appendBytes(body); - } - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/MessageReader.java b/federation/sssd/src/main/java/org/freedesktop/dbus/MessageReader.java deleted file mode 100644 index 4dbea003c9..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/MessageReader.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import cx.ath.matthew.debug.Debug; -import cx.ath.matthew.utils.Hexdump; -import org.freedesktop.dbus.exceptions.DBusException; -import org.freedesktop.dbus.exceptions.MessageProtocolVersionException; -import org.freedesktop.dbus.exceptions.MessageTypeException; - -import java.io.BufferedInputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.net.SocketTimeoutException; -import java.text.MessageFormat; - -import static org.freedesktop.dbus.Gettext.getString; - -public class MessageReader { - private InputStream in; - private byte[] buf = null; - private byte[] tbuf = null; - private byte[] header = null; - private byte[] body = null; - private int[] len = new int[4]; - - public MessageReader(InputStream in) { - this.in = new BufferedInputStream(in); - } - - public Message readMessage() throws IOException, DBusException { - int rv; - /* Read the 12 byte fixed header, retrying as neccessary */ - if (null == buf) { - buf = new byte[12]; - len[0] = 0; - } - if (len[0] < 12) { - try { - rv = in.read(buf, len[0], 12 - len[0]); - } catch (SocketTimeoutException STe) { - return null; - } - if (-1 == rv) throw new EOFException(getString("transportReturnedEOF")); - len[0] += rv; - } - if (len[0] == 0) return null; - if (len[0] < 12) { - if (Debug.debug) Debug.print(Debug.DEBUG, "Only got " + len[0] + " of 12 bytes of header"); - return null; - } - - /* Parse the details from the header */ - byte endian = buf[0]; - byte type = buf[1]; - byte protover = buf[3]; - if (protover > Message.PROTOCOL) { - buf = null; - throw new MessageProtocolVersionException(MessageFormat.format(getString("protocolVersionUnsupported"), new Object[]{protover})); - } - - /* Read the length of the variable header */ - if (null == tbuf) { - tbuf = new byte[4]; - len[1] = 0; - } - if (len[1] < 4) { - try { - rv = in.read(tbuf, len[1], 4 - len[1]); - } catch (SocketTimeoutException STe) { - return null; - } - if (-1 == rv) throw new EOFException(getString("transportReturnedEOF")); - len[1] += rv; - } - if (len[1] < 4) { - if (Debug.debug) Debug.print(Debug.DEBUG, "Only got " + len[1] + " of 4 bytes of header"); - return null; - } - - /* Parse the variable header length */ - int headerlen = 0; - if (null == header) { - headerlen = (int) Message.demarshallint(tbuf, 0, endian, 4); - if (0 != headerlen % 8) - headerlen += 8 - (headerlen % 8); - } else - headerlen = header.length - 8; - - /* Read the variable header */ - if (null == header) { - header = new byte[headerlen + 8]; - System.arraycopy(tbuf, 0, header, 0, 4); - len[2] = 0; - } - if (len[2] < headerlen) { - try { - rv = in.read(header, 8 + len[2], headerlen - len[2]); - } catch (SocketTimeoutException STe) { - return null; - } - if (-1 == rv) throw new EOFException(getString("transportReturnedEOF")); - len[2] += rv; - } - if (len[2] < headerlen) { - if (Debug.debug) Debug.print(Debug.DEBUG, "Only got " + len[2] + " of " + headerlen + " bytes of header"); - return null; - } - - /* Read the body */ - int bodylen = 0; - if (null == body) bodylen = (int) Message.demarshallint(buf, 4, endian, 4); - if (null == body) { - body = new byte[bodylen]; - len[3] = 0; - } - if (len[3] < body.length) { - try { - rv = in.read(body, len[3], body.length - len[3]); - } catch (SocketTimeoutException STe) { - return null; - } - if (-1 == rv) throw new EOFException(getString("transportReturnedEOF")); - len[3] += rv; - } - if (len[3] < body.length) { - if (Debug.debug) Debug.print(Debug.DEBUG, "Only got " + len[3] + " of " + body.length + " bytes of body"); - return null; - } - - Message m; - switch (type) { - case Message.MessageType.METHOD_CALL: - m = new MethodCall(); - break; - case Message.MessageType.METHOD_RETURN: - m = new MethodReturn(); - break; - case Message.MessageType.SIGNAL: - m = new DBusSignal(); - break; - case Message.MessageType.ERROR: - m = new Error(); - break; - default: - throw new MessageTypeException(MessageFormat.format(getString("messageTypeUnsupported"), new Object[]{type})); - } - if (Debug.debug) { - Debug.print(Debug.VERBOSE, Hexdump.format(buf)); - Debug.print(Debug.VERBOSE, Hexdump.format(tbuf)); - Debug.print(Debug.VERBOSE, Hexdump.format(header)); - Debug.print(Debug.VERBOSE, Hexdump.format(body)); - } - try { - m.populate(buf, header, body); - } catch (DBusException DBe) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); - buf = null; - tbuf = null; - body = null; - header = null; - throw DBe; - } catch (RuntimeException Re) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, Re); - buf = null; - tbuf = null; - body = null; - header = null; - throw Re; - } - if (Debug.debug) { - Debug.print(Debug.INFO, "=> " + m); - } - buf = null; - tbuf = null; - body = null; - header = null; - return m; - } - - public void close() throws IOException { - if (Debug.debug) Debug.print(Debug.INFO, "Closing Message Reader"); - in.close(); - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/MessageWriter.java b/federation/sssd/src/main/java/org/freedesktop/dbus/MessageWriter.java deleted file mode 100644 index 45e8cb75f5..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/MessageWriter.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import cx.ath.matthew.debug.Debug; -import cx.ath.matthew.unix.USOutputStream; -import cx.ath.matthew.utils.Hexdump; - -import java.io.BufferedOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -public class MessageWriter { - private OutputStream out; - private boolean isunix; - - public MessageWriter(OutputStream out) { - this.out = out; - this.isunix = false; - try { - if (out instanceof USOutputStream) - this.isunix = true; - } catch (Throwable t) { - } - if (!this.isunix) - this.out = new BufferedOutputStream(this.out); - } - - public void writeMessage(Message m) throws IOException { - if (Debug.debug) { - Debug.print(Debug.INFO, "<= " + m); - } - if (null == m) return; - if (null == m.getWireData()) { - if (Debug.debug) Debug.print(Debug.WARN, "Message " + m + " wire-data was null!"); - return; - } - if (isunix) { - if (Debug.debug) { - Debug.print(Debug.DEBUG, "Writing all " + m.getWireData().length + " buffers simultaneously to Unix Socket"); - for (byte[] buf : m.getWireData()) - Debug.print(Debug.VERBOSE, "(" + buf + "):" + (null == buf ? "" : Hexdump.format(buf))); - } - ((USOutputStream) out).write(m.getWireData()); - } else - for (byte[] buf : m.getWireData()) { - if (Debug.debug) - Debug.print(Debug.VERBOSE, "(" + buf + "):" + (null == buf ? "" : Hexdump.format(buf))); - if (null == buf) break; - out.write(buf); - } - out.flush(); - } - - public void close() throws IOException { - if (Debug.debug) Debug.print(Debug.INFO, "Closing Message Writer"); - out.close(); - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/MethodCall.java b/federation/sssd/src/main/java/org/freedesktop/dbus/MethodCall.java deleted file mode 100644 index 72242ccb9a..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/MethodCall.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import cx.ath.matthew.debug.Debug; -import cx.ath.matthew.utils.Hexdump; -import org.freedesktop.dbus.exceptions.DBusException; -import org.freedesktop.dbus.exceptions.MessageFormatException; - -import java.util.Vector; - -import static org.freedesktop.dbus.Gettext.getString; - -public class MethodCall extends Message { - MethodCall() { - } - - public MethodCall(String dest, String path, String iface, String member, byte flags, String sig, Object... args) throws DBusException { - this(null, dest, path, iface, member, flags, sig, args); - } - - public MethodCall(String source, String dest, String path, String iface, String member, byte flags, String sig, Object... args) throws DBusException { - super(Message.Endian.BIG, Message.MessageType.METHOD_CALL, flags); - - if (null == member || null == path) - throw new MessageFormatException(getString("missingDestinationPathFunction")); - headers.put(Message.HeaderField.PATH, path); - headers.put(Message.HeaderField.MEMBER, member); - - Vector hargs = new Vector(); - - hargs.add(new Object[]{Message.HeaderField.PATH, new Object[]{ArgumentType.OBJECT_PATH_STRING, path}}); - - if (null != source) { - headers.put(Message.HeaderField.SENDER, source); - hargs.add(new Object[]{Message.HeaderField.SENDER, new Object[]{ArgumentType.STRING_STRING, source}}); - } - - if (null != dest) { - headers.put(Message.HeaderField.DESTINATION, dest); - hargs.add(new Object[]{Message.HeaderField.DESTINATION, new Object[]{ArgumentType.STRING_STRING, dest}}); - } - - if (null != iface) { - hargs.add(new Object[]{Message.HeaderField.INTERFACE, new Object[]{ArgumentType.STRING_STRING, iface}}); - headers.put(Message.HeaderField.INTERFACE, iface); - } - - hargs.add(new Object[]{Message.HeaderField.MEMBER, new Object[]{ArgumentType.STRING_STRING, member}}); - - if (null != sig) { - if (Debug.debug) Debug.print(Debug.DEBUG, "Appending arguments with signature: " + sig); - hargs.add(new Object[]{Message.HeaderField.SIGNATURE, new Object[]{ArgumentType.SIGNATURE_STRING, sig}}); - headers.put(Message.HeaderField.SIGNATURE, sig); - setArgs(args); - } - - byte[] blen = new byte[4]; - appendBytes(blen); - append("ua(yv)", serial, hargs.toArray()); - pad((byte) 8); - - long c = bytecounter; - if (null != sig) append(sig, args); - if (Debug.debug) - Debug.print(Debug.DEBUG, "Appended body, type: " + sig + " start: " + c + " end: " + bytecounter + " size: " + (bytecounter - c)); - marshallint(bytecounter - c, blen, 0, 4); - if (Debug.debug) Debug.print("marshalled size (" + blen + "): " + Hexdump.format(blen)); - } - - private static long REPLY_WAIT_TIMEOUT = 20000; - - /** - * Set the default timeout for method calls. - * Default is 20s. - * - * @param timeout New timeout in ms. - */ - public static void setDefaultTimeout(long timeout) { - REPLY_WAIT_TIMEOUT = timeout; - } - - Message reply = null; - - public synchronized boolean hasReply() { - return null != reply; - } - - /** - * Block (if neccessary) for a reply. - * - * @param timeout The length of time to block before timing out (ms). - * @return The reply to this MethodCall, or null if a timeout happens. - */ - public synchronized Message getReply(long timeout) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Blocking on " + this); - if (null != reply) return reply; - try { - wait(timeout); - return reply; - } catch (InterruptedException Ie) { - return reply; - } - } - - /** - * Block (if neccessary) for a reply. - * Default timeout is 20s, or can be configured with setDefaultTimeout() - * - * @return The reply to this MethodCall, or null if a timeout happens. - */ - public synchronized Message getReply() { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Blocking on " + this); - if (null != reply) return reply; - try { - wait(REPLY_WAIT_TIMEOUT); - return reply; - } catch (InterruptedException Ie) { - return reply; - } - } - - protected synchronized void setReply(Message reply) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Setting reply to " + this + " to " + reply); - this.reply = reply; - notifyAll(); - } - -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/MethodReturn.java b/federation/sssd/src/main/java/org/freedesktop/dbus/MethodReturn.java deleted file mode 100644 index 35f4d0d745..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/MethodReturn.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import org.freedesktop.dbus.exceptions.DBusException; - -import java.util.Vector; - -public class MethodReturn extends Message { - MethodReturn() { - } - - public MethodReturn(String dest, long replyserial, String sig, Object... args) throws DBusException { - this(null, dest, replyserial, sig, args); - } - - public MethodReturn(String source, String dest, long replyserial, String sig, Object... args) throws DBusException { - super(Message.Endian.BIG, Message.MessageType.METHOD_RETURN, (byte) 0); - - headers.put(Message.HeaderField.REPLY_SERIAL, replyserial); - - Vector hargs = new Vector(); - hargs.add(new Object[]{Message.HeaderField.REPLY_SERIAL, new Object[]{ArgumentType.UINT32_STRING, replyserial}}); - - if (null != source) { - headers.put(Message.HeaderField.SENDER, source); - hargs.add(new Object[]{Message.HeaderField.SENDER, new Object[]{ArgumentType.STRING_STRING, source}}); - } - - if (null != dest) { - headers.put(Message.HeaderField.DESTINATION, dest); - hargs.add(new Object[]{Message.HeaderField.DESTINATION, new Object[]{ArgumentType.STRING_STRING, dest}}); - } - - if (null != sig) { - hargs.add(new Object[]{Message.HeaderField.SIGNATURE, new Object[]{ArgumentType.SIGNATURE_STRING, sig}}); - headers.put(Message.HeaderField.SIGNATURE, sig); - setArgs(args); - } - - byte[] blen = new byte[4]; - appendBytes(blen); - append("ua(yv)", serial, hargs.toArray()); - pad((byte) 8); - - long c = bytecounter; - if (null != sig) append(sig, args); - marshallint(bytecounter - c, blen, 0, 4); - } - - public MethodReturn(MethodCall mc, String sig, Object... args) throws DBusException { - this(null, mc, sig, args); - } - - public MethodReturn(String source, MethodCall mc, String sig, Object... args) throws DBusException { - this(source, mc.getSource(), mc.getSerial(), sig, args); - this.call = mc; - } - - MethodCall call; - - public MethodCall getCall() { - return call; - } - - protected void setCall(MethodCall call) { - this.call = call; - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/MethodTuple.java b/federation/sssd/src/main/java/org/freedesktop/dbus/MethodTuple.java index d135714edd..2c92d53673 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/MethodTuple.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/MethodTuple.java @@ -1,37 +1,52 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; -import cx.ath.matthew.debug.Debug; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -class MethodTuple { - String name; - String sig; +import java.util.Objects; - public MethodTuple(String name, String sig) { - this.name = name; - if (null != sig) - this.sig = sig; - else - this.sig = ""; - if (Debug.debug) Debug.print(Debug.VERBOSE, "new MethodTuple(" + this.name + ", " + this.sig + ")"); - } - - public boolean equals(Object o) { - return o.getClass().equals(MethodTuple.class) - && ((MethodTuple) o).name.equals(this.name) - && ((MethodTuple) o).sig.equals(this.sig); +public class MethodTuple { + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final String name; + private final String sig; + + public MethodTuple(String _name, String _sig) { + name = _name; + if (null != _sig) { + sig = _sig; + } else { + sig = ""; + } + logger.trace("new MethodTuple({}, {})", name, sig); } + @Override public int hashCode() { - return name.hashCode() + sig.hashCode(); + return Objects.hash(name, sig); + } + + @Override + public boolean equals(Object _obj) { + if (this == _obj) { + return true; + } + if (!(_obj instanceof MethodTuple)) { + return false; + } + MethodTuple other = (MethodTuple) _obj; + return Objects.equals(name, other.name) && Objects.equals(sig, other.sig); + } + + public Logger getLogger() { + return logger; + } + + public String getName() { + return name; + } + + public String getSig() { + return sig; } } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/ObjectPath.java b/federation/sssd/src/main/java/org/freedesktop/dbus/ObjectPath.java index 5ea11583c3..53e86b1d30 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/ObjectPath.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/ObjectPath.java @@ -1,22 +1,19 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; -class ObjectPath extends Path { - public String source; +public class ObjectPath extends DBusPath { + private String source; - // public DBusConnection conn; - public ObjectPath(String source, String path/*, DBusConnection conn*/) { - super(path); - this.source = source; - // this.conn = conn; + public ObjectPath(String _source, String _path) { + super(_path); + this.source = _source; } + + public String getSource() { + return source; + } + + public void setSource(String _source) { + source = _source; + } + } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/ObjectTree.java b/federation/sssd/src/main/java/org/freedesktop/dbus/ObjectTree.java deleted file mode 100644 index 0332e6a33f..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/ObjectTree.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import cx.ath.matthew.debug.Debug; - -import java.util.regex.Pattern; - -/** - * Keeps track of the exported objects for introspection data - */ -class ObjectTree { - class TreeNode { - String name; - ExportedObject object; - String data; - TreeNode right; - TreeNode down; - - public TreeNode(String name) { - this.name = name; - } - - public TreeNode(String name, ExportedObject object, String data) { - this.name = name; - this.object = object; - this.data = data; - } - } - - private TreeNode root; - - public ObjectTree() { - root = new TreeNode(""); - } - - public static final Pattern slashpattern = Pattern.compile("/"); - - private TreeNode recursiveFind(TreeNode current, String path) { - if ("/".equals(path)) return current; - String[] elements = path.split("/", 2); - // this is us or a parent node - if (path.startsWith(current.name)) { - // this is us - if (path.equals(current.name)) { - return current; - } - // recurse down - else { - if (current.down == null) - return null; - else return recursiveFind(current.down, elements[1]); - } - } else if (current.right == null) { - return null; - } else if (0 > current.right.name.compareTo(elements[0])) { - return null; - } - // recurse right - else { - return recursiveFind(current.right, path); - } - } - - private TreeNode recursiveAdd(TreeNode current, String path, ExportedObject object, String data) { - String[] elements = slashpattern.split(path, 2); - // this is us or a parent node - if (path.startsWith(current.name)) { - // this is us - if (1 == elements.length || "".equals(elements[1])) { - current.object = object; - current.data = data; - } - // recurse down - else { - if (current.down == null) { - String[] el = elements[1].split("/", 2); - current.down = new TreeNode(el[0]); - } - current.down = recursiveAdd(current.down, elements[1], object, data); - } - } - // need to create a new sub-tree on the end - else if (current.right == null) { - current.right = new TreeNode(elements[0]); - current.right = recursiveAdd(current.right, path, object, data); - } - // need to insert here - else if (0 > current.right.name.compareTo(elements[0])) { - TreeNode t = new TreeNode(elements[0]); - t.right = current.right; - current.right = t; - current.right = recursiveAdd(current.right, path, object, data); - } - // recurse right - else { - current.right = recursiveAdd(current.right, path, object, data); - } - return current; - } - - public void add(String path, ExportedObject object, String data) { - if (Debug.debug) Debug.print(Debug.DEBUG, "Adding " + path + " to object tree"); - root = recursiveAdd(root, path, object, data); - } - - public void remove(String path) { - if (Debug.debug) Debug.print(Debug.DEBUG, "Removing " + path + " from object tree"); - TreeNode t = recursiveFind(root, path); - t.object = null; - t.data = null; - } - - public String Introspect(String path) { - TreeNode t = recursiveFind(root, path); - if (null == t) return null; - StringBuilder sb = new StringBuilder(); - sb.append("\n"); - if (null != t.data) sb.append(t.data); - t = t.down; - while (null != t) { - sb.append("\n"); - t = t.right; - } - sb.append(""); - return sb.toString(); - } - - private String recursivePrint(TreeNode current) { - String s = ""; - if (null != current) { - s += current.name; - if (null != current.object) - s += "*"; - if (null != current.down) - s += "/{" + recursivePrint(current.down) + "}"; - if (null != current.right) - s += ", " + recursivePrint(current.right); - } - return s; - } - - public String toString() { - return recursivePrint(root); - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Path.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Path.java deleted file mode 100644 index 91a5a6d716..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/Path.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -public class Path implements Comparable { - protected String path; - - public Path(String path) { - this.path = path; - } - - public String getPath() { - return path; - } - - public String toString() { - return path; - } - - public boolean equals(Object other) { - return (other instanceof Path) && path.equals(((Path) other).path); - } - - public int hashCode() { - return path.hashCode(); - } - - public int compareTo(Path that) { - return path.compareTo(that.path); - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Position.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Position.java deleted file mode 100644 index ed0bb11652..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/Position.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Position annotation, to annotate Struct fields - * to be sent over DBus. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -public @interface Position { - /** - * The order of this field in the Struct. - */ - int value(); -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/RemoteInvocationHandler.java b/federation/sssd/src/main/java/org/freedesktop/dbus/RemoteInvocationHandler.java index f6b7867859..f2d96e9b47 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/RemoteInvocationHandler.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/RemoteInvocationHandler.java @@ -1,187 +1,216 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; -import cx.ath.matthew.debug.Debug; -import org.freedesktop.DBus; +import org.freedesktop.dbus.annotations.MethodNoReply; +import org.freedesktop.dbus.connections.AbstractConnection; +import org.freedesktop.dbus.errors.Error; +import org.freedesktop.dbus.errors.NoReply; import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.freedesktop.dbus.exceptions.NotConnected; +import org.freedesktop.dbus.interfaces.CallbackHandler; +import org.freedesktop.dbus.interfaces.DBusInterface; +import org.freedesktop.dbus.messages.Message; +import org.freedesktop.dbus.messages.MethodCall; +import org.freedesktop.dbus.utils.DBusNamingUtil; +import org.freedesktop.dbus.utils.LoggingHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.Type; -import java.text.MessageFormat; import java.util.Arrays; -import static org.freedesktop.dbus.Gettext.getString; - -class RemoteInvocationHandler implements InvocationHandler { - public static final int CALL_TYPE_SYNC = 0; - public static final int CALL_TYPE_ASYNC = 1; +public class RemoteInvocationHandler implements InvocationHandler { + public static final int CALL_TYPE_SYNC = 0; + public static final int CALL_TYPE_ASYNC = 1; public static final int CALL_TYPE_CALLBACK = 2; - public static Object convertRV(String sig, Object[] rp, Method m, AbstractConnection conn) throws DBusException { - Class c = m.getReturnType(); + private static final Logger LOGGER = LoggerFactory.getLogger(RemoteInvocationHandler.class); + // CHECKSTYLE:OFF + AbstractConnection conn; + RemoteObject remote; + // CHECKSTYLE:ON - if (null == rp) { - if (null == c || Void.TYPE.equals(c)) return null; - else throw new DBusExecutionException(getString("voidReturnType")); + public RemoteInvocationHandler(AbstractConnection _conn, RemoteObject _remote) { + this.remote = _remote; + this.conn = _conn; + } + + public RemoteObject getRemote() { + return remote; + } + + @Override + public Object invoke(Object _proxy, Method _method, Object[] _args) throws Throwable { + if (_method.getName().equals("isRemote")) { + return true; + } else if (_method.getName().equals("getObjectPath")) { + return remote.getObjectPath(); + } else if (_method.getName().equals("clone")) { + return null; + } else if (_method.getName().equals("equals")) { + try { + if (1 == _args.length) { + return Boolean.valueOf(_args[0] != null && remote.equals(((RemoteInvocationHandler) Proxy.getInvocationHandler(_args[0])).remote)); + } + } catch (IllegalArgumentException _exIa) { + return Boolean.FALSE; + } + } else if (_method.getName().equals("finalize")) { + return null; + } else if (_method.getName().equals("getClass")) { + return DBusInterface.class; + } else if (_method.getName().equals("hashCode")) { + return remote.hashCode(); + } else if (_method.getName().equals("notify")) { + remote.notify(); + return null; + } else if (_method.getName().equals("notifyAll")) { + remote.notifyAll(); + return null; + } else if (_method.getName().equals("wait")) { + if (0 == _args.length) { + remote.wait(); + } else if (1 == _args.length && _args[0] instanceof Long) { + remote.wait((Long) _args[0]); + } else if (2 == _args.length && _args[0] instanceof Long && _args[1] instanceof Integer) { + remote.wait((Long) _args[0], (Integer) _args[1]); + } + if (_args.length <= 2) { + return null; + } + } else if (_method.getName().equals("toString")) { + return remote.toString(); + } + + return executeRemoteMethod(remote, _method, conn, CALL_TYPE_SYNC, null, _args); + } + + // CHECKSTYLE:ON + + public static Object convertRV(String _sig, Object[] _rp, Method _m, AbstractConnection _conn) throws DBusException { + Class c = _m.getReturnType(); + Object[] rp = _rp; + if (rp == null) { + if (null == c || Void.TYPE.equals(c)) { + return null; + } else { + throw new DBusException("Wrong return type (got void, expected a value)"); + } } else { try { - if (Debug.debug) - Debug.print(Debug.VERBOSE, "Converting return parameters from " + Arrays.deepToString(rp) + " to type " + m.getGenericReturnType()); - rp = Marshalling.deSerializeParameters(rp, - new Type[]{m.getGenericReturnType()}, conn); - } catch (Exception e) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - throw new DBusExecutionException(MessageFormat.format(getString("invalidReturnType"), new Object[]{e.getMessage()})); + LoggingHelper.logIf(LOGGER.isTraceEnabled(), () -> LOGGER.trace("Converting return parameters from {} to type {}", + Arrays.deepToString(_rp), _m.getGenericReturnType())); + + rp = Marshalling.deSerializeParameters(rp, new Type[] { + _m.getGenericReturnType() + }, _conn); + } catch (Exception _ex) { + LOGGER.debug("Wrong return type.", _ex); + throw new DBusException(String.format("Wrong return type (failed to de-serialize correct types: %s )", _ex.getMessage())); } } switch (rp.length) { - case 0: - if (null == c || Void.TYPE.equals(c)) - return null; - else throw new DBusExecutionException(getString("voidReturnType")); - case 1: - return rp[0]; - default: + case 0: + if (null == c || Void.TYPE.equals(c)) { + return null; + } else { + throw new DBusException("Wrong return type (got void, expected a value)"); + } + case 1: + return rp[0]; + default: - // check we are meant to return multiple values - if (!Tuple.class.isAssignableFrom(c)) - throw new DBusExecutionException(getString("tupleReturnType")); + // check we are meant to return multiple values + if (!Tuple.class.isAssignableFrom(c)) { + throw new DBusException("Wrong return type (not expecting Tuple)"); + } - Constructor cons = c.getConstructors()[0]; - try { - return cons.newInstance(rp); - } catch (Exception e) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - throw new DBusException(e.getMessage()); - } + Constructor cons = c.getConstructors()[0]; + try { + return cons.newInstance(rp); + } catch (Exception _ex) { + LOGGER.debug("", _ex); + throw new DBusException(_ex.getMessage()); + } } } - @SuppressWarnings("unchecked") - public static Object executeRemoteMethod(RemoteObject ro, Method m, AbstractConnection conn, int syncmethod, CallbackHandler callback, Object... args) throws DBusExecutionException { - Type[] ts = m.getGenericParameterTypes(); + public static Object executeRemoteMethod(RemoteObject _ro, Method _m, AbstractConnection _conn, int _syncmethod, CallbackHandler _callback, Object... _args) throws DBusException { + Type[] ts = _m.getGenericParameterTypes(); String sig = null; - if (ts.length > 0) try { - sig = Marshalling.getDBusType(ts); - args = Marshalling.convertParameters(args, ts, conn); - } catch (DBusException DBe) { - throw new DBusExecutionException(getString("contructDBusTypeFailure") + DBe.getMessage()); + Object[] args = _args; + if (ts.length > 0) { + try { + sig = Marshalling.getDBusType(ts); + args = Marshalling.convertParameters(args, ts, _conn); + } catch (DBusException _ex) { + throw new DBusExecutionException("Failed to construct D-Bus type: " + _ex.getMessage()); + } } MethodCall call; byte flags = 0; - if (!ro.autostart) flags |= Message.Flags.NO_AUTO_START; - if (syncmethod == CALL_TYPE_ASYNC) flags |= Message.Flags.ASYNC; - if (m.isAnnotationPresent(DBus.Method.NoReply.class)) flags |= Message.Flags.NO_REPLY_EXPECTED; - try { - String name; - if (m.isAnnotationPresent(DBusMemberName.class)) - name = m.getAnnotation(DBusMemberName.class).value(); - else - name = m.getName(); - if (null == ro.iface) - call = new MethodCall(ro.busname, ro.objectpath, null, name, flags, sig, args); - else { - if (null != ro.iface.getAnnotation(DBusInterfaceName.class)) { - call = new MethodCall(ro.busname, ro.objectpath, ro.iface.getAnnotation(DBusInterfaceName.class).value(), name, flags, sig, args); - } else - call = new MethodCall(ro.busname, ro.objectpath, AbstractConnection.dollar_pattern.matcher(ro.iface.getName()).replaceAll("."), name, flags, sig, args); - } - } catch (DBusException DBe) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); - throw new DBusExecutionException(getString("constructOutgoingMethodCallFailure") + DBe.getMessage()); + if (!_ro.isAutostart()) { + flags |= Message.Flags.NO_AUTO_START; + } + if (_syncmethod == CALL_TYPE_ASYNC) { + flags |= Message.Flags.ASYNC; + } + if (_m.isAnnotationPresent(MethodNoReply.class)) { + flags |= Message.Flags.NO_REPLY_EXPECTED; + } + try { + String name = DBusNamingUtil.getMethodName(_m); + if (null == _ro.getInterface()) { + call = new MethodCall(_ro.getBusName(), _ro.getObjectPath(), null, name, flags, sig, args); + } else { + String iface = DBusNamingUtil.getInterfaceName(_ro.getInterface()); + call = new MethodCall(_ro.getBusName(), _ro.getObjectPath(), iface, name, flags, sig, args); + } + } catch (DBusException _ex) { + LOGGER.debug("Failed to construct outgoing method call.", _ex); + throw new DBusExecutionException("Failed to construct outgoing method call: " + _ex.getMessage()); + } + if (!_conn.isConnected()) { + throw new NotConnected("Not Connected"); } - if (null == conn.outgoing) throw new NotConnected(getString("notConnected")); - switch (syncmethod) { + switch (_syncmethod) { case CALL_TYPE_ASYNC: - conn.queueOutgoing(call); - return new DBusAsyncReply(call, m, conn); + _conn.sendMessage(call); + return new DBusAsyncReply<>(call, _m, _conn); case CALL_TYPE_CALLBACK: - synchronized (conn.pendingCallbacks) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Queueing Callback " + callback + " for " + call); - conn.pendingCallbacks.put(call, callback); - conn.pendingCallbackReplys.put(call, new DBusAsyncReply(call, m, conn)); - } - conn.queueOutgoing(call); + _conn.queueCallback(call, _m, _callback); + _conn.sendMessage(call); return null; case CALL_TYPE_SYNC: - conn.queueOutgoing(call); + _conn.sendMessage(call); break; } // get reply - if (m.isAnnotationPresent(DBus.Method.NoReply.class)) return null; + if (_m.isAnnotationPresent(MethodNoReply.class)) { + return null; + } Message reply = call.getReply(); - if (null == reply) throw new DBus.Error.NoReply(getString("notReplyWithSpecifiedTime")); + if (null == reply) { + throw new NoReply("No reply within specified time"); + } - if (reply instanceof Error) + if (reply instanceof Error) { ((Error) reply).throwException(); + } try { - return convertRV(reply.getSig(), reply.getParameters(), m, conn); - } catch (DBusException e) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - throw new DBusExecutionException(e.getMessage()); + return convertRV(reply.getSig(), reply.getParameters(), _m, _conn); + } catch (DBusException _ex) { + LOGGER.debug("", _ex); + throw new DBusExecutionException(_ex.getMessage()); } } - - AbstractConnection conn; - RemoteObject remote; - - public RemoteInvocationHandler(AbstractConnection conn, RemoteObject remote) { - this.remote = remote; - this.conn = conn; - } - - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (method.getName().equals("isRemote")) return true; - else if (method.getName().equals("clone")) return null; - else if (method.getName().equals("equals")) { - try { - if (1 == args.length) - return new Boolean(remote.equals(((RemoteInvocationHandler) Proxy.getInvocationHandler(args[0])).remote)); - } catch (IllegalArgumentException IAe) { - return Boolean.FALSE; - } - } else if (method.getName().equals("finalize")) return null; - else if (method.getName().equals("getClass")) return DBusInterface.class; - else if (method.getName().equals("hashCode")) return remote.hashCode(); - else if (method.getName().equals("notify")) { - remote.notify(); - return null; - } else if (method.getName().equals("notifyAll")) { - remote.notifyAll(); - return null; - } else if (method.getName().equals("wait")) { - if (0 == args.length) remote.wait(); - else if (1 == args.length - && args[0] instanceof Long) remote.wait((Long) args[0]); - else if (2 == args.length - && args[0] instanceof Long - && args[1] instanceof Integer) - remote.wait((Long) args[0], (Integer) args[1]); - if (args.length <= 2) - return null; - } else if (method.getName().equals("toString")) - return remote.toString(); - - return executeRemoteMethod(remote, method, conn, CALL_TYPE_SYNC, null, args); - } } - diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/RemoteObject.java b/federation/sssd/src/main/java/org/freedesktop/dbus/RemoteObject.java index c157d12b1d..828160355c 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/RemoteObject.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/RemoteObject.java @@ -1,51 +1,52 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; -class RemoteObject { - String busname; - String objectpath; - Class iface; - boolean autostart; +import org.freedesktop.dbus.interfaces.DBusInterface; - public RemoteObject(String busname, String objectpath, Class iface, boolean autostart) { - this.busname = busname; - this.objectpath = objectpath; - this.iface = iface; - this.autostart = autostart; +public class RemoteObject { + private final String busname; + private final String objectpath; + private final Class iface; + private final boolean autostart; + + public RemoteObject(String _busname, String _objectpath, Class _iface, boolean _autostart) { + this.busname = _busname; + this.objectpath = _objectpath; + this.iface = _iface; + this.autostart = _autostart; } - public boolean equals(Object o) { - if (!(o instanceof RemoteObject)) return false; - RemoteObject them = (RemoteObject) o; + @Override + public boolean equals(Object _o) { + if (!(_o instanceof RemoteObject)) { + return false; + } + RemoteObject them = (RemoteObject) _o; - if (!them.objectpath.equals(this.objectpath)) return false; - - if (null == this.busname && null != them.busname) return false; - if (null != this.busname && null == them.busname) return false; - if (null != them.busname && !them.busname.equals(this.busname)) return false; - - if (null == this.iface && null != them.iface) return false; - if (null != this.iface && null == them.iface) return false; - if (null != them.iface && !them.iface.equals(this.iface)) return false; + if (!them.objectpath.equals(this.objectpath)) { + return false; + } else if (null == this.busname && null != them.busname) { + return false; + } else if (null != this.busname && null == them.busname) { + return false; + } else if (null != them.busname && !them.busname.equals(this.busname)) { + return false; + } else if (null == this.iface && null != them.iface) { + return false; + } else if (null != this.iface && null == them.iface) { + return false; + } else if (null != them.iface && !them.iface.equals(this.iface)) { + return false; + } return true; } + @Override public int hashCode() { - return (null == busname ? 0 : busname.hashCode()) + objectpath.hashCode() + - (null == iface ? 0 : iface.hashCode()); + return (null == busname ? 0 : busname.hashCode()) + objectpath.hashCode() + (null == iface ? 0 : iface.hashCode()); } - public boolean autoStarting() { + public boolean isAutostart() { return autostart; } @@ -61,6 +62,7 @@ class RemoteObject { return iface; } + @Override public String toString() { return busname + ":" + objectpath + ":" + iface; } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/SignalTuple.java b/federation/sssd/src/main/java/org/freedesktop/dbus/SignalTuple.java index 29ec987d2b..f468d6d5f4 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/SignalTuple.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/SignalTuple.java @@ -1,51 +1,106 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; -class SignalTuple { - String type; - String name; - String object; - String source; +import java.util.HashSet; +import java.util.Set; - public SignalTuple(String type, String name, String object, String source) { - this.type = type; - this.name = name; - this.object = object; - this.source = source; +/** + * @deprecated this class was used as map key internally and is no longer in use + */ +@Deprecated(forRemoval = true, since = "4.2.0 - 2022-08-18") +public class SignalTuple { + private final String type; + private final String name; + private final String object; + private final String source; + + public SignalTuple(String _type, String _name, String _object, String _source) { + this.type = _type; + this.name = _name; + this.object = _object; + this.source = _source; } - public boolean equals(Object o) { - if (!(o instanceof SignalTuple)) return false; - SignalTuple other = (SignalTuple) o; - if (null == this.type && null != other.type) return false; - if (null != this.type && !this.type.equals(other.type)) return false; - if (null == this.name && null != other.name) return false; - if (null != this.name && !this.name.equals(other.name)) return false; - if (null == this.object && null != other.object) return false; - if (null != this.object && !this.object.equals(other.object)) return false; - if (null == this.source && null != other.source) return false; - if (null != this.source && !this.source.equals(other.source)) return false; + @Override + public boolean equals(Object _o) { + if (!(_o instanceof SignalTuple)) { + return false; + } + SignalTuple other = (SignalTuple) _o; + if (null == this.type && null != other.type) { + return false; + } else if (null != this.type && !this.type.equals(other.type)) { + return false; + } else if (null == this.name && null != other.name) { + return false; + } else if (null != this.name && !this.name.equals(other.name)) { + return false; + } else if (null == this.object && null != other.object) { + return false; + } else if (null != this.object && !this.object.equals(other.object)) { + return false; + } else if (null == this.source && null != other.source) { + return false; + } else if (null != this.source && !this.source.equals(other.source)) { + return false; + } return true; } + @Override public int hashCode() { - return (null == type ? 0 : type.hashCode()) - + (null == name ? 0 : name.hashCode()) - + (null == source ? 0 : source.hashCode()) - + (null == object ? 0 : object.hashCode()); + return (null == type ? 0 : type.hashCode()) + (null == name ? 0 : name.hashCode()) + (null == source ? 0 : source.hashCode()) + (null == object ? 0 : object.hashCode()); } + @Override public String toString() { return "SignalTuple(" + type + "," + name + "," + object + "," + source + ")"; } -} + /** + * Get a {@link Set} of all possible SignalTuples that we can have, given the 4 parameters. + * @param _type interface type + * @param _name name + * @param _object object + * @param _source source + * @return {@link Set} of {@link SignalTuple}, never null + * @deprecated should no longer be used + */ + @Deprecated + public static Set getAllPossibleTuples(String _type, String _name, String _object, String _source) { + Set allTuples = new HashSet<>(); + + // Tuple with no null + allTuples.add(new SignalTuple(_type, _name, _object, _source)); + + // Tuples with one null + allTuples.add(new SignalTuple(null, _name, _object, _source)); + allTuples.add(new SignalTuple(_type, null, _object, _source)); + allTuples.add(new SignalTuple(_type, _name, null, _source)); + allTuples.add(new SignalTuple(_type, _name, _object, null)); + + // Tuples where type is null, and one other null + allTuples.add(new SignalTuple(null, null, _object, _source)); + allTuples.add(new SignalTuple(null, _name, null, _source)); + allTuples.add(new SignalTuple(null, _name, _object, null)); + + // Tuples where name is null, and one other null + allTuples.add(new SignalTuple(_type, null, null, _source)); + allTuples.add(new SignalTuple(_type, null, _object, null)); + + // Tuples where object is null, and one other null + allTuples.add(new SignalTuple(null, _name, null, _source)); + allTuples.add(new SignalTuple(_type, _name, null, null)); + + // Tuples where source is null, and one other null + allTuples.add(new SignalTuple(null, _name, _object, null)); + allTuples.add(new SignalTuple(_type, _name, null, null)); + + // Tuples with three nulls + allTuples.add(new SignalTuple(_type, null, null, null)); + allTuples.add(new SignalTuple(null, _name, null, null)); + allTuples.add(new SignalTuple(null, null, _object, null)); + allTuples.add(new SignalTuple(null, null, null, _source)); + + return allTuples; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/StrongReference.java b/federation/sssd/src/main/java/org/freedesktop/dbus/StrongReference.java index d5358eca5d..2505a20cc3 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/StrongReference.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/StrongReference.java @@ -1,42 +1,32 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; import java.lang.ref.WeakReference; /** * An alternative to a WeakReference when you don't want - * that behaviour. + * that behavior. */ public class StrongReference extends WeakReference { - T referant; + private T referant; - public StrongReference(T referant) { - super(referant); - this.referant = referant; + public StrongReference(T _referant) { + super(_referant); + this.referant = _referant; } + @Override public void clear() { referant = null; } + @Override public boolean enqueue() { return false; } + @Override public T get() { return referant; } - public boolean isEnqueued() { - return false; - } } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Struct.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Struct.java index 18667e58ec..4b3b8ca053 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/Struct.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/Struct.java @@ -1,13 +1,3 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; /** diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/StructHelper.java b/federation/sssd/src/main/java/org/freedesktop/dbus/StructHelper.java new file mode 100644 index 0000000000..85186b3368 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/StructHelper.java @@ -0,0 +1,96 @@ +package org.freedesktop.dbus; + +import org.freedesktop.dbus.types.DBusStructType; +import org.freedesktop.dbus.types.Variant; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; + +/** + * Helper util to create {@link Struct} subclasses when receiving it from DBus. + * + * @author David M. + * @since v3.2.1 - 2019-10-25 + */ +public final class StructHelper { + + private StructHelper() { + + } + + /** + * Creates a instance of the given {@link Struct} subclass if the given variant is some sort of Struct. + * @param _variant variant to convert + * @param _structClass {@link Struct} subclass to create + * + * @param type of struct + * + * @return instance of _structClass or null if _variant is not Struct compatible or any input parameter is null + * + * @throws NoSuchMethodException when no constructor can be found for the arguments of the struct + * @throws SecurityException when constructor cannot be accesses + * @throws InstantiationException when reflection fails + * @throws IllegalAccessException if this Constructor object is enforcing Java language access control and the underlying constructor is inaccessible. + * @throws IllegalArgumentException when data types are incompatible + * @throws InvocationTargetException if the underlying constructor throws an exception + */ + public static T createStructFromVariant(Variant _variant, Class _structClass) + throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + if (_variant == null || _structClass == null) { + return null; + } + + if (_variant.getType() instanceof DBusStructType && _variant.getValue() instanceof Object[]) { + Class[] argTypes = Arrays.stream((Object[]) _variant.getValue()).map(a -> a.getClass()).toArray(size -> new Class[size]); + return createStruct(argTypes, _variant.getValue(), _structClass); + } + + return null; + } + + /** + * Will create a new {@link Struct} subclass instance if possible. + * May replace Wrapper-classes with primitive classes in _constructorArgs if constructor does not match. + * + * @param _constructorArgs argument-classes expected by constructor + * @param _values values passed to the constructor + * @param _classToConstruct {@link Struct} subclass to instantiate + * + * @param type of struct + * + * @return instance of _classToConstruct or null if any input argument is null + * + * @throws NoSuchMethodException when no constructor can be found for the arguments of the struct + * @throws SecurityException when constructor cannot be accesses + * @throws InstantiationException when reflection fails + * @throws IllegalAccessException if this Constructor object is enforcing Java language access control and the underlying constructor is inaccessible. + * @throws IllegalArgumentException when data types are incompatible + * @throws InvocationTargetException if the underlying constructor throws an exception + */ + public static T createStruct(Class[] _constructorArgs, Object _values, Class _classToConstruct) + throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + if (_constructorArgs == null || _classToConstruct == null || _values == null) { + return null; + } + + try { + Constructor declaredConstructor = _classToConstruct.getDeclaredConstructor(_constructorArgs); + declaredConstructor.setAccessible(true); + if (_values instanceof Object[]) { + return declaredConstructor.newInstance((Object[]) _values); + } else { + return declaredConstructor.newInstance(_values); + } + } catch (NoSuchMethodException | SecurityException _ex) { + for (int i = 0; i < _constructorArgs.length; i++) { + Class class1 = _constructorArgs[i]; + if (ArrayFrob.getWrapperToPrimitiveTypes().containsKey(class1)) { + _constructorArgs[i] = ArrayFrob.getWrapperToPrimitiveTypes().get(class1); + return createStruct(_constructorArgs, _values, _classToConstruct); + } + } + } + throw new NoSuchMethodException("Cannot find suitable constructor for arguments " + Arrays.toString(_constructorArgs) + " in class " + _classToConstruct + "."); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Transport.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Transport.java deleted file mode 100644 index 1745bcfec2..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/Transport.java +++ /dev/null @@ -1,835 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import cx.ath.matthew.debug.Debug; -import cx.ath.matthew.unix.UnixSocket; -import cx.ath.matthew.unix.UnixSocketAddress; -import cx.ath.matthew.utils.Hexdump; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.lang.reflect.Method; -import java.net.InetSocketAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.text.Collator; -import java.text.ParseException; -import java.util.Arrays; -import java.util.Random; -import java.util.Vector; - -import static org.freedesktop.dbus.Gettext.getString; - -public class Transport { - public static class SASL { - public static class Command { - private int command; - private int mechs; - private String data; - private String response; - - public Command() { - } - - public Command(String s) throws IOException { - String[] ss = s.split(" "); - if (Debug.debug) Debug.print(Debug.VERBOSE, "Creating command from: " + Arrays.toString(ss)); - if (0 == col.compare(ss[0], "OK")) { - command = COMMAND_OK; - data = ss[1]; - } else if (0 == col.compare(ss[0], "AUTH")) { - command = COMMAND_AUTH; - if (ss.length > 1) { - if (0 == col.compare(ss[1], "EXTERNAL")) - mechs = AUTH_EXTERNAL; - else if (0 == col.compare(ss[1], "DBUS_COOKIE_SHA1")) - mechs = AUTH_SHA; - else if (0 == col.compare(ss[1], "ANONYMOUS")) - mechs = AUTH_ANON; - } - if (ss.length > 2) - data = ss[2]; - } else if (0 == col.compare(ss[0], "DATA")) { - command = COMMAND_DATA; - data = ss[1]; - } else if (0 == col.compare(ss[0], "REJECTED")) { - command = COMMAND_REJECTED; - for (int i = 1; i < ss.length; i++) - if (0 == col.compare(ss[i], "EXTERNAL")) - mechs |= AUTH_EXTERNAL; - else if (0 == col.compare(ss[i], "DBUS_COOKIE_SHA1")) - mechs |= AUTH_SHA; - else if (0 == col.compare(ss[i], "ANONYMOUS")) - mechs |= AUTH_ANON; - } else if (0 == col.compare(ss[0], "BEGIN")) { - command = COMMAND_BEGIN; - } else if (0 == col.compare(ss[0], "CANCEL")) { - command = COMMAND_CANCEL; - } else if (0 == col.compare(ss[0], "ERROR")) { - command = COMMAND_ERROR; - data = ss[1]; - } else { - throw new IOException(getString("invalidCommand") + ss[0]); - } - if (Debug.debug) Debug.print(Debug.VERBOSE, "Created command: " + this); - } - - public int getCommand() { - return command; - } - - public int getMechs() { - return mechs; - } - - public String getData() { - return data; - } - - public String getResponse() { - return response; - } - - public void setResponse(String s) { - response = s; - } - - public String toString() { - return "Command(" + command + ", " + mechs + ", " + data + ", " + null + ")"; - } - } - - private static Collator col = Collator.getInstance(); - - static { - col.setDecomposition(Collator.FULL_DECOMPOSITION); - col.setStrength(Collator.PRIMARY); - } - - public static final int LOCK_TIMEOUT = 1000; - public static final int NEW_KEY_TIMEOUT_SECONDS = 60 * 5; - public static final int EXPIRE_KEYS_TIMEOUT_SECONDS = NEW_KEY_TIMEOUT_SECONDS + (60 * 2); - public static final int MAX_TIME_TRAVEL_SECONDS = 60 * 5; - public static final int COOKIE_TIMEOUT = 240; - public static final String COOKIE_CONTEXT = "org_freedesktop_java"; - - private String findCookie(String context, String ID) throws IOException { - String homedir = System.getProperty("user.home"); - File f = new File(homedir + "/.dbus-keyrings/" + context); - BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(f))); - String s = null; - String cookie = null; - long now = System.currentTimeMillis() / 1000; - while (null != (s = r.readLine())) { - String[] line = s.split(" "); - long timestamp = Long.parseLong(line[1]); - if (line[0].equals(ID) && (!(timestamp < 0 || - (now + MAX_TIME_TRAVEL_SECONDS) < timestamp || - (now - EXPIRE_KEYS_TIMEOUT_SECONDS) > timestamp))) { - cookie = line[2]; - break; - } - } - r.close(); - return cookie; - } - - private void addCookie(String context, String ID, long timestamp, String cookie) throws IOException { - String homedir = System.getProperty("user.home"); - File keydir = new File(homedir + "/.dbus-keyrings/"); - File cookiefile = new File(homedir + "/.dbus-keyrings/" + context); - File lock = new File(homedir + "/.dbus-keyrings/" + context + ".lock"); - File temp = new File(homedir + "/.dbus-keyrings/" + context + ".temp"); - - // ensure directory exists - if (!keydir.exists()) keydir.mkdirs(); - - // acquire lock - long start = System.currentTimeMillis(); - while (!lock.createNewFile() && LOCK_TIMEOUT > (System.currentTimeMillis() - start)) ; - - // read old file - Vector lines = new Vector(); - if (cookiefile.exists()) { - BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(cookiefile))); - String s = null; - while (null != (s = r.readLine())) { - String[] line = s.split(" "); - long time = Long.parseLong(line[1]); - // expire stale cookies - if ((timestamp - time) < COOKIE_TIMEOUT) - lines.add(s); - } - r.close(); - } - - // add cookie - lines.add(ID + " " + timestamp + " " + cookie); - - // write temp file - PrintWriter w = new PrintWriter(new FileOutputStream(temp)); - for (String l : lines) - w.println(l); - w.close(); - - // atomically move to old file - if (!temp.renameTo(cookiefile)) { - cookiefile.delete(); - temp.renameTo(cookiefile); - } - - // remove lock - lock.delete(); - } - - /** - * Takes the string, encodes it as hex and then turns it into a string again. - * No, I don't know why either. - */ - private String stupidlyEncode(String data) { - return Hexdump.toHex(data.getBytes()).replaceAll(" ", ""); - } - - private String stupidlyEncode(byte[] data) { - return Hexdump.toHex(data).replaceAll(" ", ""); - } - - private byte getNibble(char c) { - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return (byte) (c - '0'); - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - return (byte) (c - 'A' + 10); - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - return (byte) (c - 'a' + 10); - default: - return 0; - } - } - - private String stupidlyDecode(String data) { - char[] cs = new char[data.length()]; - char[] res = new char[cs.length / 2]; - data.getChars(0, data.length(), cs, 0); - for (int i = 0, j = 0; j < res.length; i += 2, j++) { - int b = 0; - b |= getNibble(cs[i]) << 4; - b |= getNibble(cs[i + 1]); - res[j] = (char) b; - } - return new String(res); - } - - public static final int MODE_SERVER = 1; - public static final int MODE_CLIENT = 2; - - public static final int AUTH_NONE = 0; - public static final int AUTH_EXTERNAL = 1; - public static final int AUTH_SHA = 2; - public static final int AUTH_ANON = 4; - - public static final int COMMAND_AUTH = 1; - public static final int COMMAND_DATA = 2; - public static final int COMMAND_REJECTED = 3; - public static final int COMMAND_OK = 4; - public static final int COMMAND_BEGIN = 5; - public static final int COMMAND_CANCEL = 6; - public static final int COMMAND_ERROR = 7; - - public static final int INITIAL_STATE = 0; - public static final int WAIT_DATA = 1; - public static final int WAIT_OK = 2; - public static final int WAIT_REJECT = 3; - public static final int WAIT_AUTH = 4; - public static final int WAIT_BEGIN = 5; - public static final int AUTHENTICATED = 6; - public static final int FAILED = 7; - - public static final int OK = 1; - public static final int CONTINUE = 2; - public static final int ERROR = 3; - public static final int REJECT = 4; - - public Command receive(InputStream s) throws IOException { - StringBuffer sb = new StringBuffer(); - top: - while (true) { - int c = s.read(); - switch (c) { - case -1: - throw new IOException("Stream unexpectedly short (broken pipe)"); - case 0: - case '\r': - continue; - case '\n': - break top; - default: - sb.append((char) c); - } - } - if (Debug.debug) Debug.print(Debug.VERBOSE, "received: " + sb); - try { - return new Command(sb.toString()); - } catch (Exception e) { - if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, e); - return new Command(); - } - } - - public void send(OutputStream out, int command, String... data) throws IOException { - StringBuffer sb = new StringBuffer(); - switch (command) { - case COMMAND_AUTH: - sb.append("AUTH"); - break; - case COMMAND_DATA: - sb.append("DATA"); - break; - case COMMAND_REJECTED: - sb.append("REJECTED"); - break; - case COMMAND_OK: - sb.append("OK"); - break; - case COMMAND_BEGIN: - sb.append("BEGIN"); - break; - case COMMAND_CANCEL: - sb.append("CANCEL"); - break; - case COMMAND_ERROR: - sb.append("ERROR"); - break; - default: - return; - } - for (String s : data) { - sb.append(' '); - sb.append(s); - } - sb.append('\r'); - sb.append('\n'); - if (Debug.debug) Debug.print(Debug.VERBOSE, "sending: " + sb); - out.write(sb.toString().getBytes()); - } - - public int do_challenge(int auth, Command c) throws IOException { - switch (auth) { - case AUTH_SHA: - String[] reply = stupidlyDecode(c.getData()).split(" "); - if (Debug.debug) Debug.print(Debug.VERBOSE, Arrays.toString(reply)); - if (3 != reply.length) { - if (Debug.debug) Debug.print(Debug.DEBUG, "Reply is not length 3"); - return ERROR; - } - String context = reply[0]; - String ID = reply[1]; - String serverchallenge = reply[2]; - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA"); - } catch (NoSuchAlgorithmException NSAe) { - if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, NSAe); - return ERROR; - } - byte[] buf = new byte[8]; - Message.marshallintBig(System.currentTimeMillis(), buf, 0, 8); - String clientchallenge = stupidlyEncode(md.digest(buf)); - md.reset(); - long start = System.currentTimeMillis(); - String cookie = null; - while (null == cookie && (System.currentTimeMillis() - start) < LOCK_TIMEOUT) - cookie = findCookie(context, ID); - if (null == cookie) { - if (Debug.debug) - Debug.print(Debug.DEBUG, "Did not find a cookie in context " + context + " with ID " + ID); - return ERROR; - } - String response = serverchallenge + ":" + clientchallenge + ":" + cookie; - buf = md.digest(response.getBytes()); - if (Debug.debug) - Debug.print(Debug.VERBOSE, "Response: " + response + " hash: " + Hexdump.format(buf)); - response = stupidlyEncode(buf); - c.setResponse(stupidlyEncode(clientchallenge + " " + response)); - return OK; - default: - if (Debug.debug) Debug.print(Debug.DEBUG, "Not DBUS_COOKIE_SHA1 authtype."); - return ERROR; - } - } - - public String challenge = ""; - public String cookie = ""; - - public int do_response(int auth, String Uid, String kernelUid, Command c) { - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA"); - } catch (NoSuchAlgorithmException NSAe) { - if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, NSAe); - return ERROR; - } - switch (auth) { - case AUTH_NONE: - switch (c.getMechs()) { - case AUTH_ANON: - return OK; - case AUTH_EXTERNAL: - if (0 == col.compare(Uid, c.getData()) && - (null == kernelUid || 0 == col.compare(Uid, kernelUid))) - return OK; - else - return ERROR; - case AUTH_SHA: - String context = COOKIE_CONTEXT; - long id = System.currentTimeMillis(); - byte[] buf = new byte[8]; - Message.marshallintBig(id, buf, 0, 8); - challenge = stupidlyEncode(md.digest(buf)); - Random r = new Random(); - r.nextBytes(buf); - cookie = stupidlyEncode(md.digest(buf)); - try { - addCookie(context, "" + id, id / 1000, cookie); - } catch (IOException IOe) { - if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, IOe); - } - if (Debug.debug) - Debug.print(Debug.DEBUG, "Sending challenge: " + context + ' ' + id + ' ' + challenge); - c.setResponse(stupidlyEncode(context + ' ' + id + ' ' + challenge)); - return CONTINUE; - default: - return ERROR; - } - case AUTH_SHA: - String[] response = stupidlyDecode(c.getData()).split(" "); - if (response.length < 2) return ERROR; - String cchal = response[0]; - String hash = response[1]; - String prehash = challenge + ":" + cchal + ":" + cookie; - byte[] buf = md.digest(prehash.getBytes()); - String posthash = stupidlyEncode(buf); - if (Debug.debug) - Debug.print(Debug.DEBUG, "Authenticating Hash; data=" + prehash + " remote hash=" + hash + " local hash=" + posthash); - if (0 == col.compare(posthash, hash)) - return OK; - else - return ERROR; - default: - return ERROR; - } - } - - public String[] getTypes(int types) { - switch (types) { - case AUTH_EXTERNAL: - return new String[]{"EXTERNAL"}; - case AUTH_SHA: - return new String[]{"DBUS_COOKIE_SHA1"}; - case AUTH_ANON: - return new String[]{"ANONYMOUS"}; - case AUTH_SHA + AUTH_EXTERNAL: - return new String[]{"EXTERNAL", "DBUS_COOKIE_SHA1"}; - case AUTH_SHA + AUTH_ANON: - return new String[]{"ANONYMOUS", "DBUS_COOKIE_SHA1"}; - case AUTH_EXTERNAL + AUTH_ANON: - return new String[]{"ANONYMOUS", "EXTERNAL"}; - case AUTH_EXTERNAL + AUTH_ANON + AUTH_SHA: - return new String[]{"ANONYMOUS", "EXTERNAL", "DBUS_COOKIE_SHA1"}; - default: - return new String[]{}; - } - } - - /** - * performs SASL auth on the given streams. - * Mode selects whether to run as a SASL server or client. - * Types is a bitmask of the available auth types. - * Returns true if the auth was successful and false if it failed. - */ - @SuppressWarnings("unchecked") - public boolean auth(int mode, int types, String guid, OutputStream out, InputStream in, UnixSocket us) throws IOException { - String username = System.getProperty("user.name"); - String Uid = null; - String kernelUid = null; - try { - Class c = Class.forName("com.sun.security.auth.module.UnixSystem"); - Method m = c.getMethod("getUid"); - Object o = c.newInstance(); - long uid = (Long) m.invoke(o); - Uid = stupidlyEncode("" + uid); - } catch (Exception e) { - Uid = stupidlyEncode(username); - } - Command c; - int failed = 0; - int current = 0; - int state = INITIAL_STATE; - - while (state != AUTHENTICATED && state != FAILED) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "AUTH state: " + state); - switch (mode) { - case MODE_CLIENT: - switch (state) { - case INITIAL_STATE: - if (null == us) - out.write(new byte[]{0}); - else - us.sendCredentialByte((byte) 0); - send(out, COMMAND_AUTH); - state = WAIT_DATA; - break; - case WAIT_DATA: - c = receive(in); - switch (c.getCommand()) { - case COMMAND_DATA: - switch (do_challenge(current, c)) { - case CONTINUE: - send(out, COMMAND_DATA, c.getResponse()); - break; - case OK: - send(out, COMMAND_DATA, c.getResponse()); - state = WAIT_OK; - break; - case ERROR: - send(out, COMMAND_ERROR, c.getResponse()); - break; - } - break; - case COMMAND_REJECTED: - failed |= current; - int available = c.getMechs() & (~failed); - if (0 != (available & AUTH_EXTERNAL)) { - send(out, COMMAND_AUTH, "EXTERNAL", Uid); - current = AUTH_EXTERNAL; - } else if (0 != (available & AUTH_SHA)) { - send(out, COMMAND_AUTH, "DBUS_COOKIE_SHA1", Uid); - current = AUTH_SHA; - } else if (0 != (available & AUTH_ANON)) { - send(out, COMMAND_AUTH, "ANONYMOUS"); - current = AUTH_ANON; - } else state = FAILED; - break; - case COMMAND_ERROR: - send(out, COMMAND_CANCEL); - state = WAIT_REJECT; - break; - case COMMAND_OK: - send(out, COMMAND_BEGIN); - state = AUTHENTICATED; - break; - default: - send(out, COMMAND_ERROR, "Got invalid command"); - break; - } - break; - case WAIT_OK: - c = receive(in); - switch (c.getCommand()) { - case COMMAND_OK: - send(out, COMMAND_BEGIN); - state = AUTHENTICATED; - break; - case COMMAND_ERROR: - case COMMAND_DATA: - send(out, COMMAND_CANCEL); - state = WAIT_REJECT; - break; - case COMMAND_REJECTED: - failed |= current; - int available = c.getMechs() & (~failed); - state = WAIT_DATA; - if (0 != (available & AUTH_EXTERNAL)) { - send(out, COMMAND_AUTH, "EXTERNAL", Uid); - current = AUTH_EXTERNAL; - } else if (0 != (available & AUTH_SHA)) { - send(out, COMMAND_AUTH, "DBUS_COOKIE_SHA1", Uid); - current = AUTH_SHA; - } else if (0 != (available & AUTH_ANON)) { - send(out, COMMAND_AUTH, "ANONYMOUS"); - current = AUTH_ANON; - } else state = FAILED; - break; - default: - send(out, COMMAND_ERROR, "Got invalid command"); - break; - } - break; - case WAIT_REJECT: - c = receive(in); - switch (c.getCommand()) { - case COMMAND_REJECTED: - failed |= current; - int available = c.getMechs() & (~failed); - if (0 != (available & AUTH_EXTERNAL)) { - send(out, COMMAND_AUTH, "EXTERNAL", Uid); - current = AUTH_EXTERNAL; - } else if (0 != (available & AUTH_SHA)) { - send(out, COMMAND_AUTH, "DBUS_COOKIE_SHA1", Uid); - current = AUTH_SHA; - } else if (0 != (available & AUTH_ANON)) { - send(out, COMMAND_AUTH, "ANONYMOUS"); - current = AUTH_ANON; - } else state = FAILED; - break; - default: - state = FAILED; - break; - } - break; - default: - state = FAILED; - } - break; - case MODE_SERVER: - switch (state) { - case INITIAL_STATE: - byte[] buf = new byte[1]; - if (null == us) { - in.read(buf); - } else { - buf[0] = us.recvCredentialByte(); - int kuid = us.getPeerUID(); - if (kuid >= 0) - kernelUid = stupidlyEncode("" + kuid); - } - if (0 != buf[0]) state = FAILED; - else state = WAIT_AUTH; - break; - case WAIT_AUTH: - c = receive(in); - switch (c.getCommand()) { - case COMMAND_AUTH: - if (null == c.getData()) { - send(out, COMMAND_REJECTED, getTypes(types)); - } else { - switch (do_response(current, Uid, kernelUid, c)) { - case CONTINUE: - send(out, COMMAND_DATA, c.getResponse()); - current = c.getMechs(); - state = WAIT_DATA; - break; - case OK: - send(out, COMMAND_OK, guid); - state = WAIT_BEGIN; - current = 0; - break; - case REJECT: - send(out, COMMAND_REJECTED, getTypes(types)); - current = 0; - break; - } - } - break; - case COMMAND_ERROR: - send(out, COMMAND_REJECTED, getTypes(types)); - break; - case COMMAND_BEGIN: - state = FAILED; - break; - default: - send(out, COMMAND_ERROR, "Got invalid command"); - break; - } - break; - case WAIT_DATA: - c = receive(in); - switch (c.getCommand()) { - case COMMAND_DATA: - switch (do_response(current, Uid, kernelUid, c)) { - case CONTINUE: - send(out, COMMAND_DATA, c.getResponse()); - state = WAIT_DATA; - break; - case OK: - send(out, COMMAND_OK, guid); - state = WAIT_BEGIN; - current = 0; - break; - case REJECT: - send(out, COMMAND_REJECTED, getTypes(types)); - current = 0; - break; - } - break; - case COMMAND_ERROR: - case COMMAND_CANCEL: - send(out, COMMAND_REJECTED, getTypes(types)); - state = WAIT_AUTH; - break; - case COMMAND_BEGIN: - state = FAILED; - break; - default: - send(out, COMMAND_ERROR, "Got invalid command"); - break; - } - break; - case WAIT_BEGIN: - c = receive(in); - switch (c.getCommand()) { - case COMMAND_ERROR: - case COMMAND_CANCEL: - send(out, COMMAND_REJECTED, getTypes(types)); - state = WAIT_AUTH; - break; - case COMMAND_BEGIN: - state = AUTHENTICATED; - break; - default: - send(out, COMMAND_ERROR, "Got invalid command"); - break; - } - break; - default: - state = FAILED; - } - break; - default: - return false; - } - } - - return state == AUTHENTICATED; - } - } - - public MessageReader min; - public MessageWriter mout; - - public Transport() { - } - - public static String genGUID() { - Random r = new Random(); - byte[] buf = new byte[16]; - r.nextBytes(buf); - String guid = Hexdump.toHex(buf); - return guid.replaceAll(" ", ""); - } - - public Transport(BusAddress address) throws IOException { - connect(address); - } - - public Transport(String address) throws IOException, ParseException { - connect(new BusAddress(address)); - } - - public Transport(String address, int timeout) throws IOException, ParseException { - connect(new BusAddress(address), timeout); - } - - public void connect(String address) throws IOException, ParseException { - connect(new BusAddress(address), 0); - } - - public void connect(String address, int timeout) throws IOException, ParseException { - connect(new BusAddress(address), timeout); - } - - public void connect(BusAddress address) throws IOException { - connect(address, 0); - } - - public void connect(BusAddress address, int timeout) throws IOException { - if (Debug.debug) Debug.print(Debug.INFO, "Connecting to " + address); - OutputStream out = null; - InputStream in = null; - UnixSocket us = null; - Socket s = null; - int mode = 0; - int types = 0; - if ("unix".equals(address.getType())) { - types = SASL.AUTH_EXTERNAL; - mode = SASL.MODE_CLIENT; - us = new UnixSocket(); - if (null != address.getParameter("abstract")) - us.connect(new UnixSocketAddress(address.getParameter("abstract"), true)); - else if (null != address.getParameter("path")) - us.connect(new UnixSocketAddress(address.getParameter("path"), false)); - us.setPassCred(true); - in = us.getInputStream(); - out = us.getOutputStream(); - } else if ("tcp".equals(address.getType())) { - types = SASL.AUTH_SHA; - if (null != address.getParameter("listen")) { - mode = SASL.MODE_SERVER; - ServerSocket ss = new ServerSocket(); - ss.bind(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port")))); - s = ss.accept(); - } else { - mode = SASL.MODE_CLIENT; - s = new Socket(); - s.connect(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port")))); - } - in = s.getInputStream(); - out = s.getOutputStream(); - } else { - throw new IOException(getString("unknownAddress") + address.getType()); - } - - if (!(new SASL()).auth(mode, types, address.getParameter("guid"), out, in, us)) { - out.close(); - throw new IOException(getString("errorAuth")); - } - if (null != us) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Setting timeout to " + timeout + " on Socket"); - if (timeout == 1) - us.setBlocking(false); - else - us.setSoTimeout(timeout); - } - if (null != s) { - if (Debug.debug) Debug.print(Debug.VERBOSE, "Setting timeout to " + timeout + " on Socket"); - s.setSoTimeout(timeout); - } - mout = new MessageWriter(out); - min = new MessageReader(in); - } - - public void disconnect() throws IOException { - if (Debug.debug) Debug.print(Debug.INFO, "Disconnecting Transport"); - min.close(); - mout.close(); - } -} - - diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Tuple.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Tuple.java index 5fc13d5fb4..528937c2e8 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/Tuple.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/Tuple.java @@ -1,19 +1,11 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; /** * This class should be extended to create Tuples. + * * Any such class may be used as the return type for a method * which returns multiple values. + * * All fields in the Tuple which you wish to be serialized and sent to the * remote method should be annotated with the org.freedesktop.dbus.Position * annotation, in the order they should appear to DBus. diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/TypeRef.java b/federation/sssd/src/main/java/org/freedesktop/dbus/TypeRef.java new file mode 100644 index 0000000000..2158be0c63 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/TypeRef.java @@ -0,0 +1,15 @@ +package org.freedesktop.dbus; + +/** + * Interface for retrieving generic type information with reflection. + *

+ * Examples: + *

{@code
+ * public interface ListOfStrings extends TypeRef> {
+ * }
+ * } 
+ * + * @param generic type + */ +public interface TypeRef { +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/TypeSignature.java b/federation/sssd/src/main/java/org/freedesktop/dbus/TypeSignature.java index eaad1667ad..a2e3e4893c 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/TypeSignature.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/TypeSignature.java @@ -1,13 +1,3 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus; import org.freedesktop.dbus.exceptions.DBusException; @@ -15,18 +5,20 @@ import org.freedesktop.dbus.exceptions.DBusException; import java.lang.reflect.Type; public class TypeSignature { + // CHECKSTYLE:OFF String sig; - - public TypeSignature(String sig) { - this.sig = sig; + // CHECKSTYLE:ON + public TypeSignature(String _sig) { + this.sig = _sig; } - public TypeSignature(Type[] types) throws DBusException { + public TypeSignature(Type[] _types) throws DBusException { StringBuffer sb = new StringBuffer(); - for (Type t : types) { + for (Type t : _types) { String[] ts = Marshalling.getDBusType(t); - for (String s : ts) + for (String s : ts) { sb.append(s); + } } this.sig = sb.toString(); } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/UInt16.java b/federation/sssd/src/main/java/org/freedesktop/dbus/UInt16.java deleted file mode 100644 index a8f0600116..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/UInt16.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import java.text.MessageFormat; - -import static org.freedesktop.dbus.Gettext.getString; - -/** - * Class to represent 16-bit unsigned integers. - */ -@SuppressWarnings("serial") -public class UInt16 extends Number implements Comparable { - /** - * Maximum possible value. - */ - public static final int MAX_VALUE = 65535; - /** - * Minimum possible value. - */ - public static final int MIN_VALUE = 0; - private int value; - - /** - * Create a UInt16 from an int. - * - * @param value Must be within MIN_VALUE–MAX_VALUE - * @throws NumberFormatException if value is not between MIN_VALUE and MAX_VALUE - */ - public UInt16(int value) { - if (value < MIN_VALUE || value > MAX_VALUE) - throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_VALUE})); - this.value = value; - } - - /** - * Create a UInt16 from a String. - * - * @param value Must parse to a valid integer within MIN_VALUE–MAX_VALUE - * @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_VALUE - */ - public UInt16(String value) { - this(Integer.parseInt(value)); - } - - /** - * The value of this as a byte. - */ - public byte byteValue() { - return (byte) value; - } - - /** - * The value of this as a double. - */ - public double doubleValue() { - return (double) value; - } - - /** - * The value of this as a float. - */ - public float floatValue() { - return (float) value; - } - - /** - * The value of this as a int. - */ - public int intValue() { - return /*(int)*/ value; - } - - /** - * The value of this as a long. - */ - public long longValue() { - return (long) value; - } - - /** - * The value of this as a short. - */ - public short shortValue() { - return (short) value; - } - - /** - * Test two UInt16s for equality. - */ - public boolean equals(Object o) { - return o instanceof UInt16 && ((UInt16) o).value == this.value; - } - - public int hashCode() { - return /*(int)*/ value; - } - - /** - * Compare two UInt16s. - * - * @return 0 if equal, -ve or +ve if they are different. - */ - public int compareTo(UInt16 other) { - return /*(int)*/ (this.value - other.value); - } - - /** - * The value of this as a string. - */ - public String toString() { - return "" + value; - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/UInt32.java b/federation/sssd/src/main/java/org/freedesktop/dbus/UInt32.java deleted file mode 100644 index 465191934d..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/UInt32.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import java.text.MessageFormat; - -import static org.freedesktop.dbus.Gettext.getString; - -/** - * Class to represent unsigned 32-bit numbers. - */ -@SuppressWarnings("serial") -public class UInt32 extends Number implements Comparable { - /** - * Maximum allowed value - */ - public static final long MAX_VALUE = 4294967295L; - /** - * Minimum allowed value - */ - public static final long MIN_VALUE = 0; - private long value; - - /** - * Create a UInt32 from a long. - * - * @param value Must be a valid integer within MIN_VALUE–MAX_VALUE - * @throws NumberFormatException if value is not between MIN_VALUE and MAX_VALUE - */ - public UInt32(long value) { - if (value < MIN_VALUE || value > MAX_VALUE) - throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_VALUE})); - this.value = value; - } - - /** - * Create a UInt32 from a String. - * - * @param value Must parse to a valid integer within MIN_VALUE–MAX_VALUE - * @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_VALUE - */ - public UInt32(String value) { - this(Long.parseLong(value)); - } - - /** - * The value of this as a byte. - */ - public byte byteValue() { - return (byte) value; - } - - /** - * The value of this as a double. - */ - public double doubleValue() { - return (double) value; - } - - /** - * The value of this as a float. - */ - public float floatValue() { - return (float) value; - } - - /** - * The value of this as a int. - */ - public int intValue() { - return (int) value; - } - - /** - * The value of this as a long. - */ - public long longValue() { - return /*(long)*/ value; - } - - /** - * The value of this as a short. - */ - public short shortValue() { - return (short) value; - } - - /** - * Test two UInt32s for equality. - */ - public boolean equals(Object o) { - return o instanceof UInt32 && ((UInt32) o).value == this.value; - } - - public int hashCode() { - return (int) value; - } - - /** - * Compare two UInt32s. - * - * @return 0 if equal, -ve or +ve if they are different. - */ - public int compareTo(UInt32 other) { - return (int) (this.value - other.value); - } - - /** - * The value of this as a string - */ - public String toString() { - return "" + value; - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/UInt64.java b/federation/sssd/src/main/java/org/freedesktop/dbus/UInt64.java deleted file mode 100644 index fde2cf0260..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/UInt64.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import java.math.BigInteger; -import java.text.MessageFormat; - -import static org.freedesktop.dbus.Gettext.getString; - -/** - * Class to represent unsigned 64-bit numbers. - * Warning: Any functions which take or return a long - * are restricted to the range of a signed 64bit number. - * Use the BigInteger methods if you wish access to the full - * range. - */ -@SuppressWarnings("serial") -public class UInt64 extends Number implements Comparable { - /** - * Maximum allowed value (when accessed as a long) - */ - public static final long MAX_LONG_VALUE = Long.MAX_VALUE; - /** - * Maximum allowed value (when accessed as a BigInteger) - */ - public static final BigInteger MAX_BIG_VALUE = new BigInteger("18446744073709551615"); - /** - * Minimum allowed value - */ - public static final long MIN_VALUE = 0; - private BigInteger value; - private long top; - private long bottom; - - /** - * Create a UInt64 from a long. - * - * @param value Must be a valid integer within MIN_VALUE–MAX_VALUE - * @throws NumberFormatException if value is not between MIN_VALUE and MAX_VALUE - */ - public UInt64(long value) { - if (value < MIN_VALUE || value > MAX_LONG_VALUE) - throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_LONG_VALUE})); - this.value = new BigInteger("" + value); - this.top = this.value.shiftRight(32).and(new BigInteger("4294967295")).longValue(); - this.bottom = this.value.and(new BigInteger("4294967295")).longValue(); - } - - /** - * Create a UInt64 from two longs. - * - * @param top Most significant 4 bytes. - * @param bottom Least significant 4 bytes. - */ - public UInt64(long top, long bottom) { - BigInteger a = new BigInteger("" + top); - a = a.shiftLeft(32); - a = a.add(new BigInteger("" + bottom)); - if (0 > a.compareTo(BigInteger.ZERO)) - throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{a, MIN_VALUE, MAX_BIG_VALUE})); - if (0 < a.compareTo(MAX_BIG_VALUE)) - throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{a, MIN_VALUE, MAX_BIG_VALUE})); - this.value = a; - this.top = top; - this.bottom = bottom; - } - - /** - * Create a UInt64 from a BigInteger - * - * @param value Must be a valid BigInteger between MIN_VALUE–MAX_BIG_VALUE - * @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_BIG_VALUE - */ - public UInt64(BigInteger value) { - if (null == value) - throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE})); - if (0 > value.compareTo(BigInteger.ZERO)) - throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE})); - if (0 < value.compareTo(MAX_BIG_VALUE)) - throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE})); - this.value = value; - this.top = this.value.shiftRight(32).and(new BigInteger("4294967295")).longValue(); - this.bottom = this.value.and(new BigInteger("4294967295")).longValue(); - } - - /** - * Create a UInt64 from a String. - * - * @param value Must parse to a valid integer within MIN_VALUE–MAX_BIG_VALUE - * @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_BIG_VALUE - */ - public UInt64(String value) { - if (null == value) - throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE})); - BigInteger a = new BigInteger(value); - if (0 > a.compareTo(BigInteger.ZERO)) - throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE})); - if (0 < a.compareTo(MAX_BIG_VALUE)) - throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE})); - this.value = a; - this.top = this.value.shiftRight(32).and(new BigInteger("4294967295")).longValue(); - this.bottom = this.value.and(new BigInteger("4294967295")).longValue(); - } - - /** - * The value of this as a BigInteger. - */ - public BigInteger value() { - return value; - } - - /** - * The value of this as a byte. - */ - public byte byteValue() { - return value.byteValue(); - } - - /** - * The value of this as a double. - */ - public double doubleValue() { - return value.doubleValue(); - } - - /** - * The value of this as a float. - */ - public float floatValue() { - return value.floatValue(); - } - - /** - * The value of this as a int. - */ - public int intValue() { - return value.intValue(); - } - - /** - * The value of this as a long. - */ - public long longValue() { - return value.longValue(); - } - - /** - * The value of this as a short. - */ - public short shortValue() { - return value.shortValue(); - } - - /** - * Test two UInt64s for equality. - */ - public boolean equals(Object o) { - return o instanceof UInt64 && this.value.equals(((UInt64) o).value); - } - - public int hashCode() { - return value.hashCode(); - } - - /** - * Compare two UInt32s. - * - * @return 0 if equal, -ve or +ve if they are different. - */ - public int compareTo(UInt64 other) { - return this.value.compareTo(other.value); - } - - /** - * The value of this as a string. - */ - public String toString() { - return value.toString(); - } - - /** - * Most significant 4 bytes. - */ - public long top() { - return top; - } - - /** - * Least significant 4 bytes. - */ - public long bottom() { - return bottom; - } -} - diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Variant.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Variant.java deleted file mode 100644 index 8fe21a592e..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/Variant.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus; - -import cx.ath.matthew.debug.Debug; -import org.freedesktop.dbus.exceptions.DBusException; - -import java.lang.reflect.Type; -import java.text.MessageFormat; -import java.util.Vector; - -import static org.freedesktop.dbus.Gettext.getString; - -/** - * A Wrapper class for Variant values. - * A method on DBus can send or receive a Variant. - * This will wrap another value whose type is determined at runtime. - * The Variant may be parameterized to restrict the types it may accept. - */ -public class Variant { - private final T o; - private final Type type; - private final String sig; - - /** - * Create a Variant from a basic type object. - * - * @param o The wrapped value. - * @throws IllegalArugmentException If you try and wrap Null or an object of a non-basic type. - */ - public Variant(T o) throws IllegalArgumentException { - if (null == o) throw new IllegalArgumentException(getString("cannotWrapNullInVariant")); - type = o.getClass(); - try { - String[] ss = Marshalling.getDBusType(o.getClass(), true); - if (ss.length != 1) - throw new IllegalArgumentException(getString("cannotWrapMultiValuedInVariant") + type); - this.sig = ss[0]; - } catch (DBusException DBe) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); - throw new IllegalArgumentException(MessageFormat.format(getString("cannotWrapUnqualifiedVariant"), new Object[]{o.getClass(), DBe.getMessage()})); - } - this.o = o; - } - - /** - * Create a Variant. - * - * @param o The wrapped value. - * @param type The explicit type of the value. - * @throws IllegalArugmentException If you try and wrap Null or an object which cannot be sent over DBus. - */ - public Variant(T o, Type type) throws IllegalArgumentException { - if (null == o) throw new IllegalArgumentException(getString("cannotWrapNullInVariant")); - this.type = type; - try { - String[] ss = Marshalling.getDBusType(type); - if (ss.length != 1) - throw new IllegalArgumentException(getString("cannotWrapMultiValuedInVariant") + type); - this.sig = ss[0]; - } catch (DBusException DBe) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); - throw new IllegalArgumentException(MessageFormat.format(getString("cannotWrapUnqualifiedVariant"), new Object[]{type, DBe.getMessage()})); - } - this.o = o; - } - - /** - * Create a Variant. - * - * @param o The wrapped value. - * @param sig The explicit type of the value, as a dbus type string. - * @throws IllegalArugmentException If you try and wrap Null or an object which cannot be sent over DBus. - */ - public Variant(T o, String sig) throws IllegalArgumentException { - if (null == o) throw new IllegalArgumentException(getString("cannotWrapNullInVariant")); - this.sig = sig; - try { - Vector ts = new Vector(); - Marshalling.getJavaType(sig, ts, 1); - if (ts.size() != 1) - throw new IllegalArgumentException(getString("cannotWrapNoTypesInVariant") + sig); - this.type = ts.get(0); - } catch (DBusException DBe) { - if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); - throw new IllegalArgumentException(MessageFormat.format(getString("cannotWrapUnqualifiedVariant"), new Object[]{sig, DBe.getMessage()})); - } - this.o = o; - } - - /** - * Return the wrapped value. - */ - public T getValue() { - return o; - } - - /** - * Return the type of the wrapped value. - */ - public Type getType() { - return type; - } - - /** - * Return the dbus signature of the wrapped value. - */ - public String getSig() { - return sig; - } - - /** - * Format the Variant as a string. - */ - public String toString() { - return "[" + o + "]"; - } - - /** - * Compare this Variant with another by comparing contents - */ - @SuppressWarnings("unchecked") - public boolean equals(Object other) { - if (null == other) return false; - if (!(other instanceof Variant)) return false; - return this.o.equals(((Variant) other).o); - } -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusIgnore.java b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusIgnore.java new file mode 100644 index 0000000000..1cdd5b4de6 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusIgnore.java @@ -0,0 +1,28 @@ +package org.freedesktop.dbus.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Mark an exported method as ignored.
+ * It will not be included in introspection data, and it will not be + * remotely callable. This is only useful for a local DBus object, it has no meaning to remote objects. + *

+ * Usage: + *

+ *
+ * {@literal @}DBusInterfaceName("com.example.Bar")
+ * public interface Bar extends DBusInterface {
+ *
+ *     {@literal @}DBusIgnore
+ *     public void doSomethingInternal() {
+ *     }
+ * }
+ * 
+ */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface DBusIgnore { +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusInterfaceName.java b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusInterfaceName.java new file mode 100644 index 0000000000..3559360276 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusInterfaceName.java @@ -0,0 +1,18 @@ +package org.freedesktop.dbus.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Force the interface name to be different to the Java class name. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DBusInterfaceName { + /** The replacement interface name. + * @return value + */ + String value(); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusMemberName.java b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusMemberName.java new file mode 100644 index 0000000000..d0720dad31 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusMemberName.java @@ -0,0 +1,20 @@ +package org.freedesktop.dbus.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Force the member (method/signal) name on the bus to be different to the Java name. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ + ElementType.TYPE, ElementType.METHOD +}) +public @interface DBusMemberName { + /** The replacement member name. + * @return value + */ + String value(); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusProperties.java b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusProperties.java new file mode 100644 index 0000000000..dcda801335 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusProperties.java @@ -0,0 +1,24 @@ +package org.freedesktop.dbus.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Container for the multiple {@link DBusProperty} annotations in the single class. + * You probably don't want to use this annotation in your code, please use {@link DBusProperty}. + * + * @see DBusProperty + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface DBusProperties { + + /** + * Container for multiple properties + * + * @return value + */ + DBusProperty[] value(); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusProperty.java b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusProperty.java new file mode 100644 index 0000000000..c815c6e458 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DBusProperty.java @@ -0,0 +1,74 @@ +package org.freedesktop.dbus.annotations; + +import org.freedesktop.dbus.types.Variant; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Appends information about properties in the interface. The annotated properties are added to the introspection data. + * In case of complex type of the property please use {@link org.freedesktop.dbus.TypeRef}. + *

+ * Usage: + *

+ *
+ * {@literal @}DBusInterfaceName("com.example.Bar")
+ * {@literal @}DBusProperty(name = "Name", type = String.class)
+ * {@literal @}DBusProperty(name = "ListOfVariables", type = List.class, access = Access.READ)
+ * {@literal @}DBusProperty(name = "MapOfStringList", type = ComplexTypeWithMapAndList.class, access = Access.READ)
+ * public interface Bar extends DBusInterface {
+ *
+ *   // TypeRef allows to provide detailed information about type
+ *   interface ComplexTypeWithMapAndList extends TypeRef<Map<String, List<String>>> {
+ *   }
+ * }
+ * 
+ * + * @see org.freedesktop.dbus.interfaces.DBusInterface + * @see org.freedesktop.dbus.TypeRef + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Repeatable(DBusProperties.class) +public @interface DBusProperty { + + /** + * Property name + * + * @return name + */ + String name(); + + /** + * type of the property, in case of complex types please create custom interface that extends {@link org.freedesktop.dbus.TypeRef} + * + * @return type + */ + Class type() default Variant.class; + + /** + * Property access type + * + * @return access + */ + Access access() default Access.READ_WRITE; + + enum Access { + READ("read"), + READ_WRITE("readwrite"), + WRITE("write"); + + private final String accessName; + + Access(String _accessName) { + this.accessName = _accessName; + } + + public String getAccessName() { + return accessName; + } + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DeprecatedOnDBus.java b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DeprecatedOnDBus.java new file mode 100644 index 0000000000..c740bdd8f5 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/DeprecatedOnDBus.java @@ -0,0 +1,19 @@ +package org.freedesktop.dbus.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Indicates that a DBus interface or method is deprecated + */ +@Retention(RetentionPolicy.RUNTIME) +@DBusInterfaceName("org.freedesktop.DBus.Deprecated") +public @interface DeprecatedOnDBus { + + /** + * Annotation value, true by default + * + * @return true when the annotated element deprecated + */ + boolean value() default true; +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/GlibCSymbol.java b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/GlibCSymbol.java new file mode 100644 index 0000000000..f7db73c895 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/GlibCSymbol.java @@ -0,0 +1,16 @@ +package org.freedesktop.dbus.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Define a C symbol to map to this method. Used by GLib only + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@DBusInterfaceName("org.freedesktop.DBus.GLib.CSymbol") +public @interface GlibCSymbol { + String value(); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/IntrospectionDescription.java b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/IntrospectionDescription.java new file mode 100644 index 0000000000..446e7f3ffb --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/IntrospectionDescription.java @@ -0,0 +1,13 @@ +package org.freedesktop.dbus.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** +* Description of the interface or method, returned in the introspection data +*/ +@Retention(RetentionPolicy.RUNTIME) +@DBusInterfaceName("org.freedesktop.DBus.Description") +public @interface IntrospectionDescription { + String value(); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/MethodError.java b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/MethodError.java new file mode 100644 index 0000000000..b19559e6cd --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/MethodError.java @@ -0,0 +1,16 @@ +package org.freedesktop.dbus.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Give an error that the method can return + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@DBusInterfaceName("org.freedesktop.DBus.Method.Error") +public @interface MethodError { + String value(); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/MethodNoReply.java b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/MethodNoReply.java new file mode 100644 index 0000000000..02cc2bd172 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/MethodNoReply.java @@ -0,0 +1,22 @@ +package org.freedesktop.dbus.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Methods annotated with this do not send a reply + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@DBusInterfaceName("org.freedesktop.DBus.Method.NoReply") +public @interface MethodNoReply { + + /** + * Annotation value, true by default + * + * @return true when a method doesn't send a reply + */ + boolean value() default true; +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/Position.java b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/Position.java new file mode 100644 index 0000000000..3debbaa862 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/Position.java @@ -0,0 +1,19 @@ +package org.freedesktop.dbus.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Position annotation, to annotate Struct fields + * to be sent over DBus. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Position { + /** The order of this field in the Struct. + * @return value + */ + int value(); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/PropertiesEmitsChangedSignal.java b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/PropertiesEmitsChangedSignal.java new file mode 100644 index 0000000000..d6ea994b0f --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/annotations/PropertiesEmitsChangedSignal.java @@ -0,0 +1,40 @@ +package org.freedesktop.dbus.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * From DBUS Specification:
+ * If set to false, the org.freedesktop.DBus.Properties.PropertiesChanged signal,
+ * see the section called “org.freedesktop.DBus.Properties” is not guaranteed to be emitted if the property changes.
+ *
+ * If set to const the property never changes value during the lifetime of the object it belongs to,
+ * and hence the signal is never emitted for it.
+ *
+ * If set to invalidates the signal is emitted but the value is not included in the signal.
+ *
+ * If set to true the signal is emitted with the value included.
+ * The value for the annotation defaults to true if the enclosing interface element does not specify the annotation. + * Otherwise it defaults to the value specified in the enclosing interface element.
+ *
+ * This annotation is intended to be used by code generators to implement client-side caching of property values.
+ * For all properties for which the annotation is set to const, invalidates or true the client may unconditionally
+ * cache the values as the properties don't change or notifications are generated for them if they do. + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@DBusInterfaceName("org.freedesktop.DBus.Property.EmitsChangedSignal") +public @interface PropertiesEmitsChangedSignal { + EmitChangeSignal value(); + + enum EmitChangeSignal { + TRUE, INVALIDATES, CONST, FALSE; + + @Override + public String toString() { + return name().toLowerCase(); + } + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/bin/DBusDaemon.java b/federation/sssd/src/main/java/org/freedesktop/dbus/bin/DBusDaemon.java new file mode 100644 index 0000000000..286fa8ffb0 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/bin/DBusDaemon.java @@ -0,0 +1,875 @@ +package org.freedesktop.dbus.bin; + +import org.freedesktop.dbus.Marshalling; +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.connections.transports.AbstractTransport; +import org.freedesktop.dbus.connections.transports.TransportBuilder; +import org.freedesktop.dbus.connections.transports.TransportBuilder.SaslAuthMode; +import org.freedesktop.dbus.connections.transports.TransportConnection; +import org.freedesktop.dbus.errors.AccessDenied; +import org.freedesktop.dbus.errors.Error; +import org.freedesktop.dbus.errors.MatchRuleInvalid; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.freedesktop.dbus.interfaces.DBus; +import org.freedesktop.dbus.interfaces.DBus.NameOwnerChanged; +import org.freedesktop.dbus.interfaces.FatalException; +import org.freedesktop.dbus.interfaces.Introspectable; +import org.freedesktop.dbus.interfaces.Peer; +import org.freedesktop.dbus.messages.DBusSignal; +import org.freedesktop.dbus.messages.Message; +import org.freedesktop.dbus.messages.MethodCall; +import org.freedesktop.dbus.messages.MethodReturn; +import org.freedesktop.dbus.types.UInt32; +import org.freedesktop.dbus.types.Variant; +import org.freedesktop.dbus.utils.Hexdump; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Closeable; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationTargetException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.BlockingDeque; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A replacement DBusDaemon + */ +public class DBusDaemon extends Thread implements Closeable { + public static final int QUEUE_POLL_WAIT = 500; + + private static final Logger LOGGER = + LoggerFactory.getLogger(DBusDaemon.class); + + private final Map conns = + new ConcurrentHashMap<>(); + private final Map names = + Collections.synchronizedMap(new HashMap<>()); // required because of "null" key + + private final BlockingDeque>> outqueue = + new LinkedBlockingDeque<>(); + private final BlockingDeque>> inqueue = + new LinkedBlockingDeque<>(); + + private final List sigrecips = new ArrayList<>(); + private final DBusServer dbusServer = new DBusServer(); + + private final DBusDaemonSenderThread sender = + new DBusDaemonSenderThread(); + private final AtomicBoolean run = + new AtomicBoolean(false); + private final AtomicInteger nextUnique = new AtomicInteger(0); + + private final AbstractTransport transport; + + public DBusDaemon(AbstractTransport _transport) { + setName(getClass().getSimpleName() + "-Thread"); + transport = _transport; + names.put("org.freedesktop.DBus", null); + } + + private void send(ConnectionStruct _connStruct, Message _msg) { + send(_connStruct, _msg, false); + } + + private void send(ConnectionStruct _connStruct, Message _msg, boolean _head) { + + // send to all connections + if (null == _connStruct) { + LOGGER.trace("Queuing message {} for all connections", _msg); + synchronized (conns) { + for (ConnectionStruct d : conns.keySet()) { + if (_head) { + outqueue.addFirst(new Pair<>(_msg, new WeakReference<>(d))); + } else { + outqueue.addLast(new Pair<>(_msg, new WeakReference<>(d))); + } + } + } + } else { + LOGGER.trace("Queuing message {} for {}", _msg, _connStruct.unique); + if (_head) { + outqueue.addFirst(new Pair<>(_msg, new WeakReference<>(_connStruct))); + } else { + outqueue.addLast(new Pair<>(_msg, new WeakReference<>(_connStruct))); + } + } + } + + @Override + public void run() { + run.set(true); + sender.start(); + + while (isRunning()) { + try { + Pair> pollFirst = inqueue.take(); + ConnectionStruct connectionStruct = pollFirst.second.get(); + if (connectionStruct != null) { + Message m = pollFirst.first; + logMessage(" Got message {} from {}", m, connectionStruct.unique); + + // check if they have hello'd + if (null == connectionStruct.unique && (!(m instanceof MethodCall) || !"org.freedesktop.DBus".equals(m.getDestination()) || !"Hello".equals(m.getName()))) { + send(connectionStruct, new Error("org.freedesktop.DBus", null, "org.freedesktop.DBus.Error.AccessDenied", m.getSerial(), "s", "You must send a Hello message")); + } else { + try { + if (null != connectionStruct.unique) { + m.setSource(connectionStruct.unique); + LOGGER.trace("Updated source to {}", connectionStruct.unique); + } + } catch (DBusException _ex) { + LOGGER.debug("Error setting source", _ex); + send(connectionStruct, new Error("org.freedesktop.DBus", null, "org.freedesktop.DBus.Error.GeneralError", m.getSerial(), "s", "Sending message failed")); + } + + if ("org.freedesktop.DBus".equals(m.getDestination())) { + dbusServer.handleMessage(connectionStruct, pollFirst.first); + } else { + if (m instanceof DBusSignal) { + List l; + synchronized (sigrecips) { + l = new ArrayList<>(sigrecips); + } + List list = l; + for (ConnectionStruct d : list) { + send(d, m); + } + } else { + ConnectionStruct dest = names.get(m.getDestination()); + + if (null == dest) { + send(connectionStruct, new Error("org.freedesktop.DBus", null, + "org.freedesktop.DBus.Error.ServiceUnknown", m.getSerial(), "s", + String.format("The name `%s' does not exist", m.getDestination()))); + } else { + send(dest, m); + } + } + } + } + } + + } catch (DBusException _ex) { + LOGGER.debug("Error processing connection", _ex); + } catch (InterruptedException _ex) { + LOGGER.debug("Interrupted"); + close(); + interrupt(); + } + } + + } + + private static void logMessage(String _logStr, Message _m, String _connUniqueId) { + Object logMsg = _m; + if (_m != null && Introspectable.class.getName().equals(_m.getInterface()) && !LOGGER.isTraceEnabled()) { + logMsg = ""; + } + + if (LOGGER.isTraceEnabled()) { + LOGGER.trace(_logStr, logMsg, _connUniqueId); + } else { + LOGGER.debug(_logStr, _m, _connUniqueId); + } + } + + public synchronized boolean isRunning() { + return run.get(); + } + + @Override + public void close() { + run.set(false); + if (!conns.isEmpty()) { + // disconnect all remaining connection + Set connections = new HashSet<>(conns.keySet()); + for (ConnectionStruct c : connections) { + removeConnection(c); + } + } + sender.terminate(); + if (transport != null && transport.isConnected()) { + LOGGER.debug("Terminating transport {}", transport); + try { + // shutdown listener + transport.close(); + } catch (IOException _ex) { + LOGGER.debug("Error closing transport", _ex); + } + } + } + + private void removeConnection(ConnectionStruct _c) { + + boolean exists = false; + synchronized (conns) { + if (conns.containsKey(_c)) { + DBusDaemonReaderThread r = conns.get(_c); + exists = true; + r.terminate(); + conns.remove(_c); + } + } + if (exists) { + try { + if (null != _c.connection) { + _c.connection.close(); + } + } catch (IOException _exIo) { + LOGGER.trace("Error while closing socketchannel", _exIo); + } + + synchronized (names) { + List toRemove = new ArrayList<>(); + for (String name : names.keySet()) { + if (names.get(name) == _c) { + toRemove.add(name); + try { + send(null, new NameOwnerChanged("/org/freedesktop/DBus", name, _c.unique, "")); + } catch (DBusException _ex) { + LOGGER.debug("", _ex); + } + } + } + for (String name : toRemove) { + names.remove(name); + } + } + } + + } + + void addSock(TransportConnection _s) throws IOException { + LOGGER.debug("New Client"); + + ConnectionStruct c = new ConnectionStruct(_s); + DBusDaemonReaderThread r = new DBusDaemonReaderThread(c); + conns.put(c, r); + r.start(); + } + + public static void syntax() { + System.out.println("Syntax: DBusDaemon [--version] [-v] [--help] [-h] [--listen address] " + + "[-l address] [--print-address] [-r] [--pidfile file] [-p file] [--addressfile file] " + + "[--auth-mode AUTH_ANONYMOUS|AUTH_COOKIE|AUTH_EXTERNAL] [-m AUTH_ANONYMOUS|AUTH_COOKIE|AUTH_EXTERNAL]" + + "[-a file] [--unix] [-u] [--tcp] [-t] "); + System.exit(1); + } + + public static void version() { + System.out.println("D-Bus Java Version: " + System.getProperty("Version")); + System.exit(1); + } + + public static void saveFile(String _data, String _file) throws IOException { + try (PrintWriter w = new PrintWriter(new FileOutputStream(_file))) { + w.println(_data); + } + } + + public static void main(String[] _args) throws Exception { + + String addr = null; + String pidfile = null; + String addrfile = null; + String authModeStr = null; + boolean printaddress = false; + boolean unix = true; + boolean tcp = false; + // parse options + try { + for (int i = 0; i < _args.length; i++) { + if ("--help".equals(_args[i]) || "-h".equals(_args[i])) { + syntax(); + } else if ("--version".equals(_args[i]) || "-v".equals(_args[i])) { + version(); + } else if ("--listen".equals(_args[i]) || "-l".equals(_args[i])) { + addr = _args[++i]; + } else if ("--pidfile".equals(_args[i]) || "-p".equals(_args[i])) { + pidfile = _args[++i]; + } else if ("--addressfile".equals(_args[i]) || "-a".equals(_args[i])) { + addrfile = _args[++i]; + } else if ("--print-address".equals(_args[i]) || "-r".equals(_args[i])) { + printaddress = true; + } else if ("--unix".equals(_args[i]) || "-u".equals(_args[i])) { + unix = true; + tcp = false; + } else if ("--tcp".equals(_args[i]) || "-t".equals(_args[i])) { + tcp = true; + unix = false; + } else if ("--auth-mode".equals(_args[i]) || "-m".equals(_args[i])) { + authModeStr = _args[++i]; + } else { + syntax(); + } + } + } catch (ArrayIndexOutOfBoundsException _ex) { + syntax(); + } + + // generate a random address if none specified + if (null == addr && unix) { + addr = TransportBuilder.createDynamicSession("UNIX", true); + } else if (null == addr && tcp) { + addr = TransportBuilder.createDynamicSession("TCP", true); + } + + BusAddress address = BusAddress.of(addr); + + // print address to stdout + if (printaddress) { + System.out.println(addr); + } + + SaslAuthMode saslAuthMode = null; + if (authModeStr != null) { + String selectedMode = authModeStr; + saslAuthMode = Arrays.stream(SaslAuthMode.values()) + .filter(e -> e.name().toLowerCase().matches(selectedMode.toLowerCase())) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Auth mode '" + selectedMode + "' unsupported")); + } + + // print address to file + if (null != addrfile) { + saveFile(addr, addrfile); + } + + // print PID to file + if (null != pidfile) { + saveFile(System.getProperty("Pid"), pidfile); + } + + // start the daemon + LOGGER.info("Binding to {}", addr); + try (EmbeddedDBusDaemon daemon = new EmbeddedDBusDaemon(address)) { + daemon.setSaslAuthMode(saslAuthMode); + daemon.startInForeground(); + } + + } + + /** + * Create a 'NameAcquired' signal manually. + * This is required because the implementation in DBusNameAquired is for receiving of this signal only. + * + * @param _name name to announce + * + * @return signal + * @throws DBusException if signal creation fails + */ + private DBusSignal generateNameAcquiredSignal(String _name) throws DBusException { + return new DBusSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameAcquired", "s", _name); + } + + /** + * Create a 'NameOwnerChanged' signal manually. + * This is required because the implementation in DBusNameAquired is for receiving of this signal only. + * + * @param _name name to announce + * @param _oldOwner previous owner + * @param _newOwner new owner + * + * @return signal + * @throws DBusException if signal creation fails + */ + private DBusSignal generatedNameOwnerChangedSignal(String _name, String _oldOwner, String _newOwner) throws DBusException { + return new DBusSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", "sss", _name, _oldOwner, _newOwner); + } + + public static class ConnectionStruct { + private final TransportConnection connection; + private String unique; + + ConnectionStruct(TransportConnection _c) throws IOException { + connection = _c; + } + + @Override + public String toString() { + return null == unique ? ":?-?" : unique; + } + } + + public class DBusServer implements DBus, Introspectable, Peer { + + private final String machineId; + private ConnectionStruct connStruct; + + public DBusServer() { + String ascii; + try { + ascii = Hexdump.toAscii(MessageDigest.getInstance("MD5").digest(InetAddress.getLocalHost().getHostName().getBytes())); + } catch (NoSuchAlgorithmException | UnknownHostException _ex) { + ascii = this.hashCode() + ""; + } + + machineId = ascii; + } + + @Override + public boolean isRemote() { + return false; + } + + @Override + public String Hello() { + synchronized (connStruct) { + if (null != connStruct.unique) { + throw new AccessDenied("Connection has already sent a Hello message"); + } + connStruct.unique = ":1." + nextUnique.incrementAndGet(); + } + names.put(connStruct.unique, connStruct); + + LOGGER.info("Client {} registered", connStruct.unique); + + try { + send(connStruct, generateNameAcquiredSignal(connStruct.unique)); + send(null, generatedNameOwnerChangedSignal(connStruct.unique, "", connStruct.unique)); + } catch (DBusException _ex) { + LOGGER.debug("", _ex); + } + + return connStruct.unique; + } + + @Override + public String[] ListNames() { + String[] ns; + Set nss = names.keySet(); + ns = nss.toArray(new String[0]); + return ns; + } + + @Override + public boolean NameHasOwner(String _name) { + return names.containsKey(_name); + } + + @Override + public String GetNameOwner(String _name) { + + ConnectionStruct owner = names.get(_name); + String o; + if (null == owner) { + o = ""; + } else { + o = owner.unique; + } + + return o; + } + + @Override + public UInt32 GetConnectionUnixUser(String _connectionName) { + return new UInt32(0); + } + + @Override + public UInt32 StartServiceByName(String _name, UInt32 _flags) { + return new UInt32(0); + } + + @Override + @SuppressWarnings("checkstyle:innerassignment") + public UInt32 RequestName(String _name, UInt32 _flags) { + boolean exists = false; + synchronized (names) { + if (!(exists = names.containsKey(_name))) { + names.put(_name, connStruct); + } + } + + int rv; + if (exists) { + rv = DBus.DBUS_REQUEST_NAME_REPLY_EXISTS; + } else { + + LOGGER.info("Client {} acquired name {}", connStruct.unique, _name); + + rv = DBus.DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER; + try { + send(connStruct, generateNameAcquiredSignal(_name)); + send(null, generatedNameOwnerChangedSignal(_name, "", connStruct.unique)); + } catch (DBusException _ex) { + LOGGER.debug("", _ex); + } + } + return new UInt32(rv); + } + + @Override + public UInt32 ReleaseName(String _name) { + + boolean exists = false; + synchronized (names) { + if (names.containsKey(_name) && names.get(_name).equals(connStruct)) { + exists = names.remove(_name) != null; + } + } + + int rv; + if (!exists) { + rv = DBus.DBUS_RELEASE_NAME_REPLY_NON_EXISTANT; + } else { + LOGGER.info("Client {} acquired name {}", connStruct.unique, _name); + rv = DBus.DBUS_RELEASE_NAME_REPLY_RELEASED; + try { + send(connStruct, new NameLost("/org/freedesktop/DBus", _name)); + send(null, new NameOwnerChanged("/org/freedesktop/DBus", _name, connStruct.unique, "")); + } catch (DBusException _ex) { + LOGGER.debug("", _ex); + } + } + + return new UInt32(rv); + } + + @Override + public void AddMatch(String _matchrule) throws MatchRuleInvalid { + + LOGGER.trace("Adding match rule: {}", _matchrule); + + synchronized (sigrecips) { + if (!sigrecips.contains(connStruct)) { + sigrecips.add(connStruct); + } + } + } + + @Override + public void RemoveMatch(String _matchrule) throws MatchRuleInvalid { + LOGGER.trace("Removing match rule: {}", _matchrule); + } + + @Override + public String[] ListQueuedOwners(String _name) { + return new String[0]; + } + + @Override + public UInt32 GetConnectionUnixProcessID(String _connectionName) { + return new UInt32(0); + } + + @Override + public Byte[] GetConnectionSELinuxSecurityContext(String _args) { + return new Byte[0]; + } + + @SuppressWarnings("unchecked") + private void handleMessage(ConnectionStruct _connStruct, Message _msg) throws DBusException { + LOGGER.trace("Handling message {} from {}", _msg, _connStruct.unique); + + if (!(_msg instanceof MethodCall)) { + return; + } + Object[] args = _msg.getParameters(); + + Class[] cs = new Class[args.length]; + + for (int i = 0; i < cs.length; i++) { + cs[i] = args[i].getClass(); + } + + java.lang.reflect.Method meth = null; + Object rv = null; + + try { + meth = DBusServer.class.getMethod(_msg.getName(), cs); + try { + this.connStruct = _connStruct; + rv = meth.invoke(dbusServer, args); + if (null == rv) { + + send(_connStruct, new MethodReturn("org.freedesktop.DBus", (MethodCall) _msg, null), true); + } else { + String sig = Marshalling.getDBusType(meth.getGenericReturnType())[0]; + send(_connStruct, new MethodReturn("org.freedesktop.DBus", (MethodCall) _msg, sig, rv), true); + } + } catch (InvocationTargetException _exIte) { + LOGGER.debug("", _exIte); + send(_connStruct, new Error("org.freedesktop.DBus", _msg, _exIte.getCause())); + } catch (DBusExecutionException _exDnEe) { + LOGGER.debug("", _exDnEe); + send(_connStruct, new Error("org.freedesktop.DBus", _msg, _exDnEe)); + } catch (Exception _ex) { + LOGGER.debug("", _ex); + send(_connStruct, new Error("org.freedesktop.DBus", _connStruct.unique, + "org.freedesktop.DBus.Error.GeneralError", _msg.getSerial(), "s", "An error occurred while calling " + _msg.getName())); + } + } catch (NoSuchMethodException _exNsm) { + send(_connStruct, new Error("org.freedesktop.DBus", _connStruct.unique, + "org.freedesktop.DBus.Error.UnknownMethod", _msg.getSerial(), "s", "This service does not support " + _msg.getName())); + } + + } + + @Override + public String getObjectPath() { + return null; + } + + @Override + public String Introspect() { + return "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + ""; + } + + @Override + public void Ping() { + } + + @Override + public String[] ListActivatableNames() { + return null; + } + + @Override + public Map> GetConnectionCredentials(String _busName) { + return null; + } + + @Override + public Byte[] GetAdtAuditSessionData(String _busName) { + return null; + } + + @Override + public void UpdateActivationEnvironment(Map[] _environment) { + + } + + @Override + public String GetId() { + return null; + } + + @Override + public String GetMachineId() { + return machineId; + } + + } + + public class DBusDaemonSenderThread extends Thread { + private final Logger logger = LoggerFactory.getLogger(getClass()); + private final AtomicBoolean running = new AtomicBoolean(false); // switch running status when thread begins + + public DBusDaemonSenderThread() { + setName(getClass().getSimpleName().replace('$', '-')); + } + + @Override + public void run() { + logger.debug(">>>> Sender thread started <<<<"); + running.set(true); + while (isRunning() && running.get()) { + + logger.trace("Acquiring lock on outqueue and blocking for data"); + + // block on outqueue + try { + Pair> pollFirst = outqueue.take(); + if (pollFirst != null) { + ConnectionStruct connectionStruct = pollFirst.second.get(); + if (connectionStruct != null) { + if (connectionStruct.connection.getChannel().isConnected()) { + logger.debug(" Got message {} for {}", pollFirst.first, connectionStruct.unique); + + try { + connectionStruct.connection.getWriter().writeMessage(pollFirst.first); + } catch (IOException _ex) { + logger.debug("Disconnecting client due to previous exception", _ex); + removeConnection(connectionStruct); + } + } else { + logger.warn("Connection to {} broken", pollFirst.first.getDestination()); + removeConnection(connectionStruct); + } + + } else { + logger.info("Discarding {} connection reaped", pollFirst.first); + } + } + } catch (InterruptedException _ex) { + logger.debug("Got interrupted", _ex); + } + } + logger.debug(">>>> Sender Thread terminated <<<<"); + } + + public synchronized void terminate() { + running.set(false); + interrupt(); + } + } + + public class DBusDaemonReaderThread extends Thread { + private final Logger logger = LoggerFactory.getLogger(getClass()); + private ConnectionStruct conn; + private final WeakReference weakconn; + private final AtomicBoolean running = new AtomicBoolean(false); + + public DBusDaemonReaderThread(ConnectionStruct _conn) { + this.conn = _conn; + weakconn = new WeakReference<>(_conn); + setName(getClass().getSimpleName()); + } + + public void terminate() { + running.set(false); + } + + @Override + public void run() { + logger.debug(">>>> Reader Thread started <<<<"); + running.set(true); + while (isRunning() && running.get()) { + + Message m = null; + try { + m = conn.connection.getReader().readMessage(); + } catch (IOException _ex) { + LOGGER.debug("", _ex); + removeConnection(conn); + } catch (DBusException _ex) { + LOGGER.debug("", _ex); + if (_ex instanceof FatalException) { + removeConnection(conn); + } + } + + if (null != m) { + logMessage("Read {} from {}", m, conn.unique); + + inqueue.add(new Pair<>(m, weakconn)); + } + } + conn = null; + logger.debug(">>>> Reader Thread terminated <<<<"); + } + } + + static class Pair { + private final A first; + private final B second; + + Pair(A _first, B _second) { + first = _first; + second = _second; + } + + @Override + public int hashCode() { + return Objects.hash(first, second); + } + + @Override + public boolean equals(Object _obj) { + if (this == _obj) { + return true; + } + if (!(_obj instanceof Pair)) { + return false; + } + Pair other = (Pair) _obj; + return Objects.equals(first, other.first) && Objects.equals(second, other.second); + } + + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/bin/EmbeddedDBusDaemon.java b/federation/sssd/src/main/java/org/freedesktop/dbus/bin/EmbeddedDBusDaemon.java new file mode 100644 index 0000000000..b40792542d --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/bin/EmbeddedDBusDaemon.java @@ -0,0 +1,224 @@ +package org.freedesktop.dbus.bin; + +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.connections.transports.AbstractTransport; +import org.freedesktop.dbus.connections.transports.TransportBuilder; +import org.freedesktop.dbus.connections.transports.TransportBuilder.SaslAuthMode; +import org.freedesktop.dbus.connections.transports.TransportConnection; +import org.freedesktop.dbus.exceptions.AuthenticationException; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.SocketClosedException; +import org.freedesktop.dbus.utils.Util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Closeable; +import java.io.IOException; +import java.nio.file.attribute.PosixFilePermission; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Simple DBusDaemon implementation to use if no DBusDaemon is running on the OS level. + */ +public class EmbeddedDBusDaemon implements Closeable { + + private static final Logger LOGGER = LoggerFactory.getLogger(EmbeddedDBusDaemon.class); + + private final BusAddress address; + + private DBusDaemon daemon; + + private final AtomicBoolean closed = new AtomicBoolean(false); + private final AtomicBoolean connectionReady = new AtomicBoolean(false); + + private SaslAuthMode saslAuthMode; + + private String unixSocketFileOwner; + + private String unixSocketFileGroup; + + private PosixFilePermission[] unixSocketFilePermissions; + + public EmbeddedDBusDaemon(BusAddress _address) { + // create copy of address so manipulation happens later does not interfere with our instance + address = BusAddress.of(Objects.requireNonNull(_address, "Address required")); + } + + public EmbeddedDBusDaemon(String _address) throws DBusException { + this(BusAddress.of(_address)); + } + + /** + * Shutdown the running DBusDaemon instance. + */ + @Override + public synchronized void close() throws IOException { + closed.set(true); + connectionReady.set(false); + if (daemon != null) { + daemon.close(); + daemon = null; + } + } + + /** + * Run the DBusDaemon in foreground. + *

+ * This is a blocking operation. + */ + public void startInForeground() { + try { + startListening(); + } catch (IOException | DBusException _ex) { + if (!closed.get()) { + throw new RuntimeException(_ex); + } + } + } + + /** + * Start the DBusDaemon in background and returns immediately. + *

+ * This method may return before the background thread is ready. + * To ensure the the background thread is running on return use {@link #startInBackgroundAndWait(long)}. + */ + public void startInBackground() { + Thread thread = new Thread(this::startInForeground); + String threadName = address.toString().replaceAll("^([^,]+),.+", "$1"); + + thread.setName("EmbeddedDBusDaemon-" + threadName); + thread.setDaemon(true); + thread.setUncaughtExceptionHandler((th, ex) -> LOGGER.error("Got uncaught exception", ex)); + thread.start(); + } + + /** + * Starts the DBusDaemon in background. + *

+ * Will wait up to the given period of milliseconds for the background thread to get ready. + * If given wait time exceeded, a {@link RuntimeException} is thrown. + * + * @param _maxWaitMillis maximum wait time in milliseconds + */ + public void startInBackgroundAndWait(long _maxWaitMillis) { + startInBackground(); + Util.waitFor("EmbeddedDbusDaemon", this::isRunning, _maxWaitMillis, 100); + } + + /** + * Whether the DBusDaemon is still running. + * + * @return true if running, false otherwise + */ + public synchronized boolean isRunning() { + return connectionReady.get() && daemon != null && daemon.isRunning(); + } + + /** + * The currently configured {@link SaslAuthMode}. + * When null is returned, the {@link SaslAuthMode} of the transport provider is used. + * + * @return {@link SaslAuthMode} or null + */ + public SaslAuthMode getSaslAuthMode() { + return saslAuthMode; + } + + /** + * Use this to override the default authentication mode which would + * be used by the transport based on the {@link BusAddress}. + * + * @param _saslAuthMode auth mode, null to use default + */ + public void setSaslAuthMode(SaslAuthMode _saslAuthMode) { + saslAuthMode = _saslAuthMode; + } + + /** + * The file owner for the created unix socket.
+ * Ignored if TCP is used.
+ *
+ * Will only work if currently running JVM process user + * has suitable permissions to change the owner. + * + * @param _owner owner to set + */ + public void setUnixSocketOwner(String _owner) { + unixSocketFileOwner = _owner; + } + + /** + * The file group for the created unix socket.
+ * Ignored if TCP is used.
+ *
+ * Will only work if currently running JVM process user + * has suitable permissions to change the group. + * + * @param _group group to set + */ + public void setUnixSocketGroup(String _group) { + unixSocketFileGroup = _group; + } + + /** + * The file permissions for the created unix socket.
+ * Ignored if TCP is used or if the OS is Windows.
+ *
+ * Will only work if currently running JVM process user + * has suitable permissions to change the permissions. + * + * @param _permissions permissions to set + */ + public void setUnixSocketPermissions(PosixFilePermission... _permissions) { + unixSocketFilePermissions = _permissions; + } + + private synchronized void setDaemonAndStart(AbstractTransport _transport) { + daemon = new DBusDaemon(_transport); + daemon.start(); + } + + /** + * Start listening for incoming connections. + *

+ * Will throw {@link IllegalArgumentException} if a unsupported transport is used. + * + * @throws IOException when connection fails + * @throws DBusException when the provided bus address is wrong + */ + private void startListening() throws IOException, DBusException { + if (!TransportBuilder.getRegisteredBusTypes().contains(address.getBusType())) { + throw new IllegalArgumentException("Unknown or unsupported address type: " + address.getType()); + } + + LOGGER.debug("About to initialize transport on: {}", address); + try (AbstractTransport transport = TransportBuilder.create(address).configure() + .withUnixSocketFileOwner(unixSocketFileOwner) + .withUnixSocketFileGroup(unixSocketFileGroup) + .withUnixSocketFilePermissions(unixSocketFilePermissions) + .withAutoConnect(false) + .configureSasl().withAuthMode(getSaslAuthMode()).back() + .back() + .build()) { + + setDaemonAndStart(transport); + + // use tail-controlled loop so we at least try to get a client connection once + do { + try { + LOGGER.debug("Begin listening to: {}", transport); + connectionReady.set(true); + TransportConnection s = transport.listen(); + daemon.addSock(s); + } catch (AuthenticationException _ex) { + LOGGER.error("Authentication failed", _ex); + } catch (SocketClosedException _ex) { + LOGGER.debug("Connection closed", _ex); + } + + } while (daemon.isRunning()); + + } + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/config/DBusSysProps.java b/federation/sssd/src/main/java/org/freedesktop/dbus/config/DBusSysProps.java new file mode 100644 index 0000000000..5c142e67db --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/config/DBusSysProps.java @@ -0,0 +1,21 @@ +package org.freedesktop.dbus.config; + +/** + * Constant class containing all properties supported as system properties. + * + * @author hypfvieh + * @since v4.2.2 - 2023-01-20 + */ +public final class DBusSysProps { + public static final String SYSPROP_DBUS_TEST_HOME_DIR = "DBUS_TEST_HOMEDIR"; + + public static final String DBUS_SYSTEM_BUS_ADDRESS = "DBUS_SYSTEM_BUS_ADDRESS"; + public static final String DEFAULT_SYSTEM_BUS_ADDRESS = "unix:path=/var/run/dbus/system_bus_socket"; + public static final String DBUS_SESSION_BUS_ADDRESS = "DBUS_SESSION_BUS_ADDRESS"; + + public static final String DBUS_MACHINE_ID_SYS_VAR = "DBUS_MACHINE_ID_LOCATION"; + + public static final String DBUS_SESSION_BUS_ADDRESS_MACOS = "DBUS_LAUNCHD_SESSION_BUS_SOCKET"; + + private DBusSysProps() {} +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/AbstractConnection.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/AbstractConnection.java new file mode 100644 index 0000000000..f11ee68bf9 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/AbstractConnection.java @@ -0,0 +1,1308 @@ +package org.freedesktop.dbus.connections; + +import org.freedesktop.dbus.DBusAsyncReply; +import org.freedesktop.dbus.DBusCallInfo; +import org.freedesktop.dbus.DBusMatchRule; +import org.freedesktop.dbus.Marshalling; +import org.freedesktop.dbus.MethodTuple; +import org.freedesktop.dbus.RemoteInvocationHandler; +import org.freedesktop.dbus.RemoteObject; +import org.freedesktop.dbus.connections.config.ReceivingServiceConfig; +import org.freedesktop.dbus.connections.config.TransportConfig; +import org.freedesktop.dbus.connections.transports.AbstractTransport; +import org.freedesktop.dbus.connections.transports.TransportBuilder; +import org.freedesktop.dbus.errors.Error; +import org.freedesktop.dbus.errors.UnknownMethod; +import org.freedesktop.dbus.errors.UnknownObject; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.freedesktop.dbus.exceptions.FatalDBusException; +import org.freedesktop.dbus.exceptions.NotConnected; +import org.freedesktop.dbus.interfaces.CallbackHandler; +import org.freedesktop.dbus.interfaces.DBusInterface; +import org.freedesktop.dbus.interfaces.DBusSigHandler; +import org.freedesktop.dbus.messages.DBusSignal; +import org.freedesktop.dbus.messages.ExportedObject; +import org.freedesktop.dbus.messages.Message; +import org.freedesktop.dbus.messages.MethodCall; +import org.freedesktop.dbus.messages.MethodReturn; +import org.freedesktop.dbus.messages.ObjectTree; +import org.freedesktop.dbus.utils.LoggingHelper; +import org.freedesktop.dbus.utils.NameableThreadFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Closeable; +import java.io.EOFException; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.nio.ByteOrder; +import java.nio.channels.ClosedByInterruptException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +/** + * Handles a connection to DBus. + */ +public abstract class AbstractConnection implements Closeable { + + public static final boolean FLOAT_SUPPORT = null != System.getenv("DBUS_JAVA_FLOATS"); + public static final Pattern BUSNAME_REGEX = Pattern.compile("^[-_a-zA-Z][-_a-zA-Z0-9]*(\\.[-_a-zA-Z][-_a-zA-Z0-9]*)*$"); + public static final Pattern CONNID_REGEX = Pattern.compile("^:[0-9]*\\.[0-9]*$"); + public static final Pattern OBJECT_REGEX_PATTERN = Pattern.compile("^/([-_a-zA-Z0-9]+(/[-_a-zA-Z0-9]+)*)?$"); + public static final Pattern DOLLAR_PATTERN = Pattern.compile("[$]"); + + public static final int MAX_ARRAY_LENGTH = 67108864; + public static final int MAX_NAME_LENGTH = 255; + + /** + * Connect timeout, used for TCP only. + * @deprecated no longer used + */ + @Deprecated(forRemoval = true, since = "4.2.2 - 2022-12-23") + public static final int TCP_CONNECT_TIMEOUT = 100000; + + /** + * System property name containing the DBUS TCP SESSION address used by dbus-java DBusDaemon in TCP mode. + * @deprecated is no longer in use + */ + @Deprecated(since = "4.2.0 - 2022-08-04", forRemoval = true) + public static final String TCP_ADDRESS_PROPERTY = "DBUS_TCP_SESSION"; + + private static final Map INFOMAP = new ConcurrentHashMap<>(); + + /** Lame method to setup endianness used on DBus messages */ + private static byte endianness = getSystemEndianness(); + + private final Logger logger; + + private final ObjectTree objectTree; + + private final Map exportedObjects; + private final Map importedObjects; + + private final PendingCallbackManager callbackManager; + + private final FallbackContainer fallbackContainer; + + private final Queue pendingErrorQueue; + + private final Map>> handledSignals; + private final Map>> genericHandledSignals; + private final Map pendingCalls; + + private final IncomingMessageThread readerThread; + private final ExecutorService senderService; + private final ReceivingService receivingService; + private final TransportBuilder transportBuilder; + + private boolean weakreferences = false; + private volatile boolean disconnecting = false; + + private AbstractTransport transport; + + private Optional disconnectCallback = + Optional.ofNullable(null); + + protected AbstractConnection(TransportConfig _transportConfig, ReceivingServiceConfig _rsCfg) throws DBusException { + logger = LoggerFactory.getLogger(getClass()); + exportedObjects = Collections.synchronizedMap(new HashMap<>()); + importedObjects = new ConcurrentHashMap<>(); + + exportedObjects.put(null, new ExportedObject(new GlobalHandler(this), weakreferences)); + + handledSignals = new ConcurrentHashMap<>(); + genericHandledSignals = new ConcurrentHashMap<>(); + + pendingCalls = Collections.synchronizedMap(new LinkedHashMap<>()); + callbackManager = new PendingCallbackManager(); + + pendingErrorQueue = new ConcurrentLinkedQueue<>(); + + receivingService = new ReceivingService(_rsCfg); + senderService = + Executors.newFixedThreadPool(1, new NameableThreadFactory("DBus Sender Thread-", false)); + + objectTree = new ObjectTree(); + fallbackContainer = new FallbackContainer(); + + transportBuilder = TransportBuilder.create(_transportConfig); + readerThread = new IncomingMessageThread(this, transportBuilder.getAddress()); + + try { + transport = transportBuilder.build(); + } catch (IOException | DBusException _ex) { + logger.debug("Error creating transport", _ex); + if (_ex instanceof IOException) { + internalDisconnect((IOException) _ex); + } + throw new DBusException("Failed to connect to bus: " + _ex.getMessage(), _ex); + } + } + + /** + * Retrieves an remote object using source and path. + * Will try to find suitable exported DBusInterface automatically. + * + * @param _source source + * @param _path path + * + * @return {@link DBusInterface} compatible object + */ + public abstract DBusInterface getExportedObject(String _source, String _path) throws DBusException; + + /** + * Retrieves an remote object using source and path. + * Will use the given type as object class. + * + * @param _source source + * @param _path path + * @param _type class of remote object + * + * @return {@link DBusInterface} compatible object + */ + public abstract T getExportedObject(String _source, String _path, Class _type) throws DBusException; + + /** + * Remove a match rule with the given {@link DBusSigHandler}. + * The rule will only be removed from DBus if no other additional handlers are registered to the same rule. + * + * @param _rule rule to remove + * @param _handler handler to remove + * + * @param signal type + * + * @throws DBusException on error + */ + protected abstract void removeSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) throws DBusException; + + /** + * Add a signal handler with the given {@link DBusMatchRule} to DBus. + * The rule will be added to DBus if it was not added before. + * If the rule was already added, the signal handler is added to the internal map receiving + * the same signal as the first (and additional) handlers for this rule. + * + * @param _rule rule to add + * @param _handler handler to use + * + * @param signal type + * @return closeable that removes signal handler + * + * @throws DBusException on error + */ + protected abstract AutoCloseable addSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) throws DBusException; + + /** + * Remove a generic signal handler with the given {@link DBusMatchRule}. + * The rule will only be removed from DBus if no other additional handlers are registered to the same rule. + * + * @param _rule rule to remove + * @param _handler handler to remove + * @throws DBusException on error + */ + protected abstract void removeGenericSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) throws DBusException; + + /** + * Adds a {@link DBusMatchRule} to with a generic signal handler. + * Generic signal handlers allow receiving different signals with the same handler. + * If the rule was already added, the signal handler is added to the internal map receiving + * the same signal as the first (and additional) handlers for this rule. + * + * @param _rule rule to add + * @param _handler handler to use + * @return closeable that removes signal handler + * @throws DBusException on error + */ + protected abstract AutoCloseable addGenericSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) throws DBusException; + + /** + * The generated UUID of this machine. + * @return String + */ + public abstract String getMachineId(); + + /** + * If given type is null, will try to find suitable types by examining the given ifaces. + * If a non-null type is given, returns the given type. + * + * @param any DBusInterface compatible object + * @param _type type or null + * @param _ifaces interfaces to examining when type is null + * + * @return List + */ + protected List> findMatchingTypes(Class _type, List _ifaces) { + List> ifcs = new ArrayList<>(); + if (_type == null) { + for (String iface : _ifaces) { + + logger.debug("Trying interface {}", iface); + int j = 0; + while (j >= 0) { + try { + Class ifclass = Class.forName(iface); + if (!ifcs.contains(ifclass)) { + ifcs.add(ifclass); + } + break; + } catch (Exception _ex) { + logger.trace("No class found for {}", iface, _ex); + } + j = iface.lastIndexOf('.'); + char[] cs = iface.toCharArray(); + if (j >= 0) { + cs[j] = '$'; + iface = String.valueOf(cs); + } + } + } + } else { + ifcs.add(_type); + } + return ifcs; + } + + /** + * Start reading and sending messages. + */ + protected void listen() { + readerThread.start(); + } + + public String getExportedObject(DBusInterface _interface) throws DBusException { + + Optional> foundInterface = + getExportedObjects().entrySet().stream() + .filter(e -> _interface.equals(e.getValue().getObject().get())) + .findFirst(); + if (foundInterface.isPresent()) { + return foundInterface.get().getKey(); + } else { + RemoteObject rObj = getImportedObjects().get(_interface); + if (rObj != null) { + String s = rObj.getObjectPath(); + if (s != null) { + return s; + } + } + + throw new DBusException("Not an object exported or imported by this connection"); + } + + } + + /** + * Change the number of worker threads to receive method calls and handle signals. Default is 4 threads + * + * @param _newPoolSize + * The new number of worker Threads to use. + * @deprecated does nothing as threading has been changed significantly + */ + @Deprecated(forRemoval = true, since = "4.1.0") + public void changeThreadCount(byte _newPoolSize) { + + } + + /** + * If set to true the bus will not hold a strong reference to exported objects. If they go out of scope they will + * automatically be unexported from the bus. The default is to hold a strong reference, which means objects must be + * explicitly unexported before they will be garbage collected. + * + * @param _weakreferences + * reference + */ + public void setWeakReferences(boolean _weakreferences) { + this.weakreferences = _weakreferences; + } + + /** + * Export an object so that its methods can be called on DBus. + * + * @param _objectPath + * The path to the object we are exposing. MUST be in slash-notation, like "/org/freedesktop/Local", and + * SHOULD end with a capitalised term. Only one object may be exposed on each path at any one time, but + * an object may be exposed on several paths at once. + * @param _object + * The object to export. + * @throws DBusException + * If the objectpath is already exporting an object. or if objectpath is incorrectly formatted, + */ + public void exportObject(String _objectPath, DBusInterface _object) throws DBusException { + if (null == _objectPath || _objectPath.isEmpty()) { + throw new DBusException("Must Specify an Object Path"); + } + if (_objectPath.length() > MAX_NAME_LENGTH || !(OBJECT_REGEX_PATTERN.matcher(_objectPath).matches())) { + throw new DBusException("Invalid object path: " + _objectPath); + } + synchronized (getExportedObjects()) { + if (null != getExportedObjects().get(_objectPath)) { + throw new DBusException("Object already exported"); + } + ExportedObject eo = new ExportedObject(_object, weakreferences); + getExportedObjects().put(_objectPath, eo); + synchronized (getObjectTree()) { + getObjectTree().add(_objectPath, eo, eo.getIntrospectiondata()); + } + } + } + + /** + * Export an object so that its methods can be called on DBus. The path to the object will be taken from the + * {@link DBusInterface#getObjectPath()} method, make sure it is implemented and returns immutable value. + * If you want export object with multiple paths, please use {@link AbstractConnection#exportObject(String, DBusInterface)}. + * + * @param _object + * The object to export. + * @throws DBusException + * If the object path is already exporting an object or if object path is incorrectly formatted. + */ + public void exportObject(DBusInterface _object) throws DBusException { + Objects.requireNonNull(_object, "object must not be null"); + exportObject(_object.getObjectPath(), _object); + } + + /** + * Export an object as a fallback object. This object will have it's methods invoked for all paths starting with + * this object path. + * + * @param _objectPrefix + * The path below which the fallback handles calls. MUST be in slash-notation, like + * "/org/freedesktop/Local", + * @param _object + * The object to export. + * @throws DBusException + * If the objectpath is incorrectly formatted, + */ + public void addFallback(String _objectPrefix, DBusInterface _object) throws DBusException { + if (null == _objectPrefix || _objectPrefix.isEmpty()) { + throw new DBusException("Must Specify an Object Path"); + } + if (_objectPrefix.length() > MAX_NAME_LENGTH || !OBJECT_REGEX_PATTERN.matcher(_objectPrefix).matches()) { + throw new DBusException("Invalid object path: " + _objectPrefix); + } + ExportedObject eo = new ExportedObject(_object, weakreferences); + fallbackContainer.add(_objectPrefix, eo); + } + + /** + * Remove a fallback + * + * @param _objectprefix + * The prefix to remove the fallback for. + */ + public void removeFallback(String _objectprefix) { + fallbackContainer.remove(_objectprefix); + } + + /** + * Stop Exporting an object + * + * @param _objectpath + * The objectpath to stop exporting. + */ + public void unExportObject(String _objectpath) { + synchronized (getExportedObjects()) { + getExportedObjects().remove(_objectpath); + getObjectTree().remove(_objectpath); + } + } + + /** + * Send a message or signal to the DBus daemon. + * @param _message message to send + */ + public void sendMessage(Message _message) { + if (!isConnected()) { + throw new NotConnected("Cannot send message: Not connected"); + } + + Runnable runnable = new Runnable() { + @Override + public void run() { + sendMessageInternally(_message); + } + }; + + senderService.execute(runnable); + } + + /** + * Remove a Signal Handler. Stops listening for this signal. + * + * @param + * class extending {@link DBusSignal} + * @param _type + * The signal to watch for. + * @param _handler + * the handler + * @throws DBusException + * If listening for the signal on the bus failed. + * @throws ClassCastException + * If type is not a sub-type of DBusSignal. + */ + public void removeSigHandler(Class _type, DBusSigHandler _handler) throws DBusException { + if (!DBusSignal.class.isAssignableFrom(_type)) { + throw new ClassCastException("Not A DBus Signal"); + } + + removeSigHandler(new DBusMatchRule(_type), _handler); + } + + /** + * Remove a Signal Handler. Stops listening for this signal. + * + * @param + * class extending {@link DBusSignal} + * @param _type + * The signal to watch for. + * @param _object + * The object emitting the signal. + * @param _handler + * the handler + * @throws DBusException + * If listening for the signal on the bus failed. + * @throws ClassCastException + * If type is not a sub-type of DBusSignal. + */ + public void removeSigHandler(Class _type, DBusInterface _object, DBusSigHandler _handler) + throws DBusException { + if (!DBusSignal.class.isAssignableFrom(_type)) { + throw new ClassCastException("Not A DBus Signal"); + } + String objectpath = getImportedObjects().get(_object).getObjectPath(); + if (objectpath.length() > MAX_NAME_LENGTH || !OBJECT_REGEX_PATTERN.matcher(objectpath).matches()) { + throw new DBusException("Invalid object path: " + objectpath); + } + removeSigHandler(new DBusMatchRule(_type, null, objectpath), _handler); + } + + /** + * Add a Signal Handler. Adds a signal handler to call when a signal is received which matches the specified type + * and name. + * + * @param + * class extending {@link DBusSignal} + * @param _type + * The signal to watch for. + * @param _handler + * The handler to call when a signal is received. + * @return closeable that removes signal handler + * @throws DBusException + * If listening for the signal on the bus failed. + * @throws ClassCastException + * If type is not a sub-type of DBusSignal. + */ + public AutoCloseable addSigHandler(Class _type, DBusSigHandler _handler) throws DBusException { + if (!DBusSignal.class.isAssignableFrom(_type)) { + throw new ClassCastException("Not A DBus Signal"); + } + return addSigHandler(new DBusMatchRule(_type), _handler); + } + + /** + * Add a Signal Handler. Adds a signal handler to call when a signal is received which matches the specified type, + * name and object. + * + * @param + * class extending {@link DBusSignal} + * @param _type + * The signal to watch for. + * @param _object + * The object from which the signal will be emitted + * @param _handler + * The handler to call when a signal is received. + * @return closeable that removes signal handler + * @throws DBusException + * If listening for the signal on the bus failed. + * @throws ClassCastException + * If type is not a sub-type of DBusSignal. + */ + public AutoCloseable addSigHandler(Class _type, DBusInterface _object, DBusSigHandler _handler) + throws DBusException { + if (!DBusSignal.class.isAssignableFrom(_type)) { + throw new ClassCastException("Not A DBus Signal"); + } + RemoteObject rObj = getImportedObjects().get(_object); + if (rObj == null) { + throw new DBusException("Not an object exported or imported by this connection"); + } + String objectpath = rObj.getObjectPath(); + if (objectpath.length() > MAX_NAME_LENGTH || !OBJECT_REGEX_PATTERN.matcher(objectpath).matches()) { + throw new DBusException("Invalid object path: " + objectpath); + } + return addSigHandler(new DBusMatchRule(_type, null, objectpath), _handler); + } + + protected void addSigHandlerWithoutMatch(Class _signal, DBusSigHandler _handler) throws DBusException { + DBusMatchRule rule = new DBusMatchRule(_signal); + synchronized (getHandledSignals()) { + Queue> v = getHandledSignals().get(rule); + if (null == v) { + v = new ConcurrentLinkedQueue<>(); + v.add(_handler); + getHandledSignals().put(rule, v); + } else { + v.add(_handler); + } + } + } + + /** + * Special disconnect method which may be used whenever some cleanup before or after + * disconnection to DBus is required. + * @param _before action execute before actual disconnect, null if not needed + * @param _after action execute after disconnect, null if not needed + */ + protected synchronized void disconnect(IDisconnectAction _before, IDisconnectAction _after) { + if (_before != null) { + _before.perform(); + } + internalDisconnect(null); + if (_after != null) { + _after.perform(); + } + } + + /** + * Disconnects the DBus session. + * This method is private as it should never be overwritten by subclasses, + * otherwise we have an endless recursion when using {@link #disconnect(IDisconnectAction, IDisconnectAction)} + * which then will cause a StackOverflowError. + * + * @param _connectionError exception caused the disconnection (null if intended disconnect) + */ + protected final synchronized void internalDisconnect(IOException _connectionError) { + + if (!isConnected()) { // already disconnected + logger.debug("Ignoring disconnect, already disconnected"); + return; + } + disconnecting = true; + + logger.debug("Disconnecting Abstract Connection"); + + disconnectCallback.ifPresent(cb -> { + Optional.ofNullable(_connectionError) + .ifPresentOrElse(ex -> cb.disconnectOnError(ex), () -> cb.requestedDisconnect(null)); + }); + + // stop reading new messages + readerThread.terminate(); + + // terminate the signal handling pool + receivingService.shutdown(10, TimeUnit.SECONDS); + + // stop potentially waiting method-calls + logger.debug("Notifying {} method call(s) to stop waiting for replies", getPendingCalls().size()); + Exception interrupt = _connectionError == null ? new IOException("Disconnecting") : _connectionError; + for (MethodCall mthCall : getPendingCalls().values()) { + try { + mthCall.setReply(new Error(mthCall, interrupt)); + } catch (DBusException _ex) { + logger.debug("Cannot set method reply to error", _ex); + } + } + + // shutdown sender executor service, send all remaining messages in main thread when no exception caused disconnection + logger.debug("Shutting down SenderService"); + List remainingMsgsToSend = senderService.shutdownNow(); + // only try to send remaining messages when disconnection was not + // caused by an IOException, otherwise we may block for method calls waiting for + // reply which will never be received (due to disconnection by IOException) + if (_connectionError == null) { + for (Runnable runnable : remainingMsgsToSend) { + runnable.run(); + } + } else if (!remainingMsgsToSend.isEmpty()) { + logger.debug("Will not send {} messages due to connection closed by IOException", remainingMsgsToSend.size()); + } + + // disconnect from the transport layer + try { + if (transport != null) { + transport.close(); + transport = null; + } + } catch (IOException _ex) { + logger.debug("Exception while disconnecting transport.", _ex); + } + + // stop all the workers + receivingService.shutdownNow(); + disconnecting = false; + } + + /** + * Disconnect from the Bus. + */ + public synchronized void disconnect() { + logger.debug("Disconnect called"); + internalDisconnect(null); + } + + /** + * Disconnect this session (for use in try-with-resources). + */ + @Override + public void close() throws IOException { + disconnect(); + } + + /** + * Call a method asynchronously and set a callback. This handler will be called in a separate thread. + * + * @param + * whatever + * @param _object + * The remote object on which to call the method. + * @param _m + * The name of the method on the interface to call. + * @param _callback + * The callback handler. + * @param _parameters + * The parameters to call the method with. + */ + public void callWithCallback(DBusInterface _object, String _m, CallbackHandler _callback, + Object... _parameters) { + logger.trace("callWithCallback({}, {}, {})", _object, _m, _callback); + Class[] types = createTypesArray(_parameters); + RemoteObject ro = getImportedObjects().get(_object); + + try { + Method me; + if (null == ro.getInterface()) { + me = _object.getClass().getMethod(_m, types); + } else { + me = ro.getInterface().getMethod(_m, types); + } + RemoteInvocationHandler.executeRemoteMethod(ro, me, this, RemoteInvocationHandler.CALL_TYPE_CALLBACK, + _callback, _parameters); + } catch (DBusExecutionException _ex) { + logger.debug("", _ex); + throw _ex; + } catch (Exception _ex) { + logger.debug("", _ex); + throw new DBusExecutionException(_ex.getMessage()); + } + } + + /** + * Call a method asynchronously and get a handle with which to get the reply. + * + * @param _object + * The remote object on which to call the method. + * @param _method + * The name of the method on the interface to call. + * @param _parameters + * The parameters to call the method with. + * @return A handle to the call. + */ + public DBusAsyncReply callMethodAsync(DBusInterface _object, String _method, Object... _parameters) { + Class[] types = createTypesArray(_parameters); + RemoteObject ro = getImportedObjects().get(_object); + + try { + Method me; + if (null == ro.getInterface()) { + me = _object.getClass().getMethod(_method, types); + } else { + me = ro.getInterface().getMethod(_method, types); + } + return (DBusAsyncReply) RemoteInvocationHandler.executeRemoteMethod(ro, me, this, + RemoteInvocationHandler.CALL_TYPE_ASYNC, null, _parameters); + } catch (DBusExecutionException _ex) { + logger.debug("", _ex); + throw _ex; + } catch (Exception _ex) { + logger.debug("", _ex); + throw new DBusExecutionException(_ex.getMessage()); + } + } + + private static Class[] createTypesArray(Object... _parameters) { + if (_parameters == null) { + return null; + } + return Arrays.stream(_parameters) + .filter(p -> p != null) // do no try to convert null values to concrete class + .map(p -> { + if (List.class.isAssignableFrom(p.getClass())) { // turn possible List subclasses (e.g. ArrayList) to interface class List + return List.class; + } else if (Map.class.isAssignableFrom(p.getClass())) { // do the same for Map subclasses + return Map.class; + } else if (Set.class.isAssignableFrom(p.getClass())) { // and also for Set subclasses + return Set.class; + } else { + return p.getClass(); + } + }) + .toArray(Class[]::new); + } + + protected void handleException(Message _methodOrSignal, DBusExecutionException _exception) { + try { + sendMessage(new Error(_methodOrSignal, _exception)); + } catch (DBusException _ex) { + logger.warn("Exception caught while processing previous error.", _ex); + } + } + + /** + * Handle received message from DBus. + * @param _message + * @throws DBusException + */ + void handleMessage(Message _message) throws DBusException { + if (_message instanceof DBusSignal) { + handleMessage((DBusSignal) _message, true); + } else if (_message instanceof MethodCall) { + handleMessage((MethodCall) _message); + } else if (_message instanceof MethodReturn) { + handleMessage((MethodReturn) _message); + } else if (_message instanceof Error) { + handleMessage((Error) _message); + } + } + + private void handleMessage(final MethodCall _methodCall) throws DBusException { + logger.debug("Handling incoming method call: {}", _methodCall); + + ExportedObject exportObject; + Method meth = null; + Object o = null; + + if (null == _methodCall.getInterface() || _methodCall.getInterface().equals("org.freedesktop.DBus.Peer") + || _methodCall.getInterface().equals("org.freedesktop.DBus.Introspectable")) { + exportObject = getExportedObjects().get(null); + if (null != exportObject && null == exportObject.getObject().get()) { + unExportObject(null); + exportObject = null; + } + if (null != exportObject) { + meth = exportObject.getMethods().get(new MethodTuple(_methodCall.getName(), _methodCall.getSig())); + } + if (null != meth) { + o = new GlobalHandler(this, _methodCall.getPath()); + } + } + if (null == o) { + // now check for specific exported functions + + exportObject = getExportedObjects().get(_methodCall.getPath()); + if (exportObject != null && exportObject.getObject().get() == null) { + logger.info("Unexporting {} implicitly", _methodCall.getPath()); + unExportObject(_methodCall.getPath()); + exportObject = null; + } + + if (null == exportObject) { + exportObject = fallbackContainer.get(_methodCall.getPath()); + } + + if (null == exportObject) { + sendMessage(new Error(_methodCall, + new UnknownObject(_methodCall.getPath() + " is not an object provided by this process."))); + return; + } + if (logger.isTraceEnabled()) { + logger.trace("Searching for method {} with signature {}", _methodCall.getName(), _methodCall.getSig()); + logger.trace("List of methods on {}: ", exportObject); + for (MethodTuple mt : exportObject.getMethods().keySet()) { + logger.trace(" {} => {}", mt, exportObject.getMethods().get(mt)); + } + } + meth = exportObject.getMethods().get(new MethodTuple(_methodCall.getName(), _methodCall.getSig())); + if (null == meth) { + sendMessage(new Error(_methodCall, new UnknownMethod(String.format( + "The method `%s.%s' does not exist on this object.", _methodCall.getInterface(), _methodCall.getName())))); + return; + } + o = exportObject.getObject().get(); + } + + if (ExportedObject.isExcluded(meth)) { + sendMessage(new Error(_methodCall, new UnknownMethod(String.format( + "The method `%s.%s' is not exported.", _methodCall.getInterface(), _methodCall.getName())))); + return; + } + + // now execute it + final Method me = meth; + final Object ob = o; + final boolean noreply = 1 == (_methodCall.getFlags() & Message.Flags.NO_REPLY_EXPECTED); + final DBusCallInfo info = new DBusCallInfo(_methodCall); + final AbstractConnection conn = this; + + logger.trace("Adding Runnable for method {}", meth); + Runnable r = new Runnable() { + + @Override + public void run() { + logger.debug("Running method {} for remote call", me); + + try { + Type[] ts = me.getGenericParameterTypes(); + _methodCall.setArgs(Marshalling.deSerializeParameters(_methodCall.getParameters(), ts, conn)); + LoggingHelper.logIf(logger.isTraceEnabled(), () -> { + try { + logger.trace("Deserialised {} to types {}", Arrays.deepToString(_methodCall.getParameters()), Arrays.deepToString(ts)); + } catch (Exception _ex) { + logger.trace("Error getting method call parameters", _ex); + } + }); + } catch (Exception _ex) { + logger.debug("", _ex); + handleException(_methodCall, new UnknownMethod("Failure in de-serializing message: " + _ex)); + return; + } + + try { + INFOMAP.put(Thread.currentThread(), info); + Object result; + try { + LoggingHelper.logIf(logger.isTraceEnabled(), () -> { + try { + logger.trace("Invoking Method: {} on {} with parameters {}", me, ob, Arrays.deepToString(_methodCall.getParameters())); + } catch (DBusException _ex) { + logger.trace("Error getting parameters from method call", _ex); + } + }); + + result = me.invoke(ob, _methodCall.getParameters()); + } catch (InvocationTargetException _ex) { + logger.debug(_ex.getMessage(), _ex); + throw _ex.getCause(); + } + INFOMAP.remove(Thread.currentThread()); + if (!noreply) { + MethodReturn reply; + if (Void.TYPE.equals(me.getReturnType())) { + reply = new MethodReturn(_methodCall, null); + } else { + StringBuffer sb = new StringBuffer(); + for (String s : Marshalling.getDBusType(me.getGenericReturnType())) { + sb.append(s); + } + Object[] nr = Marshalling.convertParameters(new Object[] { + result + }, new Type[] { + me.getGenericReturnType() + }, conn); + + reply = new MethodReturn(_methodCall, sb.toString(), nr); + } + conn.sendMessage(reply); + } + } catch (DBusExecutionException _ex) { + logger.debug("", _ex); + handleException(_methodCall, _ex); + } catch (Throwable _ex) { + logger.debug("", _ex); + handleException(_methodCall, + new DBusExecutionException(String.format("Error Executing Method %s.%s: %s", + _methodCall.getInterface(), _methodCall.getName(), _ex.getMessage()))); + } + } + }; + receivingService.execMethodCallHandler(r); + } + + /** + * Handle a signal received on DBus. + * + * @param _signal signal to handle + * @param _useThreadPool whether to handle this signal in another thread or handle it byself + */ + @SuppressWarnings({ + "unchecked" + }) + private void handleMessage(final DBusSignal _signal, boolean _useThreadPool) { + logger.debug("Handling incoming signal: {}", _signal); + + List> handlers = new ArrayList<>(); + List> genericHandlers = new ArrayList<>(); + + for (Entry>> e : getHandledSignals().entrySet()) { + if (e.getKey().matches(_signal, false)) { + handlers.addAll(e.getValue()); + } + } + + for (Entry>> e : getGenericHandledSignals().entrySet()) { + if (e.getKey().matches(_signal, false)) { + genericHandlers.addAll(e.getValue()); + } + } + + if (handlers.isEmpty() && genericHandlers.isEmpty()) { + return; + } + + final AbstractConnection conn = this; + for (final DBusSigHandler h : handlers) { + logger.trace("Adding Runnable for signal {} with handler {}", _signal, h); + Runnable command = () -> { + try { + DBusSignal rs; + if (_signal.getClass().equals(DBusSignal.class)) { + rs = _signal.createReal(conn); + } else { + rs = _signal; + } + if (rs == null) { + return; + } + ((DBusSigHandler) h).handle(rs); + } catch (DBusException _ex) { + logger.warn("Exception while running signal handler '{}' for signal '{}':", h, _signal, _ex); + handleException(_signal, new DBusExecutionException("Error handling signal " + _signal.getInterface() + + "." + _signal.getName() + ": " + _ex.getMessage())); + } + }; + if (_useThreadPool) { + receivingService.execSignalHandler(command); + } else { + command.run(); + } + } + + for (final DBusSigHandler h : genericHandlers) { + logger.trace("Adding Runnable for signal {} with handler {}", _signal, h); + Runnable command = () -> h.handle(_signal); + if (_useThreadPool) { + receivingService.execSignalHandler(command); + } else { + command.run(); + } + } + } + + private void handleMessage(final Error _err) { + logger.debug("Handling incoming error: {}", _err); + MethodCall m = null; + if (getPendingCalls() == null) { + return; + } + synchronized (getPendingCalls()) { + if (getPendingCalls().containsKey(_err.getReplySerial())) { + m = getPendingCalls().remove(_err.getReplySerial()); + } + } + if (m != null) { + m.setReply(_err); + CallbackHandler cbh; + cbh = callbackManager.removeCallback(m); + logger.trace("{} = pendingCallbacks.remove({})", cbh, m); + + // queue callback for execution + if (null != cbh) { + final CallbackHandler fcbh = cbh; + logger.trace("Adding Error Runnable with callback handler {}", fcbh); + Runnable command = new Runnable() { + + @Override + public synchronized void run() { + try { + logger.trace("Running Error Callback for {}", _err); + DBusCallInfo info = new DBusCallInfo(_err); + INFOMAP.put(Thread.currentThread(), info); + + fcbh.handleError(_err.getException()); + INFOMAP.remove(Thread.currentThread()); + + } catch (Exception _ex) { + logger.debug("Exception while running error callback.", _ex); + } + } + }; + receivingService.execErrorHandler(command); + } + + } else { + getPendingErrorQueue().add(_err); + } + } + + @SuppressWarnings("unchecked") + private void handleMessage(final MethodReturn _mr) { + logger.debug("Handling incoming method return: {}", _mr); + MethodCall m = null; + + if (null == getPendingCalls()) { + return; + } + + synchronized (getPendingCalls()) { + if (getPendingCalls().containsKey(_mr.getReplySerial())) { + m = getPendingCalls().remove(_mr.getReplySerial()); + } + } + + if (null != m) { + m.setReply(_mr); + _mr.setCall(m); + @SuppressWarnings("rawtypes") + CallbackHandler cbh = callbackManager.getCallback(m); + DBusAsyncReply asr = callbackManager.getCallbackReply(m); + callbackManager.removeCallback(m); + + // queue callback for execution + if (null != cbh) { + final CallbackHandler fcbh = cbh; + final DBusAsyncReply fasr = asr; + if (fasr == null) { + logger.debug("Cannot add runnable for method, given method callback was null"); + return; + } + logger.trace("Adding Runnable for method {} with callback handler {}", fcbh, fasr.getMethod()); + Runnable r = new Runnable() { + + @Override + public synchronized void run() { + try { + logger.trace("Running Callback for {}", _mr); + DBusCallInfo info = new DBusCallInfo(_mr); + INFOMAP.put(Thread.currentThread(), info); + Object convertRV = RemoteInvocationHandler.convertRV(_mr.getSig(), _mr.getParameters(), + fasr.getMethod(), fasr.getConnection()); + fcbh.handle(convertRV); + INFOMAP.remove(Thread.currentThread()); + + } catch (Exception _ex) { + logger.debug("Exception while running callback.", _ex); + } + } + }; + receivingService.execMethodReturnHandler(r); + } + + } else { + try { + sendMessage(new Error(_mr, new DBusExecutionException( + "Spurious reply. No message with the given serial id was awaiting a reply."))); + } catch (DBusException _exDe) { + logger.trace("Could not send error message", _exDe); + } + } + } + + public void queueCallback(MethodCall _call, Method _method, CallbackHandler _callback) { + callbackManager.queueCallback(_call, _method, _callback, this); + } + + /** + * Send a message to DBus. + * @param _message message to send + */ + private void sendMessageInternally(Message _message) { + try { + if (!isConnected()) { + throw new NotConnected("Disconnected"); + } + if (_message instanceof DBusSignal) { + ((DBusSignal) _message).appendbody(this); + } + + if (_message instanceof MethodCall && 0 == (_message.getFlags() & Message.Flags.NO_REPLY_EXPECTED) && null != getPendingCalls()) { + synchronized (getPendingCalls()) { + getPendingCalls().put(_message.getSerial(), (MethodCall) _message); + } + } + + transport.writeMessage(_message); + + } catch (Exception _ex) { + logger.trace("Exception while sending message.", _ex); + + if (_message instanceof MethodCall && _ex instanceof DBusExecutionException) { + try { + ((MethodCall) _message).setReply(new Error(_message, _ex)); + } catch (DBusException _exDe) { + logger.trace("Could not set message reply", _exDe); + } + } else if (_message instanceof MethodCall) { + try { + logger.info("Setting reply to {} as an error", _message); + ((MethodCall) _message).setReply( + new Error(_message, new DBusExecutionException("Message Failed to Send: " + _ex.getMessage()))); + } catch (DBusException _exDe) { + logger.trace("Could not set message reply", _exDe); + } + } else if (_message instanceof MethodReturn) { + try { + transport.writeMessage(new Error(_message, _ex)); + } catch (IOException | DBusException _exIo) { + logger.debug("Error writing method return to transport", _exIo); + } + } + if (_ex instanceof IOException) { + logger.debug("Fatal IOException while sending message, disconnecting", _ex); + internalDisconnect((IOException) _ex); + } + } + } + + Message readIncoming() throws DBusException { + if (!isConnected()) { + return null; + } + Message m = null; + try { + m = transport.readMessage(); + } catch (IOException _exIo) { + if (_exIo instanceof EOFException || _exIo instanceof ClosedByInterruptException) { + disconnectCallback.ifPresent(cb -> cb.clientDisconnect()); + if (disconnecting // when we are already disconnecting, ignore further errors + || transportBuilder.getAddress().isListeningSocket()) { // when we are listener, a client may disconnect any time which is no error + return null; + } + } + + if (isConnected()) { + throw new FatalDBusException(_exIo); + } // if run is false, suppress all exceptions - the connection either is already disconnected or should be disconnected right now + } + return m; + } + + protected synchronized Map getExportedObjects() { + return exportedObjects; + } + + FallbackContainer getFallbackContainer() { + return fallbackContainer; + } + + /** + * Returns a structure with information on the current method call. + * + * @return the DBusCallInfo for this method call, or null if we are not in a method call. + */ + public static DBusCallInfo getCallInfo() { + return INFOMAP.get(Thread.currentThread()); + } + + /** + * Return any DBus error which has been received. + * + * @return A DBusExecutionException, or null if no error is pending. + */ + public DBusExecutionException getError() { + Error poll = getPendingErrorQueue().poll(); + if (poll != null) { + return poll.getException(); + } + return null; + } + + /** + * Returns the address this connection is connected to. + * + * @return new {@link BusAddress} object + */ + public BusAddress getAddress() { + return transportBuilder.getAddress(); + } + + public boolean isConnected() { + return transport != null && transport.isConnected(); + } + + protected Queue getPendingErrorQueue() { + return pendingErrorQueue; + } + + protected Map>> getHandledSignals() { + return handledSignals; + } + + protected Map>> getGenericHandledSignals() { + return genericHandledSignals; + } + + protected Map getPendingCalls() { + return pendingCalls; + } + + protected Map getImportedObjects() { + return importedObjects; + } + + protected ObjectTree getObjectTree() { + return objectTree; + } + + /** + * Set the endianness to use for all connections. + * Defaults to the system architectures endianness. + * + * @param _b Message.Endian.BIG or Message.Endian.LITTLE + */ + public static void setEndianness(byte _b) { + if (_b == Message.Endian.BIG || _b == Message.Endian.LITTLE) { + endianness = _b; + } + } + + /** + * Get current endianness to use. + * @return Message.Endian.BIG or Message.Endian.LITTLE + */ + public static byte getEndianness() { + return endianness; // TODO would be nice to have this non-static! + } + + /** + * Get the default system endianness. + * + * @return LITTLE or BIG + */ + public static byte getSystemEndianness() { + return ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN) + ? Message.Endian.BIG + : Message.Endian.LITTLE; + } + + /** + * Returns the currently configured disconnect callback. + * + * @return callback or null if no callback registered + */ + public IDisconnectCallback getDisconnectCallback() { + return disconnectCallback.orElse(null); + } + + /** + * Set the callback which will be notified when a disconnection happens. + * Use null to remove. + * + * @param _disconnectCallback callback to execute or null to remove + */ + public void setDisconnectCallback(IDisconnectCallback _disconnectCallback) { + disconnectCallback = Optional.ofNullable(_disconnectCallback); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[address=" + transportBuilder.getAddress() + "]"; + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/BusAddress.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/BusAddress.java new file mode 100644 index 0000000000..34f3516bde --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/BusAddress.java @@ -0,0 +1,284 @@ +package org.freedesktop.dbus.connections; + +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.InvalidBusAddressException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Defines an address to connect to DBus. + * The address will define which transport to use. + */ +public class BusAddress { + private static final Logger LOGGER = LoggerFactory.getLogger(BusAddress.class); + + private String type; + private final Map parameters = new LinkedHashMap<>(); + + /** + * Creates a new instance from String. + * + * @param _address address String + * @throws DBusException + * + * @deprecated Use BusAddress.of instead + */ + @Deprecated(forRemoval = true, since = "4.2.0 - 2022-07-18") + public BusAddress(String _address) throws DBusException { + if (_address == null || _address.isEmpty()) { + throw new DBusException("Bus address is blank"); + } + + String[] ss = _address.split(":", 2); + if (ss.length < 2) { + throw new DBusException("Bus address is invalid: " + _address); + } + + type = ss[0] != null ? ss[0].toLowerCase(Locale.US) : null; + if (type == null) { + throw new DBusException("Unsupported transport type: " + ss[0]); + } + + String[] ps = ss[1].split(","); + for (String p : ps) { + String[] kv = p.split("=", 2); + parameters.put(kv[0], kv[1]); + } + + } + + protected BusAddress(BusAddress _obj) { + if (_obj != null) { + parameters.putAll(_obj.parameters); + type = _obj.type; + } + } + + /** + * Creates a copy of the given {@link BusAddress}. + * If given address is null, an empty {@link BusAddress} object is created. + * + * @param _address address to copy + * @return BusAddress + * @since 4.2.0 - 2022-07-18 + */ + public static BusAddress of(BusAddress _address) { + return new BusAddress(_address); + } + + /** + * Creates a new {@link BusAddress} from String. + * + * @param _address address String, never null or empty + * + * @return BusAddress + * @since 4.2.0 - 2022-07-18 + */ + public static BusAddress of(String _address) { + if (_address == null || _address.isEmpty()) { + throw new InvalidBusAddressException("Bus address is blank"); + } + + BusAddress busAddress = new BusAddress((BusAddress) null); + + LOGGER.trace("Parsing bus address: {}", _address); + + String[] ss = _address.split(":", 2); + if (ss.length < 2) { + throw new InvalidBusAddressException("Bus address is invalid: " + _address); + } + + busAddress.type = ss[0] != null ? ss[0].toLowerCase(Locale.US) : null; + if (busAddress.type == null) { + throw new InvalidBusAddressException("Unsupported transport type: " + ss[0]); + } + + LOGGER.trace("Transport type: {}", busAddress.type); + + String[] ps = ss[1].split(","); + for (String p : ps) { + String[] kv = p.split("=", 2); + busAddress.addParameter(kv[0], kv[1]); + } + + LOGGER.trace("Transport options: {}", busAddress.parameters); + + return busAddress; + } + + /** + * Returns the transport type as found in the address. + * + * @return type + */ + public String getType() { + return type; + } + + /** + * Returns the transport type in uppercase. + * + * @return type + */ + public String getBusType() { + return type == null ? null : type.toUpperCase(Locale.US); + } + + /** + * Checks if this {@link BusAddress} is for the given bus type.
+ * The given type will be compared case-insensitive. + *
+ * e.g. + *
+     * isBusType("unix");
+     * 
+ * + * @param _type to compare + * + * @return true if same type (case-insensitive), false if null or not same type + * + * @since 4.2.0 - 2022-07-20 + */ + public boolean isBusType(String _type) { + return type != null && type.equalsIgnoreCase(_type); + } + + /** + * True if this is a listening address. + * @return true if listening + */ + public boolean isListeningSocket() { + return parameters.containsKey("listen"); + } + + public String getGuid() { + return parameters.get("guid"); + } + + /** + * String version of the BusAddress. + * @return String + * + * @deprecated use {@link #toString()} + */ + @Deprecated(forRemoval = true, since = "4.2.0 - 2022-07-18") + public String getRawAddress() { + return toString(); + } + + @Override + public final String toString() { + return type + ":" + parameters.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(",")); + } + + /** + * True if this address represents a listening server address. + * @return true if server + */ + public boolean isServer() { + return isListeningSocket(); + } + + /** + * Add a parameter to the address. + * Adding multiple parameters with same name is not possible and will overwrite previous values. + * + * @param _parameter parameter name + * @param _value value + * + * @return this + * @since 4.2.0 - 2022-07-18 + */ + public BusAddress addParameter(String _parameter, String _value) { + parameters.put(_parameter, _value); + return this; + } + + /** + * Remove parameter with given name. + * If parameter does not exists, nothing will happen. + * + * @param _parameter parameter to remove + * + * @return this + * @since 4.2.0 - 2022-07-18 + */ + public BusAddress removeParameter(String _parameter) { + parameters.remove(_parameter); + return this; + } + + /** + * Checks if the given parameter is present. + * + * @param _parameter parameter to check + * + * @return true if parameter exists, false otherwise + * @since 4.2.2 - 2023-01-11 + */ + public boolean hasParameter(String _parameter) { + return parameters.containsKey(_parameter); + } + + /** + * Returns a read-only view of the parameters currently configured. + * + * @return Map, maybe empty + * @since 4.2.0 - 2022-07-18 + * + * @deprecated will be removed in future, use {@link #getParameterValue(String)} or {@link #hasParameter(String)} + */ + @Deprecated(forRemoval = true, since = "4.2.2 - 2023-01-11") + public Map getParameters() { + return Collections.unmodifiableMap(parameters); + } + + /** + * Returns a the value of the given parameter. + *

+ * When no value present, null is returned. + * + * @param _parameter parameter to get value for + * + * @return String or null + * @since 4.2.0 - 2022-07-19 + */ + public String getParameterValue(String _parameter) { + return parameters.get(_parameter); + } + + /** + * Returns a the value of the given parameter. + *

+ * When no value present, the given default is returned. + * + * @param _parameter parameter to get value for + * @param _default default to return if parameter not set + * + * @return String or default + * @since 4.2.2 - 2023-01-11 + */ + public String getParameterValue(String _parameter, String _default) { + return parameters.getOrDefault(_parameter, _default); + } + + /** + * Creates a listening BusAddress if this instance is not already listening. + * + * @return new BusAddress or this + * @since 4.2.0 - 2022-07-18 + */ + public BusAddress getListenerAddress() { + if (!isListeningSocket()) { + return new BusAddress(this).addParameter("listen", "true"); + } + return this; + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/FallbackContainer.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/FallbackContainer.java new file mode 100644 index 0000000000..d113fc45f1 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/FallbackContainer.java @@ -0,0 +1,58 @@ +package org.freedesktop.dbus.connections; + +import org.freedesktop.dbus.messages.ExportedObject; +import org.freedesktop.dbus.utils.LoggingHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class FallbackContainer { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + private final Map fallbacks = new HashMap<>(); + + FallbackContainer() { + } + + public synchronized void add(String _path, ExportedObject _eo) { + logger.debug("Adding fallback on {} of {}", _path, _eo); + fallbacks.put(_path.split("/"), _eo); + } + + public synchronized void remove(String _path) { + logger.debug("Removing fallback on {}", _path); + fallbacks.remove(_path.split("/")); + } + + public synchronized ExportedObject get(String _path) { + int best = 0; + ExportedObject bestobject = null; + String[] pathel = _path.split("/"); + for (Map.Entry entry : fallbacks.entrySet()) { + String[] fbpath = entry.getKey(); + + LoggingHelper.logIf(logger.isTraceEnabled(), () -> + logger.trace("Trying fallback path {} to match {}", + Arrays.deepToString(fbpath), + Arrays.deepToString(pathel)) + ); + + int i; + for (i = 0; i < pathel.length && i < fbpath.length; i++) { + if (!pathel[i].equals(fbpath[i])) { + break; + } + } + if (i > 0 && i == fbpath.length && i > best) { + bestobject = entry.getValue(); + } + logger.trace("Matches {} bestobject now {}", i, bestobject); + } + + logger.debug("Found fallback for {} of {}", _path, bestobject); + return bestobject; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/GlobalHandler.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/GlobalHandler.java new file mode 100644 index 0000000000..d6cd795308 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/GlobalHandler.java @@ -0,0 +1,59 @@ +package org.freedesktop.dbus.connections; + +import org.freedesktop.dbus.errors.UnknownObject; +import org.freedesktop.dbus.messages.ExportedObject; + +public class GlobalHandler implements org.freedesktop.dbus.interfaces.Peer, org.freedesktop.dbus.interfaces.Introspectable { + /** + * + */ + private final AbstractConnection connection; + private final String objectpath; + + GlobalHandler(AbstractConnection _abstractConnection) { + connection = _abstractConnection; + this.objectpath = null; + } + + GlobalHandler(AbstractConnection _abstractConnection, String _objectpath) { + connection = _abstractConnection; + this.objectpath = _objectpath; + } + + @Override + public boolean isRemote() { + return false; + } + + @Override + public void Ping() { + // nothing to do + } + + @Override + public String Introspect() { + String intro = connection.getObjectTree().Introspect(objectpath); + if (null == intro) { + ExportedObject eo = connection.getFallbackContainer().get(objectpath); + if (null != eo) { + intro = eo.getIntrospectiondata(); + } + } + if (null == intro) { + throw new UnknownObject("Introspecting on non-existant object"); + } else { + return "\n" + intro; + } + } + + @Override + public String getObjectPath() { + return objectpath; + } + + @Override + public String GetMachineId() { + return connection.getMachineId(); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/IDisconnectAction.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/IDisconnectAction.java new file mode 100644 index 0000000000..f3e227324d --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/IDisconnectAction.java @@ -0,0 +1,5 @@ +package org.freedesktop.dbus.connections; + +public interface IDisconnectAction { + void perform(); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/IDisconnectCallback.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/IDisconnectCallback.java new file mode 100644 index 0000000000..58b0da178f --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/IDisconnectCallback.java @@ -0,0 +1,39 @@ +package org.freedesktop.dbus.connections; + +import java.io.IOException; + +/** + * Callback interface which can be used to get notified about connection losses. + * + * @author hypfvieh + * @version 4.1.0 - 2022-02-03 + */ +public interface IDisconnectCallback { + /** + * Called when the connection is closed due to a connection error (e.g. stream closed). + * + * @param _ex exception which was thrown by transport + */ + default void disconnectOnError(IOException _ex) {} + + /** + * Called when the disconnect was intended. + * @param _connectionId connection Id if this was a shared connection, + * null if last shared or non-shared connection + */ + default void requestedDisconnect(Integer _connectionId) {} + + /** + * Called when a client disconnected (only if this is a server/listening connection). + */ + default void clientDisconnect() {} + + /** + * Called when the transport throws an exception + * while connection was already terminating. + * + * @param _ex exception which was thrown by transport + */ + default void exceptionOnTerminate(IOException _ex) {} + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/IncomingMessageThread.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/IncomingMessageThread.java new file mode 100644 index 0000000000..88851eeb79 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/IncomingMessageThread.java @@ -0,0 +1,71 @@ +package org.freedesktop.dbus.connections; + +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.IllegalThreadPoolStateException; +import org.freedesktop.dbus.interfaces.FatalException; +import org.freedesktop.dbus.messages.Message; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Objects; +import java.util.concurrent.RejectedExecutionException; + +public class IncomingMessageThread extends Thread { + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private volatile boolean terminate; + private final AbstractConnection connection; + + public IncomingMessageThread(AbstractConnection _connection, BusAddress _busAddress) { + Objects.requireNonNull(_connection); + connection = _connection; + setName("DBusConnection [listener=" + _busAddress.isListeningSocket() + "]"); + setDaemon(true); + } + + public void terminate() { + terminate = true; + interrupt(); + } + + @Override + public void run() { + + Message msg; + while (!terminate) { + msg = null; + + // read from the wire + try { + // this blocks on outgoing being non-empty or a message being available. + msg = connection.readIncoming(); + if (msg != null) { + logger.trace("Got Incoming Message: {}", msg); + + connection.handleMessage(msg); + } + } catch (DBusException | RejectedExecutionException | IllegalThreadPoolStateException _ex) { + if (_ex instanceof FatalException) { + if (terminate) { // requested termination, ignore failures + return; + } + logger.error("FatalException in connection thread", _ex); + if (connection.isConnected()) { + terminate = true; + if (_ex.getCause() instanceof IOException) { + connection.internalDisconnect((IOException) _ex.getCause()); + } else { + connection.internalDisconnect(null); + } + } + return; + } + + if (!terminate) { // only log exceptions if the connection was not intended to be closed + logger.error("Exception in connection thread", _ex); + } + } + } + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/PeerSet.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/PeerSet.java new file mode 100644 index 0000000000..0b7b6a91c3 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/PeerSet.java @@ -0,0 +1,137 @@ +package org.freedesktop.dbus.connections; + +import org.freedesktop.dbus.DBusMatchRule; +import org.freedesktop.dbus.connections.impl.DBusConnection; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.interfaces.DBus; +import org.freedesktop.dbus.interfaces.DBusSigHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; +import java.util.TreeSet; + +/** + * Add addresses of peers to a set which will watch for them to + * disappear and automatically remove them from the set. + */ +public class PeerSet implements Set, DBusSigHandler { + private final Logger logger = LoggerFactory.getLogger(getClass()); + private final Set addresses; + + public PeerSet(DBusConnection _connection) { + addresses = new TreeSet<>(); + try { + _connection.addSigHandler(new DBusMatchRule(DBus.NameOwnerChanged.class, null, null), this); + } catch (DBusException _ex) { + logger.debug("", _ex); + } + } + + @Override + public void handle(DBus.NameOwnerChanged _noc) { + logger.debug("Received NameOwnerChanged({}, {}, {})", _noc.name, _noc.oldOwner, _noc.newOwner); + if ("".equals(_noc.newOwner) && addresses.contains(_noc.name)) { + remove(_noc.name); + } + } + + @Override + public boolean add(String _address) { + logger.debug("Adding {}", _address); + synchronized (addresses) { + return addresses.add(_address); + } + } + + @Override + public boolean addAll(Collection _addresses) { + synchronized (this.addresses) { + return this.addresses.addAll(_addresses); + } + } + + @Override + public void clear() { + synchronized (addresses) { + addresses.clear(); + } + } + + @Override + public boolean contains(Object _o) { + return addresses.contains(_o); + } + + @Override + public boolean containsAll(Collection _os) { + return addresses.containsAll(_os); + } + + @Override + public boolean equals(Object _o) { + if (_o instanceof PeerSet) { + return ((PeerSet) _o).addresses.equals(addresses); + } else { + return false; + } + } + + @Override + public int hashCode() { + return addresses.hashCode(); + } + + @Override + public boolean isEmpty() { + return addresses.isEmpty(); + } + + @Override + public Iterator iterator() { + return addresses.iterator(); + } + + @Override + public boolean remove(Object _o) { + logger.debug("Removing {}", _o); + synchronized (addresses) { + return addresses.remove(_o); + } + } + + @Override + public boolean removeAll(Collection _os) { + synchronized (addresses) { + return addresses.removeAll(_os); + } + } + + @Override + public boolean retainAll(Collection _os) { + synchronized (addresses) { + return addresses.retainAll(_os); + } + } + + @Override + public int size() { + return addresses.size(); + } + + @Override + public Object[] toArray() { + synchronized (addresses) { + return addresses.toArray(); + } + } + + @Override + public T[] toArray(T[] _a) { + synchronized (addresses) { + return addresses.toArray(_a); + } + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/PendingCallbackManager.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/PendingCallbackManager.java new file mode 100644 index 0000000000..f8db64b12f --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/PendingCallbackManager.java @@ -0,0 +1,39 @@ +package org.freedesktop.dbus.connections; + +import org.freedesktop.dbus.DBusAsyncReply; +import org.freedesktop.dbus.interfaces.CallbackHandler; +import org.freedesktop.dbus.messages.MethodCall; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class PendingCallbackManager { + private final Map> pendingCallbacks; + private final Map> pendingCallbackReplys; + + PendingCallbackManager() { + pendingCallbacks = new ConcurrentHashMap<>(); + pendingCallbackReplys = new ConcurrentHashMap<>(); + } + + public synchronized void queueCallback(MethodCall _call, Method _method, CallbackHandler _callback, AbstractConnection _connection) { + pendingCallbacks.put(_call, _callback); + pendingCallbackReplys.put(_call, new DBusAsyncReply<>(_call, _method, _connection)); + + } + + public synchronized CallbackHandler removeCallback(MethodCall _methodCall) { + pendingCallbackReplys.remove(_methodCall); + return pendingCallbacks.remove(_methodCall); + } + + public synchronized CallbackHandler getCallback(MethodCall _methodCall) { + return pendingCallbacks.get(_methodCall); + } + + public synchronized DBusAsyncReply getCallbackReply(MethodCall _methodCall) { + return pendingCallbackReplys.get(_methodCall); + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/ReceivingService.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/ReceivingService.java new file mode 100644 index 0000000000..27970a119f --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/ReceivingService.java @@ -0,0 +1,249 @@ +package org.freedesktop.dbus.connections; + +import org.freedesktop.dbus.connections.config.ReceivingServiceConfig; +import org.freedesktop.dbus.connections.config.ReceivingServiceConfigBuilder; +import org.freedesktop.dbus.exceptions.IllegalThreadPoolStateException; +import org.freedesktop.dbus.utils.NameableThreadFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * Service providing threads for every type of message expected to be received by DBus. + * + * @author hypfvieh + * @version 4.1.0 - 2022-02-02 + */ +public class ReceivingService { + static final int MAX_RETRIES = 50; + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private boolean closed = false; + + private final Map executors = new ConcurrentHashMap<>(); + + private final IThreadPoolRetryHandler retryHandler; + + /** + * Creates a new instance. + * + * @param _rsCfg configuration + */ + ReceivingService(ReceivingServiceConfig _rsCfg) { + ReceivingServiceConfig rsCfg = Optional.ofNullable(_rsCfg).orElse(ReceivingServiceConfigBuilder.getDefaultConfig()); + executors.put(ExecutorNames.SIGNAL, + Executors.newFixedThreadPool(rsCfg.getSignalThreadPoolSize(), new NameableThreadFactory("DBus-Signal-Receiver-", true, rsCfg.getSignalThreadPriority()))); + executors.put(ExecutorNames.ERROR, + Executors.newFixedThreadPool(rsCfg.getErrorThreadPoolSize(), new NameableThreadFactory("DBus-Error-Receiver-", true, rsCfg.getErrorThreadPriority()))); + + // we need multiple threads here so recursive method calls are possible + executors.put(ExecutorNames.METHODCALL, + Executors.newFixedThreadPool(rsCfg.getMethodCallThreadPoolSize(), new NameableThreadFactory("DBus-MethodCall-Receiver-", true, rsCfg.getMethodCallThreadPriority()))); + executors.put(ExecutorNames.METHODRETURN, + Executors.newFixedThreadPool(rsCfg.getMethodReturnThreadPoolSize(), new NameableThreadFactory("DBus-MethodReturn-Receiver-", true, rsCfg.getMethodReturnThreadPriority()))); + + retryHandler = rsCfg.getRetryHandler(); + } + + /** + * Execute a runnable which handles a signal. + * + * @param _r runnable + * + * @return retries, if any input was null -1 is returned + */ + int execSignalHandler(Runnable _r) { + return execOrFail(ExecutorNames.SIGNAL, _r); + } + + /** + * Execute a runnable which handles an error. + * + * @param _r runnable + * + * @return retries, if any input was null -1 is returned + */ + int execErrorHandler(Runnable _r) { + return execOrFail(ExecutorNames.ERROR, _r); + } + + /** + * Execute a runnable which handles a method call. + * + * @param _r runnable + * + * @return retries, if any input was null -1 is returned + */ + int execMethodCallHandler(Runnable _r) { + return execOrFail(ExecutorNames.METHODCALL, _r); + } + + /** + * Execute a runnable which handles the return of a method. + * + * @param _r runnable + * + * @return retries, if any input was null -1 is returned + */ + int execMethodReturnHandler(Runnable _r) { + return execOrFail(ExecutorNames.METHODRETURN, _r); + } + + /** + * Executes a runnable in a given executor. + * May retry execution if {@link ExecutorService} has thrown an exception and retry handler + * allows re-processing. + * When re-processing fails for {@value #MAX_RETRIES} or more retries, an error will be logged + * and the runnable will not be executed. + * + * @param _executor executor to use + * @param _r runnable + * + * @return retries, if any input was null -1 is returned + */ + int execOrFail(ExecutorNames _executor, Runnable _r) { + if (_r == null || _executor == null) { // ignore invalid runnables or executors + return -1; + } + + int failCount = 0; + while (failCount < MAX_RETRIES) { + try { + ExecutorService exec = getExecutor(_executor); + if (exec == null) { // this should never happen, map is initialized in constructor + throw new IllegalThreadPoolStateException("No executor found for " + _executor); + } else if (closed || exec.isShutdown() || exec.isTerminated()) { + throw new IllegalThreadPoolStateException("Receiving service already closed"); + } + exec.execute(_r); + break; // execution done, no retry needed + } catch (IllegalThreadPoolStateException _ex) { // just throw our exception + throw _ex; + } catch (Exception _ex) { + if (retryHandler == null) { + logger.error("Could not handle runnable for executor {}, runnable will be dropped", _executor, _ex); + break; // no handler, assume ignoring runnable is ok + } + + failCount++; + if (!retryHandler.handle(_executor, _ex)) { + logger.trace("Ignoring unhandled runnable for executor {} due to {}, dropped by retry handler after {} retries", _executor, _ex.getClass().getName(), failCount); + break; + } + } + } + + if (failCount >= MAX_RETRIES) { + logger.error("Could not handle runnable for executor {} after {} retries, runnable will be dropped", _executor, failCount); + } + + return failCount; + } + + /** + * Returns the executor or null. + * + * @param _executor executor to use + * @return executor or null + */ + ExecutorService getExecutor(ExecutorNames _executor) { + return executors.get(_executor); + } + + /** + * Shutdown all executor services waiting up to the given timeout/unit. + * + * @param _timeout timeout + * @param _unit time unit + */ + public synchronized void shutdown(int _timeout, TimeUnit _unit) { + for (Entry es : executors.entrySet()) { + logger.debug("Shutting down executor: {}", es.getKey()); + es.getValue().shutdown(); + } + + for (Entry es : executors.entrySet()) { + try { + es.getValue().awaitTermination(_timeout, _unit); + } catch (InterruptedException _ex) { + logger.debug("Interrupted while waiting for termination of executor"); + } + } + + closed = true; + } + + /** + * Forcefully stop the executors. + */ + public synchronized void shutdownNow() { + for (Entry es : executors.entrySet()) { + if (!es.getValue().isTerminated()) { + logger.debug("Forcefully stopping {}", es.getKey()); + es.getValue().shutdownNow(); + } + } + + closed = true; + } + + /** + * Enum representing different executor services. + * + * @author hypfvieh + * @version 4.0.1 - 2022-02-02 + */ + public enum ExecutorNames { + SIGNAL("SignalExecutor"), + ERROR("ErrorExecutor"), + METHODCALL("MethodCallExecutor"), + METHODRETURN("MethodReturnExecutor"); + + private final String description; + + ExecutorNames(String _name) { + description = _name; + } + + public String getDescription() { + return description; + } + + @Override + public String toString() { + return description; + } + } + + /** + * Interface which specifies a handler which will be called when the thread pool throws any exception. + * + * @author hypfvieh + * @since 4.2.0 - 2022-07-14 + */ + @FunctionalInterface + public interface IThreadPoolRetryHandler { + /** + * Called to handle an exception. + *

+ * This method should return true to retry execution or false to + * just ignore the error and drop the unhandled message. + *

+ * + * @param _executor the executor which has thrown the exception + * @param _ex the exception which was thrown + * + * @return true to retry execution of the failed runnable, false to ignore runnable + */ + boolean handle(ExecutorNames _executor, Exception _ex); + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/SASL.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/SASL.java new file mode 100644 index 0000000000..95f0d56605 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/SASL.java @@ -0,0 +1,923 @@ +package org.freedesktop.dbus.connections; + +import static org.freedesktop.dbus.connections.SASL.SaslCommand.AGREE_UNIX_FD; +import static org.freedesktop.dbus.connections.SASL.SaslCommand.AUTH; +import static org.freedesktop.dbus.connections.SASL.SaslCommand.BEGIN; +import static org.freedesktop.dbus.connections.SASL.SaslCommand.CANCEL; +import static org.freedesktop.dbus.connections.SASL.SaslCommand.DATA; +import static org.freedesktop.dbus.connections.SASL.SaslCommand.ERROR; +import static org.freedesktop.dbus.connections.SASL.SaslCommand.NEGOTIATE_UNIX_FD; +import static org.freedesktop.dbus.connections.SASL.SaslCommand.REJECTED; + +import com.sun.security.auth.module.UnixSystem; +import org.freedesktop.dbus.config.DBusSysProps; +import org.freedesktop.dbus.connections.config.SaslConfig; +import org.freedesktop.dbus.connections.transports.AbstractTransport; +import org.freedesktop.dbus.connections.transports.AbstractUnixTransport; +import org.freedesktop.dbus.exceptions.AuthenticationException; +import org.freedesktop.dbus.exceptions.SocketClosedException; +import org.freedesktop.dbus.messages.Message; +import org.freedesktop.dbus.utils.Hexdump; +import org.freedesktop.dbus.utils.LoggingHelper; +import org.freedesktop.dbus.utils.TimeMeasure; +import org.freedesktop.dbus.utils.Util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.channels.NetworkChannel; +import java.nio.channels.SocketChannel; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.PosixFilePermission; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Random; +import java.util.Set; + +public class SASL { + public static final int AUTH_NONE = 0; + public static final int AUTH_EXTERNAL = 1; + public static final int AUTH_SHA = 2; + public static final int AUTH_ANON = 4; + + public static final int LOCK_TIMEOUT = 1000; + public static final int NEW_KEY_TIMEOUT_SECONDS = 60 * 5; + public static final int EXPIRE_KEYS_TIMEOUT_SECONDS = NEW_KEY_TIMEOUT_SECONDS + (60 * 2); + public static final int MAX_TIME_TRAVEL_SECONDS = 60 * 5; + public static final int COOKIE_TIMEOUT = 240; + public static final String COOKIE_CONTEXT = "org_freedesktop_java"; + + private static final int MAX_READ_BYTES = 64; + + private static final Collator COL = Collator.getInstance(); + static { + COL.setDecomposition(Collator.FULL_DECOMPOSITION); + COL.setStrength(Collator.PRIMARY); + } + + private static final String SYSPROP_USER_HOME = System.getProperty("user.home"); + private static final String DBUS_TEST_HOME_DIR = System.getProperty(DBusSysProps.SYSPROP_DBUS_TEST_HOME_DIR); + + private static final File DBUS_KEYRINGS_DIR = new File(SYSPROP_USER_HOME, ".dbus-keyrings"); + + private static final Set BAD_FILE_PERMISSIONS = + Set.of(PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, + PosixFilePermission.OTHERS_EXECUTE, PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_WRITE); + + private String challenge = ""; + private String cookie = ""; + + private final Logger logger = LoggerFactory.getLogger(getClass()); + /** whether file descriptor passing is supported on the current connection. */ + private boolean fileDescriptorSupported; + private final SaslConfig saslConfig; + + /** + * Create a new SASL auth handler. + * Defaults to disable file descriptor passing. + * + * @deprecated should not be used as SASL configuration is not provided + */ + @Deprecated(since = "4.2.2 - 2023-02-03", forRemoval = true) + public SASL() { + this(SaslConfig.create()); + } + + /** + * Create a new SASL auth handler. + * + * @param _hasFileDescriptorSupport true to support file descriptor passing (usually only works with UNIX_SOCKET). + * @param _saslConfig SASL configuration + */ + public SASL(SaslConfig _saslConfig) { + saslConfig = Objects.requireNonNull(_saslConfig, "Sasl Configuration required"); + } + + private String findCookie(String _context, String _id) throws IOException { + File keyringDir = DBUS_KEYRINGS_DIR; + if (!Util.isBlank(DBUS_TEST_HOME_DIR)) { + keyringDir = new File(DBUS_TEST_HOME_DIR); + } + + File f = new File(keyringDir, _context); + try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(f)))) { + String s = null; + String lCookie = null; + + TimeMeasure tm = new TimeMeasure(); + while (null != (s = r.readLine())) { + String[] line = s.split(" "); + long timestamp = Long.parseLong(line[1]); + if (line[0].equals(_id) && !(timestamp < 0 || (tm.getElapsedSeconds() + MAX_TIME_TRAVEL_SECONDS) < timestamp || tm.getElapsedSeconds() - EXPIRE_KEYS_TIMEOUT_SECONDS > timestamp)) { + lCookie = line[2]; + break; + } + } + return lCookie; + } + } + + @SuppressWarnings("checkstyle:emptyblock") + private void addCookie(String _context, String _id, long _timestamp, String _cookie) throws IOException { + File keyringDir = DBUS_KEYRINGS_DIR; + if (!Util.isBlank(DBUS_TEST_HOME_DIR)) { + keyringDir = new File(DBUS_TEST_HOME_DIR); + } + + File cookiefile = new File(keyringDir, _context); + File lock = new File(keyringDir, _context + ".lock"); + File temp = new File(keyringDir, _context + ".temp"); + + // ensure directory exists + if (!keyringDir.exists()) { + // directory did not exist, if we can create it, set proper permissions + if (keyringDir.mkdirs()) { + if (!Util.isWindows()) { + Util.setFilePermissions(keyringDir.toPath(), null, null, Set.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE)); + } + } else { + throw new AuthenticationException("Unable to create keyring directory " + keyringDir); + } + } else { // directory already exists + if (!Util.isWindows()) { // verify permissions + Set currentPermissions = Files.getPosixFilePermissions(keyringDir.toPath(), LinkOption.NOFOLLOW_LINKS); + if (Util.collectionContainsAny(currentPermissions, BAD_FILE_PERMISSIONS)) { + if (saslConfig.isStrictCookiePermissions()) { + throw new AuthenticationException("Cannot authenticate using cookies: Permissions of directory " + lock + " should be 0700"); + } else { + logger.warn("DBus keyring directory {} should have permissions 0700", lock); + } + } + } + } + + // acquire lock + Util.waitFor("Lock file " + lock, () -> lock.createNewFile(), LOCK_TIMEOUT, 50); + + // read old file + List lines = new ArrayList<>(); + if (cookiefile.exists()) { + try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(cookiefile)))) { + String s = null; + while (null != (s = r.readLine())) { + String[] line = s.split(" "); + long time = Long.parseLong(line[1]); + // expire stale cookies + if ((_timestamp - time) < COOKIE_TIMEOUT) { + lines.add(s); + } + } + } + } + + // add cookie + lines.add(_id + " " + _timestamp + " " + _cookie); + + // write temp file + Files.writeString(temp.toPath(), String.join(System.lineSeparator(), lines), Charset.defaultCharset(), + StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING); + + // atomically move to old file + if (!temp.renameTo(cookiefile)) { + cookiefile.delete(); + temp.renameTo(cookiefile); + } + + // remove lock + lock.delete(); + } + + /** + * Takes the string, encodes it as hex and then turns it into a string again. + * No, I don't know why either. + */ + private String stupidlyEncode(String _data) { + return Hexdump.toHex(_data.getBytes(), false); + } + + private String stupidlyEncode(byte[] _data) { + return Hexdump.toHex(_data, false); + } + + private byte getNibble(char _c) { + switch (_c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return (byte) (_c - '0'); + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + return (byte) (_c - 'A' + 10); + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + return (byte) (_c - 'a' + 10); + default: + return 0; + } + } + + private String stupidlyDecode(String _data) { + char[] cs = new char[_data.length()]; + char[] res = new char[cs.length / 2]; + _data.getChars(0, _data.length(), cs, 0); + for (int i = 0, j = 0; j < res.length; i += 2, j++) { + int b = 0; + b |= getNibble(cs[i]) << 4; + b |= getNibble(cs[i + 1]); + res[j] = (char) b; + } + return new String(res); + } + + public SASL.Command receive(SocketChannel _sock) throws IOException { + StringBuffer sb = new StringBuffer(); + ByteBuffer buf = ByteBuffer.allocate(1); // only read one byte at a time to avoid reading to much (which would break the next message) + + boolean runLoop = true; + int bytesRead = 0; + while (runLoop) { + + int read = _sock.read(buf); + bytesRead += read; + buf.position(0); + if (read == -1) { + throw new SocketClosedException("Stream unexpectedly short (broken pipe)"); + } + + for (int i = buf.position(); i < read; i++) { + byte c = buf.get(); + if (c == 0 || c == '\r') { + continue; + } else if (c == '\n') { + runLoop = false; + break; + } else { + sb.append((char) c); + } + } + buf.clear(); + + if (bytesRead > MAX_READ_BYTES) { // safe-guard to stop reading if no \n found + break; + } + } + + logger.trace("received: {}", sb); + try { + return new Command(sb.toString()); + } catch (Exception _ex) { + logger.error("Cannot create command.", _ex); + throw new AuthenticationException("Failed to authenticate.", _ex); + } + } + + public void send(SocketChannel _sock, SaslCommand _command, String... _data) throws IOException { + StringBuffer sb = new StringBuffer(); + sb.append(_command.name()); + + for (String s : _data) { + sb.append(' '); + sb.append(s); + } + sb.append('\r'); + sb.append('\n'); + logger.trace("sending: {}", sb); + _sock.write(ByteBuffer.wrap(sb.toString().getBytes())); + } + + SaslResult doChallenge(int _auth, SASL.Command _c) throws IOException { + switch (_auth) { + case AUTH_SHA: + String[] reply = stupidlyDecode(_c.getData()).split(" "); + logger.trace(Arrays.toString(reply)); + if (3 != reply.length) { + logger.debug("Reply is not length 3"); + return SaslResult.ERROR; + } + + String context = reply[0]; + String id = reply[1]; + final String serverchallenge = reply[2]; + + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA"); + } catch (NoSuchAlgorithmException _ex) { + logger.debug("", _ex); + return SaslResult.ERROR; + } + + byte[] buf = new byte[8]; + + // ensure we get a (more or less unique) positive long + long seed = Optional.of(System.nanoTime()).map(t -> t < 0 ? t * -1 : t).get(); + + Message.marshallintBig(seed, buf, 0, 8); + String clientchallenge = stupidlyEncode(md.digest(buf)); + md.reset(); + + TimeMeasure tm = new TimeMeasure(); + String lCookie = null; + + while (lCookie == null && tm.getElapsed() < LOCK_TIMEOUT) { + lCookie = findCookie(context, id); + } + + if (lCookie == null) { + logger.debug("Did not find a cookie in context {} with ID {}", context, id); + return SaslResult.ERROR; + } + + String response = serverchallenge + ":" + clientchallenge + ":" + lCookie; + buf = md.digest(response.getBytes()); + + logger.trace("Response: {} hash: {}", response, Hexdump.format(buf)); + + response = stupidlyEncode(buf); + _c.setResponse(stupidlyEncode(clientchallenge + " " + response)); + return SaslResult.OK; + default: + logger.debug("Not DBUS_COOKIE_SHA1 authtype."); + return SaslResult.ERROR; + } + } + + SaslResult doResponse(int _auth, String _uid, String _kernelUid, SASL.Command _c) { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA"); + } catch (NoSuchAlgorithmException _ex) { + logger.error("", _ex); + return SaslResult.ERROR; + } + switch (_auth) { + case AUTH_NONE: + switch (_c.getMechs()) { + case AUTH_ANON: + return SaslResult.OK; + case AUTH_EXTERNAL: + if (0 == COL.compare(_uid, _c.getData()) && (null == _kernelUid || 0 == COL.compare(_uid, _kernelUid))) { + return SaslResult.OK; + } else { + return SaslResult.REJECT; + } + case AUTH_SHA: + String context = COOKIE_CONTEXT; + long id = System.currentTimeMillis(); + byte[] buf = new byte[8]; + Message.marshallintBig(id, buf, 0, 8); + challenge = stupidlyEncode(md.digest(buf)); + Random r = new Random(); + r.nextBytes(buf); + cookie = stupidlyEncode(md.digest(buf)); + try { + addCookie(context, "" + id, id / 1000, cookie); + } catch (IOException _ex) { + logger.error("Error authenticating using cookie", _ex); + return SaslResult.ERROR; + } + + logger.debug("Sending challenge: {} {} {}", context, id, challenge); + + _c.setResponse(stupidlyEncode(context + ' ' + id + ' ' + challenge)); + return SaslResult.OK; + default: + return SaslResult.ERROR; + } + case AUTH_SHA: + String[] response = stupidlyDecode(_c.getData()).split(" "); + if (response.length < 2) { + return SaslResult.ERROR; + } + String cchal = response[0]; + String hash = response[1]; + String prehash = challenge + ":" + cchal + ":" + cookie; + byte[] buf = md.digest(prehash.getBytes()); + String posthash = stupidlyEncode(buf); + logger.debug("Authenticating Hash; data={} remote-hash={} local-hash={}", prehash, hash, posthash); + if (0 == COL.compare(posthash, hash)) { + return SaslResult.OK; + } else { + return SaslResult.ERROR; + } + default: + return SaslResult.ERROR; + } + } + + public String[] convertAuthTypes(int _types) { + switch (_types) { + case AUTH_EXTERNAL: + return new String[] { + "EXTERNAL" + }; + case AUTH_SHA: + return new String[] { + "DBUS_COOKIE_SHA1" + }; + case AUTH_ANON: + return new String[] { + "ANONYMOUS" + }; + case AUTH_SHA + AUTH_EXTERNAL: + return new String[] { + "EXTERNAL", "DBUS_COOKIE_SHA1" + }; + case AUTH_SHA + AUTH_ANON: + return new String[] { + "ANONYMOUS", "DBUS_COOKIE_SHA1" + }; + case AUTH_EXTERNAL + AUTH_ANON: + return new String[] { + "ANONYMOUS", "EXTERNAL" + }; + case AUTH_EXTERNAL + AUTH_ANON + AUTH_SHA: + return new String[] { + "ANONYMOUS", "EXTERNAL", "DBUS_COOKIE_SHA1" + }; + default: + return new String[] {}; + } + } + + /** + * Performs SASL auth on the given socketchannel. + * Mode selects whether to run as a SASL server or client. + * Types is a bitmask of the available auth types. + * + * @param _config sasl configuration parameters + * @param _sock socket channel + * @param _transport transport + * + * @return true if the auth was successful and false if it failed. + * @throws IOException on failure + */ + public boolean auth(SocketChannel _sock, AbstractTransport _transport) throws IOException { + String luid = null; + String kernelUid = null; + + long uid = saslConfig.getSaslUid().orElse(getUserId()); + luid = stupidlyEncode("" + uid); + + SASL.Command c; + int failed = 0; + int current = 0; + SaslAuthState state = SaslAuthState.INITIAL_STATE; + + while (state != SaslAuthState.FINISHED && state != SaslAuthState.FAILED) { + + logger.trace("Mode: {} AUTH state: {}", saslConfig.getMode(), state); + + switch (saslConfig.getMode()) { + case CLIENT: + switch (state) { + case INITIAL_STATE: + _sock.write(ByteBuffer.wrap(new byte[] {0})); + send(_sock, AUTH); + state = SaslAuthState.WAIT_DATA; + break; + case WAIT_DATA: + c = receive(_sock); + switch (c.getCommand()) { + case DATA: + switch (doChallenge(current, c)) { + case CONTINUE: + send(_sock, DATA, c.getResponse()); + break; + case OK: + send(_sock, DATA, c.getResponse()); + state = SaslAuthState.WAIT_OK; + break; + case ERROR: + default: + send(_sock, ERROR, c.getResponse()); + break; + } + break; + case REJECTED: + failed |= current; + int available = c.getMechs() & (~failed); + int retVal = handleReject(available, luid, _sock); + if (retVal == -1) { + state = SaslAuthState.FAILED; + } else { + current = retVal; + } + break; + case ERROR: + // when asking for file descriptor support, ERROR means FD support is not supported + if (state == SaslAuthState.NEGOTIATE_UNIX_FD) { + state = SaslAuthState.FINISHED; + logger.trace("File descriptors NOT supported by server"); + fileDescriptorSupported = false; + send(_sock, BEGIN); + } else { + send(_sock, CANCEL); + state = SaslAuthState.WAIT_REJECT; + } + break; + case OK: + logger.trace("Authenticated"); + state = SaslAuthState.AUTHENTICATED; + + if (saslConfig.isFileDescriptorSupport()) { + state = SaslAuthState.WAIT_DATA; + logger.trace("Asking for file descriptor support"); + // if authentication was successful, ask remote end for file descriptor support + send(_sock, NEGOTIATE_UNIX_FD); + } else { + state = SaslAuthState.FINISHED; + send(_sock, BEGIN); + } + break; + case AGREE_UNIX_FD: + if (saslConfig.isFileDescriptorSupport()) { + state = SaslAuthState.FINISHED; + logger.trace("File descriptors supported by server"); + fileDescriptorSupported = true; + send(_sock, BEGIN); + } + break; + default: + send(_sock, ERROR, "Got invalid command"); + break; + } + break; + case WAIT_OK: + c = receive(_sock); + switch (c.getCommand()) { + case OK: + send(_sock, BEGIN); + state = SaslAuthState.AUTHENTICATED; + break; + case ERROR: + case DATA: + send(_sock, CANCEL); + state = SaslAuthState.WAIT_REJECT; + break; + case REJECTED: + failed |= current; + int available = c.getMechs() & (~failed); + state = SaslAuthState.WAIT_DATA; + if (0 != (available & AUTH_EXTERNAL)) { + send(_sock, AUTH, "EXTERNAL", luid); + current = AUTH_EXTERNAL; + } else if (0 != (available & AUTH_SHA)) { + send(_sock, AUTH, "DBUS_COOKIE_SHA1", luid); + current = AUTH_SHA; + } else if (0 != (available & AUTH_ANON)) { + send(_sock, AUTH, "ANONYMOUS"); + current = AUTH_ANON; + } else { + state = SaslAuthState.FAILED; + } + break; + default: + send(_sock, ERROR, "Got invalid command"); + break; + } + break; + case WAIT_REJECT: + c = receive(_sock); + switch (c.getCommand()) { + case REJECTED: + failed |= current; + int available = c.getMechs() & (~failed); + int retVal = handleReject(available, luid, _sock); + if (retVal == -1) { + state = SaslAuthState.FAILED; + } else { + current = retVal; + } + break; + default: + state = SaslAuthState.FAILED; + break; + } + break; + default: + state = SaslAuthState.FAILED; + } + break; + case SERVER: + switch (state) { + case INITIAL_STATE: + ByteBuffer buf = ByteBuffer.allocate(1); + if (_sock instanceof NetworkChannel) { + _sock.read(buf); // 0 + state = SaslAuthState.WAIT_AUTH; + } else { + try { + int kuid = -1; + if (_transport instanceof AbstractUnixTransport) { + kuid = ((AbstractUnixTransport) _transport).getUid(_sock); + } + if (kuid >= 0) { + kernelUid = stupidlyEncode("" + kuid); + } + state = SaslAuthState.WAIT_AUTH; + + } catch (SocketException _ex) { + state = SaslAuthState.FAILED; + } + } + break; + case WAIT_AUTH: + c = receive(_sock); + switch (c.getCommand()) { + case AUTH: + switch (doResponse(current, luid, kernelUid, c)) { + case CONTINUE: + send(_sock, DATA, c.getResponse()); + current = c.getMechs(); + state = SaslAuthState.WAIT_DATA; + break; + case OK: + send(_sock, SaslCommand.OK, saslConfig.getGuid()); + state = SaslAuthState.WAIT_BEGIN; + current = 0; + break; + case REJECT: + default: + send(_sock, REJECTED, convertAuthTypes(saslConfig.getAuthMode())); + current = 0; + break; + } + break; + case ERROR: + send(_sock, REJECTED, convertAuthTypes(saslConfig.getAuthMode())); + break; + case BEGIN: + state = SaslAuthState.FAILED; + break; + default: + send(_sock, ERROR, "Got invalid command"); + break; + } + break; + case WAIT_DATA: + c = receive(_sock); + switch (c.getCommand()) { + case DATA: + switch (doResponse(current, luid, kernelUid, c)) { + case CONTINUE: + send(_sock, DATA, c.getResponse()); + state = SaslAuthState.WAIT_DATA; + break; + case OK: + send(_sock, SaslCommand.OK, saslConfig.getGuid()); + state = SaslAuthState.WAIT_BEGIN; + current = 0; + break; + case REJECT: + default: + send(_sock, REJECTED, convertAuthTypes(saslConfig.getAuthMode())); + current = 0; + break; + } + break; + case ERROR: + case CANCEL: + send(_sock, REJECTED, convertAuthTypes(saslConfig.getAuthMode())); + state = SaslAuthState.WAIT_AUTH; + break; + case BEGIN: + state = SaslAuthState.FAILED; + break; + default: + send(_sock, ERROR, "Got invalid command"); + break; + } + break; + case WAIT_BEGIN: + c = receive(_sock); + switch (c.getCommand()) { + case ERROR: + case CANCEL: + send(_sock, REJECTED, convertAuthTypes(saslConfig.getAuthMode())); + state = SaslAuthState.WAIT_AUTH; + break; + case BEGIN: + state = SaslAuthState.FINISHED; + break; + case NEGOTIATE_UNIX_FD: + logger.debug("File descriptor negotiation requested"); + if (!saslConfig.isFileDescriptorSupport()) { + send(_sock, ERROR); + } else { + send(_sock, AGREE_UNIX_FD); + } + + break; + default: + send(_sock, ERROR, "Got invalid command"); + break; + } + break; + default: + state = SaslAuthState.FAILED; + } + break; + default: + return false; + } + } + + return state == SaslAuthState.FINISHED; + } + + public boolean isFileDescriptorSupported() { + return fileDescriptorSupported; + } + + /** + * Handle reject of authentication. + * + * @param _available + * @param _luid + * @param _sock socketchannel + * @return current state or -1 if failed + * @throws IOException when sending fails + */ + private int handleReject(int _available, String _luid, SocketChannel _sock) throws IOException { + int current = -1; + if (0 != (_available & AUTH_EXTERNAL)) { + send(_sock, AUTH, "EXTERNAL", _luid); + current = AUTH_EXTERNAL; + } else if (0 != (_available & AUTH_SHA)) { + send(_sock, AUTH, "DBUS_COOKIE_SHA1", _luid); + current = AUTH_SHA; + } else if (0 != (_available & AUTH_ANON)) { + send(_sock, AUTH, "ANONYMOUS"); + current = AUTH_ANON; + } + return current; + } + + /** + * Tries to get the UID (user ID) of the current JVM process. + * Will always return 0 on windows. + * @return long + */ + private long getUserId() { + if (!Util.isWindows()) { + return new UnixSystem().getUid(); + } + + return 0; + } + + public enum SaslMode { + SERVER, CLIENT; + } + + public enum SaslCommand { + AUTH, + DATA, + REJECTED, + OK, + BEGIN, + CANCEL, + ERROR, + NEGOTIATE_UNIX_FD, + AGREE_UNIX_FD; + } + + enum SaslAuthState { + INITIAL_STATE, + WAIT_DATA, + WAIT_OK, + WAIT_REJECT, + WAIT_AUTH, + WAIT_BEGIN, + AUTHENTICATED, + NEGOTIATE_UNIX_FD, + FINISHED, + FAILED; + } + + public enum SaslResult { + OK, + CONTINUE, + ERROR, + REJECT; + } + + public static class Command { + private final Logger logger = LoggerFactory.getLogger(getClass()); + private SaslCommand command; + private int mechs; + private String data; + private String response; + + public Command() { + } + + public Command(String _s) throws IOException { + String[] ss = _s.split(" "); + LoggingHelper.logIf(logger.isTraceEnabled(), () -> logger.trace("Creating command from: {}", Arrays.toString(ss))); + if (0 == COL.compare(ss[0], "OK")) { + command = SaslCommand.OK; + data = ss[1]; + } else if (0 == COL.compare(ss[0], "AUTH")) { + command = AUTH; + if (ss.length > 1) { + if (0 == COL.compare(ss[1], "EXTERNAL")) { + mechs = AUTH_EXTERNAL; + } else if (0 == COL.compare(ss[1], "DBUS_COOKIE_SHA1")) { + mechs = AUTH_SHA; + } else if (0 == COL.compare(ss[1], "ANONYMOUS")) { + mechs = AUTH_ANON; + } + } + if (ss.length > 2) { + data = ss[2]; + } + } else if (0 == COL.compare(ss[0], "DATA")) { + command = DATA; + data = ss[1]; + } else if (0 == COL.compare(ss[0], "REJECTED")) { + command = REJECTED; + for (int i = 1; i < ss.length; i++) { + if (0 == COL.compare(ss[i], "EXTERNAL")) { + mechs |= AUTH_EXTERNAL; + } else if (0 == COL.compare(ss[i], "DBUS_COOKIE_SHA1")) { + mechs |= AUTH_SHA; + } else if (0 == COL.compare(ss[i], "ANONYMOUS")) { + mechs |= AUTH_ANON; + } + } + } else if (0 == COL.compare(ss[0], "BEGIN")) { + command = BEGIN; + } else if (0 == COL.compare(ss[0], "CANCEL")) { + command = CANCEL; + } else if (0 == COL.compare(ss[0], "ERROR")) { + command = ERROR; + data = ss[1]; + } else if (0 == COL.compare(ss[0], "NEGOTIATE_UNIX_FD")) { + command = NEGOTIATE_UNIX_FD; + } else if (0 == COL.compare(ss[0], "AGREE_UNIX_FD")) { + command = AGREE_UNIX_FD; + } else { + throw new IOException("Invalid Command " + ss[0]); + } + logger.trace("Created command: {}", this); + } + + public SaslCommand getCommand() { + return command; + } + + public int getMechs() { + return mechs; + } + + public String getData() { + return data; + } + + public String getResponse() { + return response; + } + + public void setResponse(String _s) { + response = _s; + } + + @Override + public String toString() { + return "Command(" + command + ", " + mechs + ", " + data + ")"; + } + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/SenderThread.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/SenderThread.java new file mode 100644 index 0000000000..45c59beee6 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/SenderThread.java @@ -0,0 +1,62 @@ +package org.freedesktop.dbus.connections; + +import org.freedesktop.dbus.messages.Message; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.LinkedBlockingQueue; + +public class SenderThread extends Thread { + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private boolean terminate; + + private final LinkedBlockingQueue outgoingQueue = new LinkedBlockingQueue<>(); + + private final AbstractConnection abstractConnection; + + SenderThread(AbstractConnection _abstractConnection) { + abstractConnection = _abstractConnection; + setName("DBUS Sender Thread"); + } + + public void terminate() { + terminate = true; + interrupt(); + } + + public LinkedBlockingQueue getOutgoingQueue() { + return outgoingQueue; + } + + @Override + public void run() { + Message m; + + logger.trace("Monitoring outbound queue"); + // block on the outbound queue and send from it + while (!terminate) { + try { + m = outgoingQueue.take(); + if (m != null) { + abstractConnection.sendMessage(m); + } + } catch (InterruptedException _ex) { + if (!terminate) { // if terminate is true, shutdown was requested, do not log that + logger.warn("Interrupted while waiting for a message to send", _ex); + } + } + } + + logger.debug("Flushing outbound queue and quitting"); + // flush the outbound queue before disconnect. + while (!outgoingQueue.isEmpty()) { + Message poll = outgoingQueue.poll(); + if (poll != null) { + abstractConnection.sendMessage(outgoingQueue.poll()); + } else { + break; + } + } + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/ReceivingServiceConfig.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/ReceivingServiceConfig.java new file mode 100644 index 0000000000..88cbca8c6c --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/ReceivingServiceConfig.java @@ -0,0 +1,98 @@ +package org.freedesktop.dbus.connections.config; + +import org.freedesktop.dbus.connections.ReceivingService.IThreadPoolRetryHandler; + +/** + * Bean which holds configuration for {@link org.freedesktop.dbus.connections.ReceivingService}. + * + * @author hypfvieh + * @since 4.2.0 - 2022-07-14 + */ +public final class ReceivingServiceConfig { + private int signalThreadPoolSize = 1; + private int errorThreadPoolSize = 1; + private int methodCallThreadPoolSize = 4; + private int methodReturnThreadPoolSize = 1; + private int signalThreadPriority = Thread.NORM_PRIORITY; + private int methodCallThreadPriority = Thread.NORM_PRIORITY; + private int errorThreadPriority = Thread.NORM_PRIORITY; + private int methodReturnThreadPriority = Thread.NORM_PRIORITY; + + private IThreadPoolRetryHandler retryHandler; + + ReceivingServiceConfig() { + } + + public int getSignalThreadPoolSize() { + return signalThreadPoolSize; + } + + public int getErrorThreadPoolSize() { + return errorThreadPoolSize; + } + + public int getMethodCallThreadPoolSize() { + return methodCallThreadPoolSize; + } + + public int getMethodReturnThreadPoolSize() { + return methodReturnThreadPoolSize; + } + + public int getSignalThreadPriority() { + return signalThreadPriority; + } + + public int getMethodCallThreadPriority() { + return methodCallThreadPriority; + } + + public int getErrorThreadPriority() { + return errorThreadPriority; + } + + public int getMethodReturnThreadPriority() { + return methodReturnThreadPriority; + } + + public IThreadPoolRetryHandler getRetryHandler() { + return retryHandler; + } + + void setSignalThreadPoolSize(int _signalThreadPoolSize) { + signalThreadPoolSize = _signalThreadPoolSize; + } + + void setErrorThreadPoolSize(int _errorThreadPoolSize) { + errorThreadPoolSize = _errorThreadPoolSize; + } + + void setMethodCallThreadPoolSize(int _methodCallThreadPoolSize) { + methodCallThreadPoolSize = _methodCallThreadPoolSize; + } + + void setMethodReturnThreadPoolSize(int _methodReturnThreadPoolSize) { + methodReturnThreadPoolSize = _methodReturnThreadPoolSize; + } + + void setSignalThreadPriority(int _signalThreadPriority) { + signalThreadPriority = _signalThreadPriority; + } + + void setMethodCallThreadPriority(int _methodCallThreadPriority) { + methodCallThreadPriority = _methodCallThreadPriority; + } + + void setErrorThreadPriority(int _errorThreadPriority) { + errorThreadPriority = _errorThreadPriority; + } + + void setMethodReturnThreadPriority(int _methodReturnThreadPriority) { + methodReturnThreadPriority = _methodReturnThreadPriority; + } + + void setRetryHandler(IThreadPoolRetryHandler _retryHandler) { + retryHandler = _retryHandler; + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/ReceivingServiceConfigBuilder.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/ReceivingServiceConfigBuilder.java new file mode 100644 index 0000000000..186b0ffb06 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/ReceivingServiceConfigBuilder.java @@ -0,0 +1,207 @@ +package org.freedesktop.dbus.connections.config; + +import org.freedesktop.dbus.connections.ReceivingService; +import org.freedesktop.dbus.connections.ReceivingService.ExecutorNames; +import org.freedesktop.dbus.connections.ReceivingService.IThreadPoolRetryHandler; +import org.freedesktop.dbus.connections.impl.BaseConnectionBuilder; +import org.freedesktop.dbus.utils.Util; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + +/** + * Configuration builder to configure {@link ReceivingService}. + * Only intended to be used in combination with {@link BaseConnectionBuilder} + * + * @author hypfvieh + * + * @param BaseConnectionBuilder type + * @since 4.2.0 - 2022-07-14 + */ +public final class ReceivingServiceConfigBuilder> { + public static final int DEFAULT_HANDLER_RETRIES = 10; + + private static final ReceivingServiceConfig DEFAULT_CFG = new ReceivingServiceConfig(); + + private static final IThreadPoolRetryHandler DEFAULT_RETRYHANDLER = new IThreadPoolRetryHandler() { + private AtomicInteger retries = new AtomicInteger(0); + @Override + public boolean handle(ExecutorNames _executor, Exception _ex) { + if (retries.incrementAndGet() < DEFAULT_HANDLER_RETRIES) { + return true; + } + LoggerFactory.getLogger(ReceivingService.class).error("Dropping runnable for {}, retry failed for more than {} iterations, cause:", _executor, DEFAULT_HANDLER_RETRIES, _ex); + return false; + } + }; + + private final Supplier connectionBuilder; + private final ReceivingServiceConfig config = new ReceivingServiceConfig(); + + public ReceivingServiceConfigBuilder(Supplier _bldr) { + connectionBuilder = _bldr; + config.setRetryHandler(DEFAULT_RETRYHANDLER); + } + + /** + * Set the size of the thread-pool used to handle signals from the bus. + * Caution: Using thread-pool size > 1 may cause signals to be handled out-of-order + *

+ * Default: 1 + * + * @param _threads int >= 1 + * @return this + */ + public ReceivingServiceConfigBuilder withSignalThreadCount(int _threads) { + config.setSignalThreadPoolSize(Math.max(1, _threads)); + return this; + } + + /** + * Set the size of the thread-pool used to handle error messages received on the bus. + *

+ * Default: 1 + * + * @param _threads int >= 1 + * @return this + */ + public ReceivingServiceConfigBuilder withErrorHandlerThreadCount(int _threads) { + config.setErrorThreadPoolSize(Math.max(1, _threads)); + return this; + } + + /** + * Set the size of the thread-pool used to handle methods calls previously sent to the bus. + * The thread pool size has to be > 1 to handle recursive calls. + *

+ * Default: 4 + * + * @param _threads int >= 1 + * @return this + */ + public ReceivingServiceConfigBuilder withMethodCallThreadCount(int _threads) { + config.setMethodCallThreadPoolSize(Math.max(1, _threads)); + return this; + } + + /** + * Set the size of the thread-pool used to handle method return values received on the bus. + *

+ * Default: 1 + * + * @param _threads int >= 1 + * @return this + */ + public ReceivingServiceConfigBuilder withMethodReturnThreadCount(int _threads) { + config.setMethodReturnThreadPoolSize(Math.max(1, _threads)); + return this; + } + + /** + * Sets the thread priority of the created signal thread(s). + *

+ * Default: {@link Thread#NORM_PRIORITY} ({@value Thread#NORM_PRIORITY}); + * + * @param _priority int >={@value Thread#MIN_PRIORITY} and <= {@value Thread#MAX_PRIORITY} + * @return this + * + * @throws IllegalArgumentException when value is out ouf range (value < {@value Thread#MIN_PRIORITY} && > {@value Thread#MAX_PRIORITY}) + */ + public ReceivingServiceConfigBuilder withSignalThreadPriority(int _priority) { + config.setSignalThreadPriority(Util.checkIntInRange(_priority, Thread.MIN_PRIORITY, Thread.MAX_PRIORITY)); + return this; + } + + /** + * Sets the thread priority of the created signal thread(s). + *

+ * Default: {@link Thread#NORM_PRIORITY} ({@value Thread#NORM_PRIORITY}); + * + * @param _priority int >={@value Thread#MIN_PRIORITY} and <= {@value Thread#MAX_PRIORITY} + * @return this + * + * @throws IllegalArgumentException when value is out ouf range (value < {@value Thread#MIN_PRIORITY} && > {@value Thread#MAX_PRIORITY}) + */ + public ReceivingServiceConfigBuilder withErrorThreadPriority(int _priority) { + config.setErrorThreadPriority(Util.checkIntInRange(_priority, Thread.MIN_PRIORITY, Thread.MAX_PRIORITY)); + return this; + } + + /** + * Sets the thread priority of the created signal thread(s). + *

+ * Default: {@link Thread#NORM_PRIORITY} ({@value Thread#NORM_PRIORITY}); + * + * @param _priority int >={@value Thread#MIN_PRIORITY} and <= {@value Thread#MAX_PRIORITY} + * @return this + * + * @throws IllegalArgumentException when value is out ouf range (value < {@value Thread#MIN_PRIORITY} && > {@value Thread#MAX_PRIORITY}) + */ + public ReceivingServiceConfigBuilder withMethedCallThreadPriority(int _priority) { + config.setMethodCallThreadPriority(Util.checkIntInRange(_priority, Thread.MIN_PRIORITY, Thread.MAX_PRIORITY)); + return this; + } + + /** + * Sets the thread priority of the created signal thread(s). + *

+ * Default: {@link Thread#NORM_PRIORITY} ({@value Thread#NORM_PRIORITY}); + * + * @param _priority int >={@value Thread#MIN_PRIORITY} and <= {@value Thread#MAX_PRIORITY} + * @return this + * + * @throws IllegalArgumentException when value is out ouf range (value < {@value Thread#MIN_PRIORITY} && > {@value Thread#MAX_PRIORITY}) + */ + public ReceivingServiceConfigBuilder withMethodReturnThreadPriority(int _priority) { + config.setMethodReturnThreadPriority(Util.checkIntInRange(_priority, Thread.MIN_PRIORITY, Thread.MAX_PRIORITY)); + return this; + } + + /** + * Sets the retry handler which should be called when executing a runnable in {@link ReceivingService} thread pools fail. + *

+ * Defaults to an implementation retrying executing the runnable up to ten times. + * If null is given, retrying will be disabled (but error will be logged). + * + * @param _handler handler to use + * @return this + */ + public ReceivingServiceConfigBuilder withRetryHandler(IThreadPoolRetryHandler _handler) { + config.setRetryHandler(_handler); + return this; + } + + /** + * Returns the configured {@link ReceivingServiceConfig} instance. + * @return config never null + */ + public ReceivingServiceConfig build() { + return config; + } + + /** + * Returns the used ConnectionBuilder for the connection for further configuration. + * @return connection builder + */ + public R connectionConfig() { + return connectionBuilder.get(); + } + + /** + * Returns the default configuration used for {@link ReceivingService}. + * @return default config + */ + public static ReceivingServiceConfig getDefaultConfig() { + return DEFAULT_CFG; + } + + /** + * Returns the default retry handler used for {@link ReceivingService}. + * @return default handler + */ + public static IThreadPoolRetryHandler getDefaultRetryHandler() { + return DEFAULT_RETRYHANDLER; + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/SaslConfig.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/SaslConfig.java new file mode 100644 index 0000000000..b449ee99ec --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/SaslConfig.java @@ -0,0 +1,114 @@ +package org.freedesktop.dbus.connections.config; + +import org.freedesktop.dbus.connections.SASL; +import org.freedesktop.dbus.connections.SASL.SaslMode; + +import java.util.OptionalLong; + +/** + * Bean contains configuration for SASL authentication. + * + * @author hypfvieh + * + * @since 4.2.0 - 2022-07-22 + */ +public class SaslConfig { + private SaslMode mode; + private int authMode; + private String guid; + private OptionalLong saslUid; + + private boolean strictCookiePermissions; + private boolean fileDescriptorSupport; + + SaslConfig() { + mode = SASL.SaslMode.CLIENT; + authMode = SASL.AUTH_NONE; + saslUid = OptionalLong.empty(); + } + + /** + * Creates a new empty SaslConfig object + * @return SaslConfig + * @deprecated only intended for internal backward compatibility, will be removed soon + */ + @Deprecated(forRemoval = true, since = "4.2.2 - 2023-02-03") + public static SaslConfig create() { + return new SaslConfig(); + } + + public SaslMode getMode() { + return mode; + } + + public void setMode(SaslMode _mode) { + mode = _mode; + } + + public int getAuthMode() { + return authMode; + } + + public void setAuthMode(int _types) { + authMode = _types; + } + + public String getGuid() { + return guid; + } + + public void setGuid(String _guid) { + guid = _guid; + } + + public OptionalLong getSaslUid() { + return saslUid; + } + + public void setSaslUid(OptionalLong _saslUid) { + saslUid = _saslUid; + } + + /** + * Whether the permissions of the cookie files (used for DBUS_COOKIE_SHA1) should be checked.
+ * Cookie permission check will only be used on Linux/Unix like OSes. + * + * @return boolean + * @since v4.2.2 - 2023-02-03 + */ + public boolean isStrictCookiePermissions() { + return strictCookiePermissions; + } + + /** + * Enable/disable checking of file permissions of the cookie files (used for DBUS_COOKIE_SHA1).
+ * Cookie permission check will only be used on Linux/Unix like OSes. + * + * @return boolean + * @since v4.2.2 - 2023-02-03 + */ + public void setStrictCookiePermissions(boolean _strictCookiePermissions) { + strictCookiePermissions = _strictCookiePermissions; + } + + /** + * Whether file descriptor passing is allowed. + * + * @return boolean + * @since v4.2.2 - 2023-02-03 + */ + public boolean isFileDescriptorSupport() { + return fileDescriptorSupport; + } + + /** + * Enable/disable support of file descriptor passing. + * + * @return boolean + * @since v4.2.2 - 2023-02-03 + */ + public void setFileDescriptorSupport(boolean _fileDescriptorSupport) { + fileDescriptorSupport = _fileDescriptorSupport; + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/SaslConfigBuilder.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/SaslConfigBuilder.java new file mode 100644 index 0000000000..f7732c79a7 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/SaslConfigBuilder.java @@ -0,0 +1,84 @@ +package org.freedesktop.dbus.connections.config; + +import org.freedesktop.dbus.connections.transports.TransportBuilder.SaslAuthMode; + +import java.util.OptionalLong; +import java.util.function.Supplier; + +/** + * Configuration used to setup a sasl authentication. + * + * @author hypfvieh + * @since v4.2.2 - 2023-02-03 + */ +public final class SaslConfigBuilder { + + private final SaslConfig saslConfig; + + private final Supplier> transportBuilder; + + SaslConfigBuilder(SaslConfig _saslConfig, Supplier> _transportBuilder) { + saslConfig = _saslConfig; + transportBuilder = _transportBuilder; + } + + /** + * Return to the previous builder. + *

+ * This allows you to return from the this builder to the builder which + * started this builder so you can continue using the previous builder. + *

+ * + * @return previous builder, maybe null + */ + public TransportConfigBuilder back() { + return transportBuilder.get(); + } + + /** + * Setup the authentication mode to use.
+ * null values will be ignored. + * + * @param _types auth mode to set + * @return this + */ + public SaslConfigBuilder withAuthMode(SaslAuthMode _types) { + if (_types != null) { + saslConfig.setAuthMode(_types.getAuthMode()); + } + return this; + } + + /** + * Setup the user ID to use for authentication when using unix sockets.
+ * Will default to the user ID of the user running the current process. + * + * @param _guid guid to use + * @return this + */ + public SaslConfigBuilder withSaslUid(Long _saslUid) { + saslConfig.setSaslUid(OptionalLong.of(_saslUid)); + return this; + } + + /** + * Enable/disable checking of file permissions of the cookie files (used for DBUS_COOKIE_SHA1).
+ * Cookie permission check will only be used on Linux/Unix like OSes.
+ * Default is false (no strict checking). + * + * @param _strictCookiePermissions boolean + * @return this + */ + public SaslConfigBuilder withStrictCookiePermissions(boolean _strictCookiePermissions) { + saslConfig.setStrictCookiePermissions(_strictCookiePermissions); + return this; + } + + /** + * Returns the created configuration. + * @return SaslConfig + */ + public SaslConfig build() { + return saslConfig; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/TransportConfig.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/TransportConfig.java new file mode 100644 index 0000000000..d241fbedc5 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/TransportConfig.java @@ -0,0 +1,157 @@ +package org.freedesktop.dbus.connections.config; + +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.connections.transports.AbstractTransport; +import org.freedesktop.dbus.utils.Util; + +import java.nio.file.attribute.PosixFilePermission; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; + +/** + * Configuration used to setup a transport. + * + * @author hypfvieh + * @since v4.2.0 - 2022-07-21 + */ +public final class TransportConfig { + + private final SaslConfig saslConfig; + + private BusAddress busAddress; + + private Consumer preConnectCallback; + + private int timeout = 10000; + private boolean autoConnect = true; + + /** user to set on socket file if this is a server transport (null to do nothing). */ + private String fileOwner; + /** group to set on socket file if this is a server transport (null to do nothing). */ + private String fileGroup; + + /** + * Unix file permissions to set on socket file if this is a server transport (ignored on Windows, does nothing if + * null) + */ + private Set fileUnixPermissions; + + /** + * Contains additional configuration where no direct getter/setter is available for. + */ + private Map additionalConfig = new LinkedHashMap<>(); + + public TransportConfig(BusAddress _address) { + busAddress = _address; + saslConfig = new SaslConfig(); + } + + public TransportConfig() { + this(null); + } + + public BusAddress getBusAddress() { + return busAddress; + } + + public void setBusAddress(BusAddress _busAddress) { + busAddress = Objects.requireNonNull(_busAddress, "BusAddress required"); + } + + public void setListening(boolean _listen) { + updateBusAddress(_listen); + } + + public boolean isListening() { + return busAddress != null && busAddress.isListeningSocket(); + } + + public Consumer getPreConnectCallback() { + return preConnectCallback; + } + + public void setPreConnectCallback(Consumer _preConnectCallback) { + preConnectCallback = _preConnectCallback; + } + + public boolean isAutoConnect() { + return autoConnect; + } + + public void setAutoConnect(boolean _autoConnect) { + autoConnect = _autoConnect; + } + + public int getTimeout() { + return timeout; + } + + public void setTimeout(int _timeout) { + timeout = _timeout; + } + + public String getFileOwner() { + return fileOwner; + } + + public void setFileOwner(String _fileOwner) { + fileOwner = _fileOwner; + } + + public String getFileGroup() { + return fileGroup; + } + + public void setFileGroup(String _fileGroup) { + fileGroup = _fileGroup; + } + + public Set getFileUnixPermissions() { + return fileUnixPermissions; + } + + public void setFileUnixPermissions(PosixFilePermission... _permissions) { + if (Util.isWindows()) { + return; + } + + if (_permissions == null || _permissions.length < 1) { + return; + } + + fileUnixPermissions = new LinkedHashSet<>(Arrays.asList(_permissions)); + } + + public Map getAdditionalConfig() { + return additionalConfig; + } + + public void setAdditionalConfig(Map _additionalConfig) { + additionalConfig = _additionalConfig; + } + + public SaslConfig getSaslConfig() { + return saslConfig; + } + + /** + * Toggles the busaddress to be a listening (server) or non listening (client) connection. + * @param _listening true to be a server connection + */ + void updateBusAddress(boolean _listening) { + if (busAddress == null) { + return; + } + if (!busAddress.isListeningSocket() && _listening) { // not a listening address, but should be one + busAddress.addParameter("listen", "true"); + } else if (busAddress.isListeningSocket() && !_listening) { // listening address, but should not be one + busAddress.removeParameter("listen"); + } + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/TransportConfigBuilder.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/TransportConfigBuilder.java new file mode 100644 index 0000000000..44899872a5 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/config/TransportConfigBuilder.java @@ -0,0 +1,275 @@ +package org.freedesktop.dbus.connections.config; + +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.connections.transports.AbstractTransport; +import org.freedesktop.dbus.connections.transports.TransportBuilder.SaslAuthMode; + +import java.nio.file.attribute.PosixFilePermission; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class TransportConfigBuilder, R> { + private final Supplier connectionBuilder; + + private TransportConfig config = new TransportConfig(); + + private final SaslConfigBuilder saslConfigBuilder; + + public TransportConfigBuilder(Supplier _sup) { + connectionBuilder = _sup; + saslConfigBuilder = new SaslConfigBuilder<>(config.getSaslConfig(), () -> this); + } + + /** + * Return ourselves. + * @return concrete version of this + */ + @SuppressWarnings("unchecked") + X self() { + return (X) this; + } + + /** + * Use the predefined TransportConfig. + *

+ * Using this will override any previous configuration and replaces + * the internal configuration object with the given instance. + *

+ * @param _config configuration, never null + * + * @return this + */ + public X withConfig(TransportConfig _config) { + Objects.requireNonNull(_config, "TransportConfig required"); + config = _config; + return self(); + } + + /** + * Set the {@link BusAddress} which should be used for the connection. + * + * @param _address address to use + * + * @return this + */ + public X withBusAddress(BusAddress _address) { + config.setBusAddress(Objects.requireNonNull(_address, "BusAddress required")); + return self(); + } + + /** + * Returns the currently configured BusAddress. + * + * @return BusAddress, maybe null + */ + public BusAddress getBusAddress() { + return config.getBusAddress(); + } + + /** + * Set a callback which will be called right before the connection to the transport is established. + *

+ * The given consumer will receive the created {@link AbstractTransport} object which is not yet + * connected. A callback should NEVER connect the transport, but is allowed to do further + * configuration if needed. + *

+ * + * @param _callback consumer to call, null to remove any callback + * + * @return this + */ + public X withPreConnectCallback(Consumer _callback) { + config.setPreConnectCallback(_callback); + return self(); + } + + /** + * Instantly connect to DBus when {@link #build()} is called. + *

+ * default: true + * + * @param _connect boolean + * + * @return this + */ + public X withAutoConnect(boolean _connect) { + config.setAutoConnect(_connect); + return self(); + } + + /** + * Set a different SASL authentication mode. + *

+ * Usually when a unixsocket based transport is used, {@link SaslAuthMode#AUTH_EXTERNAL} will be used. + * For TCP based transport {@link SaslAuthMode#AUTH_COOKIE} will be used. + *

+ * + * @param _authMode authmode to use, if null is given, default mode will be used + * + * @return this + * @deprecated use {@link #configureSasl()} instead + */ + @Deprecated(forRemoval = true, since = "4.2.2 - 2023-02-03") + public X withSaslAuthMode(SaslAuthMode _authMode) { + configureSasl().withAuthMode(_authMode); + return self(); + } + + /** + * Switch to the {@link SaslConfigBuilder} to configure the SASL authentication mechanism.
+ * Use {@link SaslConfigBuilder#back()} to return to this builder when finished. + * + * @return SaslConfigBuilder + */ + public SaslConfigBuilder configureSasl() { + return saslConfigBuilder; + } + + /** + * Use true to use the transport as listener (server). + * + * @param _listen true to be a listening connection + * + * @return this + */ + public X withListening(boolean _listen) { + config.setListening(_listen); + return self(); + } + + /** + * Setup a timeout for the transport. + *

+ * This option might not be used by every transport + * (e.g. unix sockets do not support a timeout). + * Timeout cannot be less than zero. + *

+ * + * @param _timeout true to be a listening connection + * + * @return this + */ + public X withTimeout(int _timeout) { + if (_timeout >= 0) { + config.setTimeout(_timeout); + } + return self(); + } + + /** + * Set to UID to present during SASL authentication. + *

+ * Default is the user of the running JVM process on Unix-like operating systems. On Windows, the default is zero.

+ * + * @param _saslUid UID to set, if a negative long is given the default is used + * + * @return this + * @deprecated use {@link #configureSasl()} instead + */ + @Deprecated(forRemoval = true, since = "4.2.2 - 2023-02-03") + public X withSaslUid(long _saslUid) { + configureSasl().withSaslUid(_saslUid); + return self(); + } + + /** + * The owner of the socket file if a unix socket is used and this is a server transport. + *

+ * Default is the user of the running JVM process.

+ * Please note:
+ * The running process user has to have suitable permissions to change the owner + * of the file. Otherwise the file owner will not be changed! + * + * @param _user user to set, if null is given JVM process user is used + * + * @return this + */ + public X withUnixSocketFileOwner(String _user) { + config.setFileOwner(_user); + return self(); + } + + /** + * The group of the socket file if a unix socket is used and this is a server transport. + *

+ * Default is the group of the running JVM process.

+ * Please note:
+ * The running process user has to have suitable permissions to change the group + * of the file. Otherwise the file group will not be changed! + * + * @param _group group to set, if null is given JVM process group is used + * + * @return this + */ + public X withUnixSocketFileGroup(String _group) { + config.setFileGroup(_group); + return self(); + } + + /** + * The permissions which will be set on socket file if a unix socket is used and this is a server transport. + *

+ * This method does nothing when used on windows systems. + * Please note:
+ * The running process user has to have suitable permissions to change the permissions + * of the file. Otherwise the file permissions will not be changed! + * + * @param _permissions permissions to set, if null is given default permissions will be used + * + * @return this + */ + public X withUnixSocketFilePermissions(PosixFilePermission... _permissions) { + config.setFileUnixPermissions(_permissions); + return self(); + } + + /** + * Adds an additional config key to the transport config.
+ * Will overwrite value if key exists. + * + * @param _key key to use + * @param _value value to use + * + * @return this + */ + public X withAdditionalConfig(String _key, Object _value) { + config.getAdditionalConfig().put(_key, _value); + return self(); + } + + /** + * Removes an additional config key to of transport config.
+ * Will do nothing if key does not exist. + * + * @param _key key to remove + * + * @return this + */ + public X withRemoveAdditionalConfig(String _key) { + config.getAdditionalConfig().remove(_key); + return self(); + } + + /** + * Return to the previous builder. + *

+ * This allows you to return from the this builder to the builder which + * started this builder so you can continue using the previous builder. + *

+ * + * @return previous builder, maybe null + */ + public R back() { + return connectionBuilder != null ? connectionBuilder.get() : null; + } + + /** + * Returns the transport config. + * @return config + */ + public TransportConfig build() { + return config; + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/BaseConnectionBuilder.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/BaseConnectionBuilder.java new file mode 100644 index 0000000000..44eee0701f --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/BaseConnectionBuilder.java @@ -0,0 +1,211 @@ +package org.freedesktop.dbus.connections.impl; + +import org.freedesktop.dbus.connections.AbstractConnection; +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.connections.IDisconnectCallback; +import org.freedesktop.dbus.connections.ReceivingService; +import org.freedesktop.dbus.connections.config.ReceivingServiceConfig; +import org.freedesktop.dbus.connections.config.ReceivingServiceConfigBuilder; +import org.freedesktop.dbus.connections.config.TransportConfig; +import org.freedesktop.dbus.connections.config.TransportConfigBuilder; +import org.freedesktop.dbus.connections.transports.TransportBuilder; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.messages.Message; +import org.freedesktop.dbus.messages.Message.Endian; + +import java.nio.ByteOrder; + +/** + * Base class for connection builders containing commonly used options. + * + * @author hypfvieh + * @since 4.2.0 - 2022-07-13 + * + * @param concrete type of connection builder + */ +public abstract class BaseConnectionBuilder, C extends AbstractConnection> { + + private final Class returnType; + + private boolean weakReference = false; + private byte endianess = getSystemEndianness(); + + private IDisconnectCallback disconnectCallback; + + private final ReceivingServiceConfigBuilder rsConfigBuilder; + + private final TransportConfigBuilder transportConfigBuilder; + + protected BaseConnectionBuilder(Class _returnType, BusAddress _address) { + returnType = _returnType; + rsConfigBuilder = new ReceivingServiceConfigBuilder<>(() -> self()); + transportConfigBuilder = new TransportConfigBuilder<>(() -> self()); + transportConfigBuilder.withBusAddress(_address); + } + + /** + * Return ourselves. + * @return concrete version of this + */ + R self() { + return returnType.cast(this); + } + + /** + * Creates the configuration to use for {@link ReceivingService}. + * + * @return config + */ + protected ReceivingServiceConfig buildThreadConfig() { + return rsConfigBuilder.build(); + } + + /** + * Creates the configuration to use for {@link TransportBuilder}. + * + * @return config + */ + protected TransportConfig buildTransportConfig() { + return transportConfigBuilder.build(); + } + + protected boolean isWeakReference() { + return weakReference; + } + + protected byte getEndianess() { + return endianess; + } + + protected IDisconnectCallback getDisconnectCallback() { + return disconnectCallback; + } + + /** + * Returns the builder to configure the receiving thread pools. + * @return builder + */ + public ReceivingServiceConfigBuilder receivingThreadConfig() { + return rsConfigBuilder; + } + + /** + * Returns the builder to configure the used transport. + * @return builder + */ + public TransportConfigBuilder transportConfig() { + return transportConfigBuilder; + } + + /** + * Set the size of the thread-pool used to handle signals from the bus. + * Caution: Using thread-pool size > 1 may cause signals to be handled out-of-order + *

+ * Default: 1 + * + * @param _threads int >= 1 + * @return this + * @deprecated use receivingThreadConfig().withSignalThreadCount(_threads) + */ + @Deprecated(since = "4.2.0", forRemoval = true) + public R withSignalThreadCount(int _threads) { + receivingThreadConfig().withSignalThreadCount(_threads); + return self(); + } + + /** + * Set the size of the thread-pool used to handle error messages received on the bus. + *

+ * Default: 1 + * + * @param _threads int >= 1 + * @return this + * @deprecated use receivingThreadConfig().withErrorHandlerThreadCount(_threads) + */ + @Deprecated(since = "4.2.0", forRemoval = true) + public R withErrorHandlerThreadCount(int _threads) { + receivingThreadConfig().withErrorHandlerThreadCount(_threads); + return self(); + } + + /** + * Set the size of the thread-pool used to handle methods calls previously sent to the bus. + * The thread pool size has to be > 1 to handle recursive calls. + *

+ * Default: 4 + * + * @param _threads int >= 1 + * @return this + * @deprecated use receivingThreadConfig().withMethodCallThreadCount(_threads) + */ + @Deprecated(since = "4.2.0", forRemoval = true) + public R withMethodCallThreadCount(int _threads) { + receivingThreadConfig().withMethodCallThreadCount(_threads); + return self(); + } + + /** + * Set the size of the thread-pool used to handle method return values received on the bus. + *

+ * Default: 1 + * + * @param _threads int >= 1 + * @return this + * @deprecated use receivingThreadConfig().withMethodReturnThreadCount(_threads) + */ + @Deprecated(since = "4.2.0", forRemoval = true) + public R withMethodReturnThreadCount(int _threads) { + receivingThreadConfig().withMethodReturnThreadCount(_threads); + return self(); + } + + /** + * Set the endianess for the connection + * Default is based on system endianess. + * + * @param _endianess {@link Endian#BIG} or {@value Endian#LITTLE} + * @return this + */ + public R withEndianess(byte _endianess) { + if (_endianess == Endian.BIG || _endianess == Endian.LITTLE) { + endianess = _endianess; + } + return self(); + } + + /** + * Enable/Disable weak references on connection. + * Default is false. + * + * @param _weakRef true to enable + * @return this + */ + public R withWeakReferences(boolean _weakRef) { + weakReference = _weakRef; + return self(); + } + + /** + * Set the given disconnect callback to the created connection. + * + * @param _disconnectCallback callback + * @return this + */ + public R withDisconnectCallback(IDisconnectCallback _disconnectCallback) { + disconnectCallback = _disconnectCallback; + return self(); + } + + public abstract C build() throws DBusException; + + /** + * Get the default system endianness. + * + * @return LITTLE or BIG + */ + public static byte getSystemEndianness() { + return ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN) + ? Message.Endian.BIG + : Message.Endian.LITTLE; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnection.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnection.java new file mode 100644 index 0000000000..49f8f9f9e2 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnection.java @@ -0,0 +1,999 @@ +package org.freedesktop.dbus.connections.impl; + +import static org.freedesktop.dbus.utils.CommonRegexPattern.DBUS_IFACE_PATTERN; +import static org.freedesktop.dbus.utils.CommonRegexPattern.IFACE_PATTERN; +import static org.freedesktop.dbus.utils.CommonRegexPattern.PROXY_SPLIT_PATTERN; + +import org.freedesktop.dbus.DBusMatchRule; +import org.freedesktop.dbus.RemoteInvocationHandler; +import org.freedesktop.dbus.RemoteObject; +import org.freedesktop.dbus.connections.AbstractConnection; +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.connections.IDisconnectAction; +import org.freedesktop.dbus.connections.config.ReceivingServiceConfig; +import org.freedesktop.dbus.connections.config.TransportConfig; +import org.freedesktop.dbus.connections.transports.TransportBuilder; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.freedesktop.dbus.exceptions.NotConnected; +import org.freedesktop.dbus.interfaces.DBus; +import org.freedesktop.dbus.interfaces.DBusInterface; +import org.freedesktop.dbus.interfaces.DBusSigHandler; +import org.freedesktop.dbus.interfaces.Introspectable; +import org.freedesktop.dbus.messages.DBusSignal; +import org.freedesktop.dbus.messages.ExportedObject; +import org.freedesktop.dbus.types.UInt32; +import org.freedesktop.dbus.utils.AddressBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * Handles a connection to DBus. + *

+ * This is a Singleton class, only 1 connection to the SYSTEM or SESSION busses can be made. Repeated calls to + * getConnection will return the same reference. + *

+ *

+ * Signal Handlers and method calls from remote objects are run in their own threads, you MUST handle the concurrency + * issues. + *

+ */ +public final class DBusConnection extends AbstractConnection { + + static final ConcurrentMap CONNECTIONS = new ConcurrentHashMap<>(); + + private static String dbusMachineIdFile; + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final List busnames; + + private final String machineId; + private DBus dbus; + + /** Count how many 'connections' we manage internally. + * This is required because a {@link DBusConnection} to the same address will always return the same object and + * the 'real' disconnection should only occur when there is no second/third/whatever connection is left. */ + //CHECKSTYLE:OFF + final AtomicInteger concurrentConnections = new AtomicInteger(1); + //CHECKSTYLE:ON + + /** + * Whether this connection is used in shared mode. + */ + private final boolean shared; + + DBusConnection(boolean _shared, String _machineId, TransportConfig _tranportCfg, ReceivingServiceConfig _rsCfg) throws DBusException { + super(_tranportCfg, _rsCfg); + busnames = new ArrayList<>(); + machineId = _machineId; + shared = _shared; + } + + /** + * Connect to the BUS. If a connection already exists to the specified Bus, a reference to it is returned. Will + * always register our own session to Dbus. + * + * @param _address The address of the bus to connect to + * @throws DBusException If there is a problem connecting to the Bus. + * @return {@link DBusConnection} + * @deprecated use {@link DBusConnectionBuilder} + */ + @Deprecated(since = "4.1.0", forRemoval = true) + public static DBusConnection getConnection(String _address) throws DBusException { + return getConnection(_address, true, true, AbstractConnection.TCP_CONNECT_TIMEOUT); + } + + /** + * Connect to the BUS. If a connection already exists to the specified Bus and the shared-flag is true, a reference is returned. + * Will register our own session to DBus if registerSelf is true (default). + * A new connection is created every time if shared-flag is false. + * + * @param _address The address of the bus to connect to + * @param _registerSelf register own session in dbus + * @param _shared use a shared connections + * @throws DBusException If there is a problem connecting to the Bus. + * @return {@link DBusConnection} + * @deprecated use {@link DBusConnectionBuilder} + */ + @Deprecated(since = "4.1.0", forRemoval = true) + public static DBusConnection getConnection(String _address, boolean _registerSelf, boolean _shared) + throws DBusException { + return getConnection(_address, _registerSelf, _shared, AbstractConnection.TCP_CONNECT_TIMEOUT); + } + + /** + * Connect to the BUS. If a connection already exists to the specified Bus and the shared-flag is true, a reference is returned. + * Will register our own session to DBus if registerSelf is true (default). + * A new connection is created every time if shared-flag is false. + * + * @param _address The address of the bus to connect to + * @param _registerSelf register own session in dbus + * @param _shared use a shared connections + * @param _timeout connect timeout if this is a TCP socket, 0 will block forever, if this is not a TCP socket this value is ignored + * @throws DBusException If there is a problem connecting to the Bus. + * @return {@link DBusConnection} + * @deprecated use {@link DBusConnectionBuilder} + */ + @Deprecated(since = "4.1.0", forRemoval = true) + public static DBusConnection getConnection(String _address, boolean _registerSelf, boolean _shared, int _timeout) + throws DBusException { + + return DBusConnectionBuilder.forAddress(_address) + .withRegisterSelf(_registerSelf) + .withShared(_shared) + .transportConfig() + .withAdditionalConfig("TIMEOUT", 10000) + .back() + .build(); + } + + /** + * Connect to DBus. + * If a connection already exists to the specified Bus, a reference to it is returned. + * + * @param _bustype The Bus to connect to. + + * @return {@link DBusConnection} + * + * @throws DBusException If there is a problem connecting to the Bus. + * @deprecated use {@link DBusConnectionBuilder} + */ + @Deprecated(since = "4.1.0", forRemoval = true) + public static DBusConnection getConnection(DBusBusType _bustype) throws DBusException { + return getConnection(_bustype, true, AbstractConnection.TCP_CONNECT_TIMEOUT); + } + + /** + * Connect to DBus using a new connection even if there is already a connection established. + * + * @param _bustype The Bus to connect to. + * + * @return {@link DBusConnection} + * + * @throws DBusException If there is a problem connecting to the Bus. + * @deprecated use {@link DBusConnectionBuilder} + */ + @Deprecated(since = "4.1.0", forRemoval = true) + public static DBusConnection newConnection(DBusBusType _bustype) throws DBusException { + return getConnection(_bustype, false, AbstractConnection.TCP_CONNECT_TIMEOUT); + } + + /** + * Connect to the BUS. + * If a connection to the specified Bus already exists and shared-flag is true, a reference to it is returned. + * Otherwise a new connection will be created. + * + * @param _bustype The Bus to connect to. + * @param _shared use shared connection + * @param _timeout connect timeout if this is a TCP socket, 0 will block forever, if this is not a TCP socket this value is ignored + * + * @return {@link DBusConnection} + * + * @throws DBusException If there is a problem connecting to the Bus. + * @deprecated use {@link DBusConnectionBuilder} + */ + @Deprecated(since = "4.1.0", forRemoval = true) + public static DBusConnection getConnection(DBusBusType _bustype, boolean _shared, int _timeout) throws DBusException { + BusAddress address; + + switch (_bustype) { + case SYSTEM: + address = AddressBuilder.getSystemConnection(); + break; + case SESSION: + address = AddressBuilder.getSessionConnection(dbusMachineIdFile); + break; + default: + throw new DBusException("Invalid Bus Type: " + _bustype); + } + + if (!TransportBuilder.getRegisteredBusTypes().contains("UNIX") // no unix transport + && TransportBuilder.getRegisteredBusTypes().contains("TCP") // but tcp transport + && (address == null || address.isBusType("UNIX"))) { // no address or unix socket address + + // no UNIX transport available, or lookup did not return anything useful + address = BusAddress.of(System.getProperty(TCP_ADDRESS_PROPERTY)); + } + + return getConnection(address.toString(), true, _shared, _timeout); + } + + private AtomicInteger getConcurrentConnections() { + return concurrentConnections; + } + + /** + * Set a specific machine-id file path to read the machine ID from. The + * system variable DBUS_MACHINE_ID_LOCATION will take precedence + * over this. + * + * @param _dbusMachineIdFile file containing DBus machine ID. + * @deprecated no longer required when {@link DBusConnectionBuilder} is used + */ + @Deprecated(since = "4.1.0", forRemoval = true) + public static void setDbusMachineIdFile(String _dbusMachineIdFile) { + DBusConnection.dbusMachineIdFile = _dbusMachineIdFile; + } + + /** + * Connect to bus and register if asked. + * Should only be called by Builder. + * + * @param _registerSelf true to register + * @throws DBusException if registering fails + */ + void connect(boolean _registerSelf) throws DBusException { + // start listening for calls + listen(); + + // register disconnect handlers + DBusSigHandler h = new SigHandler(); + addSigHandlerWithoutMatch(DBus.NameAcquired.class, h); + + // register ourselves if not disabled + if (_registerSelf) { + dbus = getRemoteObject("org.freedesktop.DBus", "/org/freedesktop/DBus", DBus.class); + try { + busnames.add(dbus.Hello()); + } catch (DBusExecutionException _ex) { + logger.debug("Error while doing 'Hello' handshake", _ex); + throw new DBusException(_ex.getMessage()); + } + } + } + + /** + * Tries to resolve a proxy to a remote object. + * If a type class is given, it tries to convert the object using that class. + * If null is given as type, it tries to find a proper interface for this object. + * + * @param object type (DBusInterface compatible) + * @param _source source + * @param _path path + * @param _type class of object type + * + * @return DBusInterface compatible object + * + * @throws DBusException when something goes wrong + * + * @apiNote This method is only intended for internal use. + * Visibility may change in future release + */ + @SuppressWarnings("unchecked") + public T dynamicProxy(String _source, String _path, Class _type) throws DBusException { + logger.debug("Introspecting {} on {} for dynamic proxy creation", _path, _source); + try { + Introspectable intro = getRemoteObject(_source, _path, Introspectable.class); + String data = intro.Introspect(); + logger.trace("Got introspection data: {}", data); + + String[] tags = PROXY_SPLIT_PATTERN.split(data); + + List ifaces = Arrays.stream(tags).filter(t -> t.startsWith("interface")) + .map(t -> IFACE_PATTERN.matcher(t).replaceAll("$1")) + .map(i -> { + if (i.startsWith("org.freedesktop.DBus.")) { // if this is a default DBus interface, look for it in our package structure + return DBUS_IFACE_PATTERN.matcher(i).replaceAll("$1"); + } + return i; + }) + .collect(Collectors.toList()); + + List> ifcs = findMatchingTypes(_type, ifaces); + + // interface could not be found, we guess that this exported object at least support DBusInterface + if (ifcs.isEmpty()) { + ifcs.add(DBusInterface.class); + } + + RemoteObject ro = new RemoteObject(_source, _path, _type, false); + DBusInterface newi = (DBusInterface) Proxy.newProxyInstance(ifcs.get(0).getClassLoader(), + ifcs.toArray(Class[]::new), new RemoteInvocationHandler(this, ro)); + getImportedObjects().put(newi, ro); + + return (T) newi; + } catch (Exception _ex) { + logger.debug("", _ex); + throw new DBusException( + String.format("Failed to create proxy object for %s exported by %s. Reason: %s", _path, + _source, _ex.getMessage())); + } + } + + @SuppressWarnings("unchecked") + @Override + public T getExportedObject(String _source, String _path, Class _type) throws DBusException { + ExportedObject o; + synchronized (getExportedObjects()) { + o = getExportedObjects().get(_path); + } + if (null != o && o.getObject().get() == null) { + unExportObject(_path); + o = null; + } + if (null != o) { + return (T) o.getObject().get(); + } + if (null == _source) { + throw new DBusException("Not an object exported by this connection and no remote specified"); + } + return dynamicProxy(_source, _path, _type); + } + + @Override + public DBusInterface getExportedObject(String _source, String _path) throws DBusException { + return getExportedObject(_source, _path, null); + } + + /** + * Release a bus name. Releases the name so that other people can use it + * + * @param _busname + * The name to release. MUST be in dot-notation like "org.freedesktop.local" + * @throws DBusException + * If the busname is incorrectly formatted. + */ + public void releaseBusName(String _busname) throws DBusException { + if (_busname.length() > MAX_NAME_LENGTH || !BUSNAME_REGEX.matcher(_busname).matches()) { + throw new DBusException("Invalid bus name"); + } + try { + dbus.ReleaseName(_busname); + } catch (DBusExecutionException _ex) { + logger.debug("", _ex); + throw new DBusException(_ex.getMessage()); + } + + synchronized (this.busnames) { + this.busnames.remove(_busname); + } + } + + /** + * Request a bus name. Request the well known name that this should respond to on the Bus. + * + * @param _busname + * The name to respond to. MUST be in dot-notation like "org.freedesktop.local" + * @throws DBusException + * If the register name failed, or our name already exists on the bus. or if busname is incorrectly + * formatted. + */ + public void requestBusName(String _busname) throws DBusException { + if (_busname.length() > MAX_NAME_LENGTH || !BUSNAME_REGEX.matcher(_busname).matches()) { + throw new DBusException("Invalid bus name"); + } + + UInt32 rv; + try { + rv = dbus.RequestName(_busname, + new UInt32(DBus.DBUS_NAME_FLAG_REPLACE_EXISTING | DBus.DBUS_NAME_FLAG_DO_NOT_QUEUE)); + } catch (DBusExecutionException _exDb) { + logger.debug("", _exDb); + throw new DBusException(_exDb); + } + switch (rv.intValue()) { + case DBus.DBUS_REQUEST_NAME_REPLY_IN_QUEUE: + case DBus.DBUS_REQUEST_NAME_REPLY_EXISTS: + throw new DBusException("Failed to register bus name"); + case DBus.DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: + case DBus.DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: + default: + break; + } + synchronized (this.busnames) { + this.busnames.add(_busname); + } + } + + /** + * Returns the unique name of this connection. + * + * @return unique name + */ + public String getUniqueName() { + return busnames.get(0); + } + + /** + * Returns all the names owned by this connection. + * + * @return connection names + */ + public String[] getNames() { + Set names = new TreeSet<>(); + names.addAll(busnames); + return names.toArray(String[]::new); + } + + public I getPeerRemoteObject(String _busname, String _objectpath, Class _type) + throws DBusException { + return getPeerRemoteObject(_busname, _objectpath, _type, true); + } + + /** + * Return a reference to a remote object. This method will resolve the well known name (if given) to a unique bus + * name when you call it. This means that if a well known name is released by one process and acquired by another + * calls to objects gained from this method will continue to operate on the original process. + * + * This method will use bus introspection to determine the interfaces on a remote object and so may block and + * may fail. The resulting proxy object will, however, be castable to any interface it implements. It will + * also autostart the process if applicable. Also note that the resulting proxy may fail to execute the correct + * method with overloaded methods and that complex types may fail in interesting ways. Basically, if something odd + * happens, try specifying the interface explicitly. + * + * @param _busname + * The bus name to connect to. Usually a well known bus name in dot-notation (such as + * "org.freedesktop.local") or may be a DBus address such as ":1-16". + * @param _objectpath + * The path on which the process is exporting the object.$ + * @return A reference to a remote object. + * @throws ClassCastException + * If type is not a sub-type of DBusInterface + * @throws DBusException + * If busname or objectpath are incorrectly formatted. + */ + public DBusInterface getPeerRemoteObject(String _busname, String _objectpath) throws DBusException { + if (null == _busname) { + throw new DBusException("Invalid bus name: null"); + } + + if (_busname.length() > MAX_NAME_LENGTH || !BUSNAME_REGEX.matcher(_busname).matches() && !CONNID_REGEX.matcher(_busname).matches()) { + throw new DBusException("Invalid bus name: " + _busname); + } + + String unique = dbus.GetNameOwner(_busname); + + return dynamicProxy(unique, _objectpath, null); + } + + /** + * Return a reference to a remote object. This method will always refer to the well known name (if given) rather + * than resolving it to a unique bus name. In particular this means that if a process providing the well known name + * disappears and is taken over by another process proxy objects gained by this method will make calls on the new + * proccess. + * + * This method will use bus introspection to determine the interfaces on a remote object and so may block and + * may fail. The resulting proxy object will, however, be castable to any interface it implements. It will + * also autostart the process if applicable. Also note that the resulting proxy may fail to execute the correct + * method with overloaded methods and that complex types may fail in interesting ways. Basically, if something odd + * happens, try specifying the interface explicitly. + * + * @param _busname + * The bus name to connect to. Usually a well known bus name name in dot-notation (such as + * "org.freedesktop.local") or may be a DBus address such as ":1-16". + * @param _objectpath + * The path on which the process is exporting the object. + * @return A reference to a remote object. + * @throws ClassCastException + * If type is not a sub-type of DBusInterface + * @throws DBusException + * If busname or objectpath are incorrectly formatted. + */ + public DBusInterface getRemoteObject(String _busname, String _objectpath) throws DBusException { + if (null == _busname) { + throw new DBusException("Invalid bus name: null"); + } + if (null == _objectpath) { + throw new DBusException("Invalid object path: null"); + } + + if (_busname.length() > MAX_NAME_LENGTH || !BUSNAME_REGEX.matcher(_busname).matches() && !CONNID_REGEX.matcher(_busname).matches()) { + throw new DBusException("Invalid bus name: " + _busname); + } + + if (_objectpath.length() > MAX_NAME_LENGTH || !OBJECT_REGEX_PATTERN.matcher(_objectpath).matches()) { + throw new DBusException("Invalid object path: " + _objectpath); + } + + return dynamicProxy(_busname, _objectpath, null); + } + + /** + * Return a reference to a remote object. This method will resolve the well known name (if given) to a unique bus + * name when you call it. This means that if a well known name is released by one process and acquired by another + * calls to objects gained from this method will continue to operate on the original process. + * + * @param + * class extending {@link DBusInterface} + * @param _busname + * The bus name to connect to. Usually a well known bus name in dot-notation (such as + * "org.freedesktop.local") or may be a DBus address such as ":1-16". + * @param _objectpath + * The path on which the process is exporting the object.$ + * @param _type + * The interface they are exporting it on. This type must have the same full class name and exposed + * method signatures as the interface the remote object is exporting. + * @param _autostart + * Disable/Enable auto-starting of services in response to calls on this object. Default is enabled; when + * calling a method with auto-start enabled, if the destination is a well-known name and is not owned the + * bus will attempt to start a process to take the name. When disabled an error is returned immediately. + * @return A reference to a remote object. + * @throws ClassCastException + * If type is not a sub-type of DBusInterface + * @throws DBusException + * If busname or objectpath are incorrectly formatted or type is not in a package. + */ + public I getPeerRemoteObject(String _busname, String _objectpath, Class _type, + boolean _autostart) throws DBusException { + if (null == _busname) { + throw new DBusException("Invalid bus name: null"); + } + + if (_busname.length() > MAX_NAME_LENGTH || !BUSNAME_REGEX.matcher(_busname).matches() && !CONNID_REGEX.matcher(_busname).matches()) { + throw new DBusException("Invalid bus name: " + _busname); + } + + String unique = dbus.GetNameOwner(_busname); + + return getRemoteObject(unique, _objectpath, _type, _autostart); + } + + /** + * Return a reference to a remote object. This method will always refer to the well known name (if given) rather + * than resolving it to a unique bus name. In particular this means that if a process providing the well known name + * disappears and is taken over by another process proxy objects gained by this method will make calls on the new + * proccess. + * + * @param + * class extending {@link DBusInterface} + * @param _busname + * The bus name to connect to. Usually a well known bus name name in dot-notation (such as + * "org.freedesktop.local") or may be a DBus address such as ":1-16". + * @param _objectpath + * The path on which the process is exporting the object. + * @param _type + * The interface they are exporting it on. This type must have the same full class name and exposed + * method signatures as the interface the remote object is exporting. + * @return A reference to a remote object. + * @throws ClassCastException + * If type is not a sub-type of DBusInterface + * @throws DBusException + * If busname or objectpath are incorrectly formatted or type is not in a package. + */ + public I getRemoteObject(String _busname, String _objectpath, Class _type) + throws DBusException { + return getRemoteObject(_busname, _objectpath, _type, true); + } + + /** + * Return a reference to a remote object. This method will always refer to the well known name (if given) rather + * than resolving it to a unique bus name. In particular this means that if a process providing the well known name + * disappears and is taken over by another process proxy objects gained by this method will make calls on the new + * proccess. + * + * @param + * class extending {@link DBusInterface} + * @param _busname + * The bus name to connect to. Usually a well known bus name name in dot-notation (such as + * "org.freedesktop.local") or may be a DBus address such as ":1-16". + * @param _objectpath + * The path on which the process is exporting the object. + * @param _type + * The interface they are exporting it on. This type must have the same full class name and exposed + * method signatures as the interface the remote object is exporting. + * @param _autostart + * Disable/Enable auto-starting of services in response to calls on this object. Default is enabled; when + * calling a method with auto-start enabled, if the destination is a well-known name and is not owned the + * bus will attempt to start a process to take the name. When disabled an error is returned immediately. + * @return A reference to a remote object. + * @throws ClassCastException + * If type is not a sub-type of DBusInterface + * @throws DBusException + * If busname or objectpath are incorrectly formatted or type is not in a package. + */ + @SuppressWarnings("unchecked") + public I getRemoteObject(String _busname, String _objectpath, Class _type, + boolean _autostart) throws DBusException { + if (null == _busname) { + throw new DBusException("Invalid bus name: null"); + } + if (null == _objectpath) { + throw new DBusException("Invalid object path: null"); + } + if (null == _type) { + throw new ClassCastException("Not A DBus Interface"); + } + + if (_busname.length() > MAX_NAME_LENGTH || !BUSNAME_REGEX.matcher(_busname).matches() && !CONNID_REGEX.matcher(_busname).matches()) { + throw new DBusException("Invalid bus name: " + _busname); + } + + if (!OBJECT_REGEX_PATTERN.matcher(_objectpath).matches() || _objectpath.length() > MAX_NAME_LENGTH) { + throw new DBusException("Invalid object path: " + _objectpath); + } + + if (!DBusInterface.class.isAssignableFrom(_type)) { + throw new ClassCastException("Not A DBus Interface"); + } + + // don't let people import things which don't have a + // valid D-Bus interface name + if (_type.getName().equals(_type.getSimpleName())) { + throw new DBusException("DBusInterfaces cannot be declared outside a package"); + } + + RemoteObject ro = new RemoteObject(_busname, _objectpath, _type, _autostart); + I i = (I) Proxy.newProxyInstance(_type.getClassLoader(), new Class[] { + _type + }, new RemoteInvocationHandler(this, ro)); + getImportedObjects().put(i, ro); + return i; + } + + /** + * Remove a Signal Handler. Stops listening for this signal. + * + * @param + * class extending {@link DBusSignal} + * @param _type + * The signal to watch for. + * @param _source + * The source of the signal. + * @param _handler + * the handler + * @throws DBusException + * If listening for the signal on the bus failed. + * @throws ClassCastException + * If type is not a sub-type of DBusSignal. + */ + public void removeSigHandler(Class _type, String _source, DBusSigHandler _handler) + throws DBusException { + validateSignal(_type, _source); + removeSigHandler(new DBusMatchRule(_type, _source, null), _handler); + } + + /** + * Remove a Signal Handler. Stops listening for this signal. + * + * @param + * class extending {@link DBusSignal} + * @param _type + * The signal to watch for. + * @param _source + * The source of the signal. + * @param _object + * The object emitting the signal. + * @param _handler + * the handler + * @throws DBusException + * If listening for the signal on the bus failed. + * @throws ClassCastException + * If type is not a sub-type of DBusSignal. + */ + public void removeSigHandler(Class _type, String _source, DBusInterface _object, + DBusSigHandler _handler) throws DBusException { + validateSignal(_type, _source); + + String objectpath = getImportedObjects().get(_object).getObjectPath(); + if (objectpath.length() > MAX_NAME_LENGTH || !OBJECT_REGEX_PATTERN.matcher(objectpath).matches()) { + throw new DBusException("Invalid object path: " + objectpath); + } + removeSigHandler(new DBusMatchRule(_type, _source, objectpath), _handler); + } + + /** + * {@inheritDoc} + */ + @Override + protected void removeSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) + throws DBusException { + + Queue> dbusSignalList = getHandledSignals().get(_rule); + + if (null != dbusSignalList) { + dbusSignalList.remove(_handler); + if (dbusSignalList.isEmpty()) { + getHandledSignals().remove(_rule); + try { + dbus.RemoveMatch(_rule.toString()); + } catch (NotConnected _ex) { + logger.debug("No connection.", _ex); + } catch (DBusExecutionException _ex) { + logger.debug("", _ex); + throw new DBusException(_ex); + } + } + } + } + + /** + * Add a Signal Handler. Adds a signal handler to call when a signal is received which matches the specified type, + * name and source. + * + * @param + * class extending {@link DBusSignal} + * @param _type + * The signal to watch for. + * @param _source + * The process which will send the signal. This MUST be a unique bus name and not a well known + * name. + * @return closeable that removes signal handler + * @param _handler + * The handler to call when a signal is received. + * @throws DBusException + * If listening for the signal on the bus failed. + * @throws ClassCastException + * If type is not a sub-type of DBusSignal. + */ + public AutoCloseable addSigHandler(Class _type, String _source, DBusSigHandler _handler) + throws DBusException { + validateSignal(_type, _source); + addSigHandler(new DBusMatchRule(_type, _source, null), (DBusSigHandler) _handler); + return new AutoCloseable() { + + @Override + public void close() throws DBusException { + removeSigHandler(_type, _source, _handler); + } + }; + } + + /** + * Add a Signal Handler. Adds a signal handler to call when a signal is received which matches the specified type, + * name, source and object. + * + * @param + * class extending {@link DBusSignal} + * @param _type + * The signal to watch for. + * @param _source + * The process which will send the signal. This MUST be a unique bus name and not a well known + * name. + * @param _object + * The object from which the signal will be emitted + * @param _handler + * The handler to call when a signal is received. + * @return closeable that removes signal handler + * @throws DBusException + * If listening for the signal on the bus failed. + * @throws ClassCastException + * If type is not a sub-type of DBusSignal. + */ + public AutoCloseable addSigHandler(Class _type, String _source, DBusInterface _object, + DBusSigHandler _handler) throws DBusException { + validateSignal(_type, _source); + + String objectpath = getImportedObjects().get(_object).getObjectPath(); + if (objectpath.length() > MAX_NAME_LENGTH || !OBJECT_REGEX_PATTERN.matcher(objectpath).matches()) { + throw new DBusException("Invalid object path: " + objectpath); + } + addSigHandler(new DBusMatchRule(_type, _source, objectpath), (DBusSigHandler) _handler); + return new AutoCloseable() { + @Override + public void close() throws DBusException { + removeSigHandler(_type, _source, _object, _handler); + } + }; + } + + /** + * Checks if given type is a DBusSignal and matches the required rules. + * + * @param type of class + * @param _type class + * @param _source + * @throws DBusException when validation fails + */ + private void validateSignal(Class _type, String _source) throws DBusException { + if (!DBusSignal.class.isAssignableFrom(_type)) { + throw new ClassCastException("Not A DBus Signal"); + } + if (BUSNAME_REGEX.matcher(_source).matches()) { + throw new DBusException( + "Cannot watch for signals based on well known bus name as source, only unique names."); + } + if (_source.length() > MAX_NAME_LENGTH || !CONNID_REGEX.matcher(_source).matches()) { + throw new DBusException("Invalid bus name: " + _source); + } + } + + /** + * {@inheritDoc} + */ + @Override + public AutoCloseable addSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) + throws DBusException { + + Objects.requireNonNull(_rule, "Match rule cannot be null"); + Objects.requireNonNull(_handler, "Handler cannot be null"); + + AtomicBoolean addMatch = new AtomicBoolean(false); // flag to perform action if this is a new signal key + + Queue> dbusSignalList = + getHandledSignals().computeIfAbsent(_rule, v -> { + Queue> signalList = new ConcurrentLinkedQueue<>(); + addMatch.set(true); + return signalList; + }); + + // add handler to signal list + dbusSignalList.add(_handler); + + // add match rule if this rule is new + if (addMatch.get()) { + try { + dbus.AddMatch(_rule.toString()); + } catch (DBusExecutionException _ex) { + logger.debug("Cannot add match rule: " + _rule.toString(), _ex); + throw new DBusException("Cannot add match rule.", _ex); + } + } + return new AutoCloseable() { + @Override + public void close() throws DBusException { + removeSigHandler(_rule, _handler); + } + }; + } + + /** + * Disconnect from the Bus. + * If this is a shared connection, it only disconnects when the last reference to the bus has called disconnect. + * If this is not a shared connection, disconnect will close the connection instantly. + */ + @Override + public void disconnect() { + if (!isConnected()) { // already disconnected + return; + } + + // if this is a shared connection, keep track of disconnect calls + if (shared) { + + synchronized (CONNECTIONS) { + DBusConnection connection = CONNECTIONS.get(getAddress().toString()); + if (connection != null) { + if (connection.getConcurrentConnections().get() <= 1) { // one left, this should be ourselves + CONNECTIONS.remove(getAddress().toString()); + + super.disconnect(); + + } else { + logger.debug("Still {} connections left, decreasing connection counter", connection.getConcurrentConnections().get() - 1); + Optional.ofNullable(getDisconnectCallback()).ifPresent(cb -> cb.requestedDisconnect(connection.getConcurrentConnections().get())); + connection.getConcurrentConnections().decrementAndGet(); + } + } + } + + } else { // this is a standalone non-shared session, disconnect directly using super's implementation + IDisconnectAction beforeDisconnectAction = () -> { + + // get all busnames from the list which matches the usual pattern + // this is required as the list also contains internal names like ":1.11" + // it is also required to put the results in a new list, otherwise we would get a + // concurrent modification exception later (calling releaseBusName() will modify the busnames List) + synchronized (busnames) { + List lBusNames = busnames.stream() + .filter(busName -> busName != null && !(busName.length() > MAX_NAME_LENGTH || !BUSNAME_REGEX.matcher(busName).matches())) + .collect(Collectors.toList()); + + lBusNames.forEach(busName -> { + try { + releaseBusName(busName); + + } catch (DBusException _ex) { + logger.error("Error while releasing busName '" + busName + "'.", _ex); + } + + }); + } + + // remove all exported objects before disconnecting + Map exportedObjects = getExportedObjects(); + synchronized (exportedObjects) { + List exportedKeys = exportedObjects.keySet().stream().filter(f -> f != null).collect(Collectors.toList()); + for (String key : exportedKeys) { + unExportObject(key); + } + } + + }; + + super.disconnect(beforeDisconnectAction, null); + } + } + + /** + * Same as disconnect. + */ + @Override + public void close() throws IOException { + disconnect(); + } + + @Override + public String getMachineId() { + return machineId; + } + + @Override + public void removeGenericSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) throws DBusException { + Queue> genericSignalsList = getGenericHandledSignals().get(_rule); + if (null != genericSignalsList) { + genericSignalsList.remove(_handler); + if (genericSignalsList.isEmpty()) { + getGenericHandledSignals().remove(_rule); + try { + dbus.RemoveMatch(_rule.toString()); + } catch (NotConnected _ex) { + logger.debug("No connection.", _ex); + } catch (DBusExecutionException _ex) { + logger.debug("", _ex); + throw new DBusException(_ex); + } + } + } + } + + @Override + public AutoCloseable addGenericSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) throws DBusException { + AtomicBoolean addMatch = new AtomicBoolean(false); // flag to perform action if this is a new signal key + + Queue> genericSignalsList = + getGenericHandledSignals().computeIfAbsent(_rule, v -> { + Queue> signalsList = new ConcurrentLinkedQueue<>(); + addMatch.set(true); + + return signalsList; + }); + + genericSignalsList.add(_handler); + + if (addMatch.get()) { + try { + dbus.AddMatch(_rule.toString()); + } catch (DBusExecutionException _ex) { + logger.debug("", _ex); + throw new DBusException(_ex.getMessage()); + } + } + return new AutoCloseable() { + @Override + public void close() throws DBusException { + removeGenericSigHandler(_rule, _handler); + } + }; + } + + private class SigHandler implements DBusSigHandler { + @Override + public void handle(DBusSignal _signal) { + if (_signal instanceof DBus.NameAcquired) { + synchronized (busnames) { + busnames.add(((DBus.NameAcquired) _signal).name); + } + } + } + } + + public enum DBusBusType { + /** + * System Bus + */ + SYSTEM, + /** + * Session Bus + */ + SESSION; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnectionBuilder.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnectionBuilder.java new file mode 100644 index 0000000000..2953d5aee2 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnectionBuilder.java @@ -0,0 +1,222 @@ +package org.freedesktop.dbus.connections.impl; + +import static org.freedesktop.dbus.utils.AddressBuilder.getDbusMachineId; + +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.connections.config.ReceivingServiceConfig; +import org.freedesktop.dbus.connections.config.TransportConfig; +import org.freedesktop.dbus.connections.impl.DBusConnection.DBusBusType; +import org.freedesktop.dbus.connections.transports.TransportBuilder; +import org.freedesktop.dbus.exceptions.AddressResolvingException; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.messages.Message; +import org.freedesktop.dbus.utils.AddressBuilder; + +import java.nio.ByteOrder; + +/** + * Builder to create a new DBusConnection. + * + * @author hypfvieh + * @version 4.1.0 - 2022-02-04 + */ +public final class DBusConnectionBuilder extends BaseConnectionBuilder { + + private final String machineId; + private boolean registerSelf = true; + private boolean shared = true; + + private DBusConnectionBuilder(BusAddress _address, String _machineId) { + super(DBusConnectionBuilder.class, _address); + machineId = _machineId; + } + + /** + * Create a new default connection connecting to DBus session bus but use an alternative input for the machineID. + * + * @param _machineIdFileLocation file with machine ID + * + * @return {@link DBusConnectionBuilder} + */ + public static DBusConnectionBuilder forSessionBus(String _machineIdFileLocation) { + BusAddress address = AddressBuilder.getSessionConnection(_machineIdFileLocation); + address = validateTransportAddress(address); + DBusConnectionBuilder instance = new DBusConnectionBuilder(address, getDbusMachineId(_machineIdFileLocation)); + return instance; + } + + /** + * Create new default connection to the DBus system bus. + * + * @return {@link DBusConnectionBuilder} + */ + public static DBusConnectionBuilder forSystemBus() { + BusAddress address = AddressBuilder.getSystemConnection(); + address = validateTransportAddress(address); + return new DBusConnectionBuilder(address, getDbusMachineId(null)); + } + + /** + * Create a new default connection connecting to the DBus session bus. + * + * @return {@link DBusConnectionBuilder} + */ + public static DBusConnectionBuilder forSessionBus() { + return forSessionBus(null); + } + + /** + * Create a default connection to DBus using the given bus type. + * + * @param _type bus type + * + * @return this + */ + public static DBusConnectionBuilder forType(DBusBusType _type) { + return forType(_type, null); + } + + /** + * Create a default connection to DBus using the given bus type and machineIdFile. + * + * @param _type bus type + * @param _machineIdFile machineId file + * + * @return this + */ + public static DBusConnectionBuilder forType(DBusBusType _type, String _machineIdFile) { + if (_type == DBusBusType.SESSION) { + return forSessionBus(_machineIdFile); + } else if (_type == DBusBusType.SYSTEM) { + return forSystemBus(); + } + + throw new IllegalArgumentException("Unknown bus type: " + _type); + } + + /** + * Use the given address to create the connection (e.g. used for remote TCP connected DBus daemons). + * + * @param _address address to use + * @return this + */ + public static DBusConnectionBuilder forAddress(String _address) { + DBusConnectionBuilder instance = new DBusConnectionBuilder(BusAddress.of(_address), getDbusMachineId(null)); + return instance; + } + + /** + * Use the given address to create the connection (e.g. used for remote TCP connected DBus daemons). + * + * @param _address address to use + * @return this + * + * @since 4.2.0 - 2022-07-18 + */ + public static DBusConnectionBuilder forAddress(BusAddress _address) { + DBusConnectionBuilder instance = new DBusConnectionBuilder(_address, getDbusMachineId(null)); + return instance; + } + + /** + * Checks if the given address can be used with the available transports. + * Will fallback to TCP if no address given and TCP transport is available. + * + * @param _address address to check + * @return address, maybe fallback address + */ + private static BusAddress validateTransportAddress(BusAddress _address) { + if (TransportBuilder.getRegisteredBusTypes().isEmpty()) { + throw new IllegalArgumentException("No transports found to connect to DBus. Please add at least one transport provider to your classpath"); + } + + BusAddress address = _address; + + // no unix transport but address wants to use a unix socket + if (!TransportBuilder.getRegisteredBusTypes().contains("UNIX") + && address != null + && address.isBusType("UNIX")) { + throw new AddressResolvingException("No transports found to handle UNIX socket connections. Please add a unix-socket transport provider to your classpath"); + } + + // no tcp transport but TCP address given + if (!TransportBuilder.getRegisteredBusTypes().contains("TCP") + && address != null + && address.isBusType("TCP")) { + throw new AddressResolvingException("No transports found to handle TCP connections. Please add a TCP transport provider to your classpath"); + } + + return address; + + } + /** + * Register the new connection on DBus using 'hello' message. Default is true. + * + * @param _register boolean + * @return this + */ + public DBusConnectionBuilder withRegisterSelf(boolean _register) { + registerSelf = _register; + return this; + } + + /** + * Use this connection as shared connection. Shared connection means that the same connection is used multiple times + * if the connection parameter did not change. Default is true. + * + * @param _shared boolean + * @return this + */ + public DBusConnectionBuilder withShared(boolean _shared) { + shared = _shared; + return this; + } + + /** + * Create the new {@link DBusConnection}. + * + * @return {@link DBusConnection} + * @throws DBusException when DBusConnection could not be opened + */ + @Override + public DBusConnection build() throws DBusException { + ReceivingServiceConfig cfg = buildThreadConfig(); + TransportConfig transportCfg = buildTransportConfig(); + + DBusConnection c; + if (shared) { + synchronized (DBusConnection.CONNECTIONS) { + String busAddressStr = transportCfg.getBusAddress().toString(); + c = DBusConnection.CONNECTIONS.get(busAddressStr); + if (c != null) { + c.concurrentConnections.incrementAndGet(); + return c; // this connection already exists, do not change anything + } else { + c = new DBusConnection(shared, machineId, transportCfg, cfg); + DBusConnection.CONNECTIONS.put(busAddressStr, c); + } + } + } else { + c = new DBusConnection(shared, machineId, transportCfg, cfg); + } + + c.setDisconnectCallback(getDisconnectCallback()); + c.setWeakReferences(isWeakReference()); + DBusConnection.setEndianness(getEndianess()); + c.connect(registerSelf); + return c; + } + + /** + * Get the default system endianness. + * + * @return LITTLE or BIG + * @deprecated if required, use {@link BaseConnectionBuilder#getSystemEndianness()} + */ + @Deprecated(forRemoval = true, since = "4.2.0") + public static byte getSystemEndianness() { + return ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN) + ? Message.Endian.BIG + : Message.Endian.LITTLE; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DirectConnection.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DirectConnection.java new file mode 100644 index 0000000000..60ba444b88 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DirectConnection.java @@ -0,0 +1,300 @@ +package org.freedesktop.dbus.connections.impl; + +import static org.freedesktop.dbus.utils.CommonRegexPattern.IFACE_PATTERN; +import static org.freedesktop.dbus.utils.CommonRegexPattern.PROXY_SPLIT_PATTERN; + +import org.freedesktop.dbus.DBusMatchRule; +import org.freedesktop.dbus.RemoteInvocationHandler; +import org.freedesktop.dbus.RemoteObject; +import org.freedesktop.dbus.connections.AbstractConnection; +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.connections.config.ReceivingServiceConfig; +import org.freedesktop.dbus.connections.config.TransportConfig; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.interfaces.DBusInterface; +import org.freedesktop.dbus.interfaces.DBusSigHandler; +import org.freedesktop.dbus.interfaces.Introspectable; +import org.freedesktop.dbus.messages.DBusSignal; +import org.freedesktop.dbus.messages.ExportedObject; +import org.freedesktop.dbus.utils.Hexdump; +import org.freedesktop.dbus.utils.Util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Proxy; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.stream.Collectors; + +/** + * Handles a peer to peer connection between two applications without a bus daemon. + *

+ * Signal Handlers and method calls from remote objects are run in their own threads, you MUST handle the concurrency issues. + *

+ */ +public class DirectConnection extends AbstractConnection { + private final Logger logger = LoggerFactory.getLogger(getClass()); + private final String machineId; + + /** + * Create a direct connection to another application. + * @param _address The address to connect to. This is a standard D-Bus address, except that the additional parameter 'listen=true' should be added in the application which is creating the socket. + * @throws DBusException on error + * @deprecated use {@link DirectConnectionBuilder} + */ + @Deprecated(since = "4.1.0", forRemoval = true) + public DirectConnection(String _address) throws DBusException { + this(_address, AbstractConnection.TCP_CONNECT_TIMEOUT); + } + + /** + * Create a direct connection to another application. + * @param _address The address to connect to. This is a standard D-Bus address, except that the additional parameter 'listen=true' should be added in the application which is creating the socket. + * @param _timeout the timeout set for the underlying socket. 0 will block forever on the underlying socket. + * @throws DBusException on error + * @deprecated use {@link DirectConnectionBuilder} + */ + @Deprecated(since = "4.1.0", forRemoval = true) + public DirectConnection(String _address, int _timeout) throws DBusException { + this(createTransportConfig(_address, _timeout), null); + } + + DirectConnection(TransportConfig _transportCfg, ReceivingServiceConfig _rsCfg) throws DBusException { + super(_transportCfg, _rsCfg); + machineId = createMachineId(); + if (!getAddress().isServer()) { + super.listen(); + } + } + + @Deprecated(since = "4.2.0", forRemoval = true) + static TransportConfig createTransportConfig(String _address, int _timeout) { + TransportConfig cfg = new TransportConfig(); + cfg.setBusAddress(BusAddress.of(_address)); + cfg.getAdditionalConfig().put("TIMEOUT", _timeout); + return cfg; + } + + /** + * Use this method when running on server side. + * Call will block. + */ + @Override + public void listen() { + if (getAddress().isServer()) { + super.listen(); + } + } + + private String createMachineId() { + String ascii; + + try { + ascii = Hexdump.toAscii(MessageDigest.getInstance("MD5").digest(InetAddress.getLocalHost().getHostName().getBytes())); + return ascii; + } catch (NoSuchAlgorithmException _ex) { + logger.trace("MD5 algorithm not present", _ex); + } catch (UnknownHostException _ex) { + logger.trace("Unable to determine this machines hostname", _ex); + } + + return Util.randomString(32); + } + + @SuppressWarnings("unchecked") + T dynamicProxy(String _path, Class _type) throws DBusException { + try { + Introspectable intro = getRemoteObject(_path, Introspectable.class); + String data = intro.Introspect(); + + String[] tags = PROXY_SPLIT_PATTERN.split(data); + + List ifaces = Arrays.stream(tags).filter(t -> t.startsWith("interface")) + .map(t -> IFACE_PATTERN.matcher(t).replaceAll("$1")) + .collect(Collectors.toList()); + + List> ifcs = findMatchingTypes(_type, ifaces); + + if (ifcs.isEmpty()) { + throw new DBusException("Could not find an interface to cast to"); + } + + RemoteObject ro = new RemoteObject(null, _path, _type, false); + DBusInterface newi = (DBusInterface) Proxy.newProxyInstance(ifcs.get(0).getClassLoader(), ifcs.toArray(new Class[0]), new RemoteInvocationHandler(this, ro)); + getImportedObjects().put(newi, ro); + return (T) newi; + } catch (Exception _ex) { + logger.debug("", _ex); + throw new DBusException(String.format("Failed to create proxy object for %s; reason: %s.", _path, _ex.getMessage())); + } + } + + @SuppressWarnings("unchecked") + T getExportedObject(String _path, Class _type) throws DBusException { + ExportedObject o = null; + synchronized (getExportedObjects()) { + o = getExportedObjects().get(_path); + } + if (null != o && null == o.getObject().get()) { + unExportObject(_path); + o = null; + } + if (null != o) { + return (T) o.getObject().get(); + } + return dynamicProxy(_path, _type); + } + + /** + * Return a reference to a remote object. + * This method will always refer to the well known name (if given) rather than resolving it to a unique bus name. + * In particular this means that if a process providing the well known name disappears and is taken over by another process + * proxy objects gained by this method will make calls on the new proccess. + * + * This method will use bus introspection to determine the interfaces on a remote object and so + * may block and may fail. The resulting proxy object will, however, be castable + * to any interface it implements. It will also autostart the process if applicable. Also note + * that the resulting proxy may fail to execute the correct method with overloaded methods + * and that complex types may fail in interesting ways. Basically, if something odd happens, + * try specifying the interface explicitly. + * + * @param _objectPath The path on which the process is exporting the object. + * @return A reference to a remote object. + * @throws ClassCastException If type is not a sub-type of DBusInterface + * @throws DBusException If busname or objectpath are incorrectly formatted. + */ + public DBusInterface getRemoteObject(String _objectPath) throws DBusException { + if (null == _objectPath) { + throw new DBusException("Invalid object path: null"); + } + + if (_objectPath.length() > MAX_NAME_LENGTH || !OBJECT_REGEX_PATTERN.matcher(_objectPath).matches()) { + throw new DBusException("Invalid object path: " + _objectPath); + } + + return dynamicProxy(_objectPath, null); + } + + /** + * Return a reference to a remote object. + * This method will always refer to the well known name (if given) rather than resolving it to a unique bus name. + * In particular this means that if a process providing the well known name disappears and is taken over by another process + * proxy objects gained by this method will make calls on the new proccess. + * @param _objectPath The path on which the process is exporting the object. + * @param _type The interface they are exporting it on. This type must have the same full class name and exposed method signatures + * as the interface the remote object is exporting. + * @param class which extends DBusInterface + * @return A reference to a remote object. + * @throws ClassCastException If type is not a sub-type of DBusInterface + * @throws DBusException If busname or objectpath are incorrectly formatted or type is not in a package. + */ + public T getRemoteObject(String _objectPath, Class _type) throws DBusException { + if (null == _objectPath) { + throw new DBusException("Invalid object path: null"); + } + if (null == _type) { + throw new ClassCastException("Not A DBus Interface"); + } + + if (_objectPath.length() > MAX_NAME_LENGTH || !OBJECT_REGEX_PATTERN.matcher(_objectPath).matches()) { + throw new DBusException("Invalid object path: " + _objectPath); + } + + if (!DBusInterface.class.isAssignableFrom(_type)) { + throw new ClassCastException("Not A DBus Interface"); + } + + // don't let people import things which don't have a + // valid D-Bus interface name + if (_type.getName().equals(_type.getSimpleName())) { + throw new DBusException("DBusInterfaces cannot be declared outside a package"); + } + + RemoteObject ro = new RemoteObject(null, _objectPath, _type, false); + + @SuppressWarnings("unchecked") + T i = (T) Proxy.newProxyInstance(_type.getClassLoader(), + new Class[] {_type}, new RemoteInvocationHandler(this, ro)); + + getImportedObjects().put(i, ro); + + return i; + } + + @Override + protected void removeSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) throws DBusException { + Queue> v = getHandledSignals().get(_rule); + if (null != v) { + v.remove(_handler); + if (0 == v.size()) { + getHandledSignals().remove(_rule); + } + } + } + + @Override + protected AutoCloseable addSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) throws DBusException { + Queue> v = + getHandledSignals().computeIfAbsent(_rule, val -> { + Queue> l = new ConcurrentLinkedQueue<>(); + return l; + }); + + v.add(_handler); + return new AutoCloseable() { + @Override + public void close() throws Exception { + removeSigHandler(_rule, _handler); + } + }; + } + + @Override + protected void removeGenericSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) throws DBusException { + Queue> v = getGenericHandledSignals().get(_rule); + if (null != v) { + v.remove(_handler); + if (0 == v.size()) { + getGenericHandledSignals().remove(_rule); + } + } + } + + @Override + protected AutoCloseable addGenericSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) throws DBusException { + Queue> v = + getGenericHandledSignals().computeIfAbsent(_rule, val -> { + Queue> l = new ConcurrentLinkedQueue<>(); + return l; + }); + + v.add(_handler); + return new AutoCloseable() { + @Override + public void close() throws Exception { + removeGenericSigHandler(_rule, _handler); + } + }; + } + + @Override + public T getExportedObject(String _source, String _path, Class _type) throws DBusException { + return getExportedObject(_path, _type); + } + + @Override + public String getMachineId() { + return machineId; + } + + @Override + public DBusInterface getExportedObject(String _source, String _path) throws DBusException { + return getExportedObject(_path, (Class) null); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DirectConnectionBuilder.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DirectConnectionBuilder.java new file mode 100644 index 0000000000..1e291656b1 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/impl/DirectConnectionBuilder.java @@ -0,0 +1,64 @@ +package org.freedesktop.dbus.connections.impl; + +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.connections.config.ReceivingServiceConfig; +import org.freedesktop.dbus.connections.config.TransportConfig; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.messages.Message; + +import java.nio.ByteOrder; + +/** + * Builder to create a new DirectConnection. + * + * @author hypfvieh + * @version 4.1.0 - 2022-02-04 + */ +public final class DirectConnectionBuilder extends BaseConnectionBuilder { + + private DirectConnectionBuilder(BusAddress _address) { + super(DirectConnectionBuilder.class, _address); + } + + /** + * Use the given address to create the connection (e.g. used for remote TCP connected DBus daemons). + * + * @param _address address to use + * @return this + */ + public static DirectConnectionBuilder forAddress(String _address) { + DirectConnectionBuilder instance = new DirectConnectionBuilder(BusAddress.of(_address)); + return instance; + } + + /** + * Create the new {@link DBusConnection}. + * + * @return {@link DBusConnection} + * @throws DBusException when DBusConnection could not be opened + */ + @Override + public DirectConnection build() throws DBusException { + ReceivingServiceConfig rsCfg = buildThreadConfig(); + TransportConfig transportCfg = buildTransportConfig(); + + DirectConnection c = new DirectConnection(transportCfg, rsCfg); + c.setDisconnectCallback(getDisconnectCallback()); + c.setWeakReferences(isWeakReference()); + DirectConnection.setEndianness(getEndianess()); + return c; + } + + /** + * Get the default system endianness. + * + * @return LITTLE or BIG + * @deprecated if required, use {@link BaseConnectionBuilder#getSystemEndianness()} + */ + @Deprecated(forRemoval = true, since = "4.2.0") + public static byte getSystemEndianness() { + return ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN) + ? Message.Endian.BIG + : Message.Endian.LITTLE; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/AbstractTransport.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/AbstractTransport.java new file mode 100644 index 0000000000..c78b6d2300 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/AbstractTransport.java @@ -0,0 +1,342 @@ +package org.freedesktop.dbus.connections.transports; + +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.connections.SASL; +import org.freedesktop.dbus.connections.config.SaslConfig; +import org.freedesktop.dbus.connections.config.TransportConfig; +import org.freedesktop.dbus.exceptions.AuthenticationException; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.InvalidBusAddressException; +import org.freedesktop.dbus.messages.Message; +import org.freedesktop.dbus.spi.message.IMessageReader; +import org.freedesktop.dbus.spi.message.IMessageWriter; +import org.freedesktop.dbus.spi.message.ISocketProvider; +import org.freedesktop.dbus.spi.message.InputStreamMessageReader; +import org.freedesktop.dbus.spi.message.OutputStreamMessageWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Closeable; +import java.io.IOException; +import java.nio.channels.SocketChannel; +import java.util.Objects; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; + +/** + * Base class for all transport types. + * + * @author hypfvieh + * @since v3.2.0 - 2019-02-08 + */ +public abstract class AbstractTransport implements Closeable { + + private static final AtomicLong TRANSPORT_ID_GENERATOR = new AtomicLong(0); + + private final ServiceLoader spiLoader = ServiceLoader.load(ISocketProvider.class); + + private final Logger logger = LoggerFactory.getLogger(getClass()); + private final BusAddress address; + + private TransportConnection transportConnection; + private boolean fileDescriptorSupported; + + private final long transportId = TRANSPORT_ID_GENERATOR.incrementAndGet(); + + private final TransportConfig config; + + protected AbstractTransport(BusAddress _address, TransportConfig _config) { + address = Objects.requireNonNull(_address, "BusAddress required"); + config = Objects.requireNonNull(_config, "Config required"); + + if (_address.isListeningSocket()) { + config.getSaslConfig().setMode(SASL.SaslMode.SERVER); + } else { + config.getSaslConfig().setMode(SASL.SaslMode.CLIENT); + } + config.getSaslConfig().setGuid(address.getGuid()); + config.getSaslConfig().setFileDescriptorSupport(hasFileDescriptorSupport()); + } + + /** + * Write a message to the underlying socket. + * + * @param _msg message to write + * @throws IOException on write error or if output was already closed or null + */ + public void writeMessage(Message _msg) throws IOException { + if (!fileDescriptorSupported && Message.ArgumentType.FILEDESCRIPTOR == _msg.getType()) { + throw new IllegalArgumentException("File descriptors are not supported!"); + } + if (transportConnection.getWriter() != null && !transportConnection.getWriter().isClosed()) { + transportConnection.getWriter().writeMessage(_msg); + } else { + throw new IOException("OutputWriter already closed or null"); + } + } + + /** + * Read a message from the underlying socket. + * + * @return read message, maybe null + * @throws IOException when input already close or null + * @throws DBusException when message could not be converted to a DBus message + */ + public Message readMessage() throws IOException, DBusException { + if (transportConnection.getReader() != null && !transportConnection.getReader().isClosed()) { + return transportConnection.getReader().readMessage(); + } + throw new IOException("InputReader already closed or null"); + } + + /** + * Returns true if inputReader and outputWriter are not yet closed. + * @return boolean + */ + public synchronized boolean isConnected() { + return transportConnection != null + && transportConnection.getWriter() != null && !transportConnection.getWriter().isClosed() + && transportConnection.getReader() != null && !transportConnection.getReader().isClosed(); + } + + /** + * Method to indicate if passing of file descriptors is allowed. + * + * @return true to allow FD passing, false otherwise + */ + protected abstract boolean hasFileDescriptorSupport(); + + /** + * Return true if the transport supports 'abstract' sockets. + * @return true if abstract sockets supported, false otherwise + * + * @deprecated Is no longer used and will be removed + */ + @Deprecated(forRemoval = true, since = "4.2.0 - 2022-07-18") + protected abstract boolean isAbstractAllowed(); + + /** + * Abstract method implemented by concrete sub classes to establish a connection + * using whatever transport type (e.g. TCP/Unix socket). + * @throws IOException when connection fails + */ + protected abstract SocketChannel connectImpl() throws IOException; + + /** + * Establish connection on created transport.
+ *

+ * This method can only be used for non-listening connections.
+ * Trying to use this with listening addresses will throw an {@link InvalidBusAddressException}. + *

+ * + * @return {@link SocketChannel} of the created connection + * @throws IOException if connection fails + */ + public final SocketChannel connect() throws IOException { + if (getAddress().isListeningSocket()) { + throw new InvalidBusAddressException("Cannot connect when using listening address (try use listen() instead)"); + } + transportConnection = internalConnect(); + return transportConnection.getChannel(); + } + + /** + * Start listening on created transport.
+ *

+ * This method can only be used for listening connections.
+ * Trying to use this with non-listening addresses will throw an {@link InvalidBusAddressException}. + *

+ *

+ * Will return the {@link TransportConnection} as soon as a client connects.
+ * Therefore this method should be called in a loop to accept multiple clients + *

+ * + * @return {@link TransportConnection} containing created {@link SocketChannel} and {@link IMessageReader}/{@link IMessageWriter} + * @throws IOException if connection fails + */ + public final TransportConnection listen() throws IOException { + if (!getAddress().isListeningSocket()) { + throw new InvalidBusAddressException("Cannot listen on client connection address (try use connect() instead)"); + } + transportConnection = internalConnect(); + return transportConnection; + } + + private TransportConnection internalConnect() throws IOException { + if (config.getPreConnectCallback() != null) { + config.getPreConnectCallback().accept(this); + } + SocketChannel channel = connectImpl(); + authenticate(channel); + return createInputOutput(channel); + } + + /** + * Set a callback which will be called right before the connection will + * be established to the transport. + * + * @param _run runnable to execute, null if no callback should be executed + * + * @since 4.2.0 - 2022-07-20 + */ + public void setPreConnectCallback(Consumer _run) { + config.setPreConnectCallback(_run); + } + + /** + * Helper method to authenticate to DBus using SASL. + * + * @param _sock socketchannel + * @throws IOException on any error + */ + private void authenticate(SocketChannel _sock) throws IOException { + SASL sasl = new SASL(config.getSaslConfig()); + try { + if (!sasl.auth(_sock, this)) { + throw new AuthenticationException("Failed to authenticate"); + } + } catch (IOException _ex) { + _sock.close(); + throw _ex; + } + fileDescriptorSupported = sasl.isFileDescriptorSupported(); // false if server does not support file descriptors + } + + /** + * Setup message reader/writer. + * Will look for SPI provider first, if none is found default implementation is used. + * The default implementation does not support file descriptor passing! + * + * @param _socket socket to use + * @return TransportConnection with configured socket channel, reader and writer + */ + private TransportConnection createInputOutput(SocketChannel _socket) { + IMessageReader reader = null; + IMessageWriter writer = null; + try { + for (ISocketProvider provider : spiLoader) { + logger.debug("Found ISocketProvider {}", provider); + + provider.setFileDescriptorSupport(hasFileDescriptorSupport() && fileDescriptorSupported); + reader = provider.createReader(_socket); + writer = provider.createWriter(_socket); + if (reader != null && writer != null) { + logger.debug("Using ISocketProvider {}", provider); + break; + } + } + } catch (ServiceConfigurationError _ex) { + logger.error("Could not initialize service provider", _ex); + } catch (IOException _ex) { + logger.error("Could not initialize alternative message reader/writer", _ex); + } + + if (reader == null || writer == null) { + logger.debug("No alternative ISocketProvider found, using built-in implementation"); + reader = new InputStreamMessageReader(_socket); + writer = new OutputStreamMessageWriter(_socket); + fileDescriptorSupported = false; // internal implementation does not support file descriptors even if server allows it + } + + return new TransportConnection(_socket, writer, reader); + } + + /** + * Returns the {@link BusAddress} used for this transport. + * + * @return BusAddress, never null + */ + protected BusAddress getAddress() { + return address; + } + + /** + * Get the logger in subclasses. + * + * @return Logger, never null + */ + protected Logger getLogger() { + return logger; + } + + /** + * Returns the current configuration used for SASL authentication. + * + * @return SaslConfig, never null + */ + protected SaslConfig getSaslConfig() { + return config.getSaslConfig(); + } + + /** + * Set the SASL authentication mode. + * + * @deprecated please use {@link #getSaslConfig()}.getAuthMode() instead + */ + @Deprecated(since = "4.2.0 - 2022-07-22", forRemoval = true) + protected int getSaslAuthMode() { + return getSaslConfig().getAuthMode(); + } + + /** + * Set the SASL authentication mode. + * + * @deprecated please use {@link #getSaslConfig()}.getMode() instead + */ + @Deprecated(since = "4.2.0 - 2022-07-22", forRemoval = true) + protected SASL.SaslMode getSaslMode() { + return getSaslConfig().getMode(); + } + /** + * Set the SASL mode (server or client). + * + * @param _saslMode mode to set + * @deprecated please use {@link #getSaslConfig()}.setMode(int) instead + */ + @Deprecated(since = "4.2.0 - 2022-07-22", forRemoval = true) + protected void setSaslMode(SASL.SaslMode _saslMode) { + getSaslConfig().setMode(_saslMode); + } + + /** + * Set the SASL authentication mode. + * + * @param _mode mode to set + * @deprecated please use {@link #getSaslConfig()}.setSaslAuthMode(int) instead + */ + @Deprecated(since = "4.2.0 - 2022-07-22", forRemoval = true) + protected void setSaslAuthMode(int _mode) { + getSaslConfig().setAuthMode(_mode); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(getClass().getSimpleName()); + sb.append(" [id=") + .append(transportId) + .append(", "); + + if (transportConnection != null) { + sb.append("connectionId=") + .append(transportConnection.getId()) + .append(", "); + } + + sb.append("address=") + .append(address) + .append("]"); + + return sb.toString(); + } + + @Override + public void close() throws IOException { + if (transportConnection != null) { + transportConnection.close(); + transportConnection = null; + } + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/AbstractUnixTransport.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/AbstractUnixTransport.java new file mode 100644 index 0000000000..9880d622eb --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/AbstractUnixTransport.java @@ -0,0 +1,17 @@ +package org.freedesktop.dbus.connections.transports; + +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.connections.config.TransportConfig; + +import java.io.IOException; +import java.nio.channels.SocketChannel; + +public abstract class AbstractUnixTransport extends AbstractTransport { + + protected AbstractUnixTransport(BusAddress _address, TransportConfig _config) { + super(_address, _config); + } + + public abstract int getUid(SocketChannel _sock) throws IOException; + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/IFileBasedBusAddress.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/IFileBasedBusAddress.java new file mode 100644 index 0000000000..b3ad9714fb --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/IFileBasedBusAddress.java @@ -0,0 +1,18 @@ +package org.freedesktop.dbus.connections.transports; + +import org.freedesktop.dbus.connections.BusAddress; + +import java.nio.file.attribute.PosixFilePermission; +import java.util.Set; + +/** + * Interface which should be implemented by {@link BusAddress} subclasses which use + * files as 'address' (e.g. unix sockets) and needs to set permission on those files. + * + * @author hypfvieh + * @since 4.2.0 - 2022-07-18 + */ +public interface IFileBasedBusAddress { + + void updatePermissions(String _fileOwner, String _fileGroup, Set _fileUnixPermissions); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/TransportBuilder.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/TransportBuilder.java new file mode 100644 index 0000000000..c02e4a40f5 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/TransportBuilder.java @@ -0,0 +1,405 @@ +package org.freedesktop.dbus.connections.transports; + +import org.freedesktop.dbus.connections.AbstractConnection; +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.connections.SASL; +import org.freedesktop.dbus.connections.config.TransportConfig; +import org.freedesktop.dbus.connections.config.TransportConfigBuilder; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.TransportConfigurationException; +import org.freedesktop.dbus.exceptions.TransportRegistrationException; +import org.freedesktop.dbus.spi.transport.ITransportProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.file.attribute.PosixFilePermission; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Builder to create transports of different types. + * + * @author hypfvieh + * @since v4.0.0 - 2021-09-17 + */ +public final class TransportBuilder { + + private static final Logger LOGGER = LoggerFactory.getLogger(TransportBuilder.class); + private static final Map PROVIDERS = getTransportProvider(); + + private TransportConfigBuilder, TransportBuilder> transportConfigBuilder; + + private TransportBuilder(TransportConfig _config) throws DBusException { + transportConfigBuilder = new TransportConfigBuilder<>(() -> this); + if (_config != null) { + transportConfigBuilder.withConfig(_config); + } + } + + static Map getTransportProvider() { + Map providers = new ConcurrentHashMap<>(); + try { + ServiceLoader spiLoader = ServiceLoader.load(ITransportProvider.class); + for (ITransportProvider provider : spiLoader) { + String providerBusType = provider.getSupportedBusType(); + if (providerBusType == null) { // invalid transport, ignore + LOGGER.warn("Transport {} is invalid: No bustype configured", provider.getClass()); + continue; + } + providerBusType = providerBusType.toUpperCase(Locale.US); + + LOGGER.debug("Found provider '{}' named '{}' providing bustype '{}'", provider.getClass().getSimpleName(), provider.getTransportName(), providerBusType); + + if (providers.containsKey(providerBusType)) { + throw new TransportRegistrationException("Found transport " + + providers.get(providerBusType).getClass().getName() + + " and " + + provider.getClass().getName() + " both providing transport for socket type " + + providerBusType + ", please only add one of them to classpath."); + } + providers.put(providerBusType, provider); + } + if (providers.isEmpty()) { + throw new TransportRegistrationException("No dbus-java-transport found in classpath, please add a transport module"); + } + } catch (ServiceConfigurationError _ex) { + LOGGER.error("Could not initialize service provider.", _ex); + } + return providers; + } + + /** + * Creates a new {@link TransportBuilder} instance with the given address. + * + * @param _address address, never null + * + * @return new {@link TransportBuilder} + * @throws DBusException if invalid address provided + * + */ + public static TransportBuilder create(String _address) throws DBusException { + TransportConfig cfg = new TransportConfig(); + cfg.setBusAddress(BusAddress.of(_address)); + return new TransportBuilder(cfg); + } + + /** + * Creates a new {@link TransportBuilder} instance using the given configuration. + * + * @param _config config, never null + * + * @return new {@link TransportBuilder} + * @throws DBusException if invalid address provided + */ + public static TransportBuilder create(TransportConfig _config) throws DBusException { + return new TransportBuilder(_config); + } + + /** + * Creates a new {@link TransportBuilder} instance using a empty transport configuration. + * + * @return new {@link TransportBuilder} + * @throws DBusException if invalid address provided + */ + public static TransportBuilder create() throws DBusException { + return new TransportBuilder(null); + } + + /** + * Creates a new {@link TransportBuilder} instance with the given address. + * + * @param _address address, never null + * + * @return new {@link TransportBuilder} + * @throws DBusException if invalid address provided + */ + public static TransportBuilder create(BusAddress _address) throws DBusException { + Objects.requireNonNull(_address, "BusAddress required"); + return new TransportBuilder(new TransportConfig(_address)); + } + + /** + * Creates a new {@link TransportBuilder} with a dynamically created address. + * + * @param _transportType type of session (e.g. UNIX or TCP) + * + * @return {@link TransportBuilder} + * + * @throws DBusException when invalid/unknown/unsupported transport type given + */ + public static TransportBuilder createWithDynamicSession(String _transportType) throws DBusException { + String dynSession = createDynamicSession(_transportType, false); + if (dynSession == null) { + throw new DBusException("Could not create dynamic session for transport type '" + _transportType + "'"); + } + return create(dynSession); + } + + /** + * Set the connection timeout (usually only used for TCP based transports). + *

+ * default: {@link AbstractConnection#TCP_CONNECT_TIMEOUT} + * + * @param _timeout timeout, if < 0 default timeout of {@link AbstractConnection#TCP_CONNECT_TIMEOUT} will be used + * + * @deprecated please use {@link #configure()} + */ + @Deprecated(since = "4.2.0 - 2022-07-21", forRemoval = true) + public TransportBuilder withTimeout(int _timeout) { + configure().withTimeout(_timeout); + return this; + } + + /** + * Toggle the created transport to be a listening (server) or initiating (client) connection. + *

+ * Default is a client connection. + * + * @param _listen true to create a listening transport (e.g. for server usage) + * + * @return this + * + * @deprecated please use {@link #configure()} + */ + @Deprecated(forRemoval = true, since = "4.2.0 - 2022-05-23") + public TransportBuilder isListening(boolean _listen) { //NOPMD + return listening(_listen); + } + + /** + * Toggle the created transport to be a listening (server) or initiating (client) connection. + *

+ * Default is a client connection. + * + * @param _listen true to create a listening transport (e.g. for server usage) + * + * @deprecated please use {@link #configure()} + */ + @Deprecated(since = "4.2.0 - 2022-07-21", forRemoval = true) + public TransportBuilder listening(boolean _listen) { + configure().withListening(_listen); + return this; + } + + /** + * Instantly connect to DBus when {@link #build()} is called. + *

+ * default: true + * + * @param _connect boolean + * + * @return this + * @deprecated please use {@link #configure()} + */ + @Deprecated(since = "4.2.0 - 2022-07-21", forRemoval = true) + public TransportBuilder withAutoConnect(boolean _connect) { + configure().withAutoConnect(_connect); + return this; + } + + /** + * Set a different SASL authentication mode. + *

+ * Usually when a unixsocket based transport is used, {@link SaslAuthMode#AUTH_EXTERNAL} will be used. + * For TCP based transport {@link SaslAuthMode#AUTH_COOKIE} will be used. + *

+ * + * @param _authMode authmode to use, if null is given, default mode will be used + * + * @return this + * @deprecated please use {@link #configure()} + */ + @Deprecated(since = "4.2.0 - 2022-07-21", forRemoval = true) + public TransportBuilder withSaslAuthMode(SaslAuthMode _authMode) { + configure().withSaslAuthMode(_authMode); + return this; + } + + /** + * The owner of the socket file if a unix socket is used and this is a server transport. + *

+ * Default is the user of the running JVM process.

+ * Please note:
+ * The running process user has to have suitable permissions to change the owner + * of the file. Otherwise the file owner will not be changed! + * + * @param _user user to set, if null is given JVM process user is used + * + * @return this + * @deprecated please use {@link #configure()} + */ + @Deprecated(since = "4.2.0 - 2022-07-21", forRemoval = true) + public TransportBuilder withUnixSocketFileOwner(String _user) { + configure().withUnixSocketFileOwner(_user); + return this; + } + + /** + * The group of the socket file if a unix socket is used and this is a server transport. + *

+ * Default is the group of the running JVM process.

+ * Please note:
+ * The running process user has to have suitable permissions to change the group + * of the file. Otherwise the file group will not be changed! + * + * @param _group group to set, if null is given JVM process group is used + * + * @return this + * @deprecated please use {@link #configure()} + */ + @Deprecated(since = "4.2.0 - 2022-07-21", forRemoval = true) + public TransportBuilder withUnixSocketFileGroup(String _group) { + configure().withUnixSocketFileGroup(_group); + return this; + } + + /** + * The permissions which will be set on socket file if a unix socket is used and this is a server transport. + *

+ * This method does nothing when used on windows systems. + * Please note:
+ * The running process user has to have suitable permissions to change the permissions + * of the file. Otherwise the file permissions will not be changed! + * + * @param _permissions permissions to set, if null is given default permissions will be used + * + * @return this + * + * @deprecated please use {@link #configure()} + */ + @Deprecated(since = "4.2.0 - 2022-07-21", forRemoval = true) + public TransportBuilder withUnixSocketFilePermissions(PosixFilePermission... _permissions) { + configure().withUnixSocketFilePermissions(_permissions); + return this; + } + + /** + * Returns the configuration builder to configure the transport. + * @return TransportConfigBuilder + */ + public TransportConfigBuilder, TransportBuilder> configure() { + return transportConfigBuilder; + } + + /** + * Create the transport with the previously provided configuration. + * + * @return {@link AbstractTransport} instance + * + * @throws DBusException when creating transport fails + * @throws IOException when autoconnect is true and connection to DBus failed + */ + public AbstractTransport build() throws DBusException, IOException { + BusAddress myBusAddress = getAddress(); + TransportConfig config = transportConfigBuilder.build(); + if (myBusAddress == null) { + throw new DBusException("Transport requires a BusAddress, use withBusAddress() to configure before building"); + } + + AbstractTransport transport = null; + ITransportProvider provider = PROVIDERS.get(config.getBusAddress().getBusType()); + if (provider == null) { + throw new DBusException("No transport provider found for bustype " + config.getBusAddress().getBusType()); + } else { + LOGGER.info("Using transport {} for address {}", provider.getTransportName(), config.getBusAddress()); + } + + try { + transport = provider.createTransport(myBusAddress, config); + Objects.requireNonNull(transport, "Transport required"); // in case the factory returns null, we cannot continue + + if (config.getSaslConfig().getAuthMode() > 0) { + transport.getSaslConfig().setAuthMode(config.getSaslConfig().getAuthMode()); + } + } catch (TransportConfigurationException _ex) { + LOGGER.error("Could not initialize transport", _ex); + } + + if (transport == null) { + throw new DBusException("Unknown address type " + myBusAddress.getType() + " or no transport provider found for bus type " + myBusAddress.getBusType()); + } + + if (myBusAddress.isListeningSocket() && myBusAddress instanceof IFileBasedBusAddress) { + ((IFileBasedBusAddress) myBusAddress).updatePermissions(config.getFileOwner(), config.getFileGroup(), config.getFileUnixPermissions()); + } + + transport.setPreConnectCallback(config.getPreConnectCallback()); + + if (config.isAutoConnect()) { + if (config.isListening()) { + transport.listen(); + } else { + transport.connect(); + } + } + return transport; + } + + /** + * The currently configured BusAddress. + * + * @return {@link BusAddress} + */ + public BusAddress getAddress() { + return configure().getBusAddress(); + } + + /** + * Returns a {@link List} of all bustypes supported in the current runtime. + * + * @return {@link List}, maybe empty + */ + public static List getRegisteredBusTypes() { + return new ArrayList<>(PROVIDERS.keySet()); + } + + /** + * Creates a new dynamic bus address for the given bus type. + * + * @param _busType bus type (e.g. UNIX or TCP), never null + * @param _listeningAddress true if a listening (server) address should be created, false otherwise + * + * @return String containing BusAddress or null + */ + public static String createDynamicSession(String _busType, boolean _listeningAddress) { + Objects.requireNonNull(_busType, "Bustype required"); + ITransportProvider provider = PROVIDERS.get(_busType.toUpperCase(Locale.US)); + if (provider != null) { + return provider.createDynamicSessionAddress(_listeningAddress); + } + return null; + } + + /** + * Represents supported SASL authentication modes. + * + * @author hypfvieh + * @since v4.0.0 - 2021-09-17 + */ + public enum SaslAuthMode { + /** No authentication (allow everyone). */ + AUTH_ANONYMOUS(SASL.AUTH_ANON), + /** Authentication using SHA Cookie. */ + AUTH_COOKIE(SASL.AUTH_SHA), + /** External authentication (e.g. by user ID). */ + AUTH_EXTERNAL(SASL.AUTH_EXTERNAL); + + private final int authMode; + + SaslAuthMode(int _authMode) { + authMode = _authMode; + } + + public int getAuthMode() { + return authMode; + } + + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/TransportConnection.java b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/TransportConnection.java new file mode 100644 index 0000000000..300714ac9f --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/connections/transports/TransportConnection.java @@ -0,0 +1,73 @@ +package org.freedesktop.dbus.connections.transports; + +import org.freedesktop.dbus.spi.message.IMessageReader; +import org.freedesktop.dbus.spi.message.IMessageWriter; + +import java.io.Closeable; +import java.io.IOException; +import java.nio.channels.SocketChannel; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Represents one transport connection of any type.
+ *

+ * A transport connection is bound to a SocketChannel which might be + * a connection to a DBusServer when used as client or a connection + * from a client when running as server. + *

+ * + * @author hypfvieh + * @since v4.2.2 - 2023-02-02 + */ +public class TransportConnection implements Closeable { + private static final AtomicLong TRANSPORT_ID_GENERATOR = new AtomicLong(0); + + private final long id = TRANSPORT_ID_GENERATOR.incrementAndGet(); + private final SocketChannel channel; + private final IMessageWriter writer; + private final IMessageReader reader; + + public TransportConnection(SocketChannel _channel, IMessageWriter _writer, IMessageReader _reader) { + channel = _channel; + writer = _writer; + reader = _reader; + } + + public SocketChannel getChannel() { + return channel; + } + + public IMessageWriter getWriter() { + return writer; + } + + public IMessageReader getReader() { + return reader; + } + + public long getId() { + return id; + } + + @Override + public String toString() { + return getClass().getSimpleName() + " [id=" + id + ", channel=" + channel + ", writer=" + writer + ", reader=" + + reader + "]"; + } + + @Override + public void close() throws IOException { + if (reader != null) { + reader.close(); + } + + if (writer != null) { + writer.close(); + } + + if (channel != null) { + channel.close(); + } + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/errors/AccessDenied.java b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/AccessDenied.java new file mode 100644 index 0000000000..bd57378f09 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/AccessDenied.java @@ -0,0 +1,14 @@ +package org.freedesktop.dbus.errors; + +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +/** + * Thrown if a message is denied due to a security policy + */ +public class AccessDenied extends DBusExecutionException { + private static final long serialVersionUID = 368173196466740803L; + + public AccessDenied(String _message) { + super(_message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/errors/Error.java b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/Error.java new file mode 100644 index 0000000000..161e11aa7f --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/Error.java @@ -0,0 +1,145 @@ +package org.freedesktop.dbus.errors; + +import static org.freedesktop.dbus.utils.CommonRegexPattern.EXCEPTION_EXTRACT_PATTERN; +import static org.freedesktop.dbus.utils.CommonRegexPattern.EXCEPTION_PARTIAL_PATTERN; + +import org.freedesktop.dbus.connections.AbstractConnection; +import org.freedesktop.dbus.connections.impl.DBusConnection; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.freedesktop.dbus.exceptions.MessageFormatException; +import org.freedesktop.dbus.messages.Message; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Error messages which can be sent over the bus. + */ +public class Error extends Message { + + private static final String DEFAULT_NULL_EXCEPTION_ERROR_MSG = "Unsupported NULL Exception"; + private static final Logger LOGGER = LoggerFactory.getLogger(Error.class); + + public Error() { + } + + public Error(String _dest, String _errorName, long _replyserial, String _sig, Object... _args) throws DBusException { + this(null, _dest, _errorName, _replyserial, _sig, _args); + } + + public Error(String _source, String _dest, String _errorName, long _replyserial, String _sig, Object... _args) + throws DBusException { + super(DBusConnection.getEndianness(), Message.MessageType.ERROR, (byte) 0); + + if (null == _errorName) { + throw new MessageFormatException("Must specify error name to Errors."); + } + + List hargs = new ArrayList<>(); + hargs.add(createHeaderArgs(HeaderField.ERROR_NAME, ArgumentType.STRING_STRING, _errorName)); + hargs.add(createHeaderArgs(HeaderField.REPLY_SERIAL, ArgumentType.UINT32_STRING, _replyserial)); + + if (null != _source) { + hargs.add(createHeaderArgs(HeaderField.SENDER, ArgumentType.STRING_STRING, _source)); + } + + if (null != _dest) { + hargs.add(createHeaderArgs(HeaderField.DESTINATION, ArgumentType.STRING_STRING, _dest)); + } + + if (null != _sig) { + hargs.add(createHeaderArgs(HeaderField.SIGNATURE, ArgumentType.SIGNATURE_STRING, _sig)); + setArgs(_args); + } + + padAndMarshall(hargs, getSerial(), _sig, _args); + } + + public Error(String _source, Message _m, Throwable _ex) throws DBusException { + this(_source, _m.getSource(), + AbstractConnection.DOLLAR_PATTERN.matcher(Optional.ofNullable(_ex).orElse(new IOException(DEFAULT_NULL_EXCEPTION_ERROR_MSG)).getClass().getName()).replaceAll("."), + _m.getSerial(), "s", _ex == null ? DEFAULT_NULL_EXCEPTION_ERROR_MSG : _ex.getMessage()); + } + + public Error(Message _m, Throwable _ex) throws DBusException { + this(_m.getSource(), + AbstractConnection.DOLLAR_PATTERN.matcher(Optional.ofNullable(_ex).orElse(new IOException(DEFAULT_NULL_EXCEPTION_ERROR_MSG)).getClass().getName()).replaceAll("."), + _m.getSerial(), "s", _ex == null ? DEFAULT_NULL_EXCEPTION_ERROR_MSG : _ex.getMessage()); + } + + @SuppressWarnings("unchecked") + private static Class createExceptionClass(String _name) { + Class c = null; + String name = _name; + // Fix package name for DBus own error messages + if (name.startsWith("org.freedesktop.DBus.Error.")) { + name = name.replace("org.freedesktop.DBus.Error.", "org.freedesktop.dbus.errors."); + } + + do { + try { + c = (Class) Class.forName(name); + } catch (ClassNotFoundException _exCnf) { + LOGGER.trace("Could not find class for name {}", name, _exCnf); + } + name = EXCEPTION_EXTRACT_PATTERN.matcher(name).replaceAll("\\$$1"); + } while (null == c && EXCEPTION_PARTIAL_PATTERN.matcher(name).matches()); + return c; + } + + /** + * Turns this into an exception of the correct type + * + * @return exception + */ + public DBusExecutionException getException() { + try { + Class c = createExceptionClass(getName()); + if (null == c || !DBusExecutionException.class.isAssignableFrom(c)) { + c = DBusExecutionException.class; + } + Constructor con = c.getConstructor(String.class); + DBusExecutionException ex; + Object[] args = getParameters(); + if (null == args || 0 == args.length) { + ex = con.newInstance(""); + } else { + ex = con.newInstance(Arrays.stream(args).map(Objects::toString).collect(Collectors.joining(" ")).trim()); + } + ex.setType(getName()); + return ex; + } catch (Exception _ex1) { + logger.debug("", _ex1); + DBusExecutionException ex; + Object[] args = null; + try { + args = getParameters(); + } catch (Exception _ex2) { + LOGGER.trace("Cannot retrieve parameters", _ex2); + } + if (null == args || 0 == args.length) { + ex = new DBusExecutionException(""); + } else { + ex = new DBusExecutionException(Arrays.stream(args).map(Objects::toString).collect(Collectors.joining(" ")).trim()); + } + ex.setType(getName()); + return ex; + } + } + + /** + * Throw this as an exception of the correct type + */ + public void throwException() throws DBusExecutionException { + throw getException(); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/errors/InvalidMethodArgument.java b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/InvalidMethodArgument.java new file mode 100644 index 0000000000..bda7a4f05b --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/InvalidMethodArgument.java @@ -0,0 +1,14 @@ +package org.freedesktop.dbus.errors; + +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +/** + * Thrown if a arguments passed to the method are invalid + */ +public class InvalidMethodArgument extends DBusExecutionException { + private static final long serialVersionUID = 2504012938615867394L; + + public InvalidMethodArgument(String _message) { + super(_message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/errors/MatchRuleInvalid.java b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/MatchRuleInvalid.java new file mode 100644 index 0000000000..be7b0f1945 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/MatchRuleInvalid.java @@ -0,0 +1,14 @@ +package org.freedesktop.dbus.errors; + +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +/** + * Thrown if the match rule is invalid + */ +public class MatchRuleInvalid extends DBusExecutionException { + private static final long serialVersionUID = 6922529529288327323L; + + public MatchRuleInvalid(String _message) { + super(_message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/errors/NoReply.java b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/NoReply.java new file mode 100644 index 0000000000..b05e2a1d31 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/NoReply.java @@ -0,0 +1,14 @@ +package org.freedesktop.dbus.errors; + +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +/** + * Thrown if there is no reply to a method call + */ +public class NoReply extends DBusExecutionException { + private static final long serialVersionUID = 5280031560938871837L; + + public NoReply(String _message) { + super(_message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/errors/NotSupported.java b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/NotSupported.java new file mode 100644 index 0000000000..27895ff855 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/NotSupported.java @@ -0,0 +1,14 @@ +package org.freedesktop.dbus.errors; + +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +/** + * Thrown if a called operation is not supported + */ +public class NotSupported extends DBusExecutionException { + private static final long serialVersionUID = -3937521136197720266L; + + public NotSupported(String _message) { + super(_message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/errors/PropertyReadOnly.java b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/PropertyReadOnly.java new file mode 100644 index 0000000000..65c4c29b64 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/PropertyReadOnly.java @@ -0,0 +1,14 @@ +package org.freedesktop.dbus.errors; + +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +/** + * Thrown if a attempt to edit read only property + */ +public class PropertyReadOnly extends DBusExecutionException { + private static final long serialVersionUID = -8493757965292570003L; + + public PropertyReadOnly(String _message) { + super(_message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/errors/ServiceUnknown.java b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/ServiceUnknown.java new file mode 100644 index 0000000000..41f2941e87 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/ServiceUnknown.java @@ -0,0 +1,14 @@ +package org.freedesktop.dbus.errors; + +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +/** + * Thrown if the requested service was not available + */ +public class ServiceUnknown extends DBusExecutionException { + private static final long serialVersionUID = -8634413313381034023L; + + public ServiceUnknown(String _message) { + super(_message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/errors/Timeout.java b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/Timeout.java new file mode 100644 index 0000000000..0795a9f4c7 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/Timeout.java @@ -0,0 +1,14 @@ +package org.freedesktop.dbus.errors; + +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +/** + * Thrown if a operation timed out + */ +public class Timeout extends DBusExecutionException { + private static final long serialVersionUID = -1212844876312953745L; + + public Timeout(String _message) { + super(_message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownInterface.java b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownInterface.java new file mode 100644 index 0000000000..f1074e223d --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownInterface.java @@ -0,0 +1,14 @@ +package org.freedesktop.dbus.errors; + +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +/** + * Thrown if a interface does not exist + */ +public class UnknownInterface extends DBusExecutionException { + private static final long serialVersionUID = -6296696668185701195L; + + public UnknownInterface(String _message) { + super(_message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownMethod.java b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownMethod.java new file mode 100644 index 0000000000..33995b4a30 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownMethod.java @@ -0,0 +1,14 @@ +package org.freedesktop.dbus.errors; + +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +/** + * Thrown if the method called was unknown on the remote object + */ +public class UnknownMethod extends DBusExecutionException { + private static final long serialVersionUID = -6712037259368315246L; + + public UnknownMethod(String _message) { + super(_message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownObject.java b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownObject.java new file mode 100644 index 0000000000..dafd5073f8 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownObject.java @@ -0,0 +1,14 @@ +package org.freedesktop.dbus.errors; + +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +/** + * Thrown if the object was unknown on a remote connection + */ +public class UnknownObject extends DBusExecutionException { + private static final long serialVersionUID = 4951706443147828582L; + + public UnknownObject(String _message) { + super(_message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownProperty.java b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownProperty.java new file mode 100644 index 0000000000..915afebb1c --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/errors/UnknownProperty.java @@ -0,0 +1,14 @@ +package org.freedesktop.dbus.errors; + +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +/** + * Thrown if a property does not exist in the interface + */ +public class UnknownProperty extends DBusExecutionException { + private static final long serialVersionUID = 7993712944238574483L; + + public UnknownProperty(String _message) { + super(_message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/AddressResolvingException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/AddressResolvingException.java new file mode 100644 index 0000000000..d23950c50b --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/AddressResolvingException.java @@ -0,0 +1,11 @@ +package org.freedesktop.dbus.exceptions; + +public class AddressResolvingException extends DBusExecutionException { + + private static final long serialVersionUID = -1636993356304776163L; + + public AddressResolvingException(String _message) { + super(_message); + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/AuthenticationException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/AuthenticationException.java new file mode 100644 index 0000000000..f5148ff93e --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/AuthenticationException.java @@ -0,0 +1,16 @@ +package org.freedesktop.dbus.exceptions; + +import java.io.IOException; + +public class AuthenticationException extends IOException { + private static final long serialVersionUID = 1L; + + public AuthenticationException(String _message, Throwable _cause) { + super(_message, _cause); + } + + public AuthenticationException(String _message) { + super(_message); + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusConnectionException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusConnectionException.java new file mode 100644 index 0000000000..2b024eee40 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusConnectionException.java @@ -0,0 +1,34 @@ +package org.freedesktop.dbus.exceptions; + +/** + * Thrown when something goes wrong with the connection to DBus.

+ * This includes find the connection parameter (e.g. machine-id file) or establishing the connection. + * + * @author David M. + * @since v3.3.0 - 2021-01-27 + */ +public class DBusConnectionException extends DBusException { + private static final long serialVersionUID = -1L; + + public DBusConnectionException() { + super(); + } + + public DBusConnectionException(String _message, Throwable _cause, boolean _enableSuppression, + boolean _writableStackTrace) { + super(_message, _cause, _enableSuppression, _writableStackTrace); + } + + public DBusConnectionException(String _message, Throwable _cause) { + super(_message, _cause); + } + + public DBusConnectionException(String _message) { + super(_message); + } + + public DBusConnectionException(Throwable _cause) { + super(_cause); + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusException.java index d9c7e93e6a..39220d425f 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusException.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusException.java @@ -1,24 +1,32 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus.exceptions; /** * An exception within DBus. */ -@SuppressWarnings("serial") public class DBusException extends Exception { + private static final long serialVersionUID = -1L; + /** - * Create an exception with the specified message - */ - public DBusException(String message) { - super(message); + * Create an exception with the specified message + * @param _message message + */ + public DBusException(String _message) { + super(_message); + } + + public DBusException() { + super(); + } + + public DBusException(String _message, Throwable _cause, boolean _enableSuppression, boolean _writableStackTrace) { + super(_message, _cause, _enableSuppression, _writableStackTrace); + } + + public DBusException(String _message, Throwable _cause) { + super(_message, _cause); + } + + public DBusException(Throwable _cause) { + super(_cause); } } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusExecutionException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusExecutionException.java index 641a96729d..0a6376c854 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusExecutionException.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusExecutionException.java @@ -1,39 +1,37 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus.exceptions; /** * An exception while running a remote method within DBus. */ -@SuppressWarnings("serial") +@SuppressWarnings("checkstyle:mutableexception") public class DBusExecutionException extends RuntimeException { + private static final long serialVersionUID = 6327661667731344250L; + private String type; /** - * Create an exception with the specified message - */ - public DBusExecutionException(String message) { - super(message); + * Create an exception with the specified message + * @param _message message + */ + public DBusExecutionException(String _message) { + super(_message); } - public void setType(String type) { - this.type = type; + public void setType(String _type) { + this.type = _type; } /** - * Get the DBus type of this exception. Use if this - * was an exception we don't have a class file for. - */ + * Get the DBus type of this exception. Use if this + * was an exception we don't have a class file for. + * + * @return string + */ public String getType() { - if (null == type) return getClass().getName(); - else return type; + if (null == type) { + return getClass().getName(); + } else { + return type; + } } } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/FatalDBusException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/FatalDBusException.java index 90002de5a3..86bc3c1dab 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/FatalDBusException.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/FatalDBusException.java @@ -1,18 +1,20 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus.exceptions; -@SuppressWarnings("serial") +import org.freedesktop.dbus.interfaces.FatalException; + public class FatalDBusException extends DBusException implements FatalException { - public FatalDBusException(String message) { - super(message); + + private static final long serialVersionUID = -3461692622913793488L; + + public FatalDBusException(String _message, Throwable _cause) { + super(_message, _cause); + } + + public FatalDBusException(Throwable _cause) { + super(_cause); + } + + public FatalDBusException(String _message) { + super(_message); } } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/FatalException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/FatalException.java deleted file mode 100644 index 58e3220428..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/FatalException.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus.exceptions; - -public interface FatalException { -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/IllegalThreadPoolStateException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/IllegalThreadPoolStateException.java new file mode 100644 index 0000000000..a6a0fba3b8 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/IllegalThreadPoolStateException.java @@ -0,0 +1,29 @@ +package org.freedesktop.dbus.exceptions; + +/** + * Thrown when a used thread pool (e.g. in ReceivingService) is in an invalid state. + * + * @author hypfvieh + * @since 4.2.0 - 2022-07-14 + */ +public class IllegalThreadPoolStateException extends IllegalStateException { + + private static final long serialVersionUID = 1L; + + public IllegalThreadPoolStateException() { + super(); + } + + public IllegalThreadPoolStateException(String _message, Throwable _cause) { + super(_message, _cause); + } + + public IllegalThreadPoolStateException(String _s) { + super(_s); + } + + public IllegalThreadPoolStateException(Throwable _cause) { + super(_cause); + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/InternalMessageException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/InternalMessageException.java index ab9ecc1b13..4e70561533 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/InternalMessageException.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/InternalMessageException.java @@ -1,18 +1,11 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus.exceptions; -@SuppressWarnings("serial") +import org.freedesktop.dbus.interfaces.NonFatalException; + public class InternalMessageException extends DBusExecutionException implements NonFatalException { - public InternalMessageException(String message) { - super(message); + private static final long serialVersionUID = 1L; + + public InternalMessageException(String _message) { + super(_message); } } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/InvalidBusAddressException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/InvalidBusAddressException.java new file mode 100644 index 0000000000..9270d8e165 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/InvalidBusAddressException.java @@ -0,0 +1,29 @@ +package org.freedesktop.dbus.exceptions; + +/** + * Thrown when a invalid BusAddress should be created. + * + * @author hypfvieh + * @since 4.2.0 - 2022-07-18 + */ +public class InvalidBusAddressException extends IllegalStateException { + + private static final long serialVersionUID = 1L; + + public InvalidBusAddressException() { + super(); + } + + public InvalidBusAddressException(String _message, Throwable _cause) { + super(_message, _cause); + } + + public InvalidBusAddressException(String _s) { + super(_s); + } + + public InvalidBusAddressException(Throwable _cause) { + super(_cause); + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MarshallingException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MarshallingException.java index 3635456488..eea0e8c574 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MarshallingException.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MarshallingException.java @@ -1,18 +1,16 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus.exceptions; -@SuppressWarnings("serial") +import org.freedesktop.dbus.interfaces.NonFatalException; + public class MarshallingException extends DBusException implements NonFatalException { - public MarshallingException(String message) { - super(message); + + private static final long serialVersionUID = 3065477360622428063L; + + public MarshallingException(String _message, Throwable _cause) { + super(_message, _cause); + } + + public MarshallingException(String _message) { + super(_message); } } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageFormatException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageFormatException.java index e8a19389f7..9769949da5 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageFormatException.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageFormatException.java @@ -1,21 +1,14 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus.exceptions; +import org.freedesktop.dbus.interfaces.NonFatalException; + /** * Thrown if a message is formatted incorrectly. */ -@SuppressWarnings("serial") public class MessageFormatException extends DBusException implements NonFatalException { - public MessageFormatException(String message) { - super(message); + private static final long serialVersionUID = -4806500517504320924L; + + public MessageFormatException(String _message) { + super(_message); } } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageProtocolVersionException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageProtocolVersionException.java index c093b41ccc..cd42231e3e 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageProtocolVersionException.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageProtocolVersionException.java @@ -1,20 +1,14 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus.exceptions; +import org.freedesktop.dbus.interfaces.FatalException; + import java.io.IOException; -@SuppressWarnings("serial") public class MessageProtocolVersionException extends IOException implements FatalException { - public MessageProtocolVersionException(String message) { - super(message); + + private static final long serialVersionUID = 3107039118803575407L; + + public MessageProtocolVersionException(String _message) { + super(_message); } } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageTypeException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageTypeException.java index 21028e107e..cf7c96a2e9 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageTypeException.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageTypeException.java @@ -1,20 +1,13 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus.exceptions; +import org.freedesktop.dbus.interfaces.NonFatalException; + import java.io.IOException; -@SuppressWarnings("serial") public class MessageTypeException extends IOException implements NonFatalException { - public MessageTypeException(String message) { - super(message); + private static final long serialVersionUID = 935695242304001622L; + + public MessageTypeException(String _message) { + super(_message); } } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/NonFatalException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/NonFatalException.java deleted file mode 100644 index e6a0f36d3f..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/NonFatalException.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ -package org.freedesktop.dbus.exceptions; - -public interface NonFatalException { -} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/NotConnected.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/NotConnected.java index 0610c2065e..28ef1b17ae 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/NotConnected.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/NotConnected.java @@ -1,21 +1,14 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus.exceptions; +import org.freedesktop.dbus.interfaces.FatalException; + /** * Thrown if a DBus action is called when not connected to the Bus. */ -@SuppressWarnings("serial") public class NotConnected extends DBusExecutionException implements FatalException { - public NotConnected(String message) { - super(message); + private static final long serialVersionUID = -3566138179099398537L; + + public NotConnected(String _message) { + super(_message); } } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/SocketClosedException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/SocketClosedException.java new file mode 100644 index 0000000000..7918830480 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/SocketClosedException.java @@ -0,0 +1,30 @@ +package org.freedesktop.dbus.exceptions; + +import java.io.IOException; + +/** + * Exception which indicates a terminated connection. + * + * @author hypfvieh + * @since v4.2.2 - 2023-02-01 + */ +public class SocketClosedException extends IOException { + private static final long serialVersionUID = 1L; + + public SocketClosedException() { + super(); + } + + public SocketClosedException(String _message, Throwable _cause) { + super(_message, _cause); + } + + public SocketClosedException(String _message) { + super(_message); + } + + public SocketClosedException(Throwable _cause) { + super(_cause); + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/TransportConfigurationException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/TransportConfigurationException.java new file mode 100644 index 0000000000..652bb30084 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/TransportConfigurationException.java @@ -0,0 +1,14 @@ +package org.freedesktop.dbus.exceptions; + +public class TransportConfigurationException extends Exception { + private static final long serialVersionUID = 1L; + + public TransportConfigurationException(String _message, Throwable _cause) { + super(_message, _cause); + } + + public TransportConfigurationException(String _message) { + super(_message); + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/TransportRegistrationException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/TransportRegistrationException.java new file mode 100644 index 0000000000..b17f1dd9c1 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/TransportRegistrationException.java @@ -0,0 +1,20 @@ +package org.freedesktop.dbus.exceptions; + +/** + * Thrown if registration of transport providers fails. + * + * @author hypfvieh + * @since v4.0.0 - 2021-09-08 + */ +public class TransportRegistrationException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public TransportRegistrationException(String _message, Throwable _cause) { + super(_message, _cause); + } + + public TransportRegistrationException(String _message) { + super(_message); + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/UnknownTypeCodeException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/UnknownTypeCodeException.java index af350758ed..2439a2c470 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/UnknownTypeCodeException.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/UnknownTypeCodeException.java @@ -1,20 +1,11 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus.exceptions; -import static org.freedesktop.dbus.Gettext.getString; +import org.freedesktop.dbus.interfaces.NonFatalException; -@SuppressWarnings("serial") public class UnknownTypeCodeException extends DBusException implements NonFatalException { - public UnknownTypeCodeException(byte code) { - super(getString("invalidDBusCode") + code); + private static final long serialVersionUID = -4688075573912580455L; + + public UnknownTypeCodeException(byte _code) { + super("Not a valid D-Bus type code: " + _code); } } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractInterfacesAddedHandler.java b/federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractInterfacesAddedHandler.java new file mode 100644 index 0000000000..cfe4146882 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractInterfacesAddedHandler.java @@ -0,0 +1,21 @@ +package org.freedesktop.dbus.handlers; + +import org.freedesktop.dbus.connections.AbstractConnection; +import org.freedesktop.dbus.interfaces.DBusSigHandler; +import org.freedesktop.dbus.interfaces.ObjectManager; +import org.freedesktop.dbus.interfaces.ObjectManager.InterfacesAdded; + +/** +* Subclass this abstract class for creating a callback for InterfaceAdded signal provided by DBus ObjectManager. +* +* As soon as your callback is registered by calling {@link AbstractConnection#addSigHandler(Class, DBusSigHandler)}, +* all property changes by Dbus will be visible in the handle(DBusSigHandler) method of your callback class. +*/ +public abstract class AbstractInterfacesAddedHandler extends AbstractSignalHandlerBase { + + @Override + public final Class getImplementationClass() { + return ObjectManager.InterfacesAdded.class; + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractInterfacesRemovedHandler.java b/federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractInterfacesRemovedHandler.java new file mode 100644 index 0000000000..15514f497f --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractInterfacesRemovedHandler.java @@ -0,0 +1,21 @@ +package org.freedesktop.dbus.handlers; + +import org.freedesktop.dbus.connections.AbstractConnection; +import org.freedesktop.dbus.interfaces.DBusSigHandler; +import org.freedesktop.dbus.interfaces.ObjectManager; +import org.freedesktop.dbus.interfaces.ObjectManager.InterfacesRemoved; + +/** +* Subclass this abstract class for creating a callback for InterfaceRemoved signal provided by DBus ObjectManager. +* +* As soon as your callback is registered by calling {@link AbstractConnection#addSigHandler(Class, DBusSigHandler)}, +* all property changes by Dbus will be visible in the handle(DBusSigHandler) method of your callback class. +*/ +public abstract class AbstractInterfacesRemovedHandler extends AbstractSignalHandlerBase { + + @Override + public final Class getImplementationClass() { + return ObjectManager.InterfacesRemoved.class; + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractPropertiesChangedHandler.java b/federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractPropertiesChangedHandler.java new file mode 100644 index 0000000000..90f866c371 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractPropertiesChangedHandler.java @@ -0,0 +1,21 @@ +package org.freedesktop.dbus.handlers; + +import org.freedesktop.dbus.connections.AbstractConnection; +import org.freedesktop.dbus.interfaces.DBusSigHandler; +import org.freedesktop.dbus.interfaces.Properties; +import org.freedesktop.dbus.interfaces.Properties.PropertiesChanged; + +/** + * Subclass this abstract class for creating a callback for changed properties. + * + * As soon as your callback is registered by calling {@link AbstractConnection#addSigHandler(Class, DBusSigHandler)}, + * all property changes by Dbus will be visible in the handle(DBusSigHandler) method of your callback class. + */ +public abstract class AbstractPropertiesChangedHandler extends AbstractSignalHandlerBase { + + @Override + public final Class getImplementationClass() { + return Properties.PropertiesChanged.class; + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractSignalHandlerBase.java b/federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractSignalHandlerBase.java new file mode 100644 index 0000000000..26acb4a9ea --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/handlers/AbstractSignalHandlerBase.java @@ -0,0 +1,18 @@ +package org.freedesktop.dbus.handlers; + +import org.freedesktop.dbus.interfaces.DBusSigHandler; +import org.freedesktop.dbus.messages.DBusSignal; + +/** + * Base class for all signal handling classes. + * @author hypfvieh + */ +public abstract class AbstractSignalHandlerBase implements DBusSigHandler { + + /** + * Signal-Class which is implemented in subclasses of this class. + * @return Class + */ + public abstract Class getImplementationClass(); + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/CallbackHandler.java b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/CallbackHandler.java new file mode 100644 index 0000000000..d0118b7784 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/CallbackHandler.java @@ -0,0 +1,12 @@ +package org.freedesktop.dbus.interfaces; + +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +/** + * Interface for callbacks in async mode + */ +public interface CallbackHandler { + void handle(T _r); + + void handleError(DBusExecutionException _ex); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBus.java b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBus.java new file mode 100644 index 0000000000..1bb1adfc2f --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBus.java @@ -0,0 +1,250 @@ +package org.freedesktop.dbus.interfaces; + +import org.freedesktop.dbus.annotations.DBusInterfaceName; +import org.freedesktop.dbus.errors.MatchRuleInvalid; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.messages.DBusSignal; +import org.freedesktop.dbus.types.UInt32; +import org.freedesktop.dbus.types.Variant; + +import java.util.Map; + +@SuppressWarnings({"checkstyle:methodname"}) +@DBusInterfaceName("org.freedesktop.DBus") +public interface DBus extends DBusInterface { + int DBUS_NAME_FLAG_ALLOW_REPLACEMENT = 0x01; + int DBUS_NAME_FLAG_REPLACE_EXISTING = 0x02; + int DBUS_NAME_FLAG_DO_NOT_QUEUE = 0x04; + int DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = 1; + int DBUS_REQUEST_NAME_REPLY_IN_QUEUE = 2; + int DBUS_REQUEST_NAME_REPLY_EXISTS = 3; + int DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER = 4; + int DBUS_RELEASE_NAME_REPLY_RELEASED = 1; + int DBUS_RELEASE_NAME_REPLY_NON_EXISTANT = 2; + int DBUS_RELEASE_NAME_REPLY_NOT_OWNER = 3; + int DBUS_START_REPLY_SUCCESS = 1; + int DBUS_START_REPLY_ALREADY_RUNNING = 2; + + /** + * Initial message to register ourselves on the Bus. + * @return The unique name of this connection to the Bus. + */ + String Hello(); + + /** + * Request a name on the bus. + * @param _name The name to request. + * @param _flags DBUS_NAME flags. + * @return DBUS_REQUEST_NAME_REPLY constants. + */ + UInt32 RequestName(String _name, UInt32 _flags); + + /** + * Release a name on the bus. + * @param _name The name to release. + * @return DBUS_RELEASE_NAME_REPLY constants. + */ + UInt32 ReleaseName(String _name); + + /** + * List the connections currently queued for a name. + * @param _name The name to query + * @return A list of unique connection IDs. + */ + String[] ListQueuedOwners(String _name); + + /** + * Lists all connected names on the Bus. + * @return An array of all connected names. + */ + String[] ListNames(); + + /** + * Returns a list of all names that can be activated on the bus. + * @return Array of strings where each string is a bus name + */ + String[] ListActivatableNames(); + + /** + * Determine if a name has an owner. + * @param _name The name to query. + * @return true if the name has an owner. + */ + boolean NameHasOwner(String _name); + + /** + * Start a service. If the given service is not provided + * by any application, it will be started according to the .service file + * for that service. + * @param _name The service name to start. + * @param _flags Unused. + * @return DBUS_START_REPLY constants. + */ + UInt32 StartServiceByName(String _name, UInt32 _flags); + + /** + * DBUS Specification:
+ * Normally, session bus activated services inherit the environment of the bus daemon. This method adds to or modifies that environment when activating services. + * Some bus instances, such as the standard system bus, may disable access to this method for some or all callers. + * Note, both the environment variable names and values must be valid UTF-8. There's no way to update the activation environment with data that is invalid UTF-8. + * + * @param _environment Environment to add or update + */ + void UpdateActivationEnvironment(Map[] _environment); + + /** + * Get the connection unique name that owns the given name. + * @param _name The name to query. + * @return The connection which owns the name. + */ + String GetNameOwner(String _name); + + /** + * Get the Unix UID that owns a connection name. + * @param _connectionName The connection name. + * @return The Unix UID that it. + */ + UInt32 GetConnectionUnixUser(String _connectionName); + + /** + * Returns the proccess ID associated with a connection. + * @param _connectionName The name of the connection + * @return The PID of the connection. + */ + UInt32 GetConnectionUnixProcessID(String _connectionName); + + /** + * DBUS Specification:
+ * Returns as many credentials as possible for the process connected to + * the server. If unable to determine certain credentials (for instance, + * because the process is not on the same machine as the bus daemon, + * or because this version of the bus daemon does not support a + * particular security framework), or if the values of those credentials + * cannot be represented as documented here, then those credentials + * are omitted. + *

+ * Keys in the returned dictionary not containing "." are defined + * by this specification. Bus daemon implementors supporting + * credentials frameworks not mentioned in this document should either + * contribute patches to this specification, or use keys containing + * "." and starting with a reversed domain name. + *

+ * + * @param _busName Unique or well-known bus name of the connection to query, such as :12.34 or com.example.tea + * @return Credentials + */ + Map> GetConnectionCredentials(String _busName); + + /** + * DBUS Specification:
+ * + * Returns auditing data used by Solaris ADT, in an unspecified
+ * binary format. If you know what this means, please contribute
+ * documentation via the D-Bus bug tracking system.
+ * This method is on the core DBus interface for historical reasons;
+ * the same information should be made available via
+ * + * the section called "org.freedesktop.DBus.GetConnectionCredentials"
+ * in future.
+ * + * @param _busName Unique or well-known bus name of the connection to query, such as :12.34 or com.example.tea + * @return auditing data as returned by adt_export_session_data() + */ + Byte[] GetAdtAuditSessionData(String _busName); + + /** + * DBUS Specification:
+ * Returns the security context used by SELinux, in an unspecified
+ * format. If you know what this means, please contribute
+ * documentation via the D-Bus bug tracking system.
+ * This method is on the core DBus interface for historical reasons;
+ * the same information should be made available via
+ * + * the section called "org.freedesktop.DBus.GetConnectionCredentials
+ * in future. + * + * @param _busName Unique or well-known bus name of the connection to query, such as :12.34 or com.example.tea + * + * @return some sort of string of bytes, not necessarily UTF-8, not including '\0' + */ + Byte[] GetConnectionSELinuxSecurityContext(String _busName); + + /** + * Add a match rule. + * Will cause you to receive messages that aren't directed to you which + * match this rule. + * @param _matchrule The Match rule as a string. Format Undocumented. + */ + void AddMatch(String _matchrule) throws MatchRuleInvalid; + + /** + * Remove a match rule. + * Will cause you to stop receiving messages that aren't directed to you which + * match this rule. + * @param _matchrule The Match rule as a string. Format Undocumented. + */ + void RemoveMatch(String _matchrule) throws MatchRuleInvalid; + + /** + * DBUS Specification:
+ * Gets the unique ID of the bus. The unique ID here is shared among all addresses the
+ * bus daemon is listening on (TCP, UNIX domain socket, etc.) and its format is described in
+ * the section called "UUIDs”.
+ * Each address the bus is listening on also has its own unique
+ * ID, as described in + * the section called "Server Addresses”. The per-bus and per-address IDs are not related.
+ * There is also a per-machine ID, described in + * the section called "org.freedesktop.DBus.Peer and returned + * by org.freedesktop.DBus.Peer.GetMachineId().
+ * For a desktop session bus, the bus ID can be used as a way to uniquely identify a user's session. + * + * @return id Unique ID identifying the bus daemon + */ + String GetId(); + + /** + * Signal sent when the owner of a name changes + */ + @SuppressWarnings("checkstyle:visibilitymodifier") + class NameOwnerChanged extends DBusSignal { + public final String name; + public final String oldOwner; + public final String newOwner; + + public NameOwnerChanged(String _path, String _name, String _oldOwner, String _newOwner) throws DBusException { + super(_path, new Object[] { + _name, _oldOwner, _newOwner + }); + name = _name; + oldOwner = _oldOwner; + newOwner = _newOwner; + } + } + + /** + * Signal sent to a connection when it loses a name. + */ + @SuppressWarnings("checkstyle:visibilitymodifier") + class NameLost extends DBusSignal { + public final String name; + + public NameLost(String _path, String _name) throws DBusException { + super(_path, _name); + name = _name; + } + } + + /** + * Signal sent to a connection when it acquires a name. + */ + @SuppressWarnings("checkstyle:visibilitymodifier") + class NameAcquired extends DBusSignal { + public final String name; + + public NameAcquired(String _path, String _name) throws DBusException { + super(_path, _name); + name = _name; + } + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBusInterface.java b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBusInterface.java new file mode 100644 index 0000000000..3fd84c29b5 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBusInterface.java @@ -0,0 +1,32 @@ +package org.freedesktop.dbus.interfaces; + +/** + * Denotes a class as exportable or a remote interface which can be called. + *

+ * Any interface which should be exported or imported should extend this interface. All public methods from that + * interface are exported/imported with the given method signatures. + *

+ *

+ * All method calls on exported objects are run in their own threads. Application writers are responsible for any + * concurrency issues. + *

+ */ +public interface DBusInterface { + + /** + * Returns true on remote objects. Local objects implementing this interface MUST return false. + * + * @return boolean + */ + default boolean isRemote() { + return false; + } + + /** + * Returns the path of this object. + * + * @return string + */ + String getObjectPath(); + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBusSerializable.java b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBusSerializable.java new file mode 100644 index 0000000000..5b4c830986 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBusSerializable.java @@ -0,0 +1,23 @@ +package org.freedesktop.dbus.interfaces; + +import org.freedesktop.dbus.exceptions.DBusException; + +/** + * Custom classes may be sent over DBus if they implement this interface. + *

+ * In addition to the serialize method, classes MUST implement a deserialize method which returns null and takes + * as it's arguments all the DBus types the class will be serialied to in order and with type + * parameterisation. They MUST also provide a zero-argument constructor. + *

+ *

+ * The serialize method should return the class properties you wish to serialize, correctly formatted for the wire + * (DBusConnection.convertParameters() can help with this), in order in an Object array. + *

+ *

+ * The deserialize method will be called once after the zero-argument constructor. This should contain all the code to + * initialise the object from the types. + *

+ */ +public interface DBusSerializable { + Object[] serialize() throws DBusException; +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBusSigHandler.java b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBusSigHandler.java new file mode 100644 index 0000000000..68fa2bdd28 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/DBusSigHandler.java @@ -0,0 +1,17 @@ +package org.freedesktop.dbus.interfaces; + +import org.freedesktop.dbus.messages.DBusSignal; + +/** + * Handle a signal on DBus. All Signal handlers are run in their own Thread. Application writers are responsible for + * managing any concurrency issues. + */ +public interface DBusSigHandler { + /** + * Handle a signal. + * + * @param _signal The signal to handle. If such a class exists, the signal will be an instance of the class with the + * correct type signature. Otherwise it will be an instance of DBusSignal + */ + void handle(T _signal); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Error.java b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Error.java new file mode 100644 index 0000000000..dca8b08eab --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Error.java @@ -0,0 +1,10 @@ +package org.freedesktop.dbus.interfaces; + +import org.freedesktop.dbus.annotations.DBusInterfaceName; + +/** + * Contains standard errors that can be thrown from methods. + */ +@DBusInterfaceName("org.freedesktop.DBus.Error") +public interface Error { +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/FatalException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/FatalException.java new file mode 100644 index 0000000000..f0e586a343 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/FatalException.java @@ -0,0 +1,4 @@ +package org.freedesktop.dbus.interfaces; + +public interface FatalException { +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Features.java b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Features.java new file mode 100644 index 0000000000..7ce0462560 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Features.java @@ -0,0 +1,8 @@ +package org.freedesktop.dbus.interfaces; + +import org.freedesktop.dbus.annotations.DBusInterfaceName; + +@DBusInterfaceName("org.freedesktop.DBus.Features") +public interface Features extends DBusInterface { + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Introspectable.java b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Introspectable.java new file mode 100644 index 0000000000..67722685b9 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Introspectable.java @@ -0,0 +1,17 @@ +package org.freedesktop.dbus.interfaces; + +import org.freedesktop.dbus.annotations.DBusInterfaceName; + +/** +* Objects can provide introspection data via this interface and method. +* See the Introspection Format. +*/ +@DBusInterfaceName("org.freedesktop.DBus.Introspectable") +public interface Introspectable extends DBusInterface { + /** + * @return The XML introspection data for this object + */ + //CHECKSTYLE:OFF + String Introspect(); + //CHECKSTYLE:ON +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Monitoring.java b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Monitoring.java new file mode 100644 index 0000000000..d97d792da8 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Monitoring.java @@ -0,0 +1,47 @@ +package org.freedesktop.dbus.interfaces; + +import org.freedesktop.dbus.annotations.DBusInterfaceName; +import org.freedesktop.dbus.types.UInt32; + +@DBusInterfaceName("org.freedesktop.DBus.Monitoring.BecomeMonitor") +@SuppressWarnings({"checkstyle:methodname"}) +public interface Monitoring { + /** + * Converts the connection into a monitor connection which can be used as a + * debugging/monitoring tool. Only a user who is privileged on this bus (by some implementation-specific definition) + * may create monitor + * connections[5]. + *

+ * Monitor connections lose all their bus names, including the unique connection name, and all their match rules. + * Sending messages on a monitor connection is not allowed: applications should use a private connection for + * monitoring. + *

+ *

+ * Monitor connections may receive all messages, even messages that should only have gone to some other connection + * ("eavesdropping"). The first argument is a list of match rules, which replace any match rules that were + * previously active for this connection. These match rules are always treated as if they contained the special + * eavesdrop='true' member. + *

+ *

+ * As a special case, an empty list of match rules (which would otherwise match nothing, making the monitor useless) + * is treated as a shorthand for matching all messages. + *

+ *

+ * The second argument might be used for flags to influence the behaviour of the monitor connection in future D-Bus + * versions. + *

+ *

+ * Message bus implementations should attempt to minimize the side-effects of monitoring — in particular, unlike + * ordinary eavesdropping, monitoring the system bus does not require the access control rules to be relaxed, which + * would change the set of messages that can be delivered to their (non-monitor) destinations. However, it is + * unavoidable that monitoring will increase the message bus's resource consumption. In edge cases where there was + * barely enough time or memory without monitoring, this might result in message deliveries failing when they would + * otherwise have succeeded. + *

+ * + * @param _rule Match rules to add to the connection + * @param _flags Not used, must be 0 + */ + void BecomeMonitor(String[] _rule, UInt32 _flags); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/NonFatalException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/NonFatalException.java new file mode 100644 index 0000000000..fe8bc35d11 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/NonFatalException.java @@ -0,0 +1,4 @@ +package org.freedesktop.dbus.interfaces; + +public interface NonFatalException { +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/ObjectManager.java b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/ObjectManager.java new file mode 100644 index 0000000000..7b4b3d1aaf --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/ObjectManager.java @@ -0,0 +1,123 @@ +package org.freedesktop.dbus.interfaces; + +import org.freedesktop.dbus.DBusPath; +import org.freedesktop.dbus.annotations.DBusInterfaceName; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.messages.DBusSignal; +import org.freedesktop.dbus.types.Variant; + +import java.util.List; +import java.util.Map; + +@DBusInterfaceName("org.freedesktop.DBus.ObjectManager") +@SuppressWarnings({"checkstyle:methodname", "checkstyle:visibilitymodifier"}) +public interface ObjectManager extends DBusInterface { + /** + * Get a sub-tree of objects. The root of the sub-tree is this object. + * + * @return A Map from object path (DBusInterface) to a Map from interface name to a properties Map (as returned by + * Properties.GetAll()) + */ + Map>>> GetManagedObjects(); + + /** + * Signal generated when a new interface is added + */ + class InterfacesAdded extends DBusSignal { + public final DBusPath signalSource; + public final String objectPath; + + public final Map>> interfaces; + + public InterfacesAdded(String _objectPath, DBusPath _source, Map>> _interfaces) + throws DBusException { + super(_objectPath, _source, _interfaces); + objectPath = _objectPath; + signalSource = _source; + interfaces = _interfaces; + } + + /** + * The source DBus object path (e.g. /org/bluez/hci0/dev_00_11_22_33_44_55). + * + * @return DBusPath + */ + public DBusPath getSignalSource() { + return signalSource; + + } + + public String getObjectPath() { + return objectPath; + } + + /** + * Returns the added interfaces. Key is a DBus interface name (like org.bluez.Device1). Value is a Map with + * properties known for the new device. + * + * @return Map + */ + public Map>> getInterfaces() { + return interfaces; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + + "signalSource=" + signalSource + + ", objectPath='" + objectPath + '\'' + + ", interfaces=" + interfaces + + ']'; + } + } + + /** + * Signal generated when an interface is removed + */ + class InterfacesRemoved extends DBusSignal { + public final DBusPath signalSource; + public final String objectPath; + + public final List interfaces; + + public InterfacesRemoved(String _objectPath, DBusPath _source, List _interfaces) + throws DBusException { + super(_objectPath, _source, _interfaces); + objectPath = _objectPath; + signalSource = _source; + interfaces = _interfaces; + } + + /** + * The source DBus object path (e.g. /org/bluez/hci0/dev_00_11_22_33_44_55). + * + * @return DBusPath + */ + public DBusPath getSignalSource() { + return signalSource; + } + + public String getObjectPath() { + return objectPath; + } + + /** + * Returns list of removed DBus interfaces (like org.bluez.Device1). + * + * @return List + */ + public List getInterfaces() { + return interfaces; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + + "signalSource=" + signalSource + + ", objectPath='" + objectPath + '\'' + + ", interfaces=" + interfaces + + ']'; + } + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Peer.java b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Peer.java new file mode 100644 index 0000000000..531b78a8b0 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Peer.java @@ -0,0 +1,14 @@ +package org.freedesktop.dbus.interfaces; + +import org.freedesktop.dbus.annotations.DBusInterfaceName; + +/** + * All DBus Applications should respond to the Ping method on this interface + */ +@DBusInterfaceName("org.freedesktop.DBus.Peer") +@SuppressWarnings({"checkstyle:methodname"}) +public interface Peer extends DBusInterface { + void Ping(); + + String GetMachineId(); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Properties.java b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Properties.java new file mode 100644 index 0000000000..91ab3c0369 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/interfaces/Properties.java @@ -0,0 +1,99 @@ +package org.freedesktop.dbus.interfaces; + +import org.freedesktop.dbus.annotations.DBusInterfaceName; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.messages.DBusSignal; +import org.freedesktop.dbus.types.Variant; + +import java.util.List; +import java.util.Map; + +/** + * A standard properties interface. + */ +@DBusInterfaceName("org.freedesktop.DBus.Properties") +@SuppressWarnings({"checkstyle:methodname"}) +public interface Properties extends DBusInterface { + /** + * Get the value for the given property. + * + * @param whatever + * @param _interfaceName The interface this property is associated with. + * @param _propertyName The name of the property. + * @return The value of the property (may be any valid DBus type). + */ + A Get(String _interfaceName, String _propertyName); + + /** + * Set the value for the given property. + * + * @param whatever + * @param _interfaceName The interface this property is associated with. + * @param _propertyName The name of the property. + * @param _value The new value of the property (may be any valid DBus type). + */ + void Set(String _interfaceName, String _propertyName, A _value); + + /** + * Get all properties and values. + * + * @param _interfaceName The interface the properties is associated with. + * @return The properties mapped to their values. + */ + Map> GetAll(String _interfaceName); + + /** + * Signal generated when a property changes. + */ + class PropertiesChanged extends DBusSignal { + private final Map> propertiesChanged; + private final List propertiesRemoved; + + private final String interfaceName; + + public PropertiesChanged(String _path, String _interfaceName, Map> _propertiesChanged, + List _propertiesRemoved) throws DBusException { + super(_path, _interfaceName, _propertiesChanged, _propertiesRemoved); + + this.propertiesChanged = _propertiesChanged; + this.propertiesRemoved = _propertiesRemoved; + this.interfaceName = _interfaceName; + } + + /** + * Get name of the interface created this signal (e.g. org.bluez.Adapter1). + * + * @return String + */ + public String getInterfaceName() { + return interfaceName; + } + + /** + * Return the changed properties. Key is the properties name, value is Variant containing any type. + * + * @return Map + */ + public Map> getPropertiesChanged() { + return propertiesChanged; + } + + /** + * Returns a list of removed property keys. + * + * @return List + */ + public List getPropertiesRemoved() { + return propertiesRemoved; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + + "propertiesChanged=" + propertiesChanged + + ", propertiesRemoved=" + propertiesRemoved + + ", interfaceName='" + interfaceName + '\'' + + ']'; + } + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/messages/DBusSignal.java b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/DBusSignal.java new file mode 100644 index 0000000000..c0dd6a81a1 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/DBusSignal.java @@ -0,0 +1,345 @@ +package org.freedesktop.dbus.messages; + +import static org.freedesktop.dbus.connections.AbstractConnection.OBJECT_REGEX_PATTERN; + +import org.freedesktop.dbus.DBusMatchRule; +import org.freedesktop.dbus.Marshalling; +import org.freedesktop.dbus.ObjectPath; +import org.freedesktop.dbus.Struct; +import org.freedesktop.dbus.connections.AbstractConnection; +import org.freedesktop.dbus.connections.impl.DBusConnection; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.MessageFormatException; +import org.freedesktop.dbus.interfaces.DBusInterface; +import org.freedesktop.dbus.utils.CommonRegexPattern; +import org.freedesktop.dbus.utils.DBusNamingUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.invoke.MethodType; +import java.lang.reflect.Constructor; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +public class DBusSignal extends Message { + private static final Logger LOGGER = + LoggerFactory.getLogger(DBusSignal.class); + + private static final Map> CLASS_CACHE = + new ConcurrentHashMap<>(); + + private static final Map, Type[]> TYPE_CACHE = + new ConcurrentHashMap<>(); + + private static final Map SIGNAL_NAMES = + new ConcurrentHashMap<>(); + private static final Map INT_NAMES = + new ConcurrentHashMap<>(); + + private static final Map, List> CACHED_CONSTRUCTORS = + new ConcurrentHashMap<>(); + + private Class clazz; + private boolean bodydone = false; + private byte[] blen; + + DBusSignal() { + } + + public DBusSignal(String _source, String _path, String _iface, String _member, String _sig, Object... _args) + throws DBusException { + super(DBusConnection.getEndianness(), Message.MessageType.SIGNAL, (byte) 0); + + if (null == _path || null == _member || null == _iface) { + throw new MessageFormatException("Must specify object path, interface and signal name to Signals."); + } + + List hargs = new ArrayList<>(); + hargs.add(createHeaderArgs(HeaderField.PATH, ArgumentType.OBJECT_PATH_STRING, _path)); + hargs.add(createHeaderArgs(HeaderField.INTERFACE, ArgumentType.STRING_STRING, _iface)); + hargs.add(createHeaderArgs(HeaderField.MEMBER, ArgumentType.STRING_STRING, _member)); + + if (null != _source) { + hargs.add(createHeaderArgs(HeaderField.SENDER, ArgumentType.STRING_STRING, _source)); + } + + if (null != _sig) { + hargs.add(createHeaderArgs(HeaderField.SIGNATURE, ArgumentType.SIGNATURE_STRING, _sig)); + setArgs(_args); + } + + setSerial(getSerial() + 1); + padAndMarshall(hargs, getSerial(), _sig, _args); + bodydone = true; + } + + /** + * Create a new signal. This contructor MUST be called by all sub classes. + * + * @param _objectPath The path to the object this is emitted from. + * @param _args The parameters of the signal. + * @throws DBusException This is thrown if the subclass is incorrectly defined. + */ + @SuppressWarnings("unchecked") + protected DBusSignal(String _objectPath, Object... _args) throws DBusException { + super(DBusConnection.getEndianness(), Message.MessageType.SIGNAL, (byte) 0); + + if (!OBJECT_REGEX_PATTERN.matcher(_objectPath).matches()) { + throw new DBusException("Invalid object path: " + _objectPath); + } + + Class tc = getClass(); + String member = DBusNamingUtil.getSignalName(tc); + Class enc = tc.getEnclosingClass(); + if (null == enc || !DBusInterface.class.isAssignableFrom(enc) || enc.getName().equals(enc.getSimpleName())) { + throw new DBusException( + "Signals must be declared as a member of a class implementing DBusInterface which is the member of a package."); + } + String iface = DBusNamingUtil.getInterfaceName(enc); + + List hargs = new ArrayList<>(); + hargs.add(createHeaderArgs(HeaderField.PATH, ArgumentType.OBJECT_PATH_STRING, _objectPath)); + hargs.add(createHeaderArgs(HeaderField.INTERFACE, ArgumentType.STRING_STRING, iface)); + hargs.add(createHeaderArgs(HeaderField.MEMBER, ArgumentType.STRING_STRING, member)); + + String sig = null; + if (0 < _args.length) { + try { + Type[] types = TYPE_CACHE.get(tc); + if (null == types) { + Constructor con = + (Constructor) tc.getDeclaredConstructors()[0]; + Type[] ts = con.getGenericParameterTypes(); + types = new Type[ts.length - 1]; + for (int i = 1; i <= types.length; i++) { + if (ts[i] instanceof TypeVariable) { + types[i - 1] = ((TypeVariable) ts[i]).getBounds()[0]; + } else { + types[i - 1] = ts[i]; + } + } + TYPE_CACHE.put(tc, types); + } + sig = Marshalling.getDBusType(types); + hargs.add(createHeaderArgs(HeaderField.SIGNATURE, ArgumentType.SIGNATURE_STRING, sig)); + setArgs(_args); + } catch (Exception _ex) { + logger.debug("", _ex); + throw new DBusException("Failed to add signal parameters: " + _ex.getMessage()); + } + } + + blen = new byte[4]; + appendBytes(blen); + long newSerial = getSerial() + 1; + setSerial(newSerial); + append("ua(yv)", newSerial, hargs.toArray()); + pad((byte) 8); + } + + static void addInterfaceMap(String _java, String _dbus) { + INT_NAMES.put(_dbus, _java); + } + + static void addSignalMap(String _java, String _dbus) { + SIGNAL_NAMES.put(_dbus, _java); + } + + @SuppressWarnings("unchecked") + private static Class createSignalClass(String _intName, String _sigName) throws DBusException { + String name = _intName + '$' + _sigName; + Class c = CLASS_CACHE.get(name); + if (null == c) { + c = DBusMatchRule.getCachedSignalType(name); + } + if (null != c) { + return c; + } + do { + try { + c = (Class) Class.forName(name); + } catch (ClassNotFoundException _exCnf) { + LOGGER.trace("Class not found for {}", name, _exCnf); + } + name = CommonRegexPattern.EXCEPTION_EXTRACT_PATTERN.matcher(name).replaceAll("\\$$1"); + } while (null == c && CommonRegexPattern.EXCEPTION_PARTIAL_PATTERN.matcher(name).matches()); + if (null == c) { + throw new DBusException("Could not create class from signal " + _intName + '.' + _sigName); + } + CLASS_CACHE.put(name, c); + return c; + } + + public DBusSignal createReal(AbstractConnection _conn) throws DBusException { + String intname = INT_NAMES.get(getInterface()); + String signame = SIGNAL_NAMES.get(getName()); + if (null == intname) { + intname = getInterface(); + } + if (null == signame) { + signame = getName(); + } + if (null == clazz) { + clazz = createSignalClass(intname, signame); + } + + logger.debug("Converting signal to type: {}", clazz); + + if (!CACHED_CONSTRUCTORS.containsKey(clazz)) { + cacheConstructors(clazz); + } + + List list = CACHED_CONSTRUCTORS.get(clazz); + + Constructor con = null; + Type[] types = null; + + Object[] parameters = getParameters(); + + // Get all classes required in constructor in order + // Primitives will always be wrapped in their wrapper classes + // because the parameters are received on the bus and will be converted + // in 'Message.extractOne' method which will always return Object and not primitives + List> wantedArgs = Arrays.stream(parameters) + .map(p -> p.getClass()) + .collect(Collectors.toList()); + + // find suitable constructor (by checking if parameter types are equal) + for (CachedConstructor type : list) { + if (type.matchesParameters(wantedArgs)) { + con = type.constructor; + types = type.types; + break; + } + } + if (con == null) { + logger.warn("Could not find suitable constructor for class {} with argument-types: {}", clazz.getName(), + wantedArgs); + return null; + } + + try { + DBusSignal s; + Object[] args = Marshalling.deSerializeParameters(parameters, types, _conn); + if (null == args) { + s = con.newInstance(getPath()); + } else { + Object[] params = new Object[args.length + 1]; + params[0] = getPath(); + System.arraycopy(args, 0, params, 1, args.length); + s = con.newInstance(params); + } + + s.setHeader(getHeader()); + s.setWiredata(getWireData()); + s.setByteCounter(getWireData().length); + return s; + } catch (Exception _ex) { + throw new DBusException(_ex); + } + } + + @SuppressWarnings("unchecked") + private void cacheConstructors(Class _clazz) { + List list = new ArrayList<>(); + for (Constructor constructor : _clazz.getDeclaredConstructors()) { + Constructor x = (Constructor) constructor; + list.add(new CachedConstructor(x)); + } + + CACHED_CONSTRUCTORS.put(_clazz, list); + } + + public void appendbody(AbstractConnection _conn) throws DBusException { + if (bodydone) { + return; + } + + Type[] types = TYPE_CACHE.get(getClass()); + Object[] args = Marshalling.convertParameters(getParameters(), types, _conn); + setArgs(args); + String sig = getSig(); + + long counter = getByteCounter(); + if (null != args && 0 < args.length) { + append(sig, args); + } + marshallint(getByteCounter() - counter, blen, 0, 4); + bodydone = true; + } + + private static class CachedConstructor { + private final Constructor constructor; + private final List> parameterTypes; + private final Type[] types; + + CachedConstructor(Constructor _constructor) { + constructor = _constructor; + parameterTypes = Arrays.stream(constructor.getParameterTypes()) + .skip(1) + .map(c -> { + // convert primitives to wrapper classes so we can compare it to parameter classes later + if (c.isPrimitive()) { + return wrap(c); + } + return c; + }) + .collect(Collectors.toList()); + types = createTypes(constructor); + } + + public boolean matchesParameters(List> _wantedArgs) { + if (parameterTypes == null || _wantedArgs == null) { + return false; + } + + if (parameterTypes.size() != _wantedArgs.size()) { + return false; + } + + for (int i = 0; i < parameterTypes.size(); i++) { + Class class1 = parameterTypes.get(i); + + if (Enum.class.isAssignableFrom(class1) && String.class.equals(_wantedArgs.get(i))) { + continue; + } else if (DBusInterface.class.isAssignableFrom(class1) && ObjectPath.class.equals(_wantedArgs.get(i))) { + continue; + } else if (Struct.class.isAssignableFrom(class1) && Object[].class.equals(_wantedArgs.get(i))) { + continue; + } else if (class1.isAssignableFrom(_wantedArgs.get(i))) { + continue; + } else { + return false; + } + } + + return true; + } + + @SuppressWarnings("unchecked") + private static Type[] createTypes(Constructor _constructor) { + Type[] ts = _constructor.getGenericParameterTypes(); + Type[] types = new Type[ts.length - 1]; + for (int i = 1; i <= types.length; i++) { + if (ts[i] instanceof TypeVariable) { + types[i - 1] = ((TypeVariable) ts[i]).getBounds()[0]; + } else { + types[i - 1] = ts[i]; + } + } + return types; + } + + @SuppressWarnings("unchecked") + private static Class wrap(Class _clz) { + return (Class) MethodType.methodType(_clz).wrap().returnType(); + } + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/messages/EmptyCollectionHelper.java b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/EmptyCollectionHelper.java new file mode 100644 index 0000000000..6013d77491 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/EmptyCollectionHelper.java @@ -0,0 +1,125 @@ +package org.freedesktop.dbus.messages; + +import java.util.Arrays; + +final class EmptyCollectionHelper { + + private EmptyCollectionHelper() {} + + /** + * This function determine the new offset in signature for empty Dictionary/Map collections. Normally the element + * inside a collection determines the new offset, however in case of empty collections there is no element to + * determine the sub signature of the list so this function determines which part of the signature to skip + * + * @param _sigb the total signature + * @param _currentOffset the current offset within the signature + * @return the index of the last element of the collection (subtype) + */ + static int determineSignatureOffsetDict(byte[] _sigb, int _currentOffset) { + return determineEndOfBracketStructure(_sigb, _currentOffset, '{', '}'); + } + + /** + * This function determine the new offset in signature for empty Array/List collections. + * Normally the element inside a collection determines the new offset, + * however in case of empty collections there is no element to determine the sub signature of the list + * so this function determines which part of the signature to skip + * + * @param _sigb the total signature + * @param _currentOffset the current offset within the signature + * @return the index of the last element of the collection (subtype) + */ + static int determineSignatureOffsetArray(byte[] _sigb, int _currentOffset) { + String sigSubString = determineSubSignature(_sigb, _currentOffset); + + // End of string so can't have any more offset + if (sigSubString.isEmpty()) { + return _currentOffset; + } + + ECollectionSubType newtype = determineCollectionSubType((char) _sigb[_currentOffset]); + switch (newtype) { + case ARRAY: + // array in array so look at the next type + return determineSignatureOffsetArray(_sigb, _currentOffset + 1); + case DICT: + return determineSignatureOffsetDict(_sigb, _currentOffset); + case STRUCT: + return determineSignatureOffsetStruct(_sigb, _currentOffset); + case PRIMITIVE: + //primitive is always one element so no need to skip more + return _currentOffset; + default: + break; + + } + throw new IllegalStateException("Unable to parse signature for empty collection"); + } + + private static int determineSignatureOffsetStruct(byte[] _sigb, int _currentOffset) { + return determineEndOfBracketStructure(_sigb, _currentOffset, '(', ')'); + } + + /** + * This is a generic function to determine the end of a structure that has opening and closing characters. + * Currently used for Struct () and Dict {} + * + */ + private static int determineEndOfBracketStructure(byte[] _sigb, int _currentOffset, char _openChar, char _closeChar) { + String sigSubString = determineSubSignature(_sigb, _currentOffset); + + // End of string so can't have any more offset + if (sigSubString.isEmpty()) { + return _currentOffset; + } + int i = 0; + int depth = 0; + + for (char chr : sigSubString.toCharArray()) { + //book keeping of depth of nested structures to solve opening closing bracket problem + if (chr == _openChar) { + depth++; + } else if (chr == _closeChar) { + depth--; + } + if (depth == 0) { + return _currentOffset + i; + } + i++; + } + throw new IllegalStateException("Unable to parse signature for empty collection"); + } + + private static String determineSubSignature(byte[] _sigb, int _currentOffset) { + byte[] restSigbytes = Arrays.copyOfRange(_sigb, _currentOffset, _sigb.length); + return new String(restSigbytes); + } + + /** + * The starting type determines of a collection determines when it ends + * @param _sig the signature letter of the type + */ + private static ECollectionSubType determineCollectionSubType(char _sig) { + switch (_sig) { + case '(': + return ECollectionSubType.STRUCT; + case '{': + return ECollectionSubType.DICT; + case 'a': + return ECollectionSubType.ARRAY; + default: + // of course there can be other types but those shouldn't be allowed in this part of the signature + return ECollectionSubType.PRIMITIVE; + } + } + + /** + * Internal Enumeration used to group the types of element + */ + enum ECollectionSubType { + STRUCT, + DICT, + ARRAY, + PRIMITIVE + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/messages/ExportedObject.java b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/ExportedObject.java new file mode 100644 index 0000000000..64218ccbfa --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/ExportedObject.java @@ -0,0 +1,341 @@ +package org.freedesktop.dbus.messages; + +import org.freedesktop.dbus.Marshalling; +import org.freedesktop.dbus.MethodTuple; +import org.freedesktop.dbus.StrongReference; +import org.freedesktop.dbus.Tuple; +import org.freedesktop.dbus.TypeRef; +import org.freedesktop.dbus.annotations.DBusIgnore; +import org.freedesktop.dbus.annotations.DBusInterfaceName; +import org.freedesktop.dbus.annotations.DBusMemberName; +import org.freedesktop.dbus.annotations.DBusProperties; +import org.freedesktop.dbus.annotations.DBusProperty; +import org.freedesktop.dbus.connections.AbstractConnection; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.freedesktop.dbus.interfaces.DBusInterface; +import org.freedesktop.dbus.interfaces.Introspectable; +import org.freedesktop.dbus.interfaces.Peer; +import org.freedesktop.dbus.utils.DBusNamingUtil; +import org.slf4j.LoggerFactory; + +import java.lang.annotation.Annotation; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Queue; +import java.util.Set; + +public class ExportedObject { + private final Map methods = new HashMap<>(); + private final String introspectionData; + private final Reference object; + + public ExportedObject(DBusInterface _object, boolean _weakreferences) throws DBusException { + object = _weakreferences ? new WeakReference<>(_object) : new StrongReference<>(_object); + + Set> implementedInterfaces = getDBusInterfaces(_object.getClass()); + implementedInterfaces.add(Introspectable.class); + implementedInterfaces.add(Peer.class); + + this.introspectionData = generateIntrospectionXml(implementedInterfaces); + } + + /** + * Generates the introspection data xml for annotations + * + * @param _c input interface/method/signal + * @return xml with annotation definition + */ + protected String generateAnnotationsXml(AnnotatedElement _c) { + StringBuilder ans = new StringBuilder(); + for (Annotation a : _c.getDeclaredAnnotations()) { + + if (!a.annotationType().isAnnotationPresent(DBusInterfaceName.class)) { + // skip all interfaces not compatible with + // DBusInterface (mother of all DBus + // related interfaces) + continue; + } + Class t = a.annotationType(); + String value = ""; + try { + Method m = t.getMethod("value"); + if (m != null) { + value = m.invoke(a).toString(); + } + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException _ex) { + LoggerFactory.getLogger(getClass()).trace("Could not find value", _ex); + } + + String name = DBusNamingUtil.getAnnotationName(t); + ans.append(" \n"); + } + return ans.toString(); + } + + /** + * Generates the introspection data for the single property. + * + * @param _property input property annotation + * @return xml with property definition + * @throws DBusException in case of unknown data types + */ + protected String generatePropertyXml(DBusProperty _property) throws DBusException { + Class propertyTypeClass = _property.type(); + String propertyTypeString; + if (TypeRef.class.isAssignableFrom(propertyTypeClass)) { + Type actualType = Arrays.stream(propertyTypeClass.getGenericInterfaces()) + .filter(t -> t instanceof ParameterizedType) + .map(t -> (ParameterizedType) t) + .filter(t -> TypeRef.class.equals(t.getRawType())) + .map(t -> t.getActualTypeArguments()[0]) // TypeRef has one generic argument + .findFirst() + .orElseThrow(() -> + new DBusException("Could not read TypeRef type for property '" + _property.name() + "'") + ); + propertyTypeString = Marshalling.getDBusType(new Type[]{actualType}); + } else if (List.class.equals(propertyTypeClass)) { + // default non generic list types + propertyTypeString = "av"; + } else if (Map.class.equals(propertyTypeClass)) { + // default non generic map type + propertyTypeString = "a{vv}"; + } else { + propertyTypeString = Marshalling.getDBusType(new Type[]{propertyTypeClass}); + } + + String access = _property.access().getAccessName(); + return ""; + } + + /** + * Generates the introspection data for the input interface properties. + * + * @param _clz input interface + * @return xml with property definitions + * @throws DBusException in case of unknown data types + */ + protected String generatePropertiesXml(Class _clz) throws DBusException { + StringBuilder xml = new StringBuilder(); + DBusProperties properties = _clz.getAnnotation(DBusProperties.class); + if (properties != null) { + for (DBusProperty property : properties.value()) { + xml.append(" ").append(generatePropertyXml(property)).append("\n"); + } + } + DBusProperty property = _clz.getAnnotation(DBusProperty.class); + if (property != null) { + xml.append(" ").append(generatePropertyXml(property)).append("\n"); + } + return xml.toString(); + } + + /** + * Generates the introspection data for the input interface methods + * + * @param _clz input interface + * @return xml with method definitions + * + * @throws DBusException if marshalling fails + */ + protected String generateMethodsXml(Class _clz) throws DBusException { + StringBuilder sb = new StringBuilder(); + for (Method meth : _clz.getDeclaredMethods()) { + if (isExcluded(meth)) { + continue; + } + String ms = ""; + String methodName = DBusNamingUtil.getMethodName(meth); + if (methodName.length() > AbstractConnection.MAX_NAME_LENGTH) { + throw new DBusException( + "Introspected method name exceeds 255 characters. Cannot export objects with method " + + methodName); + } + sb.append(" \n"); + sb.append(generateAnnotationsXml(meth)); + for (Class ex : meth.getExceptionTypes()) { + if (DBusExecutionException.class.isAssignableFrom(ex)) { + sb.append(" \n"); + } + } + for (Type pt : meth.getGenericParameterTypes()) { + for (String s : Marshalling.getDBusType(pt)) { + sb.append(" \n"); + ms += s; + } + } + if (!Void.TYPE.equals(meth.getGenericReturnType())) { + if (Tuple.class.isAssignableFrom(meth.getReturnType())) { + ParameterizedType tc = (ParameterizedType) meth.getGenericReturnType(); + Type[] ts = tc.getActualTypeArguments(); + + for (Type t : ts) { + if (t != null) { + for (String s : Marshalling.getDBusType(t)) { + sb.append(" \n"); + } + } + } + } else if (Object[].class.equals(meth.getGenericReturnType())) { + throw new DBusException("Return type of Object[] cannot be introspected properly"); + } else { + for (String s : Marshalling.getDBusType(meth.getGenericReturnType())) { + sb.append(" \n"); + } + } + } + sb.append(" \n"); + methods.putIfAbsent(new MethodTuple(methodName, ms), meth); + } + + return sb.toString(); + } + + /** + * Generates the introspection data for the input interface signals + * + * @param _clz input interface + * @return xml with signal definitions + * @throws DBusException in case of invalid signal name / data types + */ + protected String generateSignalsXml(Class _clz) throws DBusException { + StringBuilder sb = new StringBuilder(); + for (Class sig : _clz.getDeclaredClasses()) { + if (DBusSignal.class.isAssignableFrom(sig)) { + String signalName = DBusNamingUtil.getSignalName(sig); + if (sig.isAnnotationPresent(DBusMemberName.class)) { + DBusSignal.addSignalMap(sig.getSimpleName(), signalName); + } + if (signalName.length() > AbstractConnection.MAX_NAME_LENGTH) { + throw new DBusException( + "Introspected signal name exceeds 255 characters. Cannot export objects with signals of type " + + signalName); + } + sb.append(" \n"); + Constructor con = sig.getConstructors()[0]; + Type[] ts = con.getGenericParameterTypes(); + for (int j = 1; j < ts.length; j++) { + for (String s : Marshalling.getDBusType(ts[j])) { + sb.append(" \n"); + } + } + sb.append(generateAnnotationsXml(sig)); + sb.append(" \n"); + } + } + return sb.toString(); + } + + /** + * Get all valid DBus interfaces which are implemented in a given class. + * The search is performed without recursion taking into account object inheritance. + * A valid DBus interface must directly extend the {@link DBusInterface}. + * + * @param _inputClazz input object class + * @return set of DBus interfaces implements in the input class + */ + protected Set> getDBusInterfaces(Class _inputClazz) { + Objects.requireNonNull(_inputClazz, "inputClazz must not be null"); + Set> result = new LinkedHashSet<>(); + + // set of already checked classes/interfaces - used to avoid loops/redundant reflection calls + Set> checked = new LinkedHashSet<>(); + // queue with classes/interfaces to check + Queue> toCheck = new LinkedList<>(); + toCheck.add(_inputClazz); + + while (!toCheck.isEmpty()) { + Class clazz = toCheck.poll(); + checked.add(clazz); // avoid checking this class in the next loops + + // if it's class and it has super class, queue to check it later + Class superClass = clazz.getSuperclass(); + if (superClass != null && DBusInterface.class.isAssignableFrom(superClass)) { + toCheck.add(superClass); + } + + List> interfaces = Arrays.asList(clazz.getInterfaces()); + if (interfaces.contains(DBusInterface.class)) { + // clazz is interface and directly extends the DBusInterface + result.add(clazz); + } + + // iterate over the sub-interfaces and select the ones that extend DBusInterface + // this is required especially for nested interfaces + interfaces.stream() + .filter(DBusInterface.class::isAssignableFrom) + .filter(i -> i != DBusInterface.class) + .filter(i -> !checked.contains(i)) + .forEach(toCheck::add); + } + + return result; + } + + private String generateIntrospectionXml(Set> _interfaces) throws DBusException { + StringBuilder sb = new StringBuilder(); + for (Class iface : _interfaces) { + String ifaceName = DBusNamingUtil.getInterfaceName(iface); + // don't let people export things which don't have a valid D-Bus interface name + if (ifaceName.equals(iface.getSimpleName())) { + throw new DBusException("DBusInterfaces cannot be declared outside a package"); + } + if (ifaceName.length() > AbstractConnection.MAX_NAME_LENGTH) { + throw new DBusException( + "Introspected interface name exceeds 255 characters. Cannot export objects of type " + + ifaceName); + } + + // add mapping between class FQCN and name used in annotation (if present) + if (iface.isAnnotationPresent(DBusInterfaceName.class)) { + DBusSignal.addInterfaceMap(iface.getName(), ifaceName); + } + + sb.append(" \n"); + sb.append(generateAnnotationsXml(iface)); + sb.append(generateMethodsXml(iface)); + sb.append(generatePropertiesXml(iface)); + sb.append(generateSignalsXml(iface)); + sb.append(" \n"); + } + + return sb.toString(); + } + + public Map getMethods() { + return methods; + } + + public Reference getObject() { + return object; + } + + public String getIntrospectiondata() { + return introspectionData; + } + + public static boolean isExcluded(Method _meth) { + return !Modifier.isPublic(_meth.getModifiers()) + || _meth.getAnnotation(DBusIgnore.class) != null + || _meth.getName().equals("getObjectPath") && _meth.getReturnType().equals(String.class) + && _meth.getParameterCount() == 0; + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/messages/Message.java b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/Message.java new file mode 100644 index 0000000000..8b8b67400c --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/Message.java @@ -0,0 +1,1708 @@ +package org.freedesktop.dbus.messages; + +import org.freedesktop.dbus.ArrayFrob; +import org.freedesktop.dbus.Container; +import org.freedesktop.dbus.DBusMap; +import org.freedesktop.dbus.FileDescriptor; +import org.freedesktop.dbus.Marshalling; +import org.freedesktop.dbus.ObjectPath; +import org.freedesktop.dbus.connections.AbstractConnection; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.MarshallingException; +import org.freedesktop.dbus.exceptions.MessageFormatException; +import org.freedesktop.dbus.exceptions.UnknownTypeCodeException; +import org.freedesktop.dbus.interfaces.DBusInterface; +import org.freedesktop.dbus.types.UInt16; +import org.freedesktop.dbus.types.UInt32; +import org.freedesktop.dbus.types.UInt64; +import org.freedesktop.dbus.types.Variant; +import org.freedesktop.dbus.utils.Hexdump; +import org.freedesktop.dbus.utils.LoggingHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Array; +import java.lang.reflect.Type; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.BiFunction; +import java.util.stream.Collectors; + +/** + * Superclass of all messages which are sent over the Bus. This class deals with all the marshalling to/from the wire + * format. + */ +public class Message { + public static final int MAXIMUM_ARRAY_LENGTH = 67108864; + public static final int MAXIMUM_MESSAGE_LENGTH = MAXIMUM_ARRAY_LENGTH * 2; + public static final int MAXIMUM_NUM_UNIX_FDS = MAXIMUM_MESSAGE_LENGTH / 4; + + /** The current protocol major version. */ + public static final byte PROTOCOL = 1; + + /** Position of data offset in int array. */ + private static final int OFFSET_DATA = 1; + /** Position of signature offset in int array. */ + private static final int OFFSET_SIG = 0; + + /** Keep a static reference to each size of padding array to prevent allocation. */ + private static byte[][] padding; + static { + padding = new byte[][] { + null, new byte[1], new byte[2], new byte[3], new byte[4], new byte[5], new byte[6], new byte[7] + }; + } + /** Steps to increment the buffer array. */ + private static final int BUFFERINCREMENT = 20; + + private static final AtomicLong GLOBAL_SERIAL = new AtomicLong(0); + + //CHECKSTYLE:OFF + protected final Logger logger = LoggerFactory.getLogger(getClass()); + //CHECKSTYLE:ON + + private final List filedescriptors = new ArrayList<>(); + private final Object[] headers = new Object[HeaderField.MAX_FIELDS]; + + private byte[][] wiredata = new byte[BUFFERINCREMENT][]; + private long bytecounter = 0; + + private long serial; + private byte type; + private byte flags; + private byte protover; + + private boolean big; + private Object[] args; + private byte[] body; + private long bodylen = 0; + private int preallocated = 0; + private int paofs = 0; + private byte[] pabuf; + private int bufferuse = 0; + + /** + * Create a message; only to be called by sub-classes. + * + * @param _endian The endianness to create the message. + * @param _type The message type. + * @param _flags Any message flags. + * @throws DBusException on error + */ + protected Message(byte _endian, byte _type, byte _flags) throws DBusException { + this(); + big = Endian.BIG == _endian; + setSerial(GLOBAL_SERIAL.incrementAndGet()); + + logger.debug("Creating message with serial {}", getSerial()); + + type = _type; + flags = _flags; + preallocate(4); + append("yyyy", _endian, _type, _flags, Message.PROTOCOL); + } + + /** + * Create a blank message. Only to be used when calling populate. + */ + protected Message() { + } + + /** + * Create a message from wire-format data. + * + * @param _msg D-Bus serialized data of type yyyuu + * @param _headers D-Bus serialized data of type a(yv) + * @param _body D-Bus serialized data of the signature defined in headers. + */ + @SuppressWarnings("unchecked") + void populate(byte[] _msg, byte[] _headers, byte[] _body, List _descriptors) throws DBusException { + + // create a copy of the given arrays to be sure that the content is not changed outside + + byte[] msgBuf = new byte[_msg.length]; + System.arraycopy(_msg, 0, msgBuf, 0, _msg.length); + + byte[] headerBuf = new byte[_headers.length]; + System.arraycopy(_headers, 0, headerBuf, 0, _headers.length); + + byte[] bodyBuf = new byte[_body.length]; + System.arraycopy(_body, 0, bodyBuf, 0, _body.length); + + big = msgBuf[0] == Endian.BIG; + type = msgBuf[1]; + flags = msgBuf[2]; + protover = msgBuf[3]; + wiredata[0] = msgBuf; + wiredata[1] = headerBuf; + wiredata[2] = bodyBuf; + body = bodyBuf; + bufferuse = 3; + bodylen = ((Number) extract(Message.ArgumentType.UINT32_STRING, msgBuf, 4)[0]).longValue(); + setSerial(((Number) extract(Message.ArgumentType.UINT32_STRING, msgBuf, 8)[0]).longValue()); + bytecounter = msgBuf.length + headerBuf.length + bodyBuf.length; + + filedescriptors.clear(); + if (_descriptors != null) { + filedescriptors.addAll(_descriptors); + } + + LoggingHelper.logIf(logger.isTraceEnabled(), () -> logger.trace("Message header: {}", Hexdump.toAscii(headerBuf))); + + Object[] hs = extractHeader(headerBuf); + + LoggingHelper.logIf(logger.isTraceEnabled(), () -> logger.trace("Extracted objects: {}", LoggingHelper.arraysVeryDeepString(hs))); + + List list = (List) hs[0]; + for (Object o : list) { + Object[] objArr = (Object[]) o; + byte idx = (byte) objArr[0]; + this.headers[idx] = objArr[1]; + } + } + + /** + * @deprecated use getHeader(). + * This method did return a map containing message header. + * It allows changing the map, but changes did not result in changing the + * actual message header. Therefore using a map was removed and an object array is + * used instead. Changes to that array (content) will be result in a changed + * header in the message. + * + * @return map of header + */ + @Deprecated(forRemoval = true, since = "4.2.2 - 2023-01-19") + protected Map getHeaders() { + Map headerMap = new HashMap<>(); + for (byte i = 0; i < headers.length; i++) { + headerMap.put(i, headers[i]); + } + return headerMap; + } + + protected Object[] getHeader() { + return headers; + } + + /** + * Set header content. + * null value is ignored. + * + * @param _header header to set + */ + protected void setHeader(Object[] _header) { + if (_header == null) { + return; + } + if (_header.length > headers.length) { + throw new IllegalArgumentException("Given header is larger (" + _header.length + ") than allowed header size: " + headers.length); + } + System.arraycopy(_header, 0, headers, 0, _header.length); + } + + protected long getByteCounter() { + return bytecounter; + } + + protected void setByteCounter(long _bytecounter) { + bytecounter = _bytecounter; + } + + protected synchronized void setSerial(long _serial) { + serial = _serial; + } + + protected byte[][] getWiredata() { + return wiredata; + } + + protected void setWiredata(byte[][] _wiredata) { + wiredata = _wiredata; + } + + byte getProtover() { + return protover; + } + + long getBodylen() { + return bodylen; + } + + /** + * Create a buffer of num bytes. Data is copied to this rather than added to the buffer list. + */ + private void preallocate(int _num) { + preallocated = 0; + pabuf = new byte[_num]; + appendBytes(pabuf); + preallocated = _num; + paofs = 0; + } + + /** + * Ensures there are enough free buffers. + * + * @param _num number of free buffers to create. + */ + private void ensureBuffers(int _num) { + int increase = _num - wiredata.length + bufferuse; + if (increase > 0) { + if (increase < BUFFERINCREMENT) { + increase = BUFFERINCREMENT; + } + + logger.trace("Resizing {}", bufferuse); + + byte[][] temp = new byte[wiredata.length + increase][]; + System.arraycopy(wiredata, 0, temp, 0, wiredata.length); + wiredata = temp; + } + } + + /** + * Appends a buffer to the buffer list. + * + * @param _buf buffer byte array + */ + protected void appendBytes(byte[] _buf) { + if (null == _buf) { + return; + } + if (preallocated > 0) { + if (paofs + _buf.length > pabuf.length) { + throw new ArrayIndexOutOfBoundsException( + MessageFormat.format("Array index out of bounds, paofs={0}, pabuf.length={1}, buf.length={2}.", + paofs, pabuf.length, _buf.length)); + } + System.arraycopy(_buf, 0, pabuf, paofs, _buf.length); + paofs += _buf.length; + preallocated -= _buf.length; + } else { + if (bufferuse == wiredata.length) { + logger.trace("Resizing {}", bufferuse); + byte[][] temp = new byte[wiredata.length + BUFFERINCREMENT][]; + System.arraycopy(wiredata, 0, temp, 0, wiredata.length); + wiredata = temp; + } + wiredata[bufferuse++] = _buf; + bytecounter += _buf.length; + } + } + + /** + * Appends a byte to the buffer list. + * + * @param _b byte + */ + protected void appendByte(byte _b) { + if (preallocated > 0) { + pabuf[paofs++] = _b; + preallocated--; + } else { + if (bufferuse == wiredata.length) { + + logger.trace("Resizing {}", bufferuse); + byte[][] temp = new byte[wiredata.length + BUFFERINCREMENT][]; + System.arraycopy(wiredata, 0, temp, 0, wiredata.length); + wiredata = temp; + } + wiredata[bufferuse++] = new byte[] { + _b + }; + bytecounter++; + } + } + + /** + * Demarshalls an integer of a given width from a buffer. Endianness is determined from the format of the message. + * + * @param _buf The buffer to demarshall from. + * @param _ofs The offset to demarshall from. + * @param _width The byte-width of the int. + * + * @return long + */ + public long demarshallint(byte[] _buf, int _ofs, int _width) { + return big ? demarshallintBig(_buf, _ofs, _width) : demarshallintLittle(_buf, _ofs, _width); + } + + /** + * Demarshalls an integer of a given width from a buffer. + * + * @param _buf The buffer to demarshall from. + * @param _ofs The offset to demarshall from. + * @param _endian The endianness to use in demarshalling. + * @param _width The byte-width of the int. + * + * @return long + */ + public static long demarshallint(byte[] _buf, int _ofs, byte _endian, int _width) { + return _endian == Endian.BIG ? demarshallintBig(_buf, _ofs, _width) : demarshallintLittle(_buf, _ofs, _width); + } + + /** + * Demarshalls an integer of a given width from a buffer using big-endian format. + * + * @param _buf The buffer to demarshall from. + * @param _ofs The offset to demarshall from. + * @param _width The byte-width of the int. + * @return long + */ + public static long demarshallintBig(byte[] _buf, int _ofs, int _width) { + long l = 0; + for (int i = 0; i < _width; i++) { + l <<= 8; + l |= _buf[_ofs + i] & 0xFF; + } + return l; + } + + /** + * Demarshalls an integer of a given width from a buffer using little-endian format. + * + * @param _buf The buffer to demarshall from. + * @param _ofs The offset to demarshall from. + * @param _width The byte-width of the int. + * + * @return long + */ + public static long demarshallintLittle(byte[] _buf, int _ofs, int _width) { + long l = 0; + for (int i = _width - 1; i >= 0; i--) { + l <<= 8; + l |= _buf[_ofs + i] & 0xFF; + } + return l; + } + + /** + * Marshalls an integer of a given width and appends it to the message. Endianness is determined from the message. + * + * @param _l The integer to marshall. + * @param _width The byte-width of the int. + */ + public void appendint(long _l, int _width) { + byte[] buf = new byte[_width]; + marshallint(_l, buf, 0, _width); + appendBytes(buf); + } + + /** + * Marshalls an integer of a given width into a buffer. Endianness is determined from the message. + * + * @param _l The integer to marshall. + * @param _buf The buffer to marshall to. + * @param _ofs The offset to marshall to. + * @param _width The byte-width of the int. + */ + public void marshallint(long _l, byte[] _buf, int _ofs, int _width) { + if (big) { + marshallintBig(_l, _buf, _ofs, _width); + } else { + marshallintLittle(_l, _buf, _ofs, _width); + } + + LoggingHelper.logIf(logger.isTraceEnabled(), + () -> logger.trace("Marshalled int {} to {}", _l, Hexdump.toHex(_buf, _ofs, _width, true))); + } + + /** + * Marshalls an integer of a given width into a buffer using big-endian format. + * + * @param _l The integer to marshall. + * @param _buf The buffer to marshall to. + * @param _ofs The offset to marshall to. + * @param _width The byte-width of the int. + */ + public static void marshallintBig(long _l, byte[] _buf, int _ofs, int _width) { + long l = _l; + for (int i = _width - 1; i >= 0; i--) { + _buf[i + _ofs] = (byte) (l & 0xFF); + l >>= 8; + } + } + + /** + * Marshalls an integer of a given width into a buffer using little-endian format. + * + * @param _l The integer to marshall. + * @param _buf The buffer to demarshall to. + * @param _ofs The offset to demarshall to. + * @param _width The byte-width of the int. + */ + public static void marshallintLittle(long _l, byte[] _buf, int _ofs, int _width) { + long l = _l; + for (int i = 0; i < _width; i++) { + _buf[i + _ofs] = (byte) (l & 0xFF); + l >>= 8; + } + } + + public byte[][] getWireData() { + return wiredata; + } + + public List getFiledescriptors() { + return filedescriptors; + } + + /** + * Formats the message in a human-readable format. + */ + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(getClass().getSimpleName()); + sb.append('('); + sb.append(flags); + sb.append(','); + sb.append(getSerial()); + sb.append(')'); + sb.append(' '); + sb.append('{'); + sb.append(' '); + if (headers.length == 0) { + sb.append('}'); + } else { + for (int i = 0; i < headers.length; i++) { + sb.append(getHeaderFieldName((byte) i)); + sb.append('='); + sb.append('>'); + sb.append(headers[i]); + sb.append(','); + sb.append(' '); + } + sb.setCharAt(sb.length() - 2, ' '); + sb.setCharAt(sb.length() - 1, '}'); + } + sb.append(' '); + sb.append('{'); + sb.append(' '); + Object[] largs = null; + try { + largs = getParameters(); + } catch (DBusException _ex) { + logger.debug("", _ex); + } + if (null == largs || 0 == largs.length) { + sb.append('}'); + } else { + for (Object o : largs) { + if (o == null) { + sb.append("null"); + } else if (o instanceof Object[]) { + sb.append(Arrays.deepToString((Object[]) o)); + } else if (o instanceof byte[]) { + sb.append(Arrays.toString((byte[]) o)); + } else if (o instanceof int[]) { + sb.append(Arrays.toString((int[]) o)); + } else if (o instanceof short[]) { + sb.append(Arrays.toString((short[]) o)); + } else if (o instanceof long[]) { + sb.append(Arrays.toString((long[]) o)); + } else if (o instanceof boolean[]) { + sb.append(Arrays.toString((boolean[]) o)); + } else if (o instanceof double[]) { + sb.append(Arrays.toString((double[]) o)); + } else if (o instanceof float[]) { + sb.append(Arrays.toString((float[]) o)); + } else { + sb.append(o); + } + sb.append(','); + sb.append(' '); + } + sb.setCharAt(sb.length() - 2, ' '); + sb.setCharAt(sb.length() - 1, '}'); + } + return sb.toString(); + } + + /** + * Returns the value of the header field of a given field. + * + * @param _type The field to return. + * @return The value of the field or null if unset. + */ + public Object getHeader(byte _type) { + return headers.length == 0 || headers.length < _type ? null : headers[_type]; + } + + /** + * Appends a value to the message. The type of the value is read from a D-Bus signature and used to marshall the + * value. + * + * @param _sigb A buffer of the D-Bus signature. + * @param _sigofs The offset into the signature corresponding to this value. + * @param _data The value to marshall. + * @return The offset into the signature of the end of this value's type. + */ + @SuppressWarnings("unchecked") + private int appendOne(byte[] _sigb, int _sigofs, Object _data) throws DBusException { + try { + int i = _sigofs; + logger.trace("{}", bytecounter); + logger.trace("Appending type: {} value: {}", (char) _sigb[i], _data); + + // pad to the alignment of this type. + pad(_sigb[i]); + switch (_sigb[i]) { + case ArgumentType.BYTE: + appendByte(((Number) _data).byteValue()); + break; + case ArgumentType.BOOLEAN: + appendint(((Boolean) _data).booleanValue() ? 1 : 0, 4); + break; + case ArgumentType.DOUBLE: + long l = Double.doubleToLongBits(((Number) _data).doubleValue()); + appendint(l, 8); + break; + case ArgumentType.FLOAT: + int rf = Float.floatToIntBits(((Number) _data).floatValue()); + appendint(rf, 4); + break; + case ArgumentType.UINT32: + appendint(((Number) _data).longValue(), 4); + break; + case ArgumentType.INT64: + appendint(((Number) _data).longValue(), 8); + break; + case ArgumentType.UINT64: + if (big) { + appendint(((UInt64) _data).top(), 4); + appendint(((UInt64) _data).bottom(), 4); + } else { + appendint(((UInt64) _data).bottom(), 4); + appendint(((UInt64) _data).top(), 4); + } + break; + case ArgumentType.INT32: + appendint(((Number) _data).intValue(), 4); + break; + case ArgumentType.UINT16: + appendint(((Number) _data).intValue(), 2); + break; + case ArgumentType.INT16: + appendint(((Number) _data).shortValue(), 2); + break; + case ArgumentType.FILEDESCRIPTOR: + filedescriptors.add((FileDescriptor) _data); + appendint(filedescriptors.size() - 1, 4); + logger.debug("Just inserted {} as filedescriptor", filedescriptors.size() - 1); + break; + case ArgumentType.STRING: + case ArgumentType.OBJECT_PATH: + + String payload; + // if the given data is an object, not a ObjectPath itself + if (_data instanceof DBusInterface) { + payload = ((DBusInterface) _data).getObjectPath(); + } else { + // Strings are marshalled as a UInt32 with the length, + // followed by the String, followed by a null byte. + payload = _data.toString(); + } + + byte[] payloadbytes = null; + try { + payloadbytes = payload.getBytes("UTF-8"); + } catch (UnsupportedEncodingException _ex) { + logger.debug("System does not support UTF-8 encoding", _ex); + throw new DBusException("System does not support UTF-8 encoding"); + } + logger.trace("Appending String of length {}", payloadbytes.length); + appendint(payloadbytes.length, 4); + appendBytes(payloadbytes); + appendBytes(padding[1]); + // pad(ArgumentType.STRING);? do we need this? + break; + case ArgumentType.SIGNATURE: + // Signatures are marshalled as a byte with the length, + // followed by the String, followed by a null byte. + // Signatures are generally short, so preallocate the array + // for the string, length and null byte. + if (_data instanceof Type[]) { + payload = Marshalling.getDBusType((Type[]) _data); + } else { + payload = (String) _data; + } + byte[] pbytes = payload.getBytes(); + preallocate(2 + pbytes.length); + appendByte((byte) pbytes.length); + appendBytes(pbytes); + appendByte((byte) 0); + break; + case ArgumentType.ARRAY: + // Arrays are given as a UInt32 for the length in bytes, + // padding to the element alignment, then elements in + // order. The length is the length from the end of the + // initial padding to the end of the last element. + if (logger.isTraceEnabled() && _data instanceof Object[]) { + logger.trace("Appending array: {}", Arrays.deepToString((Object[]) _data)); + } + + byte[] alen = new byte[4]; + appendBytes(alen); + pad(_sigb[++i]); + long c = bytecounter; + + // optimise primitives + if (_data.getClass().isArray() && _data.getClass().getComponentType().isPrimitive()) { + byte[] primbuf; + int algn = getAlignment(_sigb[i]); + int len = Array.getLength(_data); + switch (_sigb[i]) { + case ArgumentType.BYTE: + primbuf = (byte[]) _data; + break; + case ArgumentType.INT16: + case ArgumentType.INT32: + case ArgumentType.INT64: + primbuf = new byte[len * algn]; + for (int j = 0, k = 0; j < len; j++, k += algn) { + marshallint(Array.getLong(_data, j), primbuf, k, algn); + } + break; + case ArgumentType.BOOLEAN: + primbuf = new byte[len * algn]; + for (int j = 0, k = 0; j < len; j++, k += algn) { + marshallint(Array.getBoolean(_data, j) ? 1 : 0, primbuf, k, algn); + } + break; + case ArgumentType.DOUBLE: + primbuf = new byte[len * algn]; + if (_data instanceof float[]) { + for (int j = 0, k = 0; j < len; j++, k += algn) { + marshallint(Double.doubleToRawLongBits(((float[]) _data)[j]), primbuf, k, algn); + } + } else { + for (int j = 0, k = 0; j < len; j++, k += algn) { + marshallint(Double.doubleToRawLongBits(((double[]) _data)[j]), primbuf, k, algn); + } + } + break; + case ArgumentType.FLOAT: + primbuf = new byte[len * algn]; + for (int j = 0, k = 0; j < len; j++, k += algn) { + marshallint(Float.floatToRawIntBits(((float[]) _data)[j]), primbuf, k, algn); + } + break; + default: + throw new MarshallingException("Primitive array being sent as non-primitive array."); + } + appendBytes(primbuf); + } else if (_data instanceof List) { + Object[] contents = ((List) _data).toArray(); + int diff = i; + ensureBuffers(contents.length * 4); + for (Object o : contents) { + diff = appendOne(_sigb, i, o); + } + if (contents.length == 0) { + diff = EmptyCollectionHelper.determineSignatureOffsetArray(_sigb, diff); + } + i = diff; + } else if (_data instanceof Map) { + int diff = i; + Map map = (Map) _data; + ensureBuffers(map.size() * 6); + for (Map.Entry o : map.entrySet()) { + diff = appendOne(_sigb, i, o); + } + if (map.isEmpty()) { + diff = EmptyCollectionHelper.determineSignatureOffsetDict(_sigb, diff); + } + i = diff; + } else { + Object[] contents = (Object[]) _data; + ensureBuffers(contents.length * 4); + int diff = i; + for (Object o : contents) { + diff = appendOne(_sigb, i, o); + } + if (contents.length == 0) { + diff = EmptyCollectionHelper.determineSignatureOffsetArray(_sigb, diff); + } + i = diff; + } + logger.trace("start: {} end: {} length: {}", c, bytecounter, bytecounter - c); + marshallint(bytecounter - c, alen, 0, 4); + break; + case ArgumentType.STRUCT1: + // Structs are aligned to 8 bytes + // and simply contain each element marshalled in order + Object[] contents; + if (_data instanceof Container) { + contents = ((Container) _data).getParameters(); + } else { + contents = (Object[]) _data; + } + ensureBuffers(contents.length * 4); + int j = 0; + for (i++; _sigb[i] != ArgumentType.STRUCT2; i++) { + i = appendOne(_sigb, i, contents[j++]); + } + break; + case ArgumentType.DICT_ENTRY1: + // Dict entries are the same as structs. + if (_data instanceof Map.Entry) { + i++; + i = appendOne(_sigb, i, ((Map.Entry) _data).getKey()); + i++; + i = appendOne(_sigb, i, ((Map.Entry) _data).getValue()); + i++; + } else { + contents = (Object[]) _data; + j = 0; + for (i++; _sigb[i] != ArgumentType.DICT_ENTRY2; i++) { + i = appendOne(_sigb, i, contents[j++]); + } + } + break; + case ArgumentType.VARIANT: + // Variants are marshalled as a signature + // followed by the value. + if (_data instanceof Variant) { + Variant var = (Variant) _data; + appendOne(new byte[] { + ArgumentType.SIGNATURE + }, 0, var.getSig()); + appendOne(var.getSig().getBytes(), 0, var.getValue()); + } else if (_data instanceof Object[]) { + contents = (Object[]) _data; + appendOne(new byte[] { + ArgumentType.SIGNATURE + }, 0, contents[0]); + appendOne(((String) contents[0]).getBytes(), 0, contents[1]); + } else { + String sig = Marshalling.getDBusType(_data.getClass())[0]; + appendOne(new byte[] { + ArgumentType.SIGNATURE + }, 0, sig); + appendOne(sig.getBytes(), 0, _data); + } + break; + } + return i; + } catch (ClassCastException _ex) { + logger.debug("Trying to marshall to unconvertible type.", _ex); + throw new MarshallingException( + MessageFormat.format("Trying to marshall to unconvertible type (from {0} to {1}).", + _data.getClass().getName(), (char) _sigb[_sigofs])); + } + } + + /** + * Pad the message to the proper alignment for the given type. + * + * @param _type type + */ + public void pad(byte _type) { + logger.trace("padding for {}", (char) _type); + int a = getAlignment(_type); + logger.trace("{} {} {} {}", preallocated, paofs, bytecounter, a); + int b = (int) ((bytecounter - preallocated) % a); + if (0 == b) { + return; + } + a = a - b; + if (preallocated > 0) { + paofs += a; + preallocated -= a; + } else { + appendBytes(padding[a]); + } + logger.trace("{} {} {} {}", preallocated, paofs, bytecounter, a); + + } + + /** + * Return the alignment for a given type. + * + * @param _type type + * @return int + */ + public static int getAlignment(byte _type) { + switch (_type) { + case 2: + case ArgumentType.INT16: + case ArgumentType.UINT16: + return 2; + case 4: + case ArgumentType.BOOLEAN: + case ArgumentType.FLOAT: + case ArgumentType.INT32: + case ArgumentType.UINT32: + case ArgumentType.FILEDESCRIPTOR: + case ArgumentType.STRING: + case ArgumentType.OBJECT_PATH: + case ArgumentType.ARRAY: + return 4; + case 8: + case ArgumentType.INT64: + case ArgumentType.UINT64: + case ArgumentType.DOUBLE: + case ArgumentType.STRUCT: + case ArgumentType.DICT_ENTRY: + case ArgumentType.STRUCT1: + case ArgumentType.DICT_ENTRY1: + case ArgumentType.STRUCT2: + case ArgumentType.DICT_ENTRY2: + return 8; + case 1: + case ArgumentType.BYTE: + case ArgumentType.SIGNATURE: + case ArgumentType.VARIANT: + default: + return 1; + } + } + + /** + * Append a series of values to the message. + * + * @param _sig The signature(s) of the value(s). + * @param _data The value(s). + * + * @throws DBusException on error + */ + public void append(String _sig, Object... _data) throws DBusException { + LoggingHelper.logIf(logger.isDebugEnabled(), () -> logger.debug("Appending sig: {} data: {}", _sig, LoggingHelper.arraysVeryDeepString(_data))); + + byte[] sigb = _sig.getBytes(); + int j = 0; + for (int i = 0; i < sigb.length; i++) { + logger.trace("Appending item: {} {} {}", i, (char) sigb[i], j); + i = appendOne(sigb, i, _data[j++]); + } + } + + /** + * Align a counter to the given type. + * + * @param _current The current counter. + * @param _type The type to align to. + * @return The new, aligned, counter. + */ + public int align(int _current, byte _type) { + logger.trace("aligning to {}", (char) _type); + int a = getAlignment(_type); + if (0 == _current % a) { + return _current; + } + return _current + (a - (_current % a)); + } + + /** + * Extracts the header information from the given byte array. + * + * @param _headers D-Bus serialized data of type a(yv) + * + * @return Object array containing header data + * + * @throws DBusException when parsing fails + */ + Object[] extractHeader(byte[] _headers) throws DBusException { + int[] offsets = new int[] { + 0, 0 + }; + + return extract("a(yv)", _headers, offsets, this::readHeaderVariants); + } + + /** + * Special lightweight version to read the variant objects in DBus message header. + * This method will not create {@link Variant} objects it directly extracts the Variant data content. + * + * @param _signatureBuf DBus signature string as byte array + * @param _dataBuf buffer with header data + * @param _offsets current offsets + * @param _contained boolean to indicate if nested lists should be resolved (false usually) + * + * @return Object + * + * @throws DBusException when parsing fails + */ + private Object readHeaderVariants(byte[] _signatureBuf, byte[] _dataBuf, int[] _offsets, boolean _contained) throws DBusException { + // correct the offsets before extracting values + _offsets[OFFSET_DATA] = align(_offsets[OFFSET_DATA], _signatureBuf[_offsets[OFFSET_SIG]]); + + Object result = null; + if (_signatureBuf[_offsets[OFFSET_SIG]] == ArgumentType.ARRAY) { + result = extractArray(_signatureBuf, _dataBuf, _offsets, _contained, this::readHeaderVariants); + } else if (_signatureBuf[_offsets[OFFSET_SIG]] == ArgumentType.BYTE) { + result = extractByte(_dataBuf, _offsets); + } else if (_signatureBuf[_offsets[OFFSET_SIG]] == ArgumentType.VARIANT) { + result = extractVariant(_dataBuf, _offsets, (sig, obj) -> obj); + } else if (_signatureBuf[_offsets[OFFSET_SIG]] == ArgumentType.STRUCT1) { + result = extractStruct(_signatureBuf, _dataBuf, _offsets, this::readHeaderVariants); + } else { + throw new MessageFormatException("Unsupported data type in header: " + _signatureBuf[_offsets[OFFSET_SIG]]); + } + + logger.trace("Extracted header signature type '{}' to: '{}'", (char) _signatureBuf[_offsets[OFFSET_SIG]], result); + + return result; + } + + /** + * Demarshall one value from a buffer. + * + * @param _signatureBuf A buffer of the D-Bus signature. + * @param _dataBuf The buffer to demarshall from. + * @param _offsets An array of two ints, which holds the position of the current signature offset and the current + * offset of the data buffer. + * @param _contained converts nested arrays to Lists + * @return The demarshalled value. + */ + private Object extractOne(byte[] _signatureBuf, byte[] _dataBuf, int[] _offsets, boolean _contained) + throws DBusException { + + logger.trace("Extracting type: {} from offset {}", (char) _signatureBuf[_offsets[OFFSET_SIG]], + _offsets[OFFSET_DATA]); + + Object rv = null; + _offsets[OFFSET_DATA] = align(_offsets[OFFSET_DATA], _signatureBuf[_offsets[OFFSET_SIG]]); + switch (_signatureBuf[_offsets[OFFSET_SIG]]) { + case ArgumentType.BYTE: + rv = extractByte(_dataBuf, _offsets); + break; + case ArgumentType.UINT32: + rv = new UInt32(demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4)); + _offsets[OFFSET_DATA] += 4; + break; + case ArgumentType.INT32: + rv = (int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4); + _offsets[OFFSET_DATA] += 4; + break; + case ArgumentType.INT16: + rv = (short) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 2); + _offsets[OFFSET_DATA] += 2; + break; + case ArgumentType.UINT16: + rv = new UInt16((int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 2)); + _offsets[OFFSET_DATA] += 2; + break; + case ArgumentType.INT64: + rv = demarshallint(_dataBuf, _offsets[OFFSET_DATA], 8); + _offsets[OFFSET_DATA] += 8; + break; + case ArgumentType.UINT64: + long top; + long bottom; + if (big) { + top = demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4); + _offsets[OFFSET_DATA] += 4; + bottom = demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4); + } else { + bottom = demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4); + _offsets[OFFSET_DATA] += 4; + top = demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4); + } + rv = new UInt64(top, bottom); + _offsets[OFFSET_DATA] += 4; + break; + case ArgumentType.DOUBLE: + long l = demarshallint(_dataBuf, _offsets[OFFSET_DATA], 8); + _offsets[OFFSET_DATA] += 8; + rv = Double.longBitsToDouble(l); + break; + case ArgumentType.FLOAT: + int rf = (int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4); + _offsets[OFFSET_DATA] += 4; + rv = Float.intBitsToFloat(rf); + break; + case ArgumentType.BOOLEAN: + rf = (int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4); + _offsets[OFFSET_DATA] += 4; + rv = (1 == rf) ? Boolean.TRUE : Boolean.FALSE; + break; + case ArgumentType.ARRAY: + rv = extractArray(_signatureBuf, _dataBuf, _offsets, _contained, this::extractOne); + break; + case ArgumentType.STRUCT1: + rv = extractStruct(_signatureBuf, _dataBuf, _offsets, this::extractOne); + break; + case ArgumentType.DICT_ENTRY1: + Object[] decontents = new Object[2]; + + LoggingHelper.logIf(logger.isTraceEnabled(), () -> { + logger.trace("Extracting Dict Entry ({}) from: {}", + Hexdump.toAscii(_signatureBuf, _offsets[OFFSET_SIG], _signatureBuf.length - _offsets[OFFSET_SIG]), + Hexdump.toHex(_dataBuf, _offsets[OFFSET_DATA], _dataBuf.length - _offsets[OFFSET_DATA], true)); + }); + + _offsets[OFFSET_SIG]++; + decontents[0] = extractOne(_signatureBuf, _dataBuf, _offsets, true); + _offsets[OFFSET_SIG]++; + decontents[1] = extractOne(_signatureBuf, _dataBuf, _offsets, true); + _offsets[OFFSET_SIG]++; + rv = decontents; + break; + case ArgumentType.VARIANT: + rv = extractVariant(_dataBuf, _offsets, (sig, obj) -> new Variant<>(obj, sig)); + break; + case ArgumentType.FILEDESCRIPTOR: + rv = filedescriptors.get((int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4)); + _offsets[OFFSET_DATA] += 4; + break; + case ArgumentType.STRING: + int length = (int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4); + _offsets[OFFSET_DATA] += 4; + try { + rv = new String(_dataBuf, _offsets[OFFSET_DATA], length, "UTF-8"); + } catch (UnsupportedEncodingException _ex) { + logger.debug("System does not support UTF-8 encoding", _ex); + throw new DBusException("System does not support UTF-8 encoding"); + } + _offsets[OFFSET_DATA] += length + 1; + break; + case ArgumentType.OBJECT_PATH: + length = (int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4); + _offsets[OFFSET_DATA] += 4; + rv = new ObjectPath(getSource(), new String(_dataBuf, _offsets[OFFSET_DATA], length)); + _offsets[OFFSET_DATA] += length + 1; + break; + case ArgumentType.SIGNATURE: + length = _dataBuf[_offsets[OFFSET_DATA]++] & 0xFF; + rv = new String(_dataBuf, _offsets[OFFSET_DATA], length); + _offsets[OFFSET_DATA] += length + 1; + break; + default: + throw new UnknownTypeCodeException(_signatureBuf[_offsets[OFFSET_SIG]]); + } + + if (logger.isTraceEnabled()) { + if (rv instanceof Object[]) { + logger.trace("Extracted: {} (now at {})", Arrays.deepToString((Object[]) rv), _offsets[OFFSET_DATA]); + } else { + logger.trace("Extracted: {} (now at {})", rv, _offsets[OFFSET_DATA]); + } + } + + return rv; + } + + /** + * Extracts a byte from the data received on bus. + * + * @param _dataBuf buffer holding the byte + * @param _offsets offset position in buffer (will be updated) + * + * @return Object + */ + private Object extractByte(byte[] _dataBuf, int[] _offsets) { + Object rv; + rv = _dataBuf[_offsets[OFFSET_DATA]++]; + return rv; + } + + /** + * Extracts a struct from the data received on bus. + * + * @param _signatureBuf signature (as byte array) defining the struct content + * @param _dataBuf buffer containing the struct + * @param _offsets offset position in buffer (will be updated) + * @param _extractMethod method to be called for every entry contained of the struct + * + * @return Object + * + * @throws DBusException when parsing fails + */ + private Object extractStruct(byte[] _signatureBuf, byte[] _dataBuf, int[] _offsets, ExtractMethod _extractMethod) throws DBusException { + Object rv; + List contents = new ArrayList<>(); + while (_signatureBuf[++_offsets[OFFSET_SIG]] != ArgumentType.STRUCT2) { + contents.add(_extractMethod.extractOne(_signatureBuf, _dataBuf, _offsets, true)); + } + rv = contents.toArray(); + return rv; + } + + /** + * Extracts an array from the data received on bus. + * + * @param _signatureBuf signature string (as byte array) of the content of the array + * @param _dataBuf buffer containing the array to read + * @param _offsets current offsets in the buffer (will be updated) + * @param _contained resolve nested lists + * @param _extractMethod method to be called for every entry contained in the array + * + * @return Object + * + * @throws MarshallingException when Array is too large + * @throws DBusException when parsing fails + */ + private Object extractArray(byte[] _signatureBuf, byte[] _dataBuf, int[] _offsets, boolean _contained, ExtractMethod _extractMethod) + throws MarshallingException, DBusException { + Object rv; + long size = demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4); + + logger.trace("Reading array of size: {}", size); + _offsets[OFFSET_DATA] += 4; + byte algn = (byte) getAlignment(_signatureBuf[++_offsets[OFFSET_SIG]]); + _offsets[OFFSET_DATA] = align(_offsets[OFFSET_DATA], _signatureBuf[_offsets[OFFSET_SIG]]); + int length = (int) (size / algn); + if (length > AbstractConnection.MAX_ARRAY_LENGTH) { + throw new MarshallingException("Arrays must not exceed " + AbstractConnection.MAX_ARRAY_LENGTH); + } + + rv = optimizePrimitives(_signatureBuf, _dataBuf, _offsets, size, algn, length, _extractMethod); + + if (_contained && !(rv instanceof List) && !(rv instanceof Map)) { + rv = ArrayFrob.listify(rv); + } + return rv; + } + + /** + * Extracts a {@link Variant} from the data received on bus. + * + * @param _dataBuf buffer containing the variant + * @param _offsets current offsets in the buffer (will be updated) + * @param _variantFactory method to create new {@link Variant} objects (or other object types) + * + * @return Object / Variant + * + * @throws DBusException when parsing fails + */ + private Object extractVariant(byte[] _dataBuf, int[] _offsets, BiFunction _variantFactory) throws DBusException { + Object rv; + int[] newofs = new int[] { + 0, _offsets[OFFSET_DATA] + }; + String sig = (String) extract(ArgumentType.SIGNATURE_STRING, _dataBuf, newofs)[0]; + newofs[OFFSET_SIG] = 0; + rv = _variantFactory.apply(sig, extract(sig, _dataBuf, newofs)[0]); + _offsets[OFFSET_DATA] = newofs[OFFSET_DATA]; + return rv; + } + + /** + * Will create primitive arrays when an array is read. + *
+ * In case the array is not compatible with primitives (e.g. object types are used or array contains Struct/Maps etc) + * an array of the appropriate type will be created. + * + * @param _signatureBuf signature string (as byte array) containing the type of array + * @param _dataBuf buffer containing the array + * @param _offsets current offset in buffer (will be updated) + * @param _size size of a byte + * @param _algn data offset padding width when reading primitives (except byte) + * @param _length length of the array + * @param _extractMethod method to be called for every entry contained in the array if not primitive array + * + * @return Object array + * + * @throws DBusException when parsing fails + */ + private Object optimizePrimitives(byte[] _signatureBuf, byte[] _dataBuf, int[] _offsets, long _size, byte _algn, + int _length, ExtractMethod _extractMethod) + throws DBusException { + Object rv; + switch (_signatureBuf[_offsets[OFFSET_SIG]]) { + case ArgumentType.BYTE: + rv = new byte[_length]; + System.arraycopy(_dataBuf, _offsets[OFFSET_DATA], rv, 0, _length); + _offsets[OFFSET_DATA] += _size; + break; + case ArgumentType.INT16: + rv = new short[_length]; + for (int j = 0; j < _length; j++, _offsets[OFFSET_DATA] += _algn) { + ((short[]) rv)[j] = (short) demarshallint(_dataBuf, _offsets[OFFSET_DATA], _algn); + } + break; + case ArgumentType.INT32: + rv = new int[_length]; + for (int j = 0; j < _length; j++, _offsets[OFFSET_DATA] += _algn) { + ((int[]) rv)[j] = (int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], _algn); + } + break; + case ArgumentType.INT64: + rv = new long[_length]; + for (int j = 0; j < _length; j++, _offsets[OFFSET_DATA] += _algn) { + ((long[]) rv)[j] = demarshallint(_dataBuf, _offsets[OFFSET_DATA], _algn); + } + break; + case ArgumentType.BOOLEAN: + rv = new boolean[_length]; + for (int j = 0; j < _length; j++, _offsets[OFFSET_DATA] += _algn) { + ((boolean[]) rv)[j] = 1 == demarshallint(_dataBuf, _offsets[OFFSET_DATA], _algn); + } + break; + case ArgumentType.FLOAT: + rv = new float[_length]; + for (int j = 0; j < _length; j++, _offsets[OFFSET_DATA] += _algn) { + ((float[]) rv)[j] = Float.intBitsToFloat((int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], _algn)); + } + break; + case ArgumentType.DOUBLE: + rv = new double[_length]; + for (int j = 0; j < _length; j++, _offsets[OFFSET_DATA] += _algn) { + ((double[]) rv)[j] = Double.longBitsToDouble(demarshallint(_dataBuf, _offsets[OFFSET_DATA], _algn)); + } + break; + case ArgumentType.DICT_ENTRY1: + int ofssave = prepareCollection(_signatureBuf, _offsets, _size); + long end = _offsets[OFFSET_DATA] + _size; + List entries = new ArrayList<>(); + while (_offsets[OFFSET_DATA] < end) { + _offsets[OFFSET_SIG] = ofssave; + entries.add((Object[]) _extractMethod.extractOne(_signatureBuf, _dataBuf, _offsets, true)); + } + rv = new DBusMap<>(entries.toArray(new Object[0][])); + break; + default: + ofssave = prepareCollection(_signatureBuf, _offsets, _size); + end = _offsets[OFFSET_DATA] + _size; + List contents = new ArrayList<>(); + while (_offsets[OFFSET_DATA] < end) { + _offsets[OFFSET_SIG] = ofssave; + contents.add(_extractMethod.extractOne(_signatureBuf, _dataBuf, _offsets, true)); + } + rv = contents; + } + return rv; + } + + private int prepareCollection(byte[] _signatureBuf, int[] _offsets, long _size) throws DBusException { + if (0 == _size) { + // advance the type parser even on 0-size arrays. + List temp = new ArrayList<>(); + byte[] temp2 = new byte[_signatureBuf.length - _offsets[OFFSET_SIG]]; + System.arraycopy(_signatureBuf, _offsets[OFFSET_SIG], temp2, 0, temp2.length); + String temp3 = new String(temp2); + // ofs[OFFSET_SIG] gets incremented anyway. Leave one character on the stack + int temp4 = Marshalling.getJavaType(temp3, temp, 1) - 1; + _offsets[OFFSET_SIG] += temp4; + logger.trace("Aligned type: {} {} {}", temp3, temp4, _offsets[OFFSET_SIG]); + } + int ofssave = _offsets[OFFSET_SIG]; + return ofssave; + } + + /** + * Demarshall values from a buffer. + * + * @param _signature The D-Bus signature(s) of the value(s). + * @param _dataBuf The buffer to demarshall from. + * @param _offsets The offset into the data buffer to start. + * @return The demarshalled value(s). + * + * @throws DBusException on error + */ + public Object[] extract(String _signature, byte[] _dataBuf, int _offsets) throws DBusException { + return extract(_signature, _dataBuf, new int[] { + 0, _offsets + }); + } + + /** + * Demarshall values from a buffer. + * + * @param _signature The D-Bus signature(s) of the value(s). + * @param _dataBuf The buffer to demarshall from. + * @param _offsets An array of two ints, which holds the position of the current signature offset and the current + * offset of the data buffer. These values will be updated to the start of the next value after + * demarshalling. + * @return The demarshalled value(s). + * + * @throws DBusException on error + */ + public Object[] extract(String _signature, byte[] _dataBuf, int[] _offsets) throws DBusException { + return extract(_signature, _dataBuf, _offsets, this::extractOne); + } + + Object[] extract(String _signature, byte[] _dataBuf, int[] _offsets, ExtractMethod _method) throws DBusException { + logger.trace("extract({},#{}, {{},{}}", _signature, _dataBuf.length, _offsets[OFFSET_SIG], + _offsets[OFFSET_DATA]); + List rv = new ArrayList<>(); + byte[] sigb = _signature.getBytes(); + for (int[] i = _offsets; i[OFFSET_SIG] < sigb.length; i[OFFSET_SIG]++) { + rv.add(_method.extractOne(sigb, _dataBuf, i, false)); + } + return rv.toArray(); + } + + /** + * Returns the Bus ID that sent the message. + * + * @return string + */ + public String getSource() { + return (String) getHeader(HeaderField.SENDER); + } + + /** + * Returns the destination of the message. + * + * @return string + */ + public String getDestination() { + return (String) getHeader(HeaderField.DESTINATION); + } + + /** + * Returns the interface of the message. + * + * @return string + */ + public String getInterface() { + return (String) getHeader(HeaderField.INTERFACE); + } + + /** + * Returns the object path of the message. + * + * @return string + */ + public String getPath() { + Object o = getHeader(HeaderField.PATH); + if (null == o) { + return null; + } + return o.toString(); + } + + /** + * Returns the member name or error name this message represents. + * + * @return string + */ + public String getName() { + if (this instanceof org.freedesktop.dbus.errors.Error) { + return (String) getHeader(HeaderField.ERROR_NAME); + } else { + return (String) getHeader(HeaderField.MEMBER); + } + } + + /** + * Returns the dbus signature of the parameters. + * + * @return string + */ + public String getSig() { + return (String) getHeader(HeaderField.SIGNATURE); + } + + /** + * Returns the message flags. + * + * @return int + */ + public int getFlags() { + return flags; + } + + /** + * Returns the message serial ID (unique for this connection) + * + * @return the message serial. + */ + public long getSerial() { + return serial; + } + + /** + * If this is a reply to a message, this returns its serial. + * + * @return The reply serial, or 0 if it is not a reply. + */ + public long getReplySerial() { + Number l = (Number) getHeader(HeaderField.REPLY_SERIAL); + if (null == l) { + return 0; + } + return l.longValue(); + } + + /** + * Parses and returns the parameters to this message as an Object array. + * + * @return object array + * @throws DBusException on failure + */ + public Object[] getParameters() throws DBusException { + if (null == args && null != body) { + String sig = getSig(); + if (null != sig && 0 != body.length) { + args = extract(sig, body, 0); + } else { + args = new Object[0]; + } + } + return args; + } + + public void setArgs(Object[] _args) { + this.args = _args; + } + + /** + * Warning, do not use this method unless you really know what you are doing. + * + * @param _source string + * @throws DBusException on error + */ + public void setSource(String _source) throws DBusException { + if (null != body) { + logger.trace("Setting source"); + + LoggingHelper.logIf(logger.isTraceEnabled(), () -> logger.trace("WireData before: {}", dumpWireData())); + + wiredata = new byte[BUFFERINCREMENT][]; + bufferuse = 0; + bytecounter = 0; + preallocate(12); + append("yyyyuu", big ? Endian.BIG : Endian.LITTLE, type, flags, protover, bodylen, getSerial()); + headers[HeaderField.SENDER] = _source; + + LoggingHelper.logIf(logger.isTraceEnabled(), () -> logger.trace("WireData first append: {}", dumpWireData())); + + List newHeader = new ArrayList<>(headers.length); + + for (int hIdx = 0; hIdx < headers.length; hIdx++) { + Object object = headers[hIdx]; + + if (object == null) { + continue; + } + if (hIdx == HeaderField.SIGNATURE) { + newHeader.add(createHeaderArgs(HeaderField.SIGNATURE, ArgumentType.SIGNATURE_STRING, object)); + } else { + newHeader.add(new Object[] {hIdx, object}); + } + } + + append("a(yv)", newHeader); + + LoggingHelper.logIf(logger.isTraceEnabled(), () -> { + logger.trace("New header: {}", LoggingHelper.arraysVeryDeepString(newHeader.toArray())); + logger.trace("WireData after: {}", dumpWireData()); + }); + + pad((byte) 8); + appendBytes(body); + } + } + + /** + * Dumps the current content of {@link #wiredata} to String. + * + * @return String, maybe empty + * @since v4.2.2 - 2023-01-20 + */ + String dumpWireData() { + StringBuilder sb = new StringBuilder(System.lineSeparator()); + for (int i = 0; i < wiredata.length; i++) { + byte[] arr = wiredata[i]; + if (arr != null) { + String prefix = "Wiredata[" + i + "]"; + String format = Hexdump.format(arr, 80); + String[] split = format.split("\n"); + sb.append(prefix).append(": ").append(split[0]).append(System.lineSeparator()); + if (split.length > 1) { + sb.append(Arrays.stream(split) + .skip(1) + .map(s -> String.format("%s: %80s", prefix, s)) + .collect(Collectors.joining(System.lineSeparator()))); + sb.append(System.lineSeparator()); + } + } + } + return sb.toString(); + } + + /** + * Type of this message. + * @return byte + */ + public byte getType() { + return type; + } + + public byte getEndianess() { + return big ? Endian.BIG : Endian.LITTLE; + } + + /** + * Creates a message header. + * Will automatically add the values to the current instances header map. + * + * @param _header header type (one of {@link HeaderField}) + * @param _argType argument type (one of {@link ArgumentType}) + * @param _value value + * + * @return Object array + */ + protected Object[] createHeaderArgs(byte _header, String _argType, Object _value) { + getHeader()[_header] = _value; + return new Object[] { + _header, new Object[] { + _argType, _value + } + }; + } + + /** + * Adds message padding and marshalling. + * + * @param _hargs + * @param _serial + * @param _sig + * @param _args + * @throws DBusException + */ + protected void padAndMarshall(List _hargs, long _serial, String _sig, Object... _args) throws DBusException { + byte[] blen = new byte[4]; + appendBytes(blen); + append("ua(yv)", _serial, _hargs.toArray()); + pad((byte) 8); + + long c = getByteCounter(); + if (null != _sig) { + append(_sig, _args); + } + logger.trace("Appended body, type: {} start: {} end: {} size: {}", _sig, c, getByteCounter(), getByteCounter() - c); + marshallint(getByteCounter() - c, blen, 0, 4); + logger.trace("marshalled size ({}): {}", blen, Hexdump.format(blen)); + } + + /** + * Returns the name of the given header field. + * + * @param _field field + * @return string + */ + public static String getHeaderFieldName(byte _field) { + switch (_field) { + case HeaderField.PATH: + return "Path"; + case HeaderField.INTERFACE: + return "Interface"; + case HeaderField.MEMBER: + return "Member"; + case HeaderField.ERROR_NAME: + return "Error Name"; + case HeaderField.REPLY_SERIAL: + return "Reply Serial"; + case HeaderField.DESTINATION: + return "Destination"; + case HeaderField.SENDER: + return "Sender"; + case HeaderField.SIGNATURE: + return "Signature"; + case HeaderField.UNIX_FDS: + return "Unix FD"; + default: + return "Invalid"; + } + } + + /** + * Interface defining a method to extract a specific data type. + * For internal usage only. + * + * @since 4.2.0 - 2022-08-19 + */ + @FunctionalInterface + interface ExtractMethod { + Object extractOne(byte[] _signatureBuf, byte[] _dataBuf, int[] _offsets, boolean _contained) + throws DBusException; + } + + /** Defines constants representing the flags which can be set on a message. */ + public interface Flags { + byte NO_REPLY_EXPECTED = 0x01; + byte NO_AUTO_START = 0x02; + byte ASYNC = 0x40; + } + + /** Defines constants for each message type. */ + public interface MessageType { + byte METHOD_CALL = 1; + byte METHOD_RETURN = 2; + byte ERROR = 3; + byte SIGNAL = 4; + } + + /** Defines constants for each valid header field type. */ + public interface HeaderField { + int MAX_FIELDS = 10; + + byte PATH = 1; + byte INTERFACE = 2; + byte MEMBER = 3; + byte ERROR_NAME = 4; + byte REPLY_SERIAL = 5; + byte DESTINATION = 6; + byte SENDER = 7; + byte SIGNATURE = 8; + byte UNIX_FDS = 9; + } + + /** + * Defines constants for each argument type. There are two constants for each argument type, as a byte or as a + * String (the _STRING version) + */ + public interface ArgumentType { + String BYTE_STRING = "y"; + String BOOLEAN_STRING = "b"; + String INT16_STRING = "n"; + String UINT16_STRING = "q"; + String INT32_STRING = "i"; + String UINT32_STRING = "u"; + String INT64_STRING = "x"; + String UINT64_STRING = "t"; + String DOUBLE_STRING = "d"; + String FLOAT_STRING = "f"; + String STRING_STRING = "s"; + String OBJECT_PATH_STRING = "o"; + String SIGNATURE_STRING = "g"; + String FILEDESCRIPTOR_STRING = "h"; + String ARRAY_STRING = "a"; + String VARIANT_STRING = "v"; + String STRUCT_STRING = "r"; + String STRUCT1_STRING = "("; + String STRUCT2_STRING = ")"; + String DICT_ENTRY_STRING = "e"; + String DICT_ENTRY1_STRING = "{"; + String DICT_ENTRY2_STRING = "}"; + + byte BYTE = 'y'; + byte BOOLEAN = 'b'; + byte INT16 = 'n'; + byte UINT16 = 'q'; + byte INT32 = 'i'; + byte UINT32 = 'u'; + byte INT64 = 'x'; + byte UINT64 = 't'; + byte DOUBLE = 'd'; + byte FLOAT = 'f'; + byte STRING = 's'; + byte OBJECT_PATH = 'o'; + byte SIGNATURE = 'g'; + byte FILEDESCRIPTOR = 'h'; + byte ARRAY = 'a'; + byte VARIANT = 'v'; + byte STRUCT = 'r'; + byte STRUCT1 = '('; + byte STRUCT2 = ')'; + byte DICT_ENTRY = 'e'; + byte DICT_ENTRY1 = '{'; + byte DICT_ENTRY2 = '}'; + } + + /** Defines constants representing the endianness of the message. */ + public interface Endian { + byte BIG = 'B'; + byte LITTLE = 'l'; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/messages/MessageFactory.java b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/MessageFactory.java new file mode 100644 index 0000000000..17b5ad94fd --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/MessageFactory.java @@ -0,0 +1,48 @@ +package org.freedesktop.dbus.messages; + +import org.freedesktop.dbus.FileDescriptor; +import org.freedesktop.dbus.errors.Error; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.MessageTypeException; +import org.freedesktop.dbus.utils.Hexdump; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +public final class MessageFactory { + + private static final Logger LOGGER = LoggerFactory.getLogger(MessageFactory.class); + + private MessageFactory() {} + + public static Message createMessage(byte _type, byte[] _buf, byte[] _header, byte[] _body, List _filedescriptors) throws DBusException, MessageTypeException { + Message m; + switch (_type) { + case Message.MessageType.METHOD_CALL: + m = new MethodCall(); + break; + case Message.MessageType.METHOD_RETURN: + m = new MethodReturn(); + break; + case Message.MessageType.SIGNAL: + m = new DBusSignal(); + break; + case Message.MessageType.ERROR: + m = new Error(); + break; + default: + throw new MessageTypeException(String.format("Message type %s unsupported", _type)); + } + + if (LOGGER.isTraceEnabled()) { + LOGGER.trace(Hexdump.format(_buf)); + LOGGER.trace(Hexdump.format(_header)); + LOGGER.trace(Hexdump.format(_body)); + } + + m.populate(_buf, _header, _body, _filedescriptors); + return m; + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/messages/MethodBase.java b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/MethodBase.java new file mode 100644 index 0000000000..299b77a257 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/MethodBase.java @@ -0,0 +1,38 @@ +package org.freedesktop.dbus.messages; + +import org.freedesktop.dbus.FileDescriptor; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.types.UInt32; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public abstract class MethodBase extends Message { + + MethodBase() { + } + + public MethodBase(byte _endianness, byte _methodCall, byte _flags) throws DBusException { + super(_endianness, _methodCall, _flags); + } + + /** + * Appends filedescriptors (if any). + * + * @param _hargs + * @param _sig + * @param _args + * @throws DBusException + */ + void appendFileDescriptors(List _hargs, String _sig, Object... _args) throws DBusException { + Objects.requireNonNull(_hargs); + + int totalFileDes = _args == null ? 0 : Arrays.stream(_args).filter(x -> x instanceof FileDescriptor).mapToInt(i -> 1).sum(); + + if (totalFileDes > 0) { + _hargs.add(createHeaderArgs(Message.HeaderField.UNIX_FDS, ArgumentType.UINT32_STRING, new UInt32(totalFileDes))); + } + + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/messages/MethodCall.java b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/MethodCall.java new file mode 100644 index 0000000000..bf18383698 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/MethodCall.java @@ -0,0 +1,110 @@ +package org.freedesktop.dbus.messages; + +import org.freedesktop.dbus.connections.impl.DBusConnection; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.MessageFormatException; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +public class MethodCall extends MethodBase { + private static long replyWaitTimeout = Duration.ofSeconds(20).toMillis(); + + // CHECKSTYLE:OFF + Message reply = null; + // CHECKSTYLE:ON + + MethodCall() { + } + + public MethodCall(String _dest, String _path, String _iface, String _member, byte _flags, String _sig, Object... _args) throws DBusException { + this(null, _dest, _path, _iface, _member, _flags, _sig, _args); + } + + public MethodCall(String _source, String _dest, String _path, String _iface, String _member, byte _flags, String _sig, Object... _args) throws DBusException { + super(DBusConnection.getEndianness(), Message.MessageType.METHOD_CALL, _flags); + + if (null == _member || null == _path) { + throw new MessageFormatException("Must specify destination, path and function name to MethodCalls."); + } + Object[] header = getHeader(); + header[Message.HeaderField.PATH] = _path; + header[Message.HeaderField.MEMBER] = _member; + + List hargs = new ArrayList<>(); + + hargs.add(createHeaderArgs(HeaderField.PATH, ArgumentType.OBJECT_PATH_STRING, _path)); + + if (null != _source) { + hargs.add(createHeaderArgs(HeaderField.SENDER, ArgumentType.STRING_STRING, _source)); + } + + if (null != _dest) { + hargs.add(createHeaderArgs(HeaderField.DESTINATION, ArgumentType.STRING_STRING, _dest)); + } + + if (null != _iface) { + hargs.add(createHeaderArgs(HeaderField.INTERFACE, ArgumentType.STRING_STRING, _iface)); + } + + hargs.add(createHeaderArgs(HeaderField.MEMBER, ArgumentType.STRING_STRING, _member)); + + if (null != _sig) { + logger.debug("Appending arguments with signature: {}", _sig); + hargs.add(createHeaderArgs(HeaderField.SIGNATURE, ArgumentType.SIGNATURE_STRING, _sig)); + setArgs(_args); + } + + appendFileDescriptors(hargs, _sig, _args); + padAndMarshall(hargs, getSerial(), _sig, _args); + } + + /** + * Set the default timeout for method calls. + * Default is 20s. + * @param _timeout New timeout in ms. + */ + public static void setDefaultTimeout(long _timeout) { + replyWaitTimeout = _timeout; + } + + public synchronized boolean hasReply() { + return null != reply; + } + + /** + * Block (if neccessary) for a reply. + * @return The reply to this MethodCall, or null if a timeout happens. + * @param _timeout The length of time to block before timing out (ms). + */ + public synchronized Message getReply(long _timeout) { + logger.trace("Blocking on {}", this); + if (null != reply) { + return reply; + } + try { + wait(_timeout); + return reply; + } catch (InterruptedException _exI) { + Thread.currentThread().interrupt(); // keep interrupted state + return reply; + } + } + + /** + * Block (if neccessary) for a reply. + * Default timeout is 20s, or can be configured with setDefaultTimeout() + * @return The reply to this MethodCall, or null if a timeout happens. + */ + public synchronized Message getReply() { + return getReply(replyWaitTimeout); + } + + public synchronized void setReply(Message _reply) { + logger.trace("Setting reply to {} to {}", this, _reply); + this.reply = _reply; + notifyAll(); + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/messages/MethodReturn.java b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/MethodReturn.java new file mode 100644 index 0000000000..f82ab51123 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/MethodReturn.java @@ -0,0 +1,59 @@ +package org.freedesktop.dbus.messages; + +import org.freedesktop.dbus.connections.impl.DBusConnection; +import org.freedesktop.dbus.exceptions.DBusException; + +import java.util.ArrayList; +import java.util.List; + +public class MethodReturn extends MethodBase { + + private MethodCall call; + + MethodReturn() { + } + + public MethodReturn(String _dest, long _replyserial, String _sig, Object... _args) throws DBusException { + this(null, _dest, _replyserial, _sig, _args); + } + + public MethodReturn(String _source, String _dest, long _replyserial, String _sig, Object... _args) throws DBusException { + super(DBusConnection.getEndianness(), Message.MessageType.METHOD_RETURN, (byte) 0); + + List hargs = new ArrayList<>(); + hargs.add(createHeaderArgs(HeaderField.REPLY_SERIAL, ArgumentType.UINT32_STRING, _replyserial)); + + if (null != _source) { + hargs.add(createHeaderArgs(HeaderField.SENDER, ArgumentType.STRING_STRING, _source)); + } + + if (null != _dest) { + hargs.add(createHeaderArgs(HeaderField.DESTINATION, ArgumentType.STRING_STRING, _dest)); + } + + if (null != _sig) { + hargs.add(createHeaderArgs(HeaderField.SIGNATURE, ArgumentType.SIGNATURE_STRING, _sig)); + setArgs(_args); + } + + appendFileDescriptors(hargs, _sig, _args); + padAndMarshall(hargs, getSerial(), _sig, _args); + } + + public MethodReturn(MethodCall _mc, String _sig, Object... _args) throws DBusException { + this(null, _mc, _sig, _args); + } + + public MethodReturn(String _source, MethodCall _mc, String _sig, Object... _args) throws DBusException { + this(_source, _mc.getSource(), _mc.getSerial(), _sig, _args); + this.call = _mc; + } + + public MethodCall getCall() { + return call; + } + + public void setCall(MethodCall _call) { + this.call = _call; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/messages/ObjectTree.java b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/ObjectTree.java new file mode 100644 index 0000000000..f46aa36d0d --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/messages/ObjectTree.java @@ -0,0 +1,188 @@ +package org.freedesktop.dbus.messages; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.regex.Pattern; + +/** + * Keeps track of the exported objects for introspection data */ +public class ObjectTree { + public static final Pattern SLASH_PATTERN = Pattern.compile("/"); + + private final Logger logger = LoggerFactory.getLogger(getClass()); + private TreeNode root; + + public ObjectTree() { + root = new TreeNode(""); + } + + private TreeNode recursiveFind(TreeNode _current, String _path) { + if ("/".equals(_path)) { + return _current; + } + String[] elements = _path.split("/", 2); + // this is us or a parent node + if (_path.startsWith(_current.name)) { + // this is us + if (_path.equals(_current.name)) { + return _current; + } else { // recurse down + if (_current.down == null) { + return null; + } else { + return recursiveFind(_current.down, elements[1]); + } + } + } else if (_current.right == null) { + return null; + } else if (0 > _current.right.name.compareTo(elements[0])) { + return null; + } else { // recurse right + return recursiveFind(_current.right, _path); + } + } + + private TreeNode recursiveAdd(TreeNode _current, String _path, ExportedObject _object, String _data) { + String[] elements = SLASH_PATTERN.split(_path, 2); + // this is us or a parent node + if (_path.startsWith(_current.name)) { + // this is us + if (1 == elements.length || "".equals(elements[1])) { + _current.object = _object; + _current.data = _data; + } else { // recurse down + if (_current.down == null) { + String[] el = elements[1].split("/", 2); + _current.down = new TreeNode(el[0]); + } + _current.down = recursiveAdd(_current.down, elements[1], _object, _data); + } + } else if (_current.right == null) { // need to create a new sub-tree on the end + _current.right = new TreeNode(elements[0]); + _current.right = recursiveAdd(_current.right, _path, _object, _data); + } else if (0 > _current.right.name.compareTo(elements[0])) { // need to insert here + TreeNode t = new TreeNode(elements[0]); + t.right = _current.right; + _current.right = t; + _current.right = recursiveAdd(_current.right, _path, _object, _data); + } else { // recurse right + _current.right = recursiveAdd(_current.right, _path, _object, _data); + } + return _current; + } + + public synchronized void add(String _path, ExportedObject _object, String _data) { + logger.debug("Adding {} to object tree", _path); + root = recursiveAdd(root, _path, _object, _data); + } + + private TreeNode recursiveRemove(TreeNode _current, String _path) { + String[] elements = _path.split("/", 2); + if (elements[0].equals(_current.name)) { + // this is us or a parent node + if (1 == elements.length || "".equals(elements[1])) { + // this is us + _current.object = null; + _current.data = null; + if (_current.down != null) { + // This node has a child node so it needs to be kept + return _current; + } + return _current.right; + } + + if (_current.down != null) { + // recurse down + _current.down = recursiveRemove(_current.down, elements[1]); + if (_current.down == null && _current.data == null) { + // This node has no children anymore and is not exported itself so it can be removed + return _current.right; + } + } + return _current; + } else if (_current.right == null) { + return _current; + } else if (0 > _current.right.name.compareTo(elements[0])) { + return _current; + } else { + // recurse right + _current.right = recursiveRemove(_current.right, _path); + return _current; + } + } + + public synchronized void remove(String _path) { + logger.debug("Removing {} from object tree", _path); + recursiveRemove(root, _path); + } + + // CHECKSTYLE:OFF + public String Introspect(String _path) { + // CHECKSTYLE:ON + TreeNode t = recursiveFind(root, _path); + if (null == t) { + return null; + } + StringBuilder sb = new StringBuilder(); + + sb.append("\n"); + + if (null != t.data) { + sb.append(t.data); + } + t = t.down; + while (null != t) { + sb.append("\n"); + t = t.right; + } + sb.append(""); + return sb.toString(); + } + + private String recursivePrint(TreeNode _current) { + String s = ""; + if (null != _current) { + s += _current.name; + if (null != _current.object) { + s += "*"; + } + if (null != _current.down) { + s += "/{" + recursivePrint(_current.down) + "}"; + } + if (null != _current.right) { + s += ", " + recursivePrint(_current.right); + } + } + return s; + } + + @Override + public String toString() { + return recursivePrint(root); + } + + static class TreeNode { + // CHECKSTYLE:OFF + String name; + ExportedObject object; + String data; + TreeNode right; + TreeNode down; + // CHECKSTYLE:ON + + TreeNode(String _name) { + name = _name; + } + + TreeNode(String _name, ExportedObject _object, String _data) { + name = _name; + object = _object; + data = _data; + } + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/IMessageReader.java b/federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/IMessageReader.java new file mode 100644 index 0000000000..b607c80b07 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/IMessageReader.java @@ -0,0 +1,17 @@ +package org.freedesktop.dbus.spi.message; + +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.messages.Message; + +import java.io.Closeable; +import java.io.IOException; + +/** + * Represents a way to read messages from the bus. + */ +public interface IMessageReader extends Closeable { + + boolean isClosed(); + + Message readMessage() throws IOException, DBusException; +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/IMessageWriter.java b/federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/IMessageWriter.java new file mode 100644 index 0000000000..75c275a0a1 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/IMessageWriter.java @@ -0,0 +1,22 @@ +package org.freedesktop.dbus.spi.message; + +import org.freedesktop.dbus.messages.Message; + +import java.io.Closeable; +import java.io.IOException; + +/** + * Interface that lets you write a message to the currently used transport. + */ +public interface IMessageWriter extends Closeable { + + /** + * Write a message out to the bus. + * + * @param _msg The message to write + * @throws IOException If an IO error occurs. + */ + void writeMessage(Message _msg) throws IOException; + + boolean isClosed(); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/ISocketProvider.java b/federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/ISocketProvider.java new file mode 100644 index 0000000000..0215bf211a --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/ISocketProvider.java @@ -0,0 +1,42 @@ +package org.freedesktop.dbus.spi.message; + +import org.freedesktop.dbus.connections.transports.AbstractTransport; + +import java.io.IOException; +import java.nio.channels.SocketChannel; + +public interface ISocketProvider { + /** + * Method to create a {@link IMessageReader} implementation. + * + * @param _socket socket to use for reading + * @return MessageReader + * @throws IOException if reader could not be created + */ + IMessageReader createReader(SocketChannel _socket) throws IOException; + + /** + * Method to create a {@link IMessageWriter} implementation. + * + * @param _socket socket to write to + * @return MessageWriter + * @throws IOException if write could not be created + */ + IMessageWriter createWriter(SocketChannel _socket) throws IOException; + + /** + * Called to indicate if the current {@link AbstractTransport} implementation + * supports file descriptor passing. + * + * @param _support true if file descriptor passing is supported, false otherwise + */ + void setFileDescriptorSupport(boolean _support); + + /** + * Indicate if reader/writer supports file descriptor passing. + * This is to show if the provider is able to handle file descriptors. + * + * @return true if file descriptors are supported by this provider, false otherwise + */ + boolean isFileDescriptorPassingSupported(); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/InputStreamMessageReader.java b/federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/InputStreamMessageReader.java new file mode 100644 index 0000000000..fd513fc4b5 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/InputStreamMessageReader.java @@ -0,0 +1,193 @@ +package org.freedesktop.dbus.spi.message; + +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.MessageProtocolVersionException; +import org.freedesktop.dbus.messages.Message; +import org.freedesktop.dbus.messages.MessageFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.EOFException; +import java.io.IOException; +import java.net.SocketTimeoutException; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.util.Arrays; +import java.util.Objects; + +public class InputStreamMessageReader implements IMessageReader { + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final int[] len; + private final byte[] buf; + private final byte[] tbuf; + private final SocketChannel inputChannel; + + private byte[] header; + private byte[] body; + + public InputStreamMessageReader(final SocketChannel _in) { + inputChannel = Objects.requireNonNull(_in, "SocketChannel required"); + len = new int[4]; + tbuf = new byte[4]; + buf = new byte[12]; + len[1] = 0; + len[0] = 0; + } + + @Override + public Message readMessage() throws IOException, DBusException { + /* Read the 12 byte fixed header, retrying as necessary */ + if (len[0] < 12) { + try { + final ByteBuffer wrapBuf = ByteBuffer.wrap(buf, len[0], 12 - len[0]); + final int rv = inputChannel.read(wrapBuf); + + if (rv < 0) { + throw new EOFException("(1) Underlying transport returned " + rv); + } + + len[0] += rv; + } catch (SocketTimeoutException _ex) { + return null; + } + } + + if (len[0] == 0) { + return null; + } + + if (len[0] < 12) { + logger.trace("Only got {} of 12 bytes of header", len[0]); + return null; + } + + /* Ensure protocol version. */ + final byte protoVer = buf[3]; + + if (protoVer > Message.PROTOCOL) { + throw new MessageProtocolVersionException(String.format("Protocol version %s is unsupported", protoVer)); + } + + if (len[1] < 4) { + try { + final int rv = inputChannel.read(ByteBuffer.wrap(tbuf, len[1], 4 - len[1])); + + if (rv < 0) { + throw new EOFException("(2) Underlying transport returned " + rv); + } + + len[1] += rv; + } catch (SocketTimeoutException _ex) { + return null; + } + } + + if (len[1] < 4) { + logger.trace("Only got {} of 4 bytes of header", len[1]); + return null; + } + + final byte endian = buf[0]; + + /* Parse the variable header length */ + int headerlen; + + if (header == null) { + headerlen = (int) Message.demarshallint(tbuf, 0, endian, 4); + + /* n % 2^i = n & (2^i - 1) */ + final int modlen = headerlen & 7; + + if (modlen != 0) { + headerlen += 8 - modlen; + } + } else { + headerlen = header.length - 8; + } + + /* Read the variable header */ + if (header == null) { + header = new byte[headerlen + 8]; + System.arraycopy(tbuf, 0, header, 0, 4); + len[2] = 0; + } + + if (len[2] < headerlen) { + try { + final int rv = inputChannel.read(ByteBuffer.wrap(header, 8 + len[2], headerlen - len[2])); + + if (rv < 0) { + throw new EOFException("(3) Underlying transport returned " + rv); + } + + len[2] += rv; + } catch (SocketTimeoutException _ex) { + return null; + } + } + + if (len[2] < headerlen) { + logger.trace("Only got {} of {} bytes of header", len[2], headerlen); + return null; + } + + final byte type = buf[1]; + + /* Read the body */ + if (body == null) { + body = new byte[(int) Message.demarshallint(buf, 4, endian, 4)]; + len[3] = 0; + } + + if (len[3] < body.length) { + try { + final int rv = inputChannel.read(ByteBuffer.wrap(body, len[3], body.length - len[3])); + + if (rv < 0) { + throw new EOFException("(4) Underlying transport returned " + rv); + } + + len[3] += rv; + } catch (SocketTimeoutException _ex) { + return null; + } + } + + if (len[3] < body.length) { + logger.trace("Only got {} of {} bytes of body", len[3], body.length); + return null; + } + + try { + final Message m = MessageFactory.createMessage(type, buf, header, body, null); + logger.debug("=> {}", m); + + return m; + } catch (DBusException | RuntimeException _ex) { + logger.warn("Exception while creating message.", _ex); + + throw _ex; + } finally { + Arrays.fill(tbuf, (byte) 0x00); + len[1] = 0; + body = null; + header = null; + Arrays.fill(buf, (byte) 0x00); + len[0] = 0; + } + } + + @Override + public void close() throws IOException { + if (inputChannel.isOpen()) { + logger.trace("Closing Message Reader"); + inputChannel.close(); + } + } + + @Override + public boolean isClosed() { + return !inputChannel.isOpen(); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/OutputStreamMessageWriter.java b/federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/OutputStreamMessageWriter.java new file mode 100644 index 0000000000..2c9eb133de --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/spi/message/OutputStreamMessageWriter.java @@ -0,0 +1,59 @@ +package org.freedesktop.dbus.spi.message; + +import org.freedesktop.dbus.messages.Message; +import org.freedesktop.dbus.utils.Hexdump; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; + +public class OutputStreamMessageWriter implements IMessageWriter { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private SocketChannel outputChannel; + + public OutputStreamMessageWriter(SocketChannel _out) { + this.outputChannel = _out; + } + + @Override + public void writeMessage(Message _msg) throws IOException { + logger.debug("<= {}", _msg); + if (null == _msg) { + return; + } + if (null == _msg.getWireData()) { + logger.warn("Message {} wire-data was null!", _msg); + return; + } + + for (byte[] buf : _msg.getWireData()) { + if (logger.isTraceEnabled()) { + logger.trace("{}", null == buf ? "(buffer was null)" : Hexdump.format(buf)); + } + if (null == buf) { + break; + } + + outputChannel.write(ByteBuffer.wrap(buf)); + } + logger.trace("Message sent: {}", _msg); + } + + @Override + public void close() throws IOException { + logger.debug("Closing Message Writer"); + if (outputChannel != null && outputChannel.isOpen()) { + outputChannel.close(); + } + outputChannel = null; + } + + @Override + public boolean isClosed() { + return outputChannel != null && !outputChannel.isOpen(); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/spi/transport/ITransportProvider.java b/federation/sssd/src/main/java/org/freedesktop/dbus/spi/transport/ITransportProvider.java new file mode 100644 index 0000000000..de2b07b455 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/spi/transport/ITransportProvider.java @@ -0,0 +1,79 @@ +package org.freedesktop.dbus.spi.transport; + +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.connections.config.TransportConfig; +import org.freedesktop.dbus.connections.transports.AbstractTransport; +import org.freedesktop.dbus.exceptions.TransportConfigurationException; + +import java.util.ServiceLoader; + +import javax.xml.transform.TransformerConfigurationException; + +/** + * Interface used by {@link ServiceLoader} to provide a transport used by DBus. + * + * @author hypfvieh + * @since v4.0.0 - 2021-09-05 + */ +public interface ITransportProvider { + /** + * Name of the transport implementation. + * @return String, should not be null or empty + */ + String getTransportName(); + + /** + * Creates a new instance of this transport service using the given bus address. + *

+ * If transport cannot be created because bus address type is not supported, {@code null} + * should be returned. If initialization fails because of any other error, throw a {@link TransportConfigurationException}. + *

+ * + * @param _address bus address + * @param _config configuration for this transport + * + * @return transport instance or null + * + * @throws TransformerConfigurationException when configuring transport fails + */ + AbstractTransport createTransport(BusAddress _address, TransportConfig _config) throws TransportConfigurationException; + + /** + * Creates a new instance of this transport service using the given bus address and timeout. + * + * This method is only implemented for compatibility to older transports. + * It will be removed in the future. + * Please use {@link #createTransport(BusAddress, TransportConfig)} instead. + * + * @param _address bus address + * @param _timeout timeout to use (if supported) + * + * @return transport instance or null + * + * @throws TransformerConfigurationException when configuring transport fails + * @deprecated just used for compatibility will be removed in the future. Please use {@link #createTransport(BusAddress, TransportConfig)}. + */ + @Deprecated(since = "4.2.0 - 2022-07-21", forRemoval = true) + default AbstractTransport createTransport(BusAddress _address, int _timeout) throws TransportConfigurationException { + TransportConfig transportConfig = new TransportConfig(); + transportConfig.setTimeout(_timeout); + return createTransport(_address, transportConfig); + } + + /** + * Type of transport. + * Should return an identifier for the supported socket type (e.g. UNIX for unix socket, TCP for tcp sockets). + * + * @return String, never null + */ + String getSupportedBusType(); + + /** + * Creates a new (dynamic) session for this transport. + * + * @param _listeningSocket true when listening address should be created + * + * @return String containing bus address + */ + String createDynamicSessionAddress(boolean _listeningSocket); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusListType.java b/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusListType.java index a00c3076d1..155b876371 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusListType.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusListType.java @@ -1,13 +1,3 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus.types; import java.lang.reflect.ParameterizedType; @@ -19,25 +9,29 @@ import java.util.List; * Should be used whenever you need a Type variable for a list. */ public class DBusListType implements ParameterizedType { - private Type v; + private final Type v; /** - * Create a List type. - * - * @param v Type of the list contents. - */ - public DBusListType(Type v) { - this.v = v; + * Create a List type. + * @param _v Type of the list contents. + */ + public DBusListType(Type _v) { + this.v = _v; } + @Override public Type[] getActualTypeArguments() { - return new Type[]{v}; + return new Type[] { + v + }; } + @Override public Type getRawType() { return List.class; } + @Override public Type getOwnerType() { return null; } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusMapType.java b/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusMapType.java index 754e0a2982..459e1499a5 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusMapType.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusMapType.java @@ -1,13 +1,3 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus.types; import java.lang.reflect.ParameterizedType; @@ -19,28 +9,32 @@ import java.util.Map; * Should be used whenever you need a Type variable for a map. */ public class DBusMapType implements ParameterizedType { - private Type k; - private Type v; + private final Type k; + private final Type v; /** - * Create a map type. - * - * @param k The type of the keys. - * @param v The type of the values. - */ - public DBusMapType(Type k, Type v) { - this.k = k; - this.v = v; + * Create a map type. + * @param _k The type of the keys. + * @param _v The type of the values. + */ + public DBusMapType(Type _k, Type _v) { + this.k = _k; + this.v = _v; } + @Override public Type[] getActualTypeArguments() { - return new Type[]{k, v}; + return new Type[] { + k, v + }; } + @Override public Type getRawType() { return Map.class; } + @Override public Type getOwnerType() { return null; } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusStructType.java b/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusStructType.java index fda2065601..d70521983b 100644 --- a/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusStructType.java +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusStructType.java @@ -1,13 +1,3 @@ -/* - D-Bus Java Implementation - Copyright (c) 2005-2006 Matthew Johnson - - This program is free software; you can redistribute it and/or modify it - under the terms of either the GNU Lesser General Public License Version 2 or the - Academic Free Licence Version 2.1. - - Full licence texts are included in the COPYING file with this program. -*/ package org.freedesktop.dbus.types; import org.freedesktop.dbus.Struct; @@ -20,25 +10,27 @@ import java.lang.reflect.Type; * Should be used whenever you need a Type variable for a struct. */ public class DBusStructType implements ParameterizedType { - private Type[] contents; + private final Type[] contents; /** - * Create a struct type. - * - * @param contents The types contained in this struct. - */ - public DBusStructType(Type... contents) { - this.contents = contents; + * Create a struct type. + * @param _contents The types contained in this struct. + */ + public DBusStructType(Type... _contents) { + this.contents = _contents; } + @Override public Type[] getActualTypeArguments() { return contents; } + @Override public Type getRawType() { return Struct.class; } + @Override public Type getOwnerType() { return null; } diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/types/UInt16.java b/federation/sssd/src/main/java/org/freedesktop/dbus/types/UInt16.java new file mode 100644 index 0000000000..a23d3086c3 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/types/UInt16.java @@ -0,0 +1,93 @@ +package org.freedesktop.dbus.types; + +/** + * Class to represent 16-bit unsigned integers. + */ +@SuppressWarnings("serial") +public class UInt16 extends Number implements Comparable { + /** Maximum possible value. */ + public static final int MAX_VALUE = 65535; + /** Minimum possible value. */ + public static final int MIN_VALUE = 0; + private final int value; + + /** Create a UInt16 from an int. + * @param _value Must be within MIN_VALUE–MAX_VALUE + * @throws NumberFormatException if value is not between MIN_VALUE and MAX_VALUE + */ + public UInt16(int _value) { + if (_value < MIN_VALUE || _value > MAX_VALUE) { + throw new NumberFormatException(String.format("%s is not between %s and %s.", _value, MIN_VALUE, MAX_VALUE)); + } + this.value = _value; + } + + /** Create a UInt16 from a String. + * @param _value Must parse to a valid integer within MIN_VALUE–MAX_VALUE + * @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_VALUE + */ + public UInt16(String _value) { + this(Integer.parseInt(_value)); + } + + /** The value of this as a byte. */ + @Override + public byte byteValue() { + return (byte) value; + } + + /** The value of this as a double. */ + @Override + public double doubleValue() { + return value; + } + + /** The value of this as a float. */ + @Override + public float floatValue() { + return value; + } + + /** The value of this as a int. */ + @Override + public int intValue() { + return /*(int)*/ value; + } + + /** The value of this as a long. */ + @Override + public long longValue() { + return value; + } + + /** The value of this as a short. */ + @Override + public short shortValue() { + return (short) value; + } + + /** Test two UInt16s for equality. */ + @Override + public boolean equals(Object _o) { + return _o instanceof UInt16 && ((UInt16) _o).value == this.value; + } + + @Override + public int hashCode() { + return /*(int)*/ value; + } + + /** Compare two UInt16s. + * @return 0 if equal, -ve or +ve if they are different. + */ + @Override + public int compareTo(UInt16 _other) { + return Integer.compare(value, _other.value); + } + + /** The value of this as a string. */ + @Override + public String toString() { + return String.valueOf(value); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/types/UInt32.java b/federation/sssd/src/main/java/org/freedesktop/dbus/types/UInt32.java new file mode 100644 index 0000000000..591cc9917e --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/types/UInt32.java @@ -0,0 +1,93 @@ +package org.freedesktop.dbus.types; + +/** + * Class to represent unsigned 32-bit numbers. + */ +@SuppressWarnings("serial") +public class UInt32 extends Number implements Comparable { + /** Maximum allowed value */ + public static final long MAX_VALUE = 4294967295L; + /** Minimum allowed value */ + public static final long MIN_VALUE = 0; + private final long value; + + /** Create a UInt32 from a long. + * @param _value Must be a valid integer within MIN_VALUE–MAX_VALUE + * @throws NumberFormatException if value is not between MIN_VALUE and MAX_VALUE + */ + public UInt32(long _value) { + if (_value < MIN_VALUE || _value > MAX_VALUE) { + throw new NumberFormatException(String.format("%s is not between %s and %s.", _value, MIN_VALUE, MAX_VALUE)); + } + value = _value; + } + + /** Create a UInt32 from a String. + * @param _value Must parse to a valid integer within MIN_VALUE–MAX_VALUE + * @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_VALUE + */ + public UInt32(String _value) { + this(Long.parseLong(_value)); + } + + /** The value of this as a byte. */ + @Override + public byte byteValue() { + return (byte) value; + } + + /** The value of this as a double. */ + @Override + public double doubleValue() { + return value; + } + + /** The value of this as a float. */ + @Override + public float floatValue() { + return value; + } + + /** The value of this as a int. */ + @Override + public int intValue() { + return (int) value; + } + + /** The value of this as a long. */ + @Override + public long longValue() { + return /*(long)*/ value; + } + + /** The value of this as a short. */ + @Override + public short shortValue() { + return (short) value; + } + + /** Test two UInt32s for equality. */ + @Override + public boolean equals(Object _o) { + return _o instanceof UInt32 && ((UInt32) _o).value == this.value; + } + + @Override + public int hashCode() { + return (int) value; + } + + /** Compare two UInt32s. + * @return 0 if equal, -ve or +ve if they are different. + */ + @Override + public int compareTo(UInt32 _other) { + return Long.compare(value, _other.value); + } + + /** The value of this as a string */ + @Override + public String toString() { + return String.valueOf(value); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/types/UInt64.java b/federation/sssd/src/main/java/org/freedesktop/dbus/types/UInt64.java new file mode 100644 index 0000000000..f935c1140a --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/types/UInt64.java @@ -0,0 +1,174 @@ +package org.freedesktop.dbus.types; + +import java.math.BigInteger; + +/** + * Class to represent unsigned 64-bit numbers. + * Warning: Any functions which take or return a long + * are restricted to the range of a signed 64bit number. + * Use the BigInteger methods if you wish access to the full + * range. + */ +@SuppressWarnings("serial") +public class UInt64 extends Number implements Comparable { + /** Maximum allowed value (when accessed as a long) */ + public static final long MAX_LONG_VALUE = Long.MAX_VALUE; + /** Maximum allowed value (when accessed as a BigInteger) */ + public static final BigInteger MAX_BIG_VALUE = new BigInteger("18446744073709551615"); + /** Minimum allowed value */ + public static final long MIN_VALUE = 0; + private final BigInteger value; + private final long top; + private final long bottom; + + /** Create a UInt64 from a long. + * @param _value Must be a valid integer within MIN_VALUE–MAX_VALUE + * @throws NumberFormatException if value is not between MIN_VALUE and MAX_VALUE + */ + public UInt64(long _value) { + if (_value < MIN_VALUE || _value > MAX_LONG_VALUE) { + throw new NumberFormatException(String.format("%s is not between %s and %s.", _value, MIN_VALUE, MAX_LONG_VALUE)); + } + + this.value = BigInteger.valueOf(_value); + this.top = this.value.shiftRight(32).and(new BigInteger("4294967295")).longValue(); + this.bottom = this.value.and(new BigInteger("4294967295")).longValue(); + } + + /** + * Create a UInt64 from two longs. + * @param _top Most significant 4 bytes. + * @param _bottom Least significant 4 bytes. + */ + public UInt64(long _top, long _bottom) { + BigInteger a = BigInteger.valueOf(_top); + a = a.shiftLeft(32); + a = a.add(BigInteger.valueOf(_bottom)); + if (0 > a.compareTo(BigInteger.ZERO)) { + throw new NumberFormatException(String.format("%s is not between %s and %s.", a, MIN_VALUE, MAX_BIG_VALUE)); + } + if (0 < a.compareTo(MAX_BIG_VALUE)) { + throw new NumberFormatException(String.format("%s is not between %s and %s.", a, MIN_VALUE, MAX_BIG_VALUE)); + } + this.value = a; + this.top = _top; + this.bottom = _bottom; + } + + /** Create a UInt64 from a BigInteger + * @param _value Must be a valid BigInteger between MIN_VALUE–MAX_BIG_VALUE + * @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_BIG_VALUE + */ + public UInt64(BigInteger _value) { + if (null == _value || 0 > _value.compareTo(BigInteger.ZERO) || 0 < _value.compareTo(MAX_BIG_VALUE)) { + throw new NumberFormatException(String.format("%s is not between %s and %s.", _value, MIN_VALUE, MAX_BIG_VALUE)); + } + this.value = _value; + this.top = this.value.shiftRight(32).and(new BigInteger("4294967295")).longValue(); + this.bottom = this.value.and(new BigInteger("4294967295")).longValue(); + } + + /** Create a UInt64 from a String. + * @param _value Must parse to a valid integer within MIN_VALUE–MAX_BIG_VALUE + * @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_BIG_VALUE + */ + public UInt64(String _value) { + if (null == _value) { + throw new NumberFormatException(String.format("%s is not between %s and %s.", _value, MIN_VALUE, MAX_BIG_VALUE)); + } + BigInteger a = new BigInteger(_value); + if (0 > a.compareTo(BigInteger.ZERO) || 0 < a.compareTo(MAX_BIG_VALUE)) { + throw new NumberFormatException(String.format("%s is not between %s and %s.", _value, MIN_VALUE, MAX_BIG_VALUE)); + } + this.value = a; + this.top = this.value.shiftRight(32).and(new BigInteger("4294967295")).longValue(); + this.bottom = this.value.and(new BigInteger("4294967295")).longValue(); + } + + /** The value of this as a BigInteger. + * @return value + */ + public BigInteger value() { + return value; + } + + /** The value of this as a byte. */ + @Override + public byte byteValue() { + return value.byteValue(); + } + + /** The value of this as a double. */ + @Override + public double doubleValue() { + return value.doubleValue(); + } + + /** The value of this as a float. */ + @Override + public float floatValue() { + return value.floatValue(); + } + + /** The value of this as a int. */ + @Override + public int intValue() { + return value.intValue(); + } + + /** The value of this as a long. */ + @Override + public long longValue() { + return value.longValue(); + } + + /** The value of this as a short. */ + @Override + public short shortValue() { + return value.shortValue(); + } + + /** Test two UInt64s for equality. */ + @Override + public boolean equals(Object _o) { + return _o instanceof UInt64 && this.value.equals(((UInt64) _o).value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + /** Compare two UInt32s. + * @param _other other uint64 + * @return 0 if equal, -ve or +ve if they are different. + */ + @Override + public int compareTo(UInt64 _other) { + return this.value.compareTo(_other.value); + } + + /** The value of this as a string. + * @return string + */ + @Override + public String toString() { + return value.toString(); + } + + /** + * Most significant 4 bytes. + * @return top + */ + public long top() { + return top; + } + + /** + * Least significant 4 bytes. + * @return bottom + */ + public long bottom() { + return bottom; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/types/Variant.java b/federation/sssd/src/main/java/org/freedesktop/dbus/types/Variant.java new file mode 100644 index 0000000000..f1b632f5d4 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/types/Variant.java @@ -0,0 +1,146 @@ +package org.freedesktop.dbus.types; + +import org.freedesktop.dbus.Marshalling; +import org.freedesktop.dbus.exceptions.DBusException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * A Wrapper class for Variant values. + * A method on DBus can send or receive a Variant. + * This will wrap another value whose type is determined at runtime. + * The Variant may be parameterized to restrict the types it may accept. + */ +public class Variant { + private final Logger logger = LoggerFactory.getLogger(getClass()); + private final T value; + private final Type type; + private final String sig; + + /** + * Create a Variant from a basic type object. + * @param _value The wrapped value. + * @throws IllegalArgumentException If you try and wrap Null or an object of a non-basic type. + */ + public Variant(T _value) throws IllegalArgumentException { + if (null == _value) { + throw new IllegalArgumentException("Can't wrap Null in a Variant"); + } + type = _value.getClass(); + try { + String[] ss = Marshalling.getDBusType(_value.getClass(), true); + if (ss.length != 1) { + throw new IllegalArgumentException("Can't wrap a multi-valued type in a Variant: " + type); + } + this.sig = ss[0]; + } catch (DBusException _ex) { + logger.debug("", _ex); + throw new IllegalArgumentException(String.format("Can't wrap %s in an unqualified Variant (%s).", _value.getClass(), _ex.getMessage())); + } + this.value = _value; + } + + /** + * Create a Variant. + * @param _value The wrapped value. + * @param _type The explicit type of the value. + * @throws IllegalArgumentException If you try and wrap Null or an object which cannot be sent over DBus. + */ + public Variant(T _value, Type _type) throws IllegalArgumentException { + if (null == _value) { + throw new IllegalArgumentException("Can't wrap Null in a Variant"); + } + this.type = _type; + try { + String[] ss = Marshalling.getDBusType(_type); + if (ss.length != 1) { + throw new IllegalArgumentException("Can't wrap a multi-valued type in a Variant: " + _type); + } + this.sig = ss[0]; + } catch (DBusException _ex) { + logger.debug("", _ex); + throw new IllegalArgumentException(String.format("Can't wrap %s in an unqualified Variant (%s).", _type, _ex.getMessage())); + } + this.value = _value; + } + + /** + * Create a Variant. + * @param _value The wrapped value. + * @param _sig The explicit type of the value, as a dbus type string. + * @throws IllegalArgumentException If you try and wrap Null or an object which cannot be sent over DBus. + */ + public Variant(T _value, String _sig) throws IllegalArgumentException { + if (null == _value) { + throw new IllegalArgumentException("Can't wrap Null in a Variant"); + } + this.sig = _sig; + try { + List ts = new ArrayList<>(); + Marshalling.getJavaType(_sig, ts, 1); + if (ts.size() != 1) { + throw new IllegalArgumentException("Can't wrap multiple or no types in a Variant: " + _sig); + } + this.type = ts.get(0); + } catch (DBusException _ex) { + logger.debug("", _ex); + throw new IllegalArgumentException(String.format("Can''t wrap %s in an unqualified Variant (%s).", _sig, _ex.getMessage())); + } + this.value = _value; + } + + /** Return the wrapped value. + * @return value + */ + public T getValue() { + return value; + } + + /** Return the type of the wrapped value. + * @return type + */ + public Type getType() { + return type; + } + + /** Return the dbus signature of the wrapped value. + * @return signature + */ + public String getSig() { + return sig; + } + + /** Format the Variant as a string. */ + @Override + public String toString() { + return "[" + value + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + /** + * Compare this Variant with another by comparing contents. + * @param _obj other object + * @return boolean + */ + @Override + public boolean equals(Object _obj) { + if (this == _obj) { + return true; + } + if (!(_obj instanceof Variant)) { + return false; + } + Variant other = (Variant) _obj; + return Objects.equals(value, other.value); + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/utils/AddressBuilder.java b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/AddressBuilder.java new file mode 100644 index 0000000000..2d3f76eee5 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/AddressBuilder.java @@ -0,0 +1,163 @@ +package org.freedesktop.dbus.utils; + +import org.freedesktop.dbus.config.DBusSysProps; +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.exceptions.AddressResolvingException; + +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +public final class AddressBuilder { + /** + * @deprecated Constant has been moved to {@link DBusSysProps}. + */ + @Deprecated(forRemoval = true, since = "v4.2.2 - 2023-01-20") + public static final String DBUS_SYSTEM_BUS_ADDRESS = "DBUS_SYSTEM_BUS_ADDRESS"; + /** + * @deprecated Constant has been moved to {@link DBusSysProps}. + */ + @Deprecated(forRemoval = true, since = "v4.2.2 - 2023-01-20") + public static final String DEFAULT_SYSTEM_BUS_ADDRESS = "unix:path=/var/run/dbus/system_bus_socket"; + /** + * @deprecated Constant has been moved to {@link DBusSysProps}. + */ + @Deprecated(forRemoval = true, since = "v4.2.2 - 2023-01-20") + public static final String DBUS_SESSION_BUS_ADDRESS = "DBUS_SESSION_BUS_ADDRESS"; + + private AddressBuilder() {} + + /** + * Determine the address of the DBus system connection. + * + * @return String + */ + public static BusAddress getSystemConnection() { + String bus = System.getenv(DBusSysProps.DBUS_SYSTEM_BUS_ADDRESS); + if (bus == null) { + bus = DBusSysProps.DEFAULT_SYSTEM_BUS_ADDRESS; + } + return BusAddress.of(bus); + } + + /** + * Retrieves the connection address to connect to the DBus session-bus.
+ * Will return TCP connection when no unix transport found and TCP is available. + * + * @param _dbusMachineIdFile alternative location of dbus machine id file, use null if not needed + * + * @return address + * + * @throws AddressResolvingException when no suitable address can be found for any available transport + */ + public static BusAddress getSessionConnection(String _dbusMachineIdFile) { + + // try to read session address from running process instance properties first + String s = System.getProperty(DBusSysProps.DBUS_SESSION_BUS_ADDRESS); + + // no session address in process properties, try to get it from environment + if (s == null) { + // MacOS support: e.g DBUS_LAUNCHD_SESSION_BUS_SOCKET=/private/tmp/com.apple.launchd.4ojrKe6laI/unix_domain_listener + if (Util.isMacOs()) { + s = "unix:path=" + System.getenv(DBusSysProps.DBUS_SESSION_BUS_ADDRESS_MACOS); + } else { // all others (linux) + s = System.getenv(DBusSysProps.DBUS_SESSION_BUS_ADDRESS); + } + } + + // no address found in instance properties and environment, try to get the address from session properties file + if (s == null) { + // address gets stashed in $HOME/.dbus/session-bus/`dbus-uuidgen --get`-`sed 's/:\(.\)\..*/\1/' <<< + // $DISPLAY` + String display = System.getenv("DISPLAY"); + if (display == null) { + throw new AddressResolvingException("Cannot Resolve Session Bus Address: DISPLAY variable not set"); + } + if (display.charAt(0) != ':' && display.contains(":")) { // display seems to be a remote display + // (e.g. X forward through SSH) + display = display.substring(display.indexOf(':')); + } + + String uuid = getDbusMachineId(_dbusMachineIdFile); + String homedir = System.getProperty("user.home"); + File addressfile = new File(homedir + "/.dbus/session-bus", + uuid + "-" + display.replaceAll(":([0-9]*)\\..*", "$1")); + + if (!addressfile.exists()) { + throw new AddressResolvingException("Cannot Resolve Session Bus Address: " + addressfile + " not found"); + } + + Properties readProperties = Util.readProperties(addressfile); + if (readProperties == null) { + throw new AddressResolvingException("Cannot Resolve Session Bus Address: Unable to read " + addressfile); + } + String sessionAddress = readProperties.getProperty(DBusSysProps.DBUS_SESSION_BUS_ADDRESS); + + if (Util.isEmpty(sessionAddress)) { + throw new AddressResolvingException("Cannot Resolve Session Bus Address: No session information found in " + addressfile); + } + + // sometimes (e.g. Ubuntu 18.04) the returned address is wrapped in single quotes ('), we have to remove them + if (sessionAddress.matches("^'[^']+'$")) { + sessionAddress = sessionAddress.replaceFirst("^'([^']+)'$", "$1"); + } + + return BusAddress.of(sessionAddress); + } + + return BusAddress.of(s); + } + + /** + * Extracts the machine-id usually found on Linux in various system directories, or + * generate a fake id for non-Linux platforms. + * + * @param _dbusMachineIdFile alternative location of dbus machine id file, null if not needed + * @return machine-id string, never null + */ + public static String getDbusMachineId(String _dbusMachineIdFile) { + File uuidfile = determineMachineIdFile(_dbusMachineIdFile); + if (uuidfile != null) { + String uuid = Util.readFileToString(uuidfile); + if (uuid.length() > 0) { + return uuid; + } else { + throw new AddressResolvingException("Cannot Resolve Session Bus Address: MachineId file is empty."); + } + } + if (Util.isWindows() || Util.isMacOs()) { + /* Linux *should* have a machine-id */ + return getFakeDbusMachineId(); + } + throw new AddressResolvingException("Cannot Resolve Session Bus Address: MachineId file can not be found"); + } + + /** + * Tries to find the DBus machine-id file in different locations. + * + * @param _dbusMachineIdFile alternative location of dbus machine id file + * + * @return File with machine-id + */ + private static File determineMachineIdFile(String _dbusMachineIdFile) { + List locationPriorityList = Arrays.asList(System.getenv(DBusSysProps.DBUS_MACHINE_ID_SYS_VAR), _dbusMachineIdFile, + "/var/lib/dbus/machine-id", "/usr/local/var/lib/dbus/machine-id", "/etc/machine-id"); + return locationPriorityList.stream() + .filter(s -> s != null) + .map(s -> new File(s)) + .filter(f -> f.exists() && f.length() > 0) + .findFirst() + .orElse(null); + } + + /** + * Generates a fake machine-id when DBus is running on Windows. + * @return String + */ + private static String getFakeDbusMachineId() { + // we create a fake id on windows + return String.format("%s@%s", Util.getCurrentUser(), Util.getHostName()); + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/utils/CommonRegexPattern.java b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/CommonRegexPattern.java new file mode 100644 index 0000000000..ba8d0509f7 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/CommonRegexPattern.java @@ -0,0 +1,23 @@ +package org.freedesktop.dbus.utils; + +import java.util.regex.Pattern; + +/** + * Utility class containing commonly used regular expression patterns. + * + * @author hypfvieh + * @version 4.1.0 - 2022-02-08 + */ +public final class CommonRegexPattern { + public static final Pattern PROXY_SPLIT_PATTERN = Pattern.compile("[<>]"); + public static final Pattern IFACE_PATTERN = Pattern.compile("^interface *name *= *['\"]([^'\"]*)['\"].*$"); + + public static final Pattern DBUS_IFACE_PATTERN = Pattern.compile("^.*\\.([^\\.]+)$"); + + public static final Pattern EXCEPTION_EXTRACT_PATTERN = Pattern.compile("\\.([^\\.]*)$"); + public static final Pattern EXCEPTION_PARTIAL_PATTERN = Pattern.compile(".*\\..*"); + + private CommonRegexPattern() { + + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/utils/DBusNamingUtil.java b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/DBusNamingUtil.java new file mode 100644 index 0000000000..658916ccb7 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/DBusNamingUtil.java @@ -0,0 +1,83 @@ +package org.freedesktop.dbus.utils; + +import org.freedesktop.dbus.annotations.DBusInterfaceName; +import org.freedesktop.dbus.annotations.DBusMemberName; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.Objects; +import java.util.regex.Pattern; + +/** + * DBus name Util class for internal and external use. + */ +public final class DBusNamingUtil { + private static final Pattern DOLLAR_PATTERN = Pattern.compile("[$]"); + + private DBusNamingUtil() { + } + + /** + * Get DBus interface name for specified interface class + * + * @param _clazz input DBus interface class + * @return interface name + * @see DBusInterfaceName + */ + public static String getInterfaceName(Class _clazz) { + Objects.requireNonNull(_clazz, "clazz must not be null"); + + if (_clazz.isAnnotationPresent(DBusInterfaceName.class)) { + return _clazz.getAnnotation(DBusInterfaceName.class).value(); + } + return DOLLAR_PATTERN.matcher(_clazz.getName()).replaceAll("."); + } + + /** + * Get DBus method name for specified method object. + * + * @param _method input method + * @return method name + * @see DBusMemberName + */ + public static String getMethodName(Method _method) { + Objects.requireNonNull(_method, "method must not be null"); + + if (_method.isAnnotationPresent(DBusMemberName.class)) { + return _method.getAnnotation(DBusMemberName.class).value(); + } + return _method.getName(); + } + + /** + * Get DBus signal name for specified signal class. + * + * @param _clazz input DBus signal class + * @return signal name + * @see DBusMemberName + */ + public static String getSignalName(Class _clazz) { + Objects.requireNonNull(_clazz, "clazz must not be null"); + + if (_clazz.isAnnotationPresent(DBusMemberName.class)) { + return _clazz.getAnnotation(DBusMemberName.class).value(); + } + return _clazz.getSimpleName(); + } + + /** + * Get DBus name for specified annotation class + * + * @param _clazz input DBus annotation + * @return interface name + * @see DBusInterfaceName + */ + public static String getAnnotationName(Class _clazz) { + Objects.requireNonNull(_clazz, "clazz must not be null"); + + if (_clazz.isAnnotationPresent(DBusInterfaceName.class)) { + return _clazz.getAnnotation(DBusInterfaceName.class).value(); + } + return DOLLAR_PATTERN.matcher(_clazz.getName()).replaceAll("."); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/utils/Hexdump.java b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/Hexdump.java new file mode 100644 index 0000000000..34ab8c8810 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/Hexdump.java @@ -0,0 +1,145 @@ +package org.freedesktop.dbus.utils; + +import java.io.PrintStream; + +public final class Hexdump { + public static final char[] HEX_CHARS = new char[] { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + private Hexdump() { + + } + + public static String toHex(byte[] _buf) { + return toHex(_buf, true); + } + + public static String toHex(byte[] _buf, boolean _spaces) { + return toHex(_buf, 0, _buf.length, _spaces); + } + + public static String toHex(byte[] _buf, int _ofs, int _len, boolean _spaces) { + StringBuilder sb = new StringBuilder(); + int j = _ofs + _len; + for (int i = _ofs; i < j; i++) { + if (i < _buf.length) { + sb.append(HEX_CHARS[(_buf[i] & 0xF0) >> 4]); + sb.append(HEX_CHARS[_buf[i] & 0x0F]); + if (_spaces) { + sb.append(' '); + } + } else { + if (_spaces) { + sb.append(' '); + sb.append(' '); + sb.append(' '); + } + } + } + return sb.toString(); + } + + public static String toAscii(byte[] _buf) { + return toAscii(_buf, 0, _buf.length); + } + + public static String toAscii(byte[] _buf, int _ofs, int _len) { + StringBuilder sb = new StringBuilder(); + int j = _ofs + _len; + for (int i = _ofs; i < j; i++) { + if (i < _buf.length) { + if (20 <= _buf[i] && 126 >= _buf[i]) { + sb.append((char) _buf[i]); + } else { + sb.append('.'); + } + } else { + sb.append(' '); + } + } + return sb.toString(); + } + + public static String format(byte[] _buf) { + return format(_buf, 80); + } + + public static String format(byte[] _buf, int _width) { + int bs = (_width - 8) / 4; + int i = 0; + StringBuilder sb = new StringBuilder(); + do { + for (int j = 0; j < 6; j++) { + sb.append(HEX_CHARS[(i << (j * 4) & 0xF00000) >> 20]); + } + sb.append('\t'); + sb.append(toHex(_buf, i, bs, true)); + sb.append(' '); + sb.append(toAscii(_buf, i, bs)); + sb.append('\n'); + i += bs; + } while (i < _buf.length); + sb.deleteCharAt(sb.length() - 1); // remove the last \n + return sb.toString(); + } + + public static void print(byte[] _buf) { + print(_buf, System.err); + } + + public static void print(byte[] _buf, int _width) { + print(_buf, _width, System.err); + } + + public static void print(byte[] _buf, int _width, PrintStream _out) { + _out.print(format(_buf, _width)); + } + + public static void print(byte[] _buf, PrintStream _out) { + _out.print(format(_buf)); + } + + /** + * Returns a string which can be written to a Java source file as part + * of a static initializer for a byte array. + * Returns data in the format 0xAB, 0xCD, .... + * use like: + * javafile.print("byte[] data = {") + * javafile.print(Hexdump.toByteArray(data)); + * javafile.println("};"); * @param buf + * @param _buf buffer + * @return string + */ + public static String toByteArray(byte[] _buf) { + return toByteArray(_buf, 0, _buf.length); + } + + /** + * Returns a string which can be written to a Java source file as part + * of a static initializer for a byte array. + * Returns data in the format 0xAB, 0xCD, .... + * use like: + * javafile.print("byte[] data = {") + * javafile.print(Hexdump.toByteArray(data)); + * javafile.println("};"); + * + * @param _buf buffer + * @param _ofs offset + * @param _len length + * @return string + */ + public static String toByteArray(byte[] _buf, int _ofs, int _len) { + StringBuilder sb = new StringBuilder(); + for (int i = _ofs; i < _len && i < _buf.length; i++) { + sb.append('0'); + sb.append('x'); + sb.append(HEX_CHARS[(_buf[i] & 0xF0) >> 4]); + sb.append(HEX_CHARS[_buf[i] & 0x0F]); + if ((i + 1) < _len && (i + 1) < _buf.length) { + sb.append(','); + } + } + return sb.toString(); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/utils/IThrowingSupplier.java b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/IThrowingSupplier.java new file mode 100644 index 0000000000..a677fc7609 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/IThrowingSupplier.java @@ -0,0 +1,21 @@ +package org.freedesktop.dbus.utils; + +/** + * Supplier which allows throwing any exception. + * + * @param type which is supplied + * @param type of exception which gets thrown + * + * @author hypfvieh + * @since v1.3.0 - 2023-01-12 + */ +@FunctionalInterface +public interface IThrowingSupplier { + /** + * Returns the result of the supplier or throws an exception. + * + * @return result of supplied function + * @throws T exception + */ + V get() throws T; +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/utils/LoggingHelper.java b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/LoggingHelper.java new file mode 100644 index 0000000000..46fab1e09d --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/LoggingHelper.java @@ -0,0 +1,80 @@ +package org.freedesktop.dbus.utils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +/** + * Helper for some logging stuff, e.g. avoid call {@link Arrays#deepToString(Object[])} if loglevel is not enabled. + * + * @author David M. + * @since v3.2.4 - 2020-08-24 + */ +public final class LoggingHelper { + + private LoggingHelper() { + } + + /** + * Creates a toString() result for an array. + * Will resolve nested arrays and collections. + * + * @param _array array to convert + * @return String or null if input null + * + * @since v4.2.2 - 2023-01-20 + */ + public static String arraysVeryDeepString(Object[] _array) { + if (_array == null) { + return null; + } + + return String.join(", ", arraysVeryDeepStringRecursive(_array)); + } + + /** + * Creates a toString() result for an array. + * Will resolve nested arrays and collections. + * + * @param _array array to convert + * @return List of String, null if input null + * + * @since v4.2.2 - 2023-01-20 + */ + private static List arraysVeryDeepStringRecursive(Object[] _array) { + if (_array == null) { + return null; + } + + List result = new ArrayList<>(); + for (Object object : _array) { + if (object == null) { + result.add("(null)"); + } else if (object.getClass().isArray()) { + result.add(arraysVeryDeepStringRecursive((Object[]) object).toString()); + } else if (object instanceof Collection) { + Collection c = (Collection) object; + result.add(arraysVeryDeepStringRecursive(c.toArray()).toString()); + } else { + result.add(Objects.toString(object)); + } + } + + return result; + } + + /** + * Executes the runnable if the boolean is true. + * + * @param _enabled boolean, if true runnable is executed + * @param _loggerCall runnable containing logger call + */ + public static void logIf(boolean _enabled, Runnable _loggerCall) { + if (_enabled) { + _loggerCall.run(); + } + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/utils/NameableThreadFactory.java b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/NameableThreadFactory.java new file mode 100644 index 0000000000..8c38ab3d20 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/NameableThreadFactory.java @@ -0,0 +1,63 @@ +package org.freedesktop.dbus.utils; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A thread factory which allows setting name, priority and daemon flag + * for all newly created threads. + */ +public class NameableThreadFactory implements ThreadFactory { + + private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1); + private final ThreadGroup group; + private final AtomicInteger threadNumber = new AtomicInteger(1); + private final String namePrefix; + + private final int threadPriority; + private final boolean daemonizeThreads; + + /** + * Create a new ThreadFactory instance. + * The thread name is created like this: + * _name + THREAD_NUMBER + * e.g: connectionPool-1 + * If _name is null or blank, UnnamedThreadPool-POOL_NUMBER-thread-THREAD_NUMBER will be used. + * + * @param _name prefix for all thread names + * @param _daemonizeThreads turn all created threads to daemon threads + */ + public NameableThreadFactory(String _name, boolean _daemonizeThreads) { + this(_name, _daemonizeThreads, Thread.NORM_PRIORITY); + } + + /** + * Create a new ThreadFactory instance. + * The thread name is created like this: + * _name + THREAD_NUMBER + * e.g: connectionPool-1 + * If _name is null or blank, UnnamedThreadPool-POOL_NUMBER-thread-THREAD_NUMBER will be used. + * + * @param _name prefix for all thread names + * @param _daemonizeThreads turn all created threads to daemon threads + * @param _threadPriority priority to use for new threads + * + * @since 4.2.0 - 2022-07-13 + */ + public NameableThreadFactory(String _name, boolean _daemonizeThreads, int _threadPriority) { + group = Thread.currentThread().getThreadGroup(); //NOPMD + namePrefix = Util.isBlank(_name) ? "UnnamedThreadPool-" + POOL_NUMBER.getAndIncrement() + "-thread-" : _name; + daemonizeThreads = _daemonizeThreads; + threadPriority = _threadPriority; + } + + @Override + public Thread newThread(Runnable _runnable) { + Thread t = new Thread(group, _runnable, namePrefix + threadNumber.getAndIncrement(), 0); + t.setDaemon(daemonizeThreads); + t.setPriority(threadPriority); + + return t; + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/utils/TimeMeasure.java b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/TimeMeasure.java new file mode 100644 index 0000000000..64bafe9d9b --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/TimeMeasure.java @@ -0,0 +1,136 @@ +package org.freedesktop.dbus.utils; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.Duration; +import java.util.Date; +import java.util.TimeZone; + +/** + * Utility class for time measurements. + * Instances may be reset for reuse. + */ +public class TimeMeasure { + + /** Start time in nanoseconds. */ + private volatile long startTm; + + /** Formatter used for {@link #toString()} */ + private final ITimeMeasureFormat tmf; + + /** + * Create a new instance using _ts millis as + * @param _formatter formatter to use for toString() call + */ + public TimeMeasure(ITimeMeasureFormat _formatter) { + tmf = _formatter; + reset(); + } + + /** + * Create a new instance, used a formatter converting everything >= 5000 ms to seconds (X.Y -> 6.1). + */ + public TimeMeasure() { + this(new ITimeMeasureFormat() { + @Override + public String format(long _durationInMillis) { + return _durationInMillis >= 5000 ? ((long) ((_durationInMillis / 1000d) * 10) / 10d) + "s" : _durationInMillis + "ms"; + } + }); + } + + /** + * Resets the start time. + * @return the object + */ + public final TimeMeasure reset() { + this.startTm = System.nanoTime(); + return this; + } + + /** + * Returns the start time in milliseconds. + * @return start time in ms + */ + public long getStartTime() { + return startTm; + } + + /** + * Returns the elapsed time in milliseconds. + * @return elapsed time in ms + */ + public long getElapsed() { + return Duration.ofNanos(System.nanoTime() - startTm).toMillis(); + } + + /** + * Returns the elapsed time in seconds. + * @return elapsed time in seconds + */ + public long getElapsedSeconds() { + return Duration.ofNanos(System.nanoTime() - startTm).toSeconds(); + } + + /** + * Formats the elapsed time using the given dateFormatter. + * If null is given, a new Formatter with format HH:mm:ss.SSS will be used. + * + * The timezone of the given dateFormatter will always be set to 'UTC' to avoid any timezone related offsets. + * + * @param _dateFormat date format + * @return formatted string + */ + public String getElapsedFormatted(DateFormat _dateFormat) { + return getElapsedFormatted(_dateFormat, getElapsed()); + } + + /** + * Same as above, used for proper unit testing. + * @param _dateFormat + * @param _elapsedTime + * @return formatted string + */ + String getElapsedFormatted(DateFormat _dateFormat, long _elapsedTime) { + Date elapsedTime = new Date(_elapsedTime); + + DateFormat sdf = _dateFormat; + if (_dateFormat == null) { + sdf = new SimpleDateFormat("HH:mm:ss.SSS"); + } + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); // always use UTC so we get the correct time without timezone offset on it + + return sdf.format(elapsedTime); + } + + /** + * Change the start time (for unit testing only!). + * @param _tm + */ + void setStartTm(long _tm) { + startTm = _tm; + } + + public long getElapsedAndReset() { + long elapsed = getElapsed(); + reset(); + return elapsed; + } + + /** + * Returns the elapsed time in milliseconds formatted as string. + * @return elapsed time in ms + */ + @Override + public String toString() { + if (tmf == null) { + return String.valueOf(getElapsed()); + } + return tmf.format(getElapsed()); + } + + public interface ITimeMeasureFormat { + String format(long _durationInMillis); + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/utils/Util.java b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/Util.java new file mode 100644 index 0000000000..2dc1512a17 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/Util.java @@ -0,0 +1,678 @@ +package org.freedesktop.dbus.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.attribute.GroupPrincipal; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.UserPrincipal; +import java.nio.file.attribute.UserPrincipalLookupService; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.Properties; +import java.util.Random; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Utility class providing helper methods for handling strings, files and so on. + * + * @author hypfvieh + * @since v3.2.5 - 2020-12-28 + */ +public final class Util { + private static final Logger LOGGER = LoggerFactory.getLogger(Util.class); + + /** Characters used for random strings */ + private static final char[] SYMBOLS; + + static { + StringBuilder tmp = new StringBuilder(); + for (char ch = '0'; ch <= '9'; ++ch) { + tmp.append(ch); + } + for (char ch = 'a'; ch <= 'z'; ++ch) { + tmp.append(ch); + } + for (char ch = 'A'; ch <= 'Z'; ++ch) { + tmp.append(ch); + } + SYMBOLS = tmp.toString().toCharArray(); + } + + private Util() {} + + /** + * Trys to read a properties file. + * Returns null if properties file could not be loaded + * @param _file property file to read + * @return Properties Object or null + */ + public static Properties readProperties(File _file) { + if (_file.exists()) { + try { + return readProperties(new FileInputStream(_file)); + } catch (FileNotFoundException _ex) { + LOGGER.info("Could not load properties file: " + _file, _ex); + } + } + return null; + } + + /** + * Tries to read a properties file from an inputstream. + * @param _stream input stream providing property file content + * @return properties object/null + */ + public static Properties readProperties(InputStream _stream) { + Properties props = new Properties(); + if (_stream == null) { + return null; + } + + try { + props.load(_stream); + return props; + } catch (IOException | NumberFormatException _ex) { + LOGGER.warn("Could not properties: ", _ex); + } + return null; + } + + /** + * Checks if the given String is either null or blank. + * Blank means:
+ *
+     * " " - true
+     * "" - true
+     * null - true
+     * " xx" - false
+     * 
+ * @param _str string to test + * @return true if string is blank or null, false otherwise + */ + public static boolean isBlank(String _str) { + if (_str == null) { + return true; + } + + return _str.isBlank(); + } + + /** + * Null-safe equals for two strings. + * + * @param _str1 first string + * @param _str2 second string + * @return true if both are equal (also true if both are null) + */ + public static boolean strEquals(String _str1, String _str2) { + if (_str1 == _str2) { + return true; + } else if (_str1 == null || _str2 == null) { + return false; + } else if (_str1.length() != _str2.length()) { + return false; + } + return _str1.equals(_str2); + } + + /** + * Checks if the given String is either null or empty. + * Blank means:
+ *
+     * " " - false
+     * "" - true
+     * null - true
+     * " xx" - false
+     * 
+ * @param _str string to test + * @return true if string is empty or null, false otherwise + */ + public static boolean isEmpty(String _str) { + if (_str == null) { + return true; + } + + return _str.isEmpty(); + } + + /** + * Generate a simple (cryptographic insecure) random string. + * @param _length length of random string + * @return random string or empty string if _length <= 0 + */ + public static String randomString(int _length) { + if (_length <= 0) { + return ""; + } + Random random = new Random(); + char[] buf = new char[_length]; + for (int idx = 0; idx < buf.length; ++idx) { + buf[idx] = SYMBOLS[random.nextInt(SYMBOLS.length)]; + } + return new String(buf); + } + + /** + * Upper case the first letter of the given string. + * + * @param _str string + * @return uppercased string + */ + public static String upperCaseFirstChar(String _str) { + if (_str == null) { + return null; + } + if (_str.isEmpty()) { + return _str; + } + return _str.substring(0, 1).toUpperCase() + _str.substring(1); + } + + /** + * Converts a snake-case-string to camel case string. + *
+ * Eg. this_is_snake_case → thisIsSnakeCase + * @param _input string + * @return camel case string or input if nothing todo. Returns null if input was null. + */ + public static String snakeToCamelCase(String _input) { + if (isBlank(_input)) { + return _input; + } + + Pattern compile = Pattern.compile("_[a-zA-Z]"); + Matcher matcher = compile.matcher(_input); + + String result = _input; + + while (matcher.find()) { + String match = matcher.group(); + String replacement = match.replace("_", ""); + replacement = replacement.toUpperCase(); + + result = result.replaceFirst(match, replacement); + + } + + return result; + } + + /** + * Abbreviates a String using ellipses. + * + * @param _str string to abbrivate + * @param _length max length + * @return abbreviated string, original string if string length is lower or equal then desired length or null if input was null + */ + public static String abbreviate(String _str, int _length) { + if (_str == null) { + return null; + } + if (_str.length() <= _length) { + return _str; + } + + String abbr = _str.substring(0, _length - 3) + "..."; + + return abbr; + } + + /** + * Check if the given value is a valid network port (1 - 65535). + * @param _port 'port' to check + * @param _allowWellKnown allow ports below 1024 (aka reserved well known ports) + * @return true if int is a valid network port, false otherwise + */ + public static boolean isValidNetworkPort(int _port, boolean _allowWellKnown) { + if (_allowWellKnown) { + return _port > 0 && _port < 65536; + } + + return _port > 1024 && _port < 65536; + } + + /** + * @see #isValidNetworkPort(int, boolean) + * @param _str string to check + * @param _allowWellKnown allow well known port + * @return true if valid port, false otherwise + */ + public static boolean isValidNetworkPort(String _str, boolean _allowWellKnown) { + if (isInteger(_str, false)) { + return isValidNetworkPort(Integer.parseInt(_str), _allowWellKnown); + } + return false; + } + + /** + * Check if string is an either positive or negative integer. + * + * @param _str string to validate + * @param _allowNegative negative integer allowed + * @return true if integer, false otherwise + */ + public static boolean isInteger(String _str, boolean _allowNegative) { + if (_str == null) { + return false; + } + + String regex = "[0-9]+$"; + if (_allowNegative) { + regex = "^-?" + regex; + } else { + regex = "^" + regex; + } + return _str.matches(regex); + } + + /** + * Reads a file to a List<String> (each line is one entry in list). + * Line endings (line feed/carriage return) are NOT removed! + * + * @param _fileName file to read + * @return list containing text + */ + public static List readFileToList(String _fileName) { + List localText = getTextfileFromUrl(_fileName, Charset.defaultCharset(), false); + return localText; + } + + /** + * Reads a file to a String. + * Line endings (line feed/carriage return) are NOT removed! + * + * @param _file file to read + * @return String containing content, maybe null + */ + public static String readFileToString(File _file) { + return String.join(System.lineSeparator(), readFileToList(_file.getAbsolutePath())); + } + + /** + * Reads a text file from the given URL using the provided charset. + * Using the _silent argument optionally disables all error logging. + * + * @param _url url providing the file to read + * @param _charset charset to use + * @param _silent true to not log exceptions, false otherwise + * @return list of string or null on error + */ + public static List getTextfileFromUrl(String _url, Charset _charset, boolean _silent) { + if (_url == null) { + return null; + } + String fileUrl = _url; + if (!fileUrl.contains("://")) { + fileUrl = "file://" + fileUrl; + } + + try { + URL dlUrl; + if (fileUrl.startsWith("file:/")) { + dlUrl = new URL("file", "", fileUrl.replaceFirst("file:\\/{1,2}", "")); + } else { + dlUrl = new URL(fileUrl); + } + URLConnection urlConn = dlUrl.openConnection(); + urlConn.setDoInput(true); + urlConn.setUseCaches(false); + + return readTextFileFromStream(urlConn.getInputStream(), _charset, _silent); + + } catch (IOException _ex) { + if (!_silent) { + LOGGER.warn("Error while reading file:", _ex); + } + } + + return null; + } + + /** + * Reads a text file from given {@link InputStream} using the given {@link Charset}. + * @param _input stream to read + * @param _charset charset to use + * @param _silent true to disable exception logging, false otherwise + * @return List of string or null on error + */ + public static List readTextFileFromStream(InputStream _input, Charset _charset, boolean _silent) { + if (_input == null) { + return null; + } + try { + List fileContent; + try (BufferedReader dis = new BufferedReader(new InputStreamReader(_input, _charset))) { + String s; + fileContent = new ArrayList<>(); + while ((s = dis.readLine()) != null) { + fileContent.add(s); + } + } + + return !fileContent.isEmpty() ? fileContent : null; + } catch (IOException _ex) { + if (!_silent) { + LOGGER.warn("Error while reading file:", _ex); + } + } + + return null; + } + + /** + * Write String to file with the given charset. + * Optionally appends the data to the file. + * + * @param _fileName the file to write + * @param _fileContent the content to write + * @param _charset the charset to use + * @param _append append content to file, if false file will be overwritten if existing + * + * @return true on successful write, false otherwise + */ + public static boolean writeTextFile(String _fileName, String _fileContent, Charset _charset, boolean _append) { + if (isBlank(_fileName)) { + return false; + } + String allText = ""; + if (_append) { + File file = new File(_fileName); + if (file.exists()) { + allText = readFileToString(file); + } + } + allText += _fileContent; + + try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(_fileName), _charset)) { + writer.write(allText); + } catch (IOException _ex) { + LOGGER.error("Could not write file to '" + _fileName + "'", _ex); + return false; + } + + return true; + } + + /** + * Gets the host name of the local machine. + * @return host name + */ + public static String getHostName() { + try { + return java.net.InetAddress.getLocalHost().getHostName(); + } catch (java.net.UnknownHostException _ex) { + return null; + } + } + + /** + * Checks if any of the 'needle' values are found in 'haystack'. + * + * @param type + * @param _haystack collection to check + * @param _needles values to find + * + * @return true if any value found, false if any parameter null or no matching value found + */ + public static boolean collectionContainsAny(Collection _haystack, Collection _needles) { + if (_haystack == null || _needles == null) { + return false; + } + + for (T t : _needles) { + if (_haystack.contains(t)) { + return true; + } + } + + return false; + } + + /** + * Determines the current logged on user. + * @return logged on user + */ + public static String getCurrentUser() { + String[] sysPropParms = new String[] {"user.name", "USER", "USERNAME"}; + for (int i = 0; i < sysPropParms.length; i++) { + String val = System.getProperty(sysPropParms[i]); + if (!isEmpty(val)) { + return val; + } + } + return null; + } + + /** + * Checks if the running OS is a MacOS/MacOS X. + * @return true if MacOS (or MacOS X), false otherwise + */ + public static boolean isMacOs() { + String osName = System.getProperty("os.name"); + return osName != null && osName.toLowerCase(Locale.US).startsWith("mac"); + } + + public static boolean isFreeBsd() { + String osName = System.getProperty("os.name"); + return osName != null && osName.toLowerCase(Locale.US).startsWith("freebsd"); + } + + /** + * Checks if the running OS is a MS Windows OS. + * @return true if Windows, false otherwise + */ + public static boolean isWindows() { + String osName = System.getProperty("os.name"); + return osName != null && osName.toLowerCase(Locale.US).startsWith("windows"); + } + + /** + * Get the java version of the current running JRE. + * + * @return java major + */ + public static int getJavaVersion() { + String version = System.getProperty("java.version"); + if (version.startsWith("1.")) { + version = version.substring(2, 3); + } else { + int dot = version.indexOf('.'); + if (dot != -1) { + version = version.substring(0, dot); + } + } + return Integer.parseInt(version); + } + + /** + * Create a random GUID (used for connection addresses). + * + * @return String + */ + public static String genGUID() { + Random r = new Random(); + byte[] buf = new byte[16]; + r.nextBytes(buf); + return Hexdump.toHex(buf, false); + } + + /** + * Creates a unix socket address using. + * + * @param _listeningSocket true if the address should be used for a listing socket + * @param _abstract true to create an abstract socket + * + * @return address String + */ + public static String createDynamicSessionAddress(boolean _listeningSocket, boolean _abstract) { + String address = "unix:"; + String path = new File(System.getProperty("java.io.tmpdir"), "dbus-XXXXXXXXXX").getAbsolutePath(); + Random r = new Random(); + + do { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < 10; i++) { + sb.append((char) (Math.abs(r.nextInt()) % 26) + 65); + } + path = path.replaceAll("..........$", sb.toString()); + LoggerFactory.getLogger(Util.class).trace("Trying path {}", path); + } while (new File(path).exists()); + + if (_abstract) { + address += "abstract=" + path; + } else { + address += "path=" + path; + } + + if (_listeningSocket) { + address += ",listen=true"; + } + + address += ",guid=" + Util.genGUID(); + + LoggerFactory.getLogger(Util.class).debug("Created Session address: {}", address); + + return address; + } + + /** + * Checks if given value is greater or equal to the given minimum and less or equal to the given maximum. + * + * @param _check value to check + * @param _min minimum allowed value (including) + * @param _max maximum allowed value (including) + * + * @return given value if in range + * @throws IllegalArgumentException when given value is out of range + * + * @since 4.2.0 - 2022-07-13 + */ + public static int checkIntInRange(int _check, int _min, int _max) { + if (_check >= _min && _check <= _max) { + return _check; + } + throw new IllegalArgumentException("Value " + _check + " is out ouf range (< " + _min + " && > " + _max + ")"); + } + + /** + * Setup the unix socket file permissions. + * User and group can always be set, file permissions are only set on non-windows OSes. + * + * @param _path path to file which where permissions should be set + * @param _fileOwner new owner for the file + * @param _fileGroup new group for the file + * @param _fileUnixPermissions unix permissions to set on file + */ + public static void setFilePermissions(Path _path, String _fileOwner, String _fileGroup, Set _fileUnixPermissions) { + Objects.requireNonNull(_path, "Path required"); + UserPrincipalLookupService userPrincipalLookupService = _path.getFileSystem().getUserPrincipalLookupService(); + + if (userPrincipalLookupService == null) { + LOGGER.error("Unable to set user/group permissions on {}", _path); + } + + if (!Util.isBlank(_fileOwner)) { + try { + UserPrincipal userPrincipal = userPrincipalLookupService.lookupPrincipalByName(_fileOwner); + if (userPrincipal != null) { + Files.getFileAttributeView(_path, PosixFileAttributeView.class, LinkOption.NOFOLLOW_LINKS).setOwner(userPrincipal); + } + } catch (IOException _ex) { + LOGGER.error("Could not change owner of {} to {}", _path, _fileOwner, _ex); + } + } + + if (!Util.isBlank(_fileGroup)) { + try { + GroupPrincipal groupPrincipal = userPrincipalLookupService.lookupPrincipalByGroupName(_fileGroup); + if (groupPrincipal != null) { + Files.getFileAttributeView(_path, PosixFileAttributeView.class, LinkOption.NOFOLLOW_LINKS).setGroup(groupPrincipal); + } + } catch (IOException _ex) { + LOGGER.error("Could not change group of {} to {}", _path, _fileGroup, _ex); + } + } + + if (!Util.isWindows() && _fileUnixPermissions != null) { + try { + Files.setPosixFilePermissions(_path, _fileUnixPermissions); + } catch (Exception _ex) { + LOGGER.error("Could not set file permissions of {} to {}", _path, _fileUnixPermissions, _ex); + } + } + } + + /** + * Waits for the provided supplier to return true or throws an exception. + *

+ * This method will call the provided supplier every _sleepTime milliseconds to check + * if the supplier returns true.
+ * If supplier returns true, method will return. + * If no value is present after the defined _timeoutMs a {@link IllegalStateException} is thrown. + * + * @param _lockName name for the lock (used in exception text and logging) + * @param _wait supplier to wait for + * @param _timeoutMs timeout in milliseconds when wait will fail + * @param _sleepTime sleep time between each retries + * + * @param exception type which might be thrown + * + * @throws T when timeout is reached + */ + @SuppressWarnings("unchecked") + public static void waitFor(String _lockName, IThrowingSupplier _wait, long _timeoutMs, long _sleepTime) throws T { + long waited = 0; + + boolean wait = false; + T lastEx = null; + do { + + try { + lastEx = null; + // supplier may throw, if so assume that we should still wait and retry + wait = _wait.get(); + } catch (Throwable _ex) { + lastEx = (T) _ex; + wait = false; + } + + if (waited >= _timeoutMs) { + if (lastEx != null) { // we have a timeout and last retry has thrown a exception + throw lastEx; + } + // we have a timeout and no exception happened before + throw new IllegalStateException(_lockName + " not available in the specified time of " + _timeoutMs + " ms"); + } + try { + Thread.sleep(_sleepTime); + } catch (InterruptedException _ex) { + LOGGER.debug("Interrupted while waiting for {}", _lockName); + break; + } + waited += _sleepTime; + LOGGER.debug("Waiting for {} to be available: {} of {} ms waited", _lockName, waited, _timeoutMs); + } while (!wait); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/utils/XmlErrorHandlers.java b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/XmlErrorHandlers.java new file mode 100644 index 0000000000..a7ca3ef46e --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/XmlErrorHandlers.java @@ -0,0 +1,60 @@ +package org.freedesktop.dbus.utils; + +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +/** + * Sample ErrorHandlers for XML Parsing. + * + * @author hypfvieh + */ +public class XmlErrorHandlers { + + /** + * XML Error Handler which will silently ignore all thrown Exceptions. + * + * @author hypfvieh + * @since v1.0.3 - 2018-01-10 + */ + public static class XmlErrorHandlerQuiet implements ErrorHandler { + + @Override + public void warning(SAXParseException _exception) throws SAXException { + } + + @Override + public void error(SAXParseException _exception) throws SAXException { + } + + @Override + public void fatalError(SAXParseException _exception) throws SAXException { + } + + } + + /** + * XML Error Handler which will throw RuntimeException if any Exception was thrown. + * + * @author hypfvieh + * @since v1.0.3 - 2018-01-10 + */ + public static class XmlErrorHandlerRuntimeException implements ErrorHandler { + + @Override + public void warning(SAXParseException _exception) throws SAXException { + throw new RuntimeException(_exception); + } + + @Override + public void error(SAXParseException _exception) throws SAXException { + throw new RuntimeException(_exception); + } + + @Override + public void fatalError(SAXParseException _exception) throws SAXException { + throw new RuntimeException(_exception); + } + + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/utils/XmlUtil.java b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/XmlUtil.java new file mode 100644 index 0000000000..258e9f375b --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/utils/XmlUtil.java @@ -0,0 +1,228 @@ +package org.freedesktop.dbus.utils; + +import org.freedesktop.dbus.utils.XmlErrorHandlers.XmlErrorHandlerQuiet; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.ErrorHandler; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +/** + * Assorted static XML utility methods. + * + * @author hypfvieh + */ +public final class XmlUtil { + + private XmlUtil() { + } + + /** + * Shortcut for checking if given node is of type {@link Element}. + * + * @param _node node + * @return true if {@link Element}, false otherwise + */ + public static boolean isElementType(Node _node) { + return _node instanceof Element; + } + + /** + * Checks and converts given {@link Node} to {@link Element} if possible. + * @param _node node + * @return {@link Element} or null if given {@link Node} is not {@link Element} subtype + */ + public static Element toElement(Node _node) { + if (isElementType(_node)) { + return (Element) _node; + } + return null; + } + + /** + * Applys a xpathExpression to a xml-Document and return a {@link NodeList} with the results. + * + * @param _xpathExpression xpath expression + * @param _xmlDocumentOrNode document or node + * @return {@link NodeList} + * @throws IOException on error + */ + public static NodeList applyXpathExpressionToDocument(String _xpathExpression, Node _xmlDocumentOrNode) + throws IOException { + + XPathFactory xfactory = XPathFactory.newInstance(); + XPath xpath = xfactory.newXPath(); + XPathExpression expr = null; + try { + expr = xpath.compile(_xpathExpression); + } catch (XPathExpressionException _ex) { + throw new IOException(_ex); + } + + Object result = null; + try { + result = expr.evaluate(_xmlDocumentOrNode, XPathConstants.NODESET); + } catch (Exception _ex) { + throw new IOException(_ex); + } + + return (NodeList) result; + } + + /** + * Read the given string as XML document. + * + * @param _xmlStr xml string + * @param _validating boolean + * @param _namespaceAware boolean + * @return {@link org.w3c.dom.Document} + * @throws IOException on error + */ + public static Document parseXmlString(String _xmlStr, boolean _validating, boolean _namespaceAware) throws IOException { + + DocumentBuilderFactory dbFac = DocumentBuilderFactory.newInstance(); + dbFac.setNamespaceAware(_namespaceAware); + dbFac.setValidating(_validating); + + try { + return dbFac.newDocumentBuilder().parse(new ByteArrayInputStream(_xmlStr.getBytes(StandardCharsets.UTF_8))); + + } catch (IOException _ex) { + throw _ex; + } catch (Exception _ex) { + throw new IOException("Failed to parse " + Util.abbreviate(_xmlStr, 500), _ex); + } + + } + + /** + * Convert a {@link NodeList} to a Java {@link List} of {@link Element}s. + * @param _nodeList collection of nodes + * @return list of elements + */ + public static List convertToElementList(NodeList _nodeList) { + List elemList = new ArrayList<>(); + for (int i = 0; i < _nodeList.getLength(); i++) { + Element elem = (Element) _nodeList.item(i); + elemList.add(elem); + } + return elemList; + } + + /** + * Converts {@link NamedNodeMap} to a {@link LinkedHashMap}<String,String>. + * @param _nodeMap node map + * @return {@link LinkedHashMap}, maybe empty but never null + */ + public static Map convertToAttributeMap(NamedNodeMap _nodeMap) { + Map map = new LinkedHashMap<>(); + for (int i = 0; i < _nodeMap.getLength(); i++) { + Node node = _nodeMap.item(i); + map.put(node.getNodeName(), node.getNodeValue()); + } + return map; + } + + /** + * Loads XML from string and uses referenced XSD to validate the content. + * + * + * @param _xmlStr string to validate + * @param _namespaceAware take care of namespace + * @param _errorHandler e.g. {@link XmlErrorHandlers.XmlErrorHandlerQuiet} or {@link XmlErrorHandlers.XmlErrorHandlerRuntimeException} + * @return Document + * @throws IOException on error + */ + public static Document parseXmlStringWithXsdValidation(String _xmlStr, boolean _namespaceAware, ErrorHandler _errorHandler) throws IOException { + ErrorHandler handler = _errorHandler; + + if (_errorHandler == null) { + handler = new XmlErrorHandlers.XmlErrorHandlerQuiet(); + } + + DocumentBuilderFactory dbFac = DocumentBuilderFactory.newInstance(); + dbFac.setValidating(true); + dbFac.setNamespaceAware(_namespaceAware); + dbFac.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", + "http://www.w3.org/2001/XMLSchema"); + try { + DocumentBuilder builder = dbFac.newDocumentBuilder(); + builder.setErrorHandler(handler); + return builder.parse(new ByteArrayInputStream(_xmlStr.getBytes(StandardCharsets.UTF_8))); + + } catch (IOException _ex) { + throw _ex; + } catch (Exception _ex) { + throw new IOException("Failed to parse " + Util.abbreviate(_xmlStr, 500), _ex); + } + } + + /** + * Loads XML from string and uses referenced XSD to validate the content. + * This method will use {@link XmlErrorHandlerQuiet} to suppress all errors/warnings when validating. + * + * @param _xmlStr string to validate + * @param _namespaceAware take care of namespace + * @return Document + * @throws IOException on error + */ + public static Document parseXmlStringWithXsdValidation(String _xmlStr, boolean _namespaceAware) throws IOException { + return parseXmlStringWithXsdValidation(_xmlStr, _namespaceAware, null); + } + + /** + * Dump a {@link Document} or {@link Node}-compatible object to the given {@link OutputStream} (e.g. System.out). + * + * @param _docOrNode {@link Document} or {@link Node} object + * @param _outStream {@link OutputStream} to print on + * @throws IOException on error + */ + public static void printDocument(Node _docOrNode, OutputStream _outStream) throws IOException { + if (_docOrNode == null || _outStream == null) { + throw new IOException("Cannot print (on) 'null' object"); + } + + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer; + try { + transformer = tf.newTransformer(); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + + transformer.transform(new DOMSource(_docOrNode), + new StreamResult(new OutputStreamWriter(_outStream, "UTF-8"))); + } catch (UnsupportedEncodingException | TransformerException _ex) { + throw new IOException("Could not print Document or Node.", _ex); + } + + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/Cache.java b/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/Cache.java deleted file mode 100644 index 1a45839579..0000000000 --- a/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/Cache.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.freedesktop.sssd.infopipe; - -import org.freedesktop.dbus.DBusInterface; - -import java.util.List; - -/** - * @author Bruno Oliveira. - */ -public interface Cache extends DBusInterface { - - List List(); - - List ListByDomain(String domain_name); - -} diff --git a/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/InfoPipe.java b/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/InfoPipe.java index 9ef979cdd7..aabd4e01d0 100644 --- a/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/InfoPipe.java +++ b/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/InfoPipe.java @@ -17,13 +17,12 @@ package org.freedesktop.sssd.infopipe; -import org.freedesktop.dbus.DBusInterface; -import org.freedesktop.dbus.DBusInterfaceName; -import org.freedesktop.dbus.DBusMemberName; -import org.freedesktop.dbus.Variant; - import java.util.List; import java.util.Map; +import org.freedesktop.dbus.annotations.DBusInterfaceName; +import org.freedesktop.dbus.annotations.DBusMemberName; +import org.freedesktop.dbus.interfaces.DBusInterface; +import org.freedesktop.dbus.types.Variant; /** * @author Bruno Oliveira. diff --git a/federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/NativeTransportProvider.java b/federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/NativeTransportProvider.java new file mode 100644 index 0000000000..5734f86a24 --- /dev/null +++ b/federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/NativeTransportProvider.java @@ -0,0 +1,38 @@ +package org.freedesktop.dbus.transport.jre; + +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.connections.config.TransportConfig; +import org.freedesktop.dbus.connections.transports.AbstractTransport; +import org.freedesktop.dbus.exceptions.TransportConfigurationException; +import org.freedesktop.dbus.spi.transport.ITransportProvider; +import org.freedesktop.dbus.utils.Util; + +public class NativeTransportProvider implements ITransportProvider { + + @Override + public String getTransportName() { + return "dbus-java-transport-native-unixsocket"; + } + + @Override + public AbstractTransport createTransport(BusAddress _address, TransportConfig _config) throws TransportConfigurationException { + UnixBusAddress address; + if (!(_address instanceof UnixBusAddress)) { + address = new UnixBusAddress(_address); + } else { + address = (UnixBusAddress) _address; + } + return new NativeUnixSocketTransport(address, _config); + } + + @Override + public String getSupportedBusType() { + return "UNIX"; + } + + @Override + public String createDynamicSessionAddress(boolean _listeningSocket) { + return Util.createDynamicSessionAddress(_listeningSocket, false); // native unix sockets do not support abstract sockets + } + +} diff --git a/federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/NativeUnixSocketHelper.java b/federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/NativeUnixSocketHelper.java new file mode 100644 index 0000000000..e91ecda0b8 --- /dev/null +++ b/federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/NativeUnixSocketHelper.java @@ -0,0 +1,33 @@ +package org.freedesktop.dbus.transport.jre; + +import jdk.net.ExtendedSocketOptions; +import jdk.net.UnixDomainPrincipal; + +import java.io.IOException; +import java.nio.channels.SocketChannel; +import java.nio.file.attribute.UserPrincipal; + +public final class NativeUnixSocketHelper { + + private NativeUnixSocketHelper() {} + + /** + * Get the UID of peer credentials. + * + * @param _sock socket to read from + * @return UID, -1 if given {@link SocketChannel} was null + * + * @throws IOException when socket channel fails to read SO_PEERCRED option + */ + public static int getUid(SocketChannel _sock) throws IOException { + if (_sock == null) { + return -1; + } + + UnixDomainPrincipal creds = _sock.getOption(ExtendedSocketOptions.SO_PEERCRED); + UserPrincipal user = creds.user(); + + return Integer.parseInt(user.getName()); + } + +} diff --git a/federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/NativeUnixSocketTransport.java b/federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/NativeUnixSocketTransport.java new file mode 100644 index 0000000000..e875f06dfd --- /dev/null +++ b/federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/NativeUnixSocketTransport.java @@ -0,0 +1,100 @@ +package org.freedesktop.dbus.transport.jre; + +import org.freedesktop.dbus.connections.SASL; +import org.freedesktop.dbus.connections.config.TransportConfig; +import org.freedesktop.dbus.connections.transports.AbstractUnixTransport; +import org.freedesktop.dbus.exceptions.TransportConfigurationException; + +import java.io.IOException; +import java.net.StandardProtocolFamily; +import java.net.UnixDomainSocketAddress; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +/** + * Transport type representing a transport connection to a unix socket. + * This implementation uses features of Java 16+ to connect to a unix + * socket without a 3rd party library. + *

+ * Please note: The functionality of the native unix sockets in Java are + * limited. 'Side-channel' communication (e.g. passing file descriptors) + * is not possible (unlike using jnr-unix socket + dbus-java-nativefd). + *

+ * Also using 'abstract' sockets is not possible when using this native implementation. + *
+ * In most cases this implementation should suit our needs. + * If it does not fit for you, use jnr-unixsocket instead. + * + * @author hypfvieh + * @since v4.0.0 - 2021-09-01 + */ +public class NativeUnixSocketTransport extends AbstractUnixTransport { + private final UnixDomainSocketAddress unixSocketAddress; + private SocketChannel socket; + private ServerSocketChannel serverSocket; + + NativeUnixSocketTransport(UnixBusAddress _address, TransportConfig _config) throws TransportConfigurationException { + super(_address, _config); + + if (_address.hasPath()) { + unixSocketAddress = UnixDomainSocketAddress.of(_address.getPath()); + } else { + throw new TransportConfigurationException("Native unix socket url has to specify 'path'"); + } + + getSaslConfig().setAuthMode(SASL.AUTH_EXTERNAL); + } + + @Override + protected boolean hasFileDescriptorSupport() { + return true; // file descriptor passing allowed when using UNIX_SOCK + } + + /** + * Establish a connection to DBus using unix sockets. + * + * @throws IOException on error + */ + @Override + public SocketChannel connectImpl() throws IOException { + if (getAddress().isListeningSocket()) { + if (serverSocket == null || !serverSocket.isOpen()) { + serverSocket = ServerSocketChannel.open(StandardProtocolFamily.UNIX); + serverSocket.bind(unixSocketAddress); + } + socket = serverSocket.accept(); + } else { + socket = SocketChannel.open(unixSocketAddress); + } + + socket.configureBlocking(true); + + return socket; + } + + @Override + public void close() throws IOException { + getLogger().debug("Disconnecting Transport"); + + super.close(); + + if (socket != null && socket.isOpen()) { + socket.close(); + } + + if (serverSocket != null && serverSocket.isOpen()) { + serverSocket.close(); + } + } + + @Deprecated + @Override + public boolean isAbstractAllowed() { + return false; + } + + @Override + public int getUid(SocketChannel _sock) throws IOException { + return NativeUnixSocketHelper.getUid(_sock); + } +} diff --git a/federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/UnixBusAddress.java b/federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/UnixBusAddress.java new file mode 100644 index 0000000000..f44fecde81 --- /dev/null +++ b/federation/sssd/src/main/java16/org/freedesktop/dbus/transport/jre/UnixBusAddress.java @@ -0,0 +1,30 @@ +package org.freedesktop.dbus.transport.jre; + +import org.freedesktop.dbus.connections.BusAddress; +import org.freedesktop.dbus.connections.transports.IFileBasedBusAddress; +import org.freedesktop.dbus.utils.Util; + +import java.nio.file.Path; +import java.nio.file.attribute.PosixFilePermission; +import java.util.Set; + +public class UnixBusAddress extends BusAddress implements IFileBasedBusAddress { + + public UnixBusAddress(BusAddress _obj) { + super(_obj); + } + + public boolean hasPath() { + return hasParameter("path"); + } + + public String getPath() { + return getParameterValue("path"); + } + + @Override + public void updatePermissions(String _fileOwner, String _fileGroup, Set _fileUnixPermissions) { + Util.setFilePermissions(Path.of(getPath()), _fileOwner, _fileGroup, _fileUnixPermissions); + } + +} diff --git a/federation/sssd/src/main/resources/META-INF/services/org.freedesktop.dbus.spi.transport.ITransportProvider b/federation/sssd/src/main/resources/META-INF/services/org.freedesktop.dbus.spi.transport.ITransportProvider new file mode 100644 index 0000000000..cf4dabc54c --- /dev/null +++ b/federation/sssd/src/main/resources/META-INF/services/org.freedesktop.dbus.spi.transport.ITransportProvider @@ -0,0 +1 @@ +org.freedesktop.dbus.transport.jre.NativeTransportProvider \ No newline at end of file