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 client_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 let Some(content) = p.content else {
85 return Err(ServiceError::UnsupportedContent);
86 };
87
88 use crate::proto::content::Content;
89 match content {
90 Content::DataMessage(msg) => Ok(Self::from_body(msg, metadata)),
91 Content::SyncMessage(msg) => Ok(Self::from_body(msg, metadata)),
92 Content::CallMessage(msg) => Ok(Self::from_body(msg, metadata)),
93 Content::NullMessage(msg) => Ok(Self::from_body(msg, metadata)),
94 Content::ReceiptMessage(msg) => Ok(Self::from_body(msg, metadata)),
95 Content::TypingMessage(msg) => Ok(Self::from_body(msg, metadata)),
96 Content::DecryptionErrorMessage(msg) => Ok(Self {
97 metadata,
98 body: ContentBody::DecryptionErrorMessage(
99 DecryptionErrorMessage::decode(msg.as_ref())?,
100 ),
101 }),
102 Content::StoryMessage(msg) => Ok(Self::from_body(msg, metadata)),
103 Content::EditMessage(msg) => Ok(Self::from_body(msg, metadata)),
104 }
105 }
106}
107
108impl fmt::Display for ContentBody {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 match self {
111 Self::NullMessage(_) => write!(f, "NullMessage"),
112 Self::DataMessage(m) => {
113 match (&m.body, &m.reaction, m.attachments.len()) {
114 (Some(body), _, 0) => {
115 write!(f, "DataMessage({})", body)
116 },
117 (Some(body), _, n) => {
118 write!(f, "DataMessage({}, attachments: {n})", body)
119 },
120 (None, Some(emoji), _) => {
121 write!(
122 f,
123 "DataMessage(reaction: {})",
124 emoji.emoji.as_deref().unwrap_or("None")
125 )
126 },
127 (None, _, n) if n > 0 => {
128 write!(f, "DataMessage(attachments: {n})")
129 },
130 _ => {
131 write!(f, "{self:?}")
132 },
133 }
134 },
135 Self::SynchronizeMessage(_) => write!(f, "SynchronizeMessage"),
136 Self::CallMessage(_) => write!(f, "CallMessage"),
137 Self::ReceiptMessage(_) => write!(f, "ReceiptMessage"),
138 Self::TypingMessage(_) => write!(f, "TypingMessage"),
139 Self::DecryptionErrorMessage(_) => {
141 write!(f, "DecryptionErrorMessage")
142 },
143 Self::StoryMessage(_) => write!(f, "StoryMessage"),
144 #[allow(deprecated)]
145 Self::PniSignatureMessage(_) => write!(f, "PniSignatureMessage"),
146 Self::EditMessage(_) => write!(f, "EditMessage"),
147 }
148 }
149}
150
151#[derive(Clone, Debug)]
152#[allow(clippy::large_enum_variant)]
153pub enum ContentBody {
154 NullMessage(NullMessage),
155 DataMessage(DataMessage),
156 SynchronizeMessage(SyncMessage),
157 CallMessage(CallMessage),
158 ReceiptMessage(ReceiptMessage),
159 TypingMessage(TypingMessage),
160 DecryptionErrorMessage(DecryptionErrorMessage),
162 StoryMessage(StoryMessage),
163 #[deprecated = "PNI signature messages are constructed as side-car during message delivery"]
164 PniSignatureMessage(PniSignatureMessage),
165 EditMessage(EditMessage),
166}
167
168impl NullMessage {
169 pub fn generate<R: rand::Rng + rand::CryptoRng>(rng: &mut R) -> Self {
170 let padding_length = (rng.next_u64() % 140) as usize + 1;
172 let mut padding = vec![0; padding_length];
173 rng.fill(padding.as_mut_slice());
174 NullMessage {
175 padding: Some(padding),
176 }
177 }
178}
179
180impl ContentBody {
181 pub fn into_proto(self) -> crate::proto::Content {
182 use crate::proto::content::Content;
183
184 let inner = match self {
185 Self::NullMessage(msg) => Content::NullMessage(msg),
186 Self::DataMessage(msg) => Content::DataMessage(msg),
187 Self::SynchronizeMessage(msg) => Content::SyncMessage(msg),
188 Self::CallMessage(msg) => Content::CallMessage(msg),
189 Self::ReceiptMessage(msg) => Content::ReceiptMessage(msg),
190 Self::TypingMessage(msg) => Content::TypingMessage(msg),
191 Self::DecryptionErrorMessage(msg) => {
192 Content::DecryptionErrorMessage(msg.encode_to_vec())
193 },
194 Self::StoryMessage(msg) => Content::StoryMessage(msg),
195 #[allow(deprecated)]
196 Self::PniSignatureMessage(msg) => {
197 tracing::warn!("manually constructed PniSignatureMessage");
198 return crate::proto::Content {
199 content: None,
200 sender_key_distribution_message: None,
201 pni_signature_message: Some(msg),
203 };
204 },
205 Self::EditMessage(msg) => Content::EditMessage(msg),
206 };
207 crate::proto::Content {
208 content: Some(inner),
209 sender_key_distribution_message: None,
212 pni_signature_message: None,
214 }
215 }
216}
217
218macro_rules! impl_from_for_content_body {
219 ($enum:ident ($t:ty)) => {
220 impl From<$t> for ContentBody {
221 fn from(inner: $t) -> ContentBody {
222 #[allow(deprecated)]
224 ContentBody::$enum(inner)
225 }
226 }
227 };
228}
229
230impl_from_for_content_body!(NullMessage(NullMessage));
231impl_from_for_content_body!(DataMessage(DataMessage));
232impl_from_for_content_body!(SynchronizeMessage(SyncMessage));
233impl_from_for_content_body!(CallMessage(CallMessage));
234impl_from_for_content_body!(ReceiptMessage(ReceiptMessage));
235impl_from_for_content_body!(TypingMessage(TypingMessage));
236impl_from_for_content_body!(StoryMessage(StoryMessage));
241impl_from_for_content_body!(PniSignatureMessage(PniSignatureMessage));
242impl_from_for_content_body!(EditMessage(EditMessage));
243
244macro_rules! impl_from_for_sync_message {
245 ($t:ident) => {
246 impl From<$crate::proto::sync_message::$t> for SyncMessage {
247 fn from(inner: sync_message::$t) -> SyncMessage {
248 SyncMessage {
249 content: Some($crate::proto::sync_message::Content::$t(
250 inner,
251 )),
252 ..SyncMessage::with_padding(&mut rand::rng())
253 }
254 }
255 }
256 };
257}
258
259impl_from_for_sync_message!(Sent);
260impl_from_for_sync_message!(Contacts);
261impl_from_for_sync_message!(Request);
262impl_from_for_sync_message!(Blocked);
263impl_from_for_sync_message!(Configuration);
265impl_from_for_sync_message!(ViewOnceOpen);
266impl_from_for_sync_message!(FetchLatest);
267impl_from_for_sync_message!(Keys);
268impl_from_for_sync_message!(MessageRequestResponse);
269impl_from_for_sync_message!(OutgoingPayment);
270impl_from_for_sync_message!(PniChangeNumber);
271impl_from_for_sync_message!(CallEvent);
272impl_from_for_sync_message!(CallLinkUpdate);
273impl_from_for_sync_message!(CallLogEvent);
274impl_from_for_sync_message!(DeleteForMe);
275impl_from_for_sync_message!(DeviceNameChange);
276impl_from_for_sync_message!(AttachmentBackfillRequest);
277impl_from_for_sync_message!(AttachmentBackfillResponse);
278impl_from_for_sync_message!(UsernameChange);