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