Skip to main content

libsignal_service/
content.rs

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    /// A unique UUID for this specific message, produced by the Signal servers.
36    ///
37    /// The server GUID is used to report spam messages.
38    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            // XXX: should this still be optional?
48            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    /// Converts a proto::Content into a public Content, including metadata.
80    pub fn from_proto(
81        p: crate::proto::Content,
82        metadata: Metadata,
83    ) -> Result<Self, ServiceError> {
84        // The Java version also assumes only one content type at a time.
85        // It's a bit sad that we cannot really match here, we've got no
86        // r#type() method.
87        // Allow the manual map (if let Some -> option.map(||)), because it
88        // reduces the git diff when more types would be added.
89        #[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.sender_key_distribution_message {
101        //     Ok(Self::from_body(msg, metadata))
102        } 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::SenderKeyDistributionMessage(_) => write!(f, "SenderKeyDistributionMessage"),
155            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    // SenderKeyDistributionMessage(SenderKeyDistributionMessage),
175    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        // Random length between 1 and 140 bytes
184        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            // XXX Those two are serialized as Vec<u8> and I'm not currently sure how to handle
221            // them.
222            // Self::SenderKeyDistributionMessage(msg) => crate::proto::Content {
223            //     sender_key_distribution_message: Some(msg),
224            //     ..Default::default()
225            // },
226            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));
262// impl_from_for_content_body!(SenderKeyDistributionMessage(
263//     SenderKeyDistributionMessage
264// ));
265// impl_from_for_content_body!(DecryptionErrorMessage(DecryptionErrorMessage));
266impl_from_for_content_body!(StoryMessage(StoryMessage));
267impl_from_for_content_body!(PniSignatureMessage(PniSignatureMessage));
268impl_from_for_content_body!(EditMessage(EditMessage));