1use chrono::Utc;
2use libsignal_core::DeviceId;
3use libsignal_protocol::{ProtocolAddress, ServiceId};
4use prost::Message;
5use std::fmt;
6use uuid::Uuid;
7
8pub use crate::{
9 proto::{
10 attachment_pointer::Flags as AttachmentPointerFlags,
11 data_message::Flags as DataMessageFlags, data_message::Reaction,
12 sync_message, AttachmentPointer, CallMessage, DataMessage,
13 DecryptionErrorMessage, EditMessage, GroupContextV2, NullMessage,
14 PniSignatureMessage, ReceiptMessage, StoryMessage, SyncMessage,
15 TypingMessage,
16 },
17 push_service::ServiceError,
18 ServiceIdExt,
19};
20
21mod data_message;
22mod story_message;
23
24#[derive(Clone, Debug)]
25pub struct Metadata {
26 pub sender: ServiceId,
27 pub destination: ServiceId,
28 pub sender_device: DeviceId,
29 pub timestamp: chrono::DateTime<Utc>,
30 pub server_timestamp: chrono::DateTime<Utc>,
31 pub needs_receipt: bool,
32 pub unidentified_sender: bool,
33 pub was_plaintext: bool,
34
35 pub server_guid: Option<Uuid>,
39}
40
41impl fmt::Display for Metadata {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 write!(
44 f,
45 "Metadata {{ sender: {}, guid: {}, server timestamp: {} }}",
46 self.sender.service_id_string(),
47 self.server_guid
49 .map(|u| u.to_string())
50 .as_deref()
51 .unwrap_or("None"),
52 self.server_timestamp,
53 )
54 }
55}
56
57impl Metadata {
58 pub(crate) fn protocol_address(
59 &self,
60 ) -> Result<ProtocolAddress, libsignal_core::InvalidDeviceId> {
61 self.sender.to_protocol_address(self.sender_device)
62 }
63}
64
65#[derive(Clone, Debug)]
66pub struct Content {
67 pub metadata: Metadata,
68 pub body: ContentBody,
69}
70
71impl Content {
72 pub fn from_body(body: impl Into<ContentBody>, metadata: Metadata) -> Self {
73 Self {
74 metadata,
75 body: body.into(),
76 }
77 }
78
79 pub fn from_proto(
81 p: crate::proto::Content,
82 metadata: Metadata,
83 ) -> Result<Self, ServiceError> {
84 #[allow(clippy::manual_map)]
90 if let Some(msg) = p.data_message {
91 Ok(Self::from_body(msg, metadata))
92 } else if let Some(msg) = p.sync_message {
93 Ok(Self::from_body(msg, metadata))
94 } else if let Some(msg) = p.call_message {
95 Ok(Self::from_body(msg, metadata))
96 } else if let Some(msg) = p.receipt_message {
97 Ok(Self::from_body(msg, metadata))
98 } else if let Some(msg) = p.typing_message {
99 Ok(Self::from_body(msg, metadata))
100 } else if let Some(msg) = p.decryption_error_message {
103 Ok(Self {
104 metadata,
105 body: ContentBody::DecryptionErrorMessage(
106 DecryptionErrorMessage::decode(msg.as_ref())?,
107 ),
108 })
109 } else if let Some(msg) = p.story_message {
110 Ok(Self::from_body(msg, metadata))
111 } else if let Some(msg) = p.pni_signature_message {
112 Ok(Self::from_body(msg, metadata))
113 } else if let Some(msg) = p.edit_message {
114 Ok(Self::from_body(msg, metadata))
115 } else if let Some(msg) = p.null_message {
116 Ok(Self::from_body(msg, metadata))
117 } else {
118 Err(ServiceError::UnsupportedContent)
119 }
120 }
121}
122
123impl fmt::Display for ContentBody {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 match self {
126 Self::NullMessage(_) => write!(f, "NullMessage"),
127 Self::DataMessage(m) => {
128 match (&m.body, &m.reaction, m.attachments.len()) {
129 (Some(body), _, 0) => {
130 write!(f, "DataMessage({})", body)
131 },
132 (Some(body), _, n) => {
133 write!(f, "DataMessage({}, attachments: {n})", body)
134 },
135 (None, Some(emoji), _) => {
136 write!(
137 f,
138 "DataMessage(reaction: {})",
139 emoji.emoji.as_deref().unwrap_or("None")
140 )
141 },
142 (None, _, n) if n > 0 => {
143 write!(f, "DataMessage(attachments: {n})")
144 },
145 _ => {
146 write!(f, "{self:?}")
147 },
148 }
149 },
150 Self::SynchronizeMessage(_) => write!(f, "SynchronizeMessage"),
151 Self::CallMessage(_) => write!(f, "CallMessage"),
152 Self::ReceiptMessage(_) => write!(f, "ReceiptMessage"),
153 Self::TypingMessage(_) => write!(f, "TypingMessage"),
154 Self::DecryptionErrorMessage(_) => {
156 write!(f, "DecryptionErrorMessage")
157 },
158 Self::StoryMessage(_) => write!(f, "StoryMessage"),
159 Self::PniSignatureMessage(_) => write!(f, "PniSignatureMessage"),
160 Self::EditMessage(_) => write!(f, "EditMessage"),
161 }
162 }
163}
164
165#[derive(Clone, Debug)]
166#[allow(clippy::large_enum_variant)]
167pub enum ContentBody {
168 NullMessage(NullMessage),
169 DataMessage(DataMessage),
170 SynchronizeMessage(SyncMessage),
171 CallMessage(CallMessage),
172 ReceiptMessage(ReceiptMessage),
173 TypingMessage(TypingMessage),
174 DecryptionErrorMessage(DecryptionErrorMessage),
176 StoryMessage(StoryMessage),
177 PniSignatureMessage(PniSignatureMessage),
178 EditMessage(EditMessage),
179}
180
181impl NullMessage {
182 pub fn generate<R: rand::Rng + rand::CryptoRng>(rng: &mut R) -> Self {
183 let padding_length = (rng.next_u64() % 140) as usize + 1;
185 let mut padding = vec![0; padding_length];
186 rng.fill(padding.as_mut_slice());
187 NullMessage {
188 padding: Some(padding),
189 }
190 }
191}
192
193impl ContentBody {
194 pub fn into_proto(self) -> crate::proto::Content {
195 match self {
196 Self::NullMessage(msg) => crate::proto::Content {
197 null_message: Some(msg),
198 ..Default::default()
199 },
200 Self::DataMessage(msg) => crate::proto::Content {
201 data_message: Some(msg),
202 ..Default::default()
203 },
204 Self::SynchronizeMessage(msg) => crate::proto::Content {
205 sync_message: Some(msg),
206 ..Default::default()
207 },
208 Self::CallMessage(msg) => crate::proto::Content {
209 call_message: Some(msg),
210 ..Default::default()
211 },
212 Self::ReceiptMessage(msg) => crate::proto::Content {
213 receipt_message: Some(msg),
214 ..Default::default()
215 },
216 Self::TypingMessage(msg) => crate::proto::Content {
217 typing_message: Some(msg),
218 ..Default::default()
219 },
220 Self::DecryptionErrorMessage(msg) => crate::proto::Content {
227 decryption_error_message: Some(msg.encode_to_vec()),
228 ..Default::default()
229 },
230 Self::StoryMessage(msg) => crate::proto::Content {
231 story_message: Some(msg),
232 ..Default::default()
233 },
234 Self::PniSignatureMessage(msg) => crate::proto::Content {
235 pni_signature_message: Some(msg),
236 ..Default::default()
237 },
238 Self::EditMessage(msg) => crate::proto::Content {
239 edit_message: Some(msg),
240 ..Default::default()
241 },
242 }
243 }
244}
245
246macro_rules! impl_from_for_content_body {
247 ($enum:ident ($t:ty)) => {
248 impl From<$t> for ContentBody {
249 fn from(inner: $t) -> ContentBody {
250 ContentBody::$enum(inner)
251 }
252 }
253 };
254}
255
256impl_from_for_content_body!(NullMessage(NullMessage));
257impl_from_for_content_body!(DataMessage(DataMessage));
258impl_from_for_content_body!(SynchronizeMessage(SyncMessage));
259impl_from_for_content_body!(CallMessage(CallMessage));
260impl_from_for_content_body!(ReceiptMessage(ReceiptMessage));
261impl_from_for_content_body!(TypingMessage(TypingMessage));
262impl_from_for_content_body!(StoryMessage(StoryMessage));
267impl_from_for_content_body!(PniSignatureMessage(PniSignatureMessage));
268impl_from_for_content_body!(EditMessage(EditMessage));