libsignal_service/groups_v2/
model.rs

1use std::{convert::TryFrom, convert::TryInto};
2
3use libsignal_protocol::{Aci, Pni, ServiceId};
4use serde::{Deserialize, Serialize};
5use zkgroup::profiles::ProfileKey;
6
7use crate::sender::GroupV2Id;
8
9use super::GroupDecodingError;
10
11#[derive(Copy, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
12pub enum Role {
13    Unknown,
14    Default,
15    Administrator,
16}
17
18#[derive(derive_more::Debug, Clone, Deserialize, Serialize)]
19pub struct Member {
20    #[serde(with = "aci_serde")]
21    pub aci: Aci,
22    pub role: Role,
23    #[debug(ignore)]
24    pub profile_key: ProfileKey,
25    pub joined_at_revision: u32,
26}
27
28impl PartialEq for Member {
29    fn eq(&self, other: &Self) -> bool {
30        self.aci == other.aci
31    }
32}
33
34mod aci_serde {
35    use super::*;
36    use serde::{Deserializer, Serializer};
37
38    pub fn serialize<S>(p: &Aci, s: S) -> Result<S::Ok, S::Error>
39    where
40        S: Serializer,
41    {
42        s.serialize_str(&p.service_id_string())
43    }
44
45    pub fn deserialize<'de, D>(d: D) -> Result<Aci, D::Error>
46    where
47        D: Deserializer<'de>,
48    {
49        // We have to go through String deserialization,
50        // because Aci does not implement Deserialize (duh).
51        let s = std::borrow::Cow::<str>::deserialize(d)?;
52        match Aci::parse_from_service_id_string(&s) {
53            Some(aci) => Ok(aci),
54            None => Err(serde::de::Error::custom("Invalid ACI string")),
55        }
56    }
57}
58
59#[derive(Clone, Debug, PartialEq, Eq)]
60pub struct PendingMember {
61    pub address: ServiceId,
62    pub role: Role,
63    pub added_by_aci: Aci,
64    pub timestamp: u64,
65}
66
67#[derive(derive_more::Debug, Clone)]
68pub struct RequestingMember {
69    pub aci: Aci,
70    #[debug(ignore)]
71    pub profile_key: ProfileKey,
72    pub timestamp: u64,
73}
74
75impl PartialEq for RequestingMember {
76    fn eq(&self, other: &Self) -> bool {
77        self.aci == other.aci
78    }
79}
80
81#[derive(Debug, Clone)]
82pub struct BannedMember {
83    pub service_id: ServiceId,
84    pub timestamp: u64,
85}
86
87impl PartialEq for BannedMember {
88    fn eq(&self, other: &Self) -> bool {
89        self.service_id == other.service_id
90    }
91}
92
93#[derive(derive_more::Debug, Clone)]
94pub struct PromotedMember {
95    pub aci: Aci,
96    pub pni: Pni,
97    #[debug(ignore)]
98    pub profile_key: ProfileKey,
99}
100
101impl PartialEq for PromotedMember {
102    fn eq(&self, other: &Self) -> bool {
103        self.aci == other.aci && self.pni == other.pni
104    }
105}
106
107#[derive(Copy, Debug, Clone, PartialEq, Serialize, Deserialize)]
108pub enum AccessRequired {
109    Unknown,
110    Any,
111    Member,
112    Administrator,
113    Unsatisfiable,
114}
115
116#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
117pub struct AccessControl {
118    pub attributes: AccessRequired,
119    pub members: AccessRequired,
120    pub add_from_invite_link: AccessRequired,
121}
122
123#[derive(Debug, Clone, PartialEq)]
124pub struct Group {
125    pub title: String,
126    pub avatar: String,
127    pub disappearing_messages_timer: Option<Timer>,
128    pub access_control: Option<AccessControl>,
129    pub revision: u32,
130    pub members: Vec<Member>,
131    pub pending_members: Vec<PendingMember>,
132    pub requesting_members: Vec<RequestingMember>,
133    pub invite_link_password: Vec<u8>,
134    pub description: Option<String>,
135    pub announcements_only: bool,
136    pub banned_members: Vec<BannedMember>,
137}
138
139#[derive(Debug, Clone)]
140pub struct GroupChanges {
141    pub group_id: GroupV2Id,
142    pub editor: Aci,
143    pub revision: u32,
144    pub changes: Vec<GroupChange>,
145    pub change_epoch: u32,
146}
147
148#[derive(derive_more::Debug, Clone)]
149pub enum GroupChange {
150    NewMember(Member),
151    DeleteMember(Aci),
152    ModifyMemberRole {
153        aci: Aci,
154        role: Role,
155    },
156    ModifyMemberProfileKey {
157        aci: Aci,
158        #[debug(ignore)]
159        profile_key: ProfileKey,
160    },
161    NewPendingMember(PendingMember),
162    DeletePendingMember(ServiceId),
163    PromotePendingMember {
164        address: ServiceId,
165        #[debug(ignore)]
166        profile_key: ProfileKey,
167    },
168    Title(String),
169    Avatar(String),
170    Timer(Option<Timer>),
171    AttributeAccess(AccessRequired),
172    MemberAccess(AccessRequired),
173    InviteLinkAccess(AccessRequired),
174    NewRequestingMember(RequestingMember),
175    DeleteRequestingMember(Aci),
176    PromoteRequestingMember {
177        aci: Aci,
178        role: Role,
179    },
180    InviteLinkPassword(String),
181    Description(Option<String>),
182    AnnouncementOnly(bool),
183    AddBannedMember(BannedMember),
184    DeleteBannedMember(ServiceId),
185    PromotePendingPniAciMemberProfileKey(PromotedMember),
186}
187
188#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
189pub struct Timer {
190    pub duration: u32,
191}
192
193// Conversion from and to protobuf definitions
194
195impl TryFrom<i32> for Role {
196    type Error = GroupDecodingError;
197
198    fn try_from(value: i32) -> Result<Self, Self::Error> {
199        use crate::proto::member::Role::*;
200        match crate::proto::member::Role::try_from(value) {
201            Ok(Unknown) => Ok(Role::Unknown),
202            Ok(Default) => Ok(Role::Default),
203            Ok(Administrator) => Ok(Role::Administrator),
204            Err(_e) => Err(GroupDecodingError::WrongEnumValue),
205        }
206    }
207}
208
209impl From<Role> for i32 {
210    fn from(val: Role) -> Self {
211        use crate::proto::member::Role::*;
212        match val {
213            Role::Unknown => Unknown,
214            Role::Default => Default,
215            Role::Administrator => Administrator,
216        }
217        .into()
218    }
219}
220
221impl TryFrom<i32> for AccessRequired {
222    type Error = GroupDecodingError;
223
224    fn try_from(value: i32) -> Result<Self, Self::Error> {
225        use crate::proto::access_control::AccessRequired::*;
226        match crate::proto::access_control::AccessRequired::try_from(value) {
227            Ok(Unknown) => Ok(AccessRequired::Unknown),
228            Ok(Any) => Ok(AccessRequired::Any),
229            Ok(Member) => Ok(AccessRequired::Member),
230            Ok(Administrator) => Ok(AccessRequired::Administrator),
231            Ok(Unsatisfiable) => Ok(AccessRequired::Unsatisfiable),
232            Err(_e) => Err(GroupDecodingError::WrongEnumValue),
233        }
234    }
235}
236
237impl From<AccessRequired> for i32 {
238    fn from(val: AccessRequired) -> Self {
239        use crate::proto::access_control::AccessRequired::*;
240        match val {
241            AccessRequired::Unknown => Unknown,
242            AccessRequired::Any => Any,
243            AccessRequired::Member => Member,
244            AccessRequired::Administrator => Administrator,
245            AccessRequired::Unsatisfiable => Unsatisfiable,
246        }
247        .into()
248    }
249}
250
251impl TryFrom<crate::proto::AccessControl> for AccessControl {
252    type Error = GroupDecodingError;
253
254    fn try_from(
255        value: crate::proto::AccessControl,
256    ) -> Result<Self, Self::Error> {
257        Ok(Self {
258            attributes: value.attributes.try_into()?,
259            members: value.members.try_into()?,
260            add_from_invite_link: value.add_from_invite_link.try_into()?,
261        })
262    }
263}