diff --git a/java/client/src/main/java/org/signal/libsignal/chat/DeviceClient.java b/java/client/src/main/java/org/signal/libsignal/chat/DeviceClient.java new file mode 100644 index 0000000000..f04b987fa4 --- /dev/null +++ b/java/client/src/main/java/org/signal/libsignal/chat/DeviceClient.java @@ -0,0 +1,40 @@ +package org.signal.libsignal.chat; + +import com.google.protobuf.InvalidProtocolBufferException; +import org.signal.chat.device.GetDevicesRequest; +import org.signal.chat.device.GetDevicesResponse; +import org.signal.libsignal.internal.Native; +import org.signal.libsignal.internal.NativeHandleGuard; + +public class DeviceClient implements NativeHandleGuard.Owner { + + private static final String DEFAULT_TARGET = "https://grpcproxy.gluonhq.net:443"; + + private final long unsafeHandle; + + public DeviceClient() { + this(DEFAULT_TARGET); + } + + public DeviceClient(String target) { + this.unsafeHandle = Native.DeviceClient_New(target); + } + + @Override @SuppressWarnings("deprecation") + protected void finalize() { + Native.ProfileClient_Destroy(this.unsafeHandle); + } + + public long unsafeNativeHandleWithoutGuard() { + return this.unsafeHandle; + } + + public GetDevicesResponse getDevices(GetDevicesRequest request, String authorization) throws SignalChatCommunicationFailureException { + try (NativeHandleGuard guard = new NativeHandleGuard(this)) { + byte[] serializedResponse = Native.DeviceClient_GetDevices(guard.nativeHandle(), request.toByteArray(), authorization); + return GetDevicesResponse.parseFrom(serializedResponse); + } catch (InvalidProtocolBufferException e) { + throw new SignalChatCommunicationFailureException(e); + } + } +} diff --git a/java/client/src/main/java/org/signal/libsignal/profile/ProfileClient.java b/java/client/src/main/java/org/signal/libsignal/chat/ProfileClient.java similarity index 92% rename from java/client/src/main/java/org/signal/libsignal/profile/ProfileClient.java rename to java/client/src/main/java/org/signal/libsignal/chat/ProfileClient.java index 9b1a843a64..5a6a36dc85 100644 --- a/java/client/src/main/java/org/signal/libsignal/profile/ProfileClient.java +++ b/java/client/src/main/java/org/signal/libsignal/chat/ProfileClient.java @@ -1,9 +1,8 @@ -package org.signal.libsignal.profile; +package org.signal.libsignal.chat; import com.google.protobuf.InvalidProtocolBufferException; import org.signal.chat.profile.GetVersionedProfileRequest; import org.signal.chat.profile.GetVersionedProfileResponse; -import org.signal.libsignal.SignalChatCommunicationFailureException; import org.signal.libsignal.internal.Native; import org.signal.libsignal.internal.NativeHandleGuard; diff --git a/java/client/src/main/java/org/signal/libsignal/SignalChatCommunicationFailureException.java b/java/client/src/main/java/org/signal/libsignal/chat/SignalChatCommunicationFailureException.java similarity index 86% rename from java/client/src/main/java/org/signal/libsignal/SignalChatCommunicationFailureException.java rename to java/client/src/main/java/org/signal/libsignal/chat/SignalChatCommunicationFailureException.java index a35fa92a7c..30bb4bac69 100644 --- a/java/client/src/main/java/org/signal/libsignal/SignalChatCommunicationFailureException.java +++ b/java/client/src/main/java/org/signal/libsignal/chat/SignalChatCommunicationFailureException.java @@ -1,4 +1,4 @@ -package org.signal.libsignal; +package org.signal.libsignal.chat; public class SignalChatCommunicationFailureException extends Exception { public SignalChatCommunicationFailureException(String msg) { super(msg); } diff --git a/java/shared/java/org/signal/libsignal/internal/Native.java b/java/shared/java/org/signal/libsignal/internal/Native.java index d0b04c35e0..567c21ac09 100644 --- a/java/shared/java/org/signal/libsignal/internal/Native.java +++ b/java/shared/java/org/signal/libsignal/internal/Native.java @@ -7,7 +7,6 @@ package org.signal.libsignal.internal; -import org.signal.chat.profile.GetVersionedProfileResponse; import org.signal.libsignal.grpc.GrpcReplyListener; import org.signal.libsignal.protocol.message.CiphertextMessage; import org.signal.libsignal.protocol.state.IdentityKeyStore; @@ -178,6 +177,10 @@ private Native() {} public static native byte[] DecryptionErrorMessage_GetSerialized(long obj); public static native long DecryptionErrorMessage_GetTimestamp(long obj); + public static native void DeviceClient_Destroy(long handle); + public static native byte[] DeviceClient_GetDevices(long deviceClient, byte[] request, String authorization); + public static native long DeviceClient_New(String target); + public static native byte[] DeviceTransfer_GenerateCertificate(byte[] privateKey, String name, int daysToExpire); public static native byte[] DeviceTransfer_GeneratePrivateKey(); diff --git a/rust/bridge/jni/bin/Native.java.in b/rust/bridge/jni/bin/Native.java.in index d1be869eca..9ad2e52cfc 100644 --- a/rust/bridge/jni/bin/Native.java.in +++ b/rust/bridge/jni/bin/Native.java.in @@ -7,7 +7,6 @@ package org.signal.libsignal.internal; -import org.signal.chat.profile.GetVersionedProfileResponse; import org.signal.libsignal.grpc.GrpcReplyListener; import org.signal.libsignal.protocol.message.CiphertextMessage; import org.signal.libsignal.protocol.state.IdentityKeyStore; diff --git a/rust/bridge/shared/src/chat.rs b/rust/bridge/shared/src/chat.rs index 8c6f77d72d..1c6a984432 100644 --- a/rust/bridge/shared/src/chat.rs +++ b/rust/bridge/shared/src/chat.rs @@ -3,4 +3,5 @@ // SPDX-License-Identifier: AGPL-3.0-only // +pub mod device; pub mod profile; diff --git a/rust/bridge/shared/src/chat/device.rs b/rust/bridge/shared/src/chat/device.rs new file mode 100644 index 0000000000..72f0c610f9 --- /dev/null +++ b/rust/bridge/shared/src/chat/device.rs @@ -0,0 +1,27 @@ +// +// Copyright 2023 Signal Messenger, LLC. +// SPDX-License-Identifier: AGPL-3.0-only +// + +use libsignal_bridge_macros::*; +use signal_chat::Result; +use signal_chat::device::DeviceClient; + +use crate::support::*; +use crate::*; + +bridge_handle!(DeviceClient, clone = false, mut = true); + +#[bridge_fn(ffi = false, node = false)] +pub fn DeviceClient_New(target: String) -> Result { + DeviceClient::new(target) +} + +#[bridge_fn(ffi = false, node = false)] +pub fn DeviceClient_GetDevices( + device_client: &mut DeviceClient, + request: &[u8], + authorization: String, +) -> Result> { + device_client.get_devices(request, authorization) +} diff --git a/rust/chat/src/device.rs b/rust/chat/src/device.rs new file mode 100644 index 0000000000..16ccb81ff8 --- /dev/null +++ b/rust/chat/src/device.rs @@ -0,0 +1,8 @@ +// +// Copyright 2023 Signal Messenger, LLC. +// SPDX-License-Identifier: AGPL-3.0-only +// + +pub mod client; + +pub use client::DeviceClient; diff --git a/rust/chat/src/device/client.rs b/rust/chat/src/device/client.rs new file mode 100644 index 0000000000..098b917985 --- /dev/null +++ b/rust/chat/src/device/client.rs @@ -0,0 +1,64 @@ +// +// Copyright 2023 Signal Messenger, LLC. +// SPDX-License-Identifier: AGPL-3.0-only +// + +use prost::Message; + +use crate::error::{Error, Result}; +use crate::proto::signal::device::{devices_client, GetDevicesRequest}; +use std::panic::RefUnwindSafe; + +pub struct DeviceClient { + target: String, + pub tokio_runtime: tokio::runtime::Runtime, +} + +impl RefUnwindSafe for DeviceClient {} + +impl DeviceClient { + pub fn new(target: String) -> Result { + Ok(DeviceClient { + target, + tokio_runtime: tokio::runtime::Builder::new_multi_thread() + .enable_io() + .enable_time() + .build() + .map_err(|e| Error::InvalidArgument(format!("tokio.create_runtime: {:?}", e)))?, + }) + } + + pub fn target(&mut self, target: &str) { + self.target = target.to_owned(); + } + + pub fn get_devices(&self, request: &[u8], authorization: String) -> Result> { + self.tokio_runtime + .block_on(async { self.async_get_devices(request, authorization).await }) + } + + async fn async_get_devices(&self, request: &[u8], authorization: String) -> Result> { + let channel = tonic::transport::Channel::from_shared(self.target.clone()) + .map_err(|e| Error::InvalidArgument(format!("devices_client.connect: {:?}", e)))? + .connect() + .await + .map_err(|e| Error::InvalidArgument(format!("devices_client.connect: {:?}", e)))?; + + let mut devices_client = devices_client::DevicesClient::with_interceptor(channel, move |mut req: tonic::Request<()>| { + req.metadata_mut() + .insert("authorization", tonic::metadata::MetadataValue::try_from(&authorization).unwrap()); + Ok(req) + }); + + let request: GetDevicesRequest = Message::decode(&request[..]) + .map_err(|e| Error::InvalidArgument(format!("devices_client.decode: {:?}", e)))?; + + let response = devices_client + .get_devices(request) + .await + .map_err(|e| Error::InvalidArgument(format!("devices_client.get_devices: {:?}", e)))?; + + let response = response.into_inner(); + Ok(response.encode_to_vec()) + } +} diff --git a/rust/chat/src/lib.rs b/rust/chat/src/lib.rs index 5388d94480..000aee4609 100644 --- a/rust/chat/src/lib.rs +++ b/rust/chat/src/lib.rs @@ -4,6 +4,7 @@ // mod error; +pub mod device; pub mod profile; mod proto;