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}