libqaul_types/messages.rs
1//! Low-level binary payload messages for qaul.net
2//!
3//! A set of utilities to dispatch and receive binary payload messages
4//! from a remote user on the network. Higher-level message
5//! abstractions are available as external services. Use this type if
6//! none of the existing message variants apply to your payload.
7//!
8//! A message is always properly framed and checked for data and
9//! cryptographic signature integrity. When sending a message to a
10//! set of individual users (meaning not setting [`Mode::Flood`]), it is
11//! also encrypted.
12//!
13//! [`Mode::Flood`]: ./enum.Mode.html#variant.Flood
14//!
15//! Try avoid sending massive payloads to a set of recipients because
16//! the routing layer won't be able to deduplicate frames that are
17//! encrypted. A filesharing service is available to make initiating
18//! lazy data pulls for your service.
19
20use crate::error::{Error, Result};
21use alexandria_tags::{Tag, TagSet};
22use ratman_identity::Identity;
23use serde::{Deserialize, Serialize};
24use std::sync::Arc;
25
26/// A reference to an internally stored message object
27pub type MsgRef = Arc<Message>;
28
29/// Length of an `MsgId`, for converting to and from arrays
30pub const ID_LEN: usize = 16;
31
32/// A unique, randomly generated message ID
33pub type MsgId = Identity;
34
35#[doc(hidden)]
36pub const TAG_FLOOD: &'static str = "libqaul._int.flood";
37#[doc(hidden)]
38pub const TAG_UNREAD: &'static str = "libqaul._int.unread";
39#[doc(hidden)]
40pub const TAG_SENDER: &'static str = "libqaul._int.sender";
41#[doc(hidden)]
42pub const TAG_SERVICE: &'static str = "libqaul._int.service";
43
44/// Signature trust level of an incoming `Message`
45///
46/// The three variants encode `trusted`, `unverified` and `invalid`,
47/// according to signature verification of the internal keystore.
48///
49/// The `SigTrust::ok` convenience function can be used to reject
50/// non-verifiable (unknown or bad) `Message` signatures.
51#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
52pub enum SigTrust {
53 /// A verified signature by a known contact
54 Trusted,
55 /// An unverified signature by a known contact
56 /// (pubkey not available!)
57 Unverified,
58 /// A fraudulent signature
59 Invalid,
60}
61
62impl SigTrust {
63 pub fn ok(&self) -> Result<()> {
64 match self {
65 Self::Trusted => Ok(()),
66 Self::Unverified => Err(Error::NoSign),
67 Self::Invalid => Err(Error::BadSign),
68 }
69 }
70}
71
72/// Specify the way that a message gets dispatched
73///
74/// This information is only needed during transmission, because the
75/// message should later be associated with some other metadata
76/// provided by your service (or just the message ID).
77///
78/// When sending a flooded message, it becomes publicly accessible for
79/// everybody on this node, and will most likely be stored in plain
80/// text on receiving nodes across the network. Be aware of this!
81#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
82pub enum Mode {
83 /// Send a message to everybody
84 Flood,
85 /// Address only a single identity
86 Std(Identity),
87}
88
89impl Mode {
90 pub fn id(&self) -> Option<Identity> {
91 match self {
92 Self::Std(id) => Some(*id),
93 Self::Flood => None,
94 }
95 }
96}
97
98impl From<Identity> for Mode {
99 fn from(id: Identity) -> Self {
100 Self::Std(id)
101 }
102}
103
104/// Specify the id type for a message dispatch
105///
106/// Because libqaul doesn't implement recipient groups it's up to a
107/// service to create useful categorisations for groups of users.
108/// This means that a service might send the same message to different
109/// users, that are then receiving technically different messages.
110/// This can cause all sorts of issues for services because now the
111/// database is keeping track of a message many times (for each user
112/// it was sent to).
113///
114/// This is what this type aims to circumvent: a message id can be
115/// randomised during delivery, or fixed as a group to ensure that a
116/// set of messages are all assigned the same Id.
117///
118/// **This comes with some caveats:** when inserting into the
119/// database, the message Id will already exist, and so further
120/// messages will not be stored. If you are using the grouped
121/// constraint on an unequal message set (meaning that payloads
122/// differ), this will result in data loss!
123#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
124pub enum IdType {
125 /// A unique message ID will be generated on dispatch
126 Unique,
127 /// Create a grouped message ID constraint
128 Grouped(MsgId),
129}
130
131impl IdType {
132 pub fn consume(self) -> MsgId {
133 match self {
134 Self::Unique => MsgId::random(),
135 Self::Grouped(id) => id,
136 }
137 }
138
139 /// Create an ID type that is constrained for a group
140 pub fn group(id: MsgId) -> Self {
141 Self::Grouped(id)
142 }
143
144 /// Create a new message group with a random Id
145 pub fn create_group() -> Self {
146 Self::Grouped(MsgId::random())
147 }
148
149 /// Create a new message ID for every message dispatched
150 pub fn unique() -> Self {
151 Self::Unique
152 }
153}
154
155/// A query interface for the local message store
156#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
157#[serde(rename_all = "snake_case")]
158pub struct MsgQuery {
159 #[doc(hidden)]
160 pub id: Option<MsgId>,
161 #[doc(hidden)]
162 pub sender: Option<Identity>,
163 #[doc(hidden)]
164 pub tags: TagSet,
165 #[doc(hidden)]
166 pub skip: usize,
167}
168
169impl MsgQuery {
170 /// Create a new, empty query
171 pub fn new() -> Self {
172 Self::default()
173 }
174
175 /// An override query, that only searches for a specific Id
176 ///
177 /// Ignores all other values passed into the query
178 pub fn id(id: MsgId) -> Self {
179 let id = Some(id);
180 Self { id, ..Self::new() }
181 }
182
183 /// Query for messages by a specific sender
184 pub fn sender(self, sender: Identity) -> Self {
185 Self {
186 sender: Some(sender),
187 ..self
188 }
189 }
190
191 /// Add a tag to the query that must be present
192 ///
193 /// Tag queries aim to be a subset in matching messages, which
194 /// means that more tags can exist for a message, but all provided
195 /// tags must be present.
196 pub fn tag(mut self, t: Tag) -> Self {
197 self.tags.insert(t);
198 self
199 }
200
201 /// A convenience function for addding the "unread" tag
202 pub fn unread(self) -> Self {
203 self.tag(Tag::empty(TAG_UNREAD))
204 }
205}
206
207/// A multi-purpose service Message
208///
209/// While this representation is quite "low level", i.e. forces a user
210/// to deal with payload encoding themselves and provides no
211/// functionality for async payloads (via filesharing, or similar), it
212/// is quite a high level abstraction considering the data that needs
213/// to be sent over the network in order for it to reach it's
214/// recipient.
215///
216/// This type is both returned by `listen`, `poll`, as well as
217/// specific message `queries`
218///
219#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
220pub struct Message {
221 /// A unique message ID
222 pub id: MsgId,
223 /// The sender identity
224 pub sender: Identity,
225 /// The embedded service associator
226 pub associator: String,
227 /// A tag store for persistent message metadata
228 pub tags: TagSet,
229 /// A raw byte `Message` payload
230 pub payload: Vec<u8>,
231}