libsignal_service/groups_v2/
model.rs

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