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