libsignal_service/
content.rs

1use libsignal_core::DeviceId;
2use libsignal_protocol::{ProtocolAddress, ServiceId};
3use prost::Message;
4use std::fmt;
5use uuid::Uuid;
6
7pub use crate::{
8    proto::{
9        attachment_pointer::Flags as AttachmentPointerFlags,
10        data_message::Flags as DataMessageFlags, data_message::Reaction,
11        sync_message, AttachmentPointer, CallMessage, DataMessage,
12        DecryptionErrorMessage, EditMessage, GroupContextV2, NullMessage,
13        PniSignatureMessage, ReceiptMessage, StoryMessage, SyncMessage,
14        TypingMessage,
15    },
16    push_service::ServiceError,
17    ServiceIdExt,
18};
19
20mod data_message;
21mod story_message;
22
23#[derive(Clone, Debug)]
24pub struct Metadata {
25    pub sender: ServiceId,
26    pub destination: ServiceId,
27    pub sender_device: DeviceId,
28    pub timestamp: u64,
29    pub needs_receipt: bool,
30    pub unidentified_sender: bool,
31    pub was_plaintext: bool,
32
33    /// A unique UUID for this specific message, produced by the Signal servers.
34    ///
35    /// The server GUID is used to report spam messages.
36    pub server_guid: Option<Uuid>,
37}
38
39impl fmt::Display for Metadata {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        write!(
42            f,
43            "Metadata {{ sender: {}, guid: {} }}",
44            self.sender.service_id_string(),
45            // XXX: should this still be optional?
46            self.server_guid
47                .map(|u| u.to_string())
48                .as_deref()
49                .unwrap_or("None"),
50        )
51    }
52}
53
54impl Metadata {
55    pub(crate) fn protocol_address(
56        &self,
57    ) -> Result<ProtocolAddress, libsignal_core::InvalidDeviceId> {
58        self.sender.to_protocol_address(self.sender_device)
59    }
60}
61
62#[derive(Clone, Debug)]
63pub struct Content {
64    pub metadata: Metadata,
65    pub body: ContentBody,
66}
67
68impl Content {
69    pub fn from_body(body: impl Into<ContentBody>, metadata: Metadata) -> Self {
70        Self {
71            metadata,
72            body: body.into(),
73        }
74    }
75
76    /// Converts a proto::Content into a public Content, including metadata.
77    pub fn from_proto(
78        p: crate::proto::Content,
79        metadata: Metadata,
80    ) -> Result<Self, ServiceError> {
81        // The Java version also assumes only one content type at a time.
82        // It's a bit sad that we cannot really match here, we've got no
83        // r#type() method.
84        // Allow the manual map (if let Some -> option.map(||)), because it
85        // reduces the git diff when more types would be added.
86        #[allow(clippy::manual_map)]
87        if let Some(msg) = p.data_message {
88            Ok(Self::from_body(msg, metadata))
89        } else if let Some(msg) = p.sync_message {
90            Ok(Self::from_body(msg, metadata))
91        } else if let Some(msg) = p.call_message {
92            Ok(Self::from_body(msg, metadata))
93        } else if let Some(msg) = p.receipt_message {
94            Ok(Self::from_body(msg, metadata))
95        } else if let Some(msg) = p.typing_message {
96            Ok(Self::from_body(msg, metadata))
97        // } else if let Some(msg) = p.sender_key_distribution_message {
98        //     Ok(Self::from_body(msg, metadata))
99        } else if let Some(msg) = p.decryption_error_message {
100            Ok(Self {
101                metadata,
102                body: ContentBody::DecryptionErrorMessage(
103                    DecryptionErrorMessage::decode(msg.as_ref())?,
104                ),
105            })
106        } else if let Some(msg) = p.story_message {
107            Ok(Self::from_body(msg, metadata))
108        } else if let Some(msg) = p.pni_signature_message {
109            Ok(Self::from_body(msg, metadata))
110        } else if let Some(msg) = p.edit_message {
111            Ok(Self::from_body(msg, metadata))
112        } else if let Some(msg) = p.null_message {
113            Ok(Self::from_body(msg, metadata))
114        } else {
115            Err(ServiceError::UnsupportedContent)
116        }
117    }
118}
119
120impl fmt::Display for ContentBody {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        match self {
123            Self::NullMessage(_) => write!(f, "NullMessage"),
124            Self::DataMessage(m) => {
125                match (&m.body, &m.reaction, m.attachments.len()) {
126                    (Some(body), _, 0) => {
127                        write!(f, "DataMessage({})", body)
128                    },
129                    (Some(body), _, n) => {
130                        write!(f, "DataMessage({}, attachments: {n})", body)
131                    },
132                    (None, Some(emoji), _) => {
133                        write!(
134                            f,
135                            "DataMessage(reaction: {})",
136                            emoji.emoji.as_deref().unwrap_or("None")
137                        )
138                    },
139                    (None, _, n) if n > 0 => {
140                        write!(f, "DataMessage(attachments: {n})")
141                    },
142                    _ => {
143                        write!(f, "{self:?}")
144                    },
145                }
146            },
147            Self::SynchronizeMessage(_) => write!(f, "SynchronizeMessage"),
148            Self::CallMessage(_) => write!(f, "CallMessage"),
149            Self::ReceiptMessage(_) => write!(f, "ReceiptMessage"),
150            Self::TypingMessage(_) => write!(f, "TypingMessage"),
151            // Self::SenderKeyDistributionMessage(_) => write!(f, "SenderKeyDistributionMessage"),
152            Self::DecryptionErrorMessage(_) => {
153                write!(f, "DecryptionErrorMessage")
154            },
155            Self::StoryMessage(_) => write!(f, "StoryMessage"),
156            Self::PniSignatureMessage(_) => write!(f, "PniSignatureMessage"),
157            Self::EditMessage(_) => write!(f, "EditMessage"),
158        }
159    }
160}
161
162#[derive(Clone, Debug)]
163#[allow(clippy::large_enum_variant)]
164pub enum ContentBody {
165    NullMessage(NullMessage),
166    DataMessage(DataMessage),
167    SynchronizeMessage(SyncMessage),
168    CallMessage(CallMessage),
169    ReceiptMessage(ReceiptMessage),
170    TypingMessage(TypingMessage),
171    // SenderKeyDistributionMessage(SenderKeyDistributionMessage),
172    DecryptionErrorMessage(DecryptionErrorMessage),
173    StoryMessage(StoryMessage),
174    PniSignatureMessage(PniSignatureMessage),
175    EditMessage(EditMessage),
176}
177
178impl ContentBody {
179    pub fn into_proto(self) -> crate::proto::Content {
180        match self {
181            Self::NullMessage(msg) => crate::proto::Content {
182                null_message: Some(msg),
183                ..Default::default()
184            },
185            Self::DataMessage(msg) => crate::proto::Content {
186                data_message: Some(msg),
187                ..Default::default()
188            },
189            Self::SynchronizeMessage(msg) => crate::proto::Content {
190                sync_message: Some(msg),
191                ..Default::default()
192            },
193            Self::CallMessage(msg) => crate::proto::Content {
194                call_message: Some(msg),
195                ..Default::default()
196            },
197            Self::ReceiptMessage(msg) => crate::proto::Content {
198                receipt_message: Some(msg),
199                ..Default::default()
200            },
201            Self::TypingMessage(msg) => crate::proto::Content {
202                typing_message: Some(msg),
203                ..Default::default()
204            },
205            // XXX Those two are serialized as Vec<u8> and I'm not currently sure how to handle
206            // them.
207            // Self::SenderKeyDistributionMessage(msg) => crate::proto::Content {
208            //     sender_key_distribution_message: Some(msg),
209            //     ..Default::default()
210            // },
211            Self::DecryptionErrorMessage(msg) => crate::proto::Content {
212                decryption_error_message: Some(msg.encode_to_vec()),
213                ..Default::default()
214            },
215            Self::StoryMessage(msg) => crate::proto::Content {
216                story_message: Some(msg),
217                ..Default::default()
218            },
219            Self::PniSignatureMessage(msg) => crate::proto::Content {
220                pni_signature_message: Some(msg),
221                ..Default::default()
222            },
223            Self::EditMessage(msg) => crate::proto::Content {
224                edit_message: Some(msg),
225                ..Default::default()
226            },
227        }
228    }
229}
230
231macro_rules! impl_from_for_content_body {
232    ($enum:ident ($t:ty)) => {
233        impl From<$t> for ContentBody {
234            fn from(inner: $t) -> ContentBody {
235                ContentBody::$enum(inner)
236            }
237        }
238    };
239}
240
241impl_from_for_content_body!(NullMessage(NullMessage));
242impl_from_for_content_body!(DataMessage(DataMessage));
243impl_from_for_content_body!(SynchronizeMessage(SyncMessage));
244impl_from_for_content_body!(CallMessage(CallMessage));
245impl_from_for_content_body!(ReceiptMessage(ReceiptMessage));
246impl_from_for_content_body!(TypingMessage(TypingMessage));
247// impl_from_for_content_body!(SenderKeyDistributionMessage(
248//     SenderKeyDistributionMessage
249// ));
250// impl_from_for_content_body!(DecryptionErrorMessage(DecryptionErrorMessage));
251impl_from_for_content_body!(StoryMessage(StoryMessage));
252impl_from_for_content_body!(PniSignatureMessage(PniSignatureMessage));
253impl_from_for_content_body!(EditMessage(EditMessage));