diff --git a/presage-cli/src/main.rs b/presage-cli/src/main.rs index 3c31716e4..c080cf967 100644 --- a/presage-cli/src/main.rs +++ b/presage-cli/src/main.rs @@ -17,13 +17,14 @@ use presage::libsignal_service::content::Reaction; use presage::libsignal_service::models::Contact; use presage::libsignal_service::pre_keys::PreKeysStore; use presage::libsignal_service::prelude::phonenumber::PhoneNumber; +use presage::libsignal_service::prelude::ProfileKey; use presage::libsignal_service::prelude::Uuid; use presage::libsignal_service::proto::data_message::Quote; use presage::libsignal_service::proto::sync_message::Sent; use presage::libsignal_service::zkgroup::GroupMasterKeyBytes; use presage::libsignal_service::ServiceAddress; -use presage::libsignal_service::{groups_v2::Group, prelude::ProfileKey}; use presage::manager::ReceivingMode; +use presage::model::groups::Group; use presage::proto::receipt_message; use presage::proto::EditMessage; use presage::proto::ReceiptMessage; @@ -256,7 +257,7 @@ async fn send( Recipient::Contact(uuid) => { info!(recipient =% uuid, "sending message to contact"); manager - .send_message(ServiceAddress::new_aci(uuid), content_body, timestamp) + .send_message(ServiceAddress::from_aci(uuid), content_body, timestamp) .await .expect("failed to send message"); } diff --git a/presage-store-sled/src/content.rs b/presage-store-sled/src/content.rs index 8f5d57d4f..c13248590 100644 --- a/presage-store-sled/src/content.rs +++ b/presage-store-sled/src/content.rs @@ -6,12 +6,12 @@ use std::{ use presage::{ libsignal_service::{ content::Content, - groups_v2::Group, models::Contact, prelude::Uuid, zkgroup::{profiles::ProfileKey, GroupMasterKeyBytes}, Profile, }, + model::groups::Group, store::{ContentExt, ContentsStore, StickerPack, Thread}, AvatarBytes, }; @@ -116,9 +116,9 @@ impl ContentsStore for SledStore { fn save_group( &self, master_key: GroupMasterKeyBytes, - group: &Group, + group: impl Into, ) -> Result<(), SledStoreError> { - self.insert(SLED_TREE_GROUPS, master_key, group)?; + self.insert(SLED_TREE_GROUPS, master_key, group.into())?; Ok(()) } diff --git a/presage-store-sled/src/protobuf.rs b/presage-store-sled/src/protobuf.rs index b087d1ee6..1f1dd2d82 100644 --- a/presage-store-sled/src/protobuf.rs +++ b/presage-store-sled/src/protobuf.rs @@ -34,7 +34,7 @@ impl TryFrom for ServiceAddress { .uuid .and_then(|bytes| Some(Uuid::from_bytes(bytes.try_into().ok()?))) .ok_or_else(|| SledStoreError::NoUuid) - .map(Self::new_aci) + .map(Self::from_aci) } } @@ -60,7 +60,7 @@ impl TryFrom for Metadata { fn try_from(metadata: MetadataProto) -> Result { Ok(Metadata { sender: metadata.address.ok_or(SledStoreError::NoUuid)?.try_into()?, - destination: ServiceAddress::new_aci(match metadata.destination_uuid.as_deref() { + destination: ServiceAddress::from_aci(match metadata.destination_uuid.as_deref() { Some(value) => value.parse().map_err(|_| SledStoreError::NoUuid), None => Ok(Uuid::nil()), }?), diff --git a/presage/Cargo.toml b/presage/Cargo.toml index 00837612f..7d290d1d5 100644 --- a/presage/Cargo.toml +++ b/presage/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" license = "AGPL-3.0-only" [dependencies] -libsignal-service = { git = "https://github.com/whisperfish/libsignal-service-rs", rev = "701ee93358b3fce75e26489b530e807a76254166" } +libsignal-service = { git = "https://github.com/whisperfish/libsignal-service-rs", rev = "72f89cdf6bdda1c828319e50f65d8ead2c376351" } base64 = "0.22" futures = "0.3" @@ -20,6 +20,8 @@ thiserror = "1.0" tokio = { version = "1.35", default-features = false, features = ["sync", "time"] } tracing = "0.1" url = "2.5" +serde_with = "3.11.0" +derivative = "2.2.0" [dev-dependencies] quickcheck = "1.0.3" diff --git a/presage/src/lib.rs b/presage/src/lib.rs index 0b09a9eda..55ce9e1f3 100644 --- a/presage/src/lib.rs +++ b/presage/src/lib.rs @@ -1,5 +1,6 @@ mod errors; pub mod manager; +pub mod model; mod serde; pub mod store; diff --git a/presage/src/manager/registered.rs b/presage/src/manager/registered.rs index 20c36b6c5..6d7067857 100644 --- a/presage/src/manager/registered.rs +++ b/presage/src/manager/registered.rs @@ -8,7 +8,7 @@ use futures::{future, AsyncReadExt, Stream, StreamExt}; use libsignal_service::attachment_cipher::decrypt_in_place; use libsignal_service::configuration::{ServiceConfiguration, SignalServers, SignalingKey}; use libsignal_service::content::{Content, ContentBody, DataMessageFlags, Metadata}; -use libsignal_service::groups_v2::{decrypt_group, Group, GroupsManager, InMemoryCredentialsCache}; +use libsignal_service::groups_v2::{decrypt_group, GroupsManager, InMemoryCredentialsCache}; use libsignal_service::messagepipe::{Incoming, MessagePipe, ServiceCredentials}; use libsignal_service::models::Contact; use libsignal_service::prelude::phonenumber::PhoneNumber; @@ -45,7 +45,7 @@ use url::Url; use crate::serde::serde_profile_key; use crate::store::{ContentsStore, Sticker, StickerPack, StickerPackManifest, Store, Thread}; -use crate::{AvatarBytes, Error, Manager}; +use crate::{model::groups::Group, AvatarBytes, Error, Manager}; type ServiceCipher = cipher::ServiceCipher; type MessageSender = libsignal_service::prelude::MessageSender; @@ -381,7 +381,7 @@ impl Manager { .as_millis() as u64; self.send_message( - ServiceAddress::new_aci(self.state.data.service_ids.aci), + ServiceAddress::from_aci(self.state.data.service_ids.aci), sync_message, timestamp, ) @@ -470,7 +470,7 @@ impl Manager { AccountManager::new(self.identified_push_service(), Some(profile_key)); let profile = account_manager - .retrieve_profile(ServiceAddress::new_aci(uuid)) + .retrieve_profile(ServiceAddress::from_aci(uuid)) .await?; let _ = self.store.save_profile(uuid, profile_key, profile.clone()); @@ -859,7 +859,7 @@ impl Manager { // save the message let content = Content { metadata: Metadata { - sender: ServiceAddress::new_aci(self.state.data.service_ids.aci), + sender: ServiceAddress::from_aci(self.state.data.service_ids.aci), sender_device: self.state.device_id(), destination: recipient, server_guid: None, @@ -935,7 +935,7 @@ impl Manager { }); let include_pni_signature = true; recipients.push(( - ServiceAddress::new_aci(member.uuid), + ServiceAddress::from_aci(member.uuid), unidentified_access, include_pni_signature, )); @@ -963,8 +963,8 @@ impl Manager { let content = Content { metadata: Metadata { - sender: ServiceAddress::new_aci(self.state.data.service_ids.aci), - destination: ServiceAddress::new_aci(self.state.data.service_ids.aci), + sender: ServiceAddress::from_aci(self.state.data.service_ids.aci), + destination: ServiceAddress::from_aci(self.state.data.service_ids.aci), sender_device: self.state.device_id(), server_guid: None, timestamp, @@ -1098,7 +1098,7 @@ impl Manager { .as_millis() as u64; self.send_message( - ServiceAddress::new_aci(self.state.data.aci()), + ServiceAddress::from_aci(self.state.data.aci()), sync_message, timestamp, ) @@ -1129,7 +1129,7 @@ impl Manager { .as_millis() as u64; self.send_message( - ServiceAddress::new_aci(self.state.data.aci()), + ServiceAddress::from_aci(self.state.data.aci()), sync_message, timestamp, ) @@ -1187,8 +1187,8 @@ impl Manager { self.new_service_cipher()?, self.rng.clone(), aci_protocol_store, - ServiceAddress::new_aci(self.state.data.service_ids.aci), - ServiceAddress::new_pni(self.state.data.service_ids.pni), + ServiceAddress::from_aci(self.state.data.service_ids.aci), + ServiceAddress::from_pni(self.state.data.service_ids.pni), aci_identity_keypair, Some(pni_identity_keypair), self.state.device_id().into(), @@ -1370,7 +1370,7 @@ async fn upsert_group( match groups_manager.fetch_encrypted_group(master_key_bytes).await { Ok(encrypted_group) => { let group = decrypt_group(master_key_bytes, encrypted_group)?; - if let Err(error) = store.save_group(master_key_bytes.try_into()?, &group) { + if let Err(error) = store.save_group(master_key_bytes.try_into()?, group) { error!(%error, "failed to save group"); } } @@ -1507,7 +1507,7 @@ async fn save_message( { let encrypted_profile = push_service .retrieve_profile_by_id( - ServiceAddress::new_aci(sender_uuid), + ServiceAddress::from_aci(sender_uuid), Some(profile_key), ) .await?; diff --git a/presage/src/model/groups.rs b/presage/src/model/groups.rs new file mode 100644 index 000000000..bf5583ed5 --- /dev/null +++ b/presage/src/model/groups.rs @@ -0,0 +1,84 @@ +use derivative::Derivative; +use libsignal_service::{ + groups_v2::Role, + prelude::{AccessControl, Member, ProfileKey, Timer, Uuid}, +}; +use serde::{Deserialize, Serialize}; + +use super::ServiceIdType; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Group { + pub title: String, + pub avatar: String, + pub disappearing_messages_timer: Option, + pub access_control: Option, + pub revision: u32, + pub members: Vec, + pub pending_members: Vec, + pub requesting_members: Vec, + pub invite_link_password: Vec, + pub description: Option, +} + +#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct PendingMember { + // for backwards compatibility + pub uuid: Uuid, + #[serde(default)] + pub service_id_type: ServiceIdType, + pub role: Role, + pub added_by_uuid: Uuid, + pub timestamp: u64, +} + +#[derive(Derivative, Clone, Deserialize, Serialize)] +#[derivative(Debug)] +pub struct RequestingMember { + pub uuid: Uuid, + pub profile_key: ProfileKey, + pub timestamp: u64, +} + +impl Into for libsignal_service::groups_v2::Group { + fn into(self) -> Group { + Group { + title: self.title, + avatar: self.avatar, + disappearing_messages_timer: self.disappearing_messages_timer, + access_control: self.access_control, + revision: self.revision, + members: self.members, + pending_members: self.pending_members.into_iter().map(Into::into).collect(), + requesting_members: self + .requesting_members + .into_iter() + .map(Into::into) + .collect(), + invite_link_password: self.invite_link_password, + description: self.description, + } + } +} + +impl Into for libsignal_service::groups_v2::PendingMember { + fn into(self) -> PendingMember { + PendingMember { + uuid: self.address.uuid, + service_id_type: self.address.identity.into(), + role: self.role, + added_by_uuid: self.added_by_uuid, + timestamp: self.timestamp, + } + } +} + +impl Into for libsignal_service::groups_v2::RequestingMember { + fn into(self) -> RequestingMember { + RequestingMember { + uuid: self.uuid, + profile_key: self.profile_key, + timestamp: self.timestamp, + } + } +} diff --git a/presage/src/model/mod.rs b/presage/src/model/mod.rs new file mode 100644 index 000000000..c4c132f23 --- /dev/null +++ b/presage/src/model/mod.rs @@ -0,0 +1,27 @@ +use serde::{Deserialize, Serialize}; + +pub mod groups; + +#[derive(Debug, Default, PartialEq, Eq, Deserialize, Serialize)] +pub enum ServiceIdType { + /// Account Identity (ACI) + /// + /// An account UUID without an associated phone number, probably in the future to a username + #[default] + AccountIdentity, + /// Phone number identity (PNI) + /// + /// A UUID associated with a phone number + PhoneNumberIdentity, +} + +impl Into for libsignal_service::ServiceIdType { + fn into(self) -> ServiceIdType { + match self { + libsignal_service::ServiceIdType::AccountIdentity => ServiceIdType::AccountIdentity, + libsignal_service::ServiceIdType::PhoneNumberIdentity => { + ServiceIdType::PhoneNumberIdentity + } + } + } +} diff --git a/presage/src/store.rs b/presage/src/store.rs index 77e1b3a34..b5172f845 100644 --- a/presage/src/store.rs +++ b/presage/src/store.rs @@ -4,7 +4,7 @@ use std::{fmt, ops::RangeBounds, time::SystemTime}; use libsignal_service::{ content::{ContentBody, Metadata}, - groups_v2::{Group, Timer}, + groups_v2::Timer, models::Contact, pre_keys::PreKeysStore, prelude::{Content, ProfileKey, Uuid, UuidError}, @@ -20,7 +20,7 @@ use libsignal_service::{ use serde::{Deserialize, Serialize}; use tracing::{error, trace}; -use crate::{manager::RegistrationData, AvatarBytes}; +use crate::{manager::RegistrationData, model::groups::Group, AvatarBytes}; /// An error trait implemented by store error types pub trait StoreError: std::error::Error + Sync + Send {} @@ -111,8 +111,8 @@ pub trait ContentsStore: Send + Sync { let thread = Thread::Contact(sender); let verified_sync_message = Content { metadata: Metadata { - sender: ServiceAddress::new_aci(sender), - destination: ServiceAddress::new_aci(sender), + sender: ServiceAddress::from_aci(sender), + destination: ServiceAddress::from_aci(sender), sender_device: 0, server_guid: None, timestamp: SystemTime::now() @@ -206,7 +206,7 @@ pub trait ContentsStore: Send + Sync { let group = self.group(*key)?; if let Some(mut g) = group { g.disappearing_messages_timer = Some(Timer { duration: timer }); - self.save_group(*key, &g)?; + self.save_group(*key, g)?; } Ok(()) } @@ -234,7 +234,7 @@ pub trait ContentsStore: Send + Sync { fn save_group( &self, master_key: GroupMasterKeyBytes, - group: &Group, + group: impl Into, ) -> Result<(), Self::ContentsStoreError>; /// Get an iterator on all cached groups