1use libsignal_core::DeviceId;
2use libsignal_protocol::{ProtocolAddress, ServiceId};
3use std::fmt;
4use uuid::Uuid;
5
6pub use crate::{
7 proto::{
8 attachment_pointer::Flags as AttachmentPointerFlags,
9 data_message::Flags as DataMessageFlags, data_message::Reaction,
10 sync_message, AttachmentPointer, CallMessage, DataMessage, EditMessage,
11 GroupContextV2, NullMessage, PniSignatureMessage, ReceiptMessage,
12 StoryMessage, SyncMessage, TypingMessage,
13 },
14 push_service::ServiceError,
15 ServiceIdExt,
16};
17
18mod data_message;
19mod story_message;
20
21#[derive(Clone, Debug)]
22pub struct Metadata {
23 pub sender: ServiceId,
24 pub destination: ServiceId,
25 pub sender_device: DeviceId,
26 pub timestamp: u64,
27 pub needs_receipt: bool,
28 pub unidentified_sender: bool,
29 pub was_plaintext: bool,
30
31 pub server_guid: Option<Uuid>,
35}
36
37impl fmt::Display for Metadata {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 write!(
40 f,
41 "Metadata {{ sender: {}, guid: {} }}",
42 self.sender.service_id_string(),
43 self.server_guid
45 .map(|u| u.to_string())
46 .as_deref()
47 .unwrap_or("None"),
48 )
49 }
50}
51
52impl Metadata {
53 pub(crate) fn protocol_address(
54 &self,
55 ) -> Result<ProtocolAddress, libsignal_core::InvalidDeviceId> {
56 self.sender.to_protocol_address(self.sender_device)
57 }
58}
59
60#[derive(Clone, Debug)]
61pub struct Content {
62 pub metadata: Metadata,
63 pub body: ContentBody,
64}
65
66impl Content {
67 pub fn from_body(body: impl Into<ContentBody>, metadata: Metadata) -> Self {
68 Self {
69 metadata,
70 body: body.into(),
71 }
72 }
73
74 pub fn from_proto(
76 p: crate::proto::Content,
77 metadata: Metadata,
78 ) -> Result<Self, ServiceError> {
79 #[allow(clippy::manual_map)]
85 if let Some(msg) = p.data_message {
86 Ok(Self::from_body(msg, metadata))
87 } else if let Some(msg) = p.sync_message {
88 Ok(Self::from_body(msg, metadata))
89 } else if let Some(msg) = p.call_message {
90 Ok(Self::from_body(msg, metadata))
91 } else if let Some(msg) = p.receipt_message {
92 Ok(Self::from_body(msg, metadata))
93 } else if let Some(msg) = p.typing_message {
94 Ok(Self::from_body(msg, metadata))
95 } else if let Some(msg) = p.story_message {
100 Ok(Self::from_body(msg, metadata))
101 } else if let Some(msg) = p.pni_signature_message {
102 Ok(Self::from_body(msg, metadata))
103 } else if let Some(msg) = p.edit_message {
104 Ok(Self::from_body(msg, metadata))
105 } else {
106 Err(ServiceError::UnsupportedContent)
107 }
108 }
109}
110
111impl fmt::Display for ContentBody {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 match self {
114 Self::NullMessage(_) => write!(f, "NullMessage"),
115 Self::DataMessage(m) => {
116 match (&m.body, &m.reaction, m.attachments.len()) {
117 (Some(body), _, 0) => {
118 write!(f, "DataMessage({})", body)
119 },
120 (Some(body), _, n) => {
121 write!(f, "DataMessage({}, attachments: {n})", body)
122 },
123 (None, Some(emoji), _) => {
124 write!(
125 f,
126 "DataMessage(reaction: {})",
127 emoji.emoji.as_deref().unwrap_or("None")
128 )
129 },
130 (None, _, n) if n > 0 => {
131 write!(f, "DataMessage(attachments: {n})")
132 },
133 _ => {
134 write!(f, "{self:?}")
135 },
136 }
137 },
138 Self::SynchronizeMessage(_) => write!(f, "SynchronizeMessage"),
139 Self::CallMessage(_) => write!(f, "CallMessage"),
140 Self::ReceiptMessage(_) => write!(f, "ReceiptMessage"),
141 Self::TypingMessage(_) => write!(f, "TypingMessage"),
142 Self::StoryMessage(_) => write!(f, "StoryMessage"),
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 StoryMessage(StoryMessage),
163 PniSignatureMessage(PniSignatureMessage),
164 EditMessage(EditMessage),
165}
166
167impl ContentBody {
168 pub fn into_proto(self) -> crate::proto::Content {
169 match self {
170 Self::NullMessage(msg) => crate::proto::Content {
171 null_message: Some(msg),
172 ..Default::default()
173 },
174 Self::DataMessage(msg) => crate::proto::Content {
175 data_message: Some(msg),
176 ..Default::default()
177 },
178 Self::SynchronizeMessage(msg) => crate::proto::Content {
179 sync_message: Some(msg),
180 ..Default::default()
181 },
182 Self::CallMessage(msg) => crate::proto::Content {
183 call_message: Some(msg),
184 ..Default::default()
185 },
186 Self::ReceiptMessage(msg) => crate::proto::Content {
187 receipt_message: Some(msg),
188 ..Default::default()
189 },
190 Self::TypingMessage(msg) => crate::proto::Content {
191 typing_message: Some(msg),
192 ..Default::default()
193 },
194 Self::StoryMessage(msg) => crate::proto::Content {
205 story_message: Some(msg),
206 ..Default::default()
207 },
208 Self::PniSignatureMessage(msg) => crate::proto::Content {
209 pni_signature_message: Some(msg),
210 ..Default::default()
211 },
212 Self::EditMessage(msg) => crate::proto::Content {
213 edit_message: Some(msg),
214 ..Default::default()
215 },
216 }
217 }
218}
219
220macro_rules! impl_from_for_content_body {
221 ($enum:ident ($t:ty)) => {
222 impl From<$t> for ContentBody {
223 fn from(inner: $t) -> ContentBody {
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));