diff --git a/presage-cli/src/main.rs b/presage-cli/src/main.rs index 7398b6d12..b17641c34 100644 --- a/presage-cli/src/main.rs +++ b/presage-cli/src/main.rs @@ -503,7 +503,9 @@ async fn run(subcommand: Cmd, config_store: C) -> anyhow::Re async move { match provisioning_link_rx.await { Ok(url) => { - qr2term::print_qr(url.to_string()).expect("failed to render qrcode") + println!("Please scan in the QR code:"); + qr2term::print_qr(url.to_string()).expect("failed to render qrcode"); + println!("Alternatively, use the URL: {}", url); } Err(e) => log::error!("Error linking device: {e}"), } diff --git a/presage-store-sled/src/lib.rs b/presage-store-sled/src/lib.rs index 757f5f51e..ed689d5a7 100644 --- a/presage-store-sled/src/lib.rs +++ b/presage-store-sled/src/lib.rs @@ -122,9 +122,12 @@ impl SledStore { #[cfg(feature = "encryption")] let cipher = passphrase + .as_ref() .map(|p| Self::get_or_create_store_cipher(&database, p.as_ref())) .transpose()?; + dbg!(cipher.is_some()); + #[cfg(not(feature = "encryption"))] if passphrase.is_some() { panic!("A passphrase was supplied but the encryption feature flag is not enabled") @@ -299,7 +302,7 @@ fn migrate( let run_migrations = move || { let mut store = SledStore::new(db_path, passphrase)?; - let schema_version = store.schema_version(); + let schema_version = dbg!(store.schema_version()); for step in schema_version.steps() { match &step { SchemaVersion::V1 => { @@ -374,11 +377,11 @@ impl Store for SledStore { /// State fn load_state(&self) -> Result, SledStoreError> { - self.get(SLED_TREE_STATE, SLED_KEY_REGISTRATION) + dbg!(self.get(SLED_TREE_STATE, SLED_KEY_REGISTRATION)) } fn save_state(&mut self, state: &Registered) -> Result<(), SledStoreError> { - self.insert(SLED_TREE_STATE, SLED_KEY_REGISTRATION, state)?; + self.insert(SLED_TREE_STATE, SLED_KEY_REGISTRATION, dbg!(state))?; Ok(()) } @@ -656,8 +659,10 @@ impl SledContactsIter { #[cfg(feature = "encryption")] fn decrypt_value(&self, value: &[u8]) -> Result { if let Some(cipher) = self.cipher.as_ref() { + log::trace!("Decrypt with cipher"); Ok(cipher.decrypt_value(value)?) } else { + log::trace!("Decrypt no cipher"); Ok(serde_json::from_slice(value)?) } } diff --git a/presage/Cargo.toml b/presage/Cargo.toml index f7dbc1f9b..16a46f186 100644 --- a/presage/Cargo.toml +++ b/presage/Cargo.toml @@ -6,8 +6,8 @@ authors = ["Gabriel FĂ©ron "] edition = "2021" [dependencies] -libsignal-service = { git = "https://github.com/whisperfish/libsignal-service-rs", rev = "454d234" } -libsignal-service-hyper = { git = "https://github.com/whisperfish/libsignal-service-rs", rev = "454d234" } +libsignal-service = { git = "https://github.com/whisperfish/libsignal-service-rs", rev = "aa84243" } +libsignal-service-hyper = { git = "https://github.com/whisperfish/libsignal-service-rs", rev = "aa84243" } base64 = "0.12" futures = "0.3" diff --git a/presage/src/errors.rs b/presage/src/errors.rs index 2507b0c2a..1d40da399 100644 --- a/presage/src/errors.rs +++ b/presage/src/errors.rs @@ -66,6 +66,10 @@ pub enum Error { RequestingCodeForbidden(libsignal_service::push_service::RegistrationSessionMetadataResponse), #[error("Unverified registration session (i.e. wrong verification code)")] UnverifiedRegistrationSession, + #[error("Failed to link secondary device")] + ServiceLinkError(#[from] libsignal_service::LinkError), + #[error("An operation was requested that requires the registration to be primary, but it was only secondary")] + NotPrimaryDevice, } impl From for Error { diff --git a/presage/src/lib.rs b/presage/src/lib.rs index 60a9b29c4..ac38cd9ad 100644 --- a/presage/src/lib.rs +++ b/presage/src/lib.rs @@ -5,7 +5,9 @@ mod serde; mod store; pub use errors::Error; -pub use manager::{Confirmation, Linking, Manager, Registered, Registration, RegistrationOptions}; +pub use manager::{ + Confirmation, Linking, Manager, Registered, Registration, RegistrationOptions, RegistrationType, +}; pub use store::{ContentTimestamp, Store, StoreError, Thread}; #[deprecated(note = "Please help use improve the prelude module instead")] diff --git a/presage/src/manager.rs b/presage/src/manager.rs index f06bdc529..5826357cd 100644 --- a/presage/src/manager.rs +++ b/presage/src/manager.rs @@ -17,7 +17,7 @@ use serde::{Deserialize, Serialize}; use url::Url; use libsignal_service::proto::EditMessage; -use libsignal_service::push_service::{RegistrationMethod, VerificationTransport}; +use libsignal_service::push_service::{DeviceInfo, RegistrationMethod, VerificationTransport}; use libsignal_service::{ attachment_cipher::decrypt_in_place, cipher, @@ -74,6 +74,12 @@ impl fmt::Debug for Manager { } } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RegistrationType { + Primary, + Secondary, +} + #[derive(Clone, Serialize, Deserialize)] pub struct RegistrationOptions<'a> { pub signal_servers: SignalServers, @@ -1287,6 +1293,53 @@ impl Manager { } } + /// Returns how this client was registered, either as a primary or secondary device. + pub fn registration_type(&self) -> RegistrationType { + if self.state.device_name.is_some() { + RegistrationType::Secondary + } else { + RegistrationType::Primary + } + } + + /// As a primary device, link a secondary device. + pub async fn link_secondary(&self, secondary: Url) -> Result<(), Error> { + // XXX: What happens if secondary device? Possible to use static typing to make this method call impossible in that case? + if self.registration_type() != RegistrationType::Primary { + return Err(Error::NotPrimaryDevice); + } + + let credentials = self.credentials()?.ok_or(Error::NotYetRegisteredError)?; + let mut account_manager = + AccountManager::new(self.push_service()?, Some(self.state.profile_key)); + let store = &self.config_store; + + account_manager + .link_device(secondary, store, credentials) + .await?; + Ok(()) + } + + /// As a primary device, unlink a secondary device. + pub async fn unlink_secondary(&self, device_id: i64) -> Result<(), Error> { + // XXX: What happens if secondary device? Possible to use static typing to make this method call impossible in that case? + if self.registration_type() != RegistrationType::Primary { + return Err(Error::NotPrimaryDevice); + } + self.push_service()?.unlink_device(device_id).await?; + Ok(()) + } + + /// As a primary device, list all the devices. + // XXX: Also shows the current device? + pub async fn linked_devices(&self) -> Result, Error> { + // XXX: What happens if secondary device? Possible to use static typing to make this method call impossible in that case? + if self.registration_type() != RegistrationType::Primary { + return Err(Error::NotPrimaryDevice); + } + Ok(self.push_service()?.devices().await?) + } + #[deprecated = "use Manager::contact_by_id"] pub fn get_contacts( &self,