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 pub terminated: bool,
163}
164
165#[derive(Debug, Clone)]
166pub struct GroupChanges {
167 pub group_id: GroupV2Id,
168 pub editor: Aci,
169 pub version: u32,
170 pub changes: Vec<GroupChange>,
171 pub change_epoch: u32,
172}
173
174#[derive(derive_more::Debug, Clone)]
175pub enum GroupChange {
176 NewMember(Member),
177 DeleteMember(Aci),
178 ModifyMemberRole {
179 aci: Aci,
180 role: Role,
181 },
182 ModifyMemberProfileKey {
183 aci: Aci,
184 #[debug(ignore)]
185 profile_key: ProfileKey,
186 },
187 NewPendingMember(PendingMember),
188 DeletePendingMember(ServiceId),
189 PromotePendingMember {
190 address: ServiceId,
191 #[debug(ignore)]
192 profile_key: ProfileKey,
193 },
194 Title(String),
195 Avatar(String),
196 Timer(Option<Timer>),
197 AttributeAccess(AccessRequired),
198 MemberAccess(AccessRequired),
199 InviteLinkAccess(AccessRequired),
200 NewRequestingMember(RequestingMember),
201 DeleteRequestingMember(Aci),
202 PromoteRequestingMember {
203 aci: Aci,
204 role: Role,
205 },
206 InviteLinkPassword(String),
207 Description(Option<String>),
208 AnnouncementOnly(bool),
209 AddBannedMember(BannedMember),
210 DeleteBannedMember(ServiceId),
211 PromotePendingPniAciMemberProfileKey(PromotedMember),
212 MemberLabel {
213 user_id: ServiceId,
214 label_emoji: Option<String>,
215 label_string: Option<String>,
216 },
217 MemberLabelAccess(AccessRequired),
218 TerminateGroup,
219}
220
221#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
222pub struct Timer {
223 pub duration: u32,
224}
225
226impl TryFrom<i32> for Role {
229 type Error = GroupDecodingError;
230
231 fn try_from(value: i32) -> Result<Self, Self::Error> {
232 use crate::proto::member::Role as ProtoRole;
233 match crate::proto::member::Role::try_from(value) {
234 Ok(ProtoRole::Unknown) => Ok(Role::Unknown),
235 Ok(ProtoRole::Default) => Ok(Role::Default),
236 Ok(ProtoRole::Administrator) => Ok(Role::Administrator),
237 Err(_e) => Err(GroupDecodingError::WrongEnumValue),
238 }
239 }
240}
241
242impl From<Role> for i32 {
243 fn from(val: Role) -> Self {
244 use crate::proto::member::Role::*;
245 match val {
246 Role::Unknown => Unknown,
247 Role::Default => Default,
248 Role::Administrator => Administrator,
249 }
250 .into()
251 }
252}
253
254impl TryFrom<i32> for AccessRequired {
255 type Error = GroupDecodingError;
256
257 fn try_from(value: i32) -> Result<Self, Self::Error> {
258 use crate::proto::access_control::AccessRequired as ProtoAccessRequired;
259 match crate::proto::access_control::AccessRequired::try_from(value) {
260 Ok(ProtoAccessRequired::Unknown) => Ok(AccessRequired::Unknown),
261 Ok(ProtoAccessRequired::Any) => Ok(AccessRequired::Any),
262 Ok(ProtoAccessRequired::Member) => Ok(AccessRequired::Member),
263 Ok(ProtoAccessRequired::Administrator) => {
264 Ok(AccessRequired::Administrator)
265 },
266 Ok(ProtoAccessRequired::Unsatisfiable) => {
267 Ok(AccessRequired::Unsatisfiable)
268 },
269 Err(_e) => Err(GroupDecodingError::WrongEnumValue),
270 }
271 }
272}
273
274impl From<AccessRequired> for i32 {
275 fn from(val: AccessRequired) -> Self {
276 use crate::proto::access_control::AccessRequired::*;
277 match val {
278 AccessRequired::Unknown => Unknown,
279 AccessRequired::Any => Any,
280 AccessRequired::Member => Member,
281 AccessRequired::Administrator => Administrator,
282 AccessRequired::Unsatisfiable => Unsatisfiable,
283 }
284 .into()
285 }
286}
287
288impl TryFrom<crate::proto::AccessControl> for AccessControl {
289 type Error = GroupDecodingError;
290
291 fn try_from(
292 value: crate::proto::AccessControl,
293 ) -> Result<Self, Self::Error> {
294 Ok(Self {
295 attributes: value.attributes.try_into()?,
296 members: value.members.try_into()?,
297 add_from_invite_link: value.add_from_invite_link.try_into()?,
298 member_label: value.member_label.try_into()?,
299 })
300 }
301}