use std::sync::Arc;
use rand::RngCore;
use crate::core::ThinClient;
use crate::pigeonhole::{KeypairResult, TombstoneRangeResult};
use super::db::Database;
use super::error::{PigeonholeDbError, Result};
use super::models::{
ReadChannel as ReadChannelModel, ReceivedMessage, WriteChannel as WriteChannelModel,
};
pub struct PigeonholeClient {
client: Arc<ThinClient>,
db: Database,
}
impl PigeonholeClient {
pub fn new(client: Arc<ThinClient>, db: Database) -> Self {
Self { client, db }
}
pub fn new_in_memory(client: Arc<ThinClient>) -> Result<Self> {
let db = Database::open_in_memory()?;
Ok(Self { client, db })
}
pub fn db(&self) -> &Database {
&self.db
}
pub fn thin_client(&self) -> &Arc<ThinClient> {
&self.client
}
pub fn load_write_channel(
&self,
name: &str,
write_cap: &[u8],
next_index: &[u8],
) -> Result<WriteChannel> {
let model = self.db.create_write_channel(name, write_cap, next_index)?;
Ok(WriteChannel { model, client: self.client.clone(), db: self.db.clone() })
}
pub fn get_write_channel(&self, name: &str) -> Result<WriteChannel> {
let model = self.db.get_write_channel(name)?;
Ok(WriteChannel { model, client: self.client.clone(), db: self.db.clone() })
}
pub fn list_write_channels(&self) -> Result<Vec<WriteChannelModel>> {
self.db.list_write_channels()
}
pub fn delete_write_channel(&self, name: &str) -> Result<()> {
self.db.delete_write_channel(name)
}
pub fn load_read_channel(
&self,
name: &str,
read_cap: &[u8],
next_index: &[u8],
) -> Result<ReadChannel> {
let model = self.db.create_read_channel(name, read_cap, next_index)?;
Ok(ReadChannel { model, client: self.client.clone(), db: self.db.clone() })
}
pub fn get_read_channel(&self, name: &str) -> Result<ReadChannel> {
let model = self.db.get_read_channel(name)?;
Ok(ReadChannel { model, client: self.client.clone(), db: self.db.clone() })
}
pub fn list_read_channels(&self) -> Result<Vec<ReadChannelModel>> {
self.db.list_read_channels()
}
pub fn delete_read_channel(&self, name: &str) -> Result<()> {
self.db.delete_read_channel(name)
}
pub async fn copy_stream_builder(&self) -> Result<CopyStreamBuilder> {
CopyStreamBuilder::new(self.client.clone()).await
}
}
pub struct WriteChannel {
model: WriteChannelModel,
client: Arc<ThinClient>,
db: Database,
}
impl WriteChannel {
pub fn name(&self) -> &str {
&self.model.name
}
pub fn write_cap(&self) -> &[u8] {
&self.model.write_cap
}
pub fn next_index(&self) -> &[u8] {
&self.model.next_index
}
pub fn refresh(&mut self) -> Result<()> {
self.model = self.db.get_write_channel_by_id(self.model.id)?;
Ok(())
}
pub async fn write_box(&self, plaintext: &[u8], box_index: &[u8]) -> Result<Vec<u8>> {
let result = self
.client
.encrypt_write(plaintext, &self.model.write_cap, box_index)
.await?;
self.client
.start_resending_encrypted_message(
None,
Some(&self.model.write_cap),
None,
Some(0),
&result.envelope_descriptor,
&result.message_ciphertext,
&result.envelope_hash,
)
.await?;
Ok(result.next_message_box_index)
}
pub async fn write_box_return_box_exists(
&self,
plaintext: &[u8],
box_index: &[u8],
) -> Result<Vec<u8>> {
let result = self
.client
.encrypt_write(plaintext, &self.model.write_cap, box_index)
.await?;
self.client
.start_resending_encrypted_message_return_box_exists(
None,
Some(&self.model.write_cap),
None,
Some(0),
&result.envelope_descriptor,
&result.message_ciphertext,
&result.envelope_hash,
)
.await?;
Ok(result.next_message_box_index)
}
pub async fn send(&mut self, plaintext: &[u8]) -> Result<()> {
let encrypt_result = self
.client
.encrypt_write(plaintext, &self.model.write_cap, &self.model.next_index)
.await?;
let pending = self.db.create_pending_message(
self.model.id,
plaintext,
&encrypt_result.message_ciphertext,
&encrypt_result.envelope_descriptor,
&encrypt_result.envelope_hash,
&self.model.next_index,
)?;
self.db.update_pending_message_status(pending.id, "sending")?;
let result = self
.client
.start_resending_encrypted_message(
None,
Some(&self.model.write_cap),
None,
Some(0),
&encrypt_result.envelope_descriptor,
&encrypt_result.message_ciphertext,
&encrypt_result.envelope_hash,
)
.await;
match result {
Ok(_) => {
let next = encrypt_result.next_message_box_index;
self.db.update_write_next_index(self.model.id, &next)?;
self.db.delete_pending_message(pending.id)?;
self.model.next_index = next;
Ok(())
}
Err(e) => {
self.db.update_pending_message_status(pending.id, "failed")?;
Err(e.into())
}
}
}
pub async fn send_return_box_exists(&mut self, plaintext: &[u8]) -> Result<()> {
let encrypt_result = self
.client
.encrypt_write(plaintext, &self.model.write_cap, &self.model.next_index)
.await?;
let pending = self.db.create_pending_message(
self.model.id,
plaintext,
&encrypt_result.message_ciphertext,
&encrypt_result.envelope_descriptor,
&encrypt_result.envelope_hash,
&self.model.next_index,
)?;
self.db.update_pending_message_status(pending.id, "sending")?;
let result = self
.client
.start_resending_encrypted_message_return_box_exists(
None,
Some(&self.model.write_cap),
None,
Some(0),
&encrypt_result.envelope_descriptor,
&encrypt_result.message_ciphertext,
&encrypt_result.envelope_hash,
)
.await;
match result {
Ok(_) => {
let next = encrypt_result.next_message_box_index;
self.db.update_write_next_index(self.model.id, &next)?;
self.db.delete_pending_message(pending.id)?;
self.model.next_index = next;
Ok(())
}
Err(e) => {
self.db.update_pending_message_status(pending.id, "failed")?;
Err(e.into())
}
}
}
pub async fn tombstone_current(&mut self) -> Result<()> {
let encrypt_result = self
.client
.encrypt_write(&[], &self.model.write_cap, &self.model.next_index)
.await?;
self.client
.start_resending_encrypted_message(
None,
Some(&self.model.write_cap),
None,
None,
&encrypt_result.envelope_descriptor,
&encrypt_result.message_ciphertext,
&encrypt_result.envelope_hash,
)
.await?;
let next = encrypt_result.next_message_box_index;
self.db.update_write_next_index(self.model.id, &next)?;
self.model.next_index = next;
Ok(())
}
pub async fn tombstone_range(&mut self, count: u32) -> Result<u32> {
let result: TombstoneRangeResult = self
.client
.tombstone_range(&self.model.write_cap, &self.model.next_index, count)
.await;
let mut sent = 0u32;
for envelope in &result.envelopes {
let mut hash = [0u8; 32];
hash.copy_from_slice(&envelope.envelope_hash);
match self
.client
.start_resending_encrypted_message(
None,
Some(&self.model.write_cap),
None,
None,
&envelope.envelope_descriptor,
&envelope.message_ciphertext,
&hash,
)
.await
{
Ok(_) => sent += 1,
Err(e) => {
if sent > 0 {
self.db
.update_write_next_index(self.model.id, &envelope.box_index)?;
self.model.next_index = envelope.box_index.clone();
}
return Err(e.into());
}
}
}
if sent > 0 {
self.db.update_write_next_index(self.model.id, &result.next)?;
self.model.next_index = result.next;
}
Ok(sent)
}
pub async fn tombstone_at(&self, box_index: &[u8]) -> Result<()> {
let encrypt_result = self
.client
.encrypt_write(&[], &self.model.write_cap, box_index)
.await?;
self.client
.start_resending_encrypted_message(
None,
Some(&self.model.write_cap),
None,
None,
&encrypt_result.envelope_descriptor,
&encrypt_result.message_ciphertext,
&encrypt_result.envelope_hash,
)
.await?;
Ok(())
}
pub async fn tombstone_from(&self, start_index: &[u8], count: u32) -> Result<u32> {
let result: TombstoneRangeResult = self
.client
.tombstone_range(&self.model.write_cap, start_index, count)
.await;
let mut sent = 0u32;
for envelope in &result.envelopes {
let mut hash = [0u8; 32];
hash.copy_from_slice(&envelope.envelope_hash);
match self
.client
.start_resending_encrypted_message(
None,
Some(&self.model.write_cap),
None,
None,
&envelope.envelope_descriptor,
&envelope.message_ciphertext,
&hash,
)
.await
{
Ok(_) => sent += 1,
Err(e) => return Err(e.into()),
}
}
Ok(sent)
}
pub async fn copy_stream_builder(&self) -> Result<CopyStreamBuilder> {
CopyStreamBuilder::new(self.client.clone()).await
}
pub async fn execute_copy(
&self,
courier_identity_hash: Option<&[u8]>,
courier_queue_id: Option<&[u8]>,
) -> Result<()> {
self.client
.start_resending_copy_command(&self.model.write_cap, courier_identity_hash, courier_queue_id)
.await?;
Ok(())
}
pub async fn cancel_copy(&self, write_cap_hash: &[u8; 32]) -> Result<()> {
self.client.cancel_resending_copy_command(write_cap_hash).await?;
Ok(())
}
}
pub struct ReadChannel {
model: ReadChannelModel,
client: Arc<ThinClient>,
db: Database,
}
impl ReadChannel {
pub fn name(&self) -> &str {
&self.model.name
}
pub fn read_cap(&self) -> &[u8] {
&self.model.read_cap
}
pub fn next_index(&self) -> &[u8] {
&self.model.next_index
}
pub fn refresh(&mut self) -> Result<()> {
self.model = self.db.get_read_channel_by_id(self.model.id)?;
Ok(())
}
pub async fn read_box(&self, box_index: &[u8]) -> Result<(Vec<u8>, Vec<u8>)> {
let encrypt_result = self
.client
.encrypt_read(&self.model.read_cap, box_index)
.await?;
let result = self
.client
.start_resending_encrypted_message(
Some(&self.model.read_cap),
None,
Some(box_index),
Some(0),
&encrypt_result.envelope_descriptor,
&encrypt_result.message_ciphertext,
&encrypt_result.envelope_hash,
)
.await?;
Ok((result.plaintext, encrypt_result.next_message_box_index))
}
pub async fn read_box_no_retry(&self, box_index: &[u8]) -> Result<(Vec<u8>, Vec<u8>)> {
let encrypt_result = self
.client
.encrypt_read(&self.model.read_cap, box_index)
.await?;
let result = self
.client
.start_resending_encrypted_message_no_retry(
Some(&self.model.read_cap),
None,
Some(box_index),
Some(0),
&encrypt_result.envelope_descriptor,
&encrypt_result.message_ciphertext,
&encrypt_result.envelope_hash,
)
.await?;
Ok((result.plaintext, encrypt_result.next_message_box_index))
}
pub async fn receive(&mut self) -> Result<Vec<u8>> {
let encrypt_result = self
.client
.encrypt_read(&self.model.read_cap, &self.model.next_index)
.await?;
let current_index = self.model.next_index.clone();
let result = self
.client
.start_resending_encrypted_message(
Some(&self.model.read_cap),
None,
Some(¤t_index),
Some(0),
&encrypt_result.envelope_descriptor,
&encrypt_result.message_ciphertext,
&encrypt_result.envelope_hash,
)
.await?;
self.db
.create_received_message(self.model.id, &result.plaintext, ¤t_index)?;
let next = encrypt_result.next_message_box_index;
self.db.update_read_next_index(self.model.id, &next)?;
self.model.next_index = next;
Ok(result.plaintext)
}
pub async fn receive_no_retry(&mut self) -> Result<Vec<u8>> {
let encrypt_result = self
.client
.encrypt_read(&self.model.read_cap, &self.model.next_index)
.await?;
let current_index = self.model.next_index.clone();
let result = self
.client
.start_resending_encrypted_message_no_retry(
Some(&self.model.read_cap),
None,
Some(¤t_index),
Some(0),
&encrypt_result.envelope_descriptor,
&encrypt_result.message_ciphertext,
&encrypt_result.envelope_hash,
)
.await?;
self.db
.create_received_message(self.model.id, &result.plaintext, ¤t_index)?;
let next = encrypt_result.next_message_box_index;
self.db.update_read_next_index(self.model.id, &next)?;
self.model.next_index = next;
Ok(result.plaintext)
}
pub fn get_unread_messages(&self) -> Result<Vec<ReceivedMessage>> {
self.db.get_unread_messages(self.model.id)
}
pub fn get_all_messages(&self) -> Result<Vec<ReceivedMessage>> {
self.db.get_all_messages(self.model.id)
}
pub fn mark_message_read(&self, message_id: i64) -> Result<()> {
self.db.mark_message_read(message_id)
}
}
pub struct CopyStreamBuilder {
client: Arc<ThinClient>,
temp_write_cap: Vec<u8>,
temp_index: Vec<u8>,
total_boxes: usize,
buffer: Vec<u8>,
}
impl CopyStreamBuilder {
async fn new(client: Arc<ThinClient>) -> Result<Self> {
let mut seed = [0u8; 32];
rand::thread_rng().fill_bytes(&mut seed);
let KeypairResult { write_cap: temp_write_cap, read_cap: _, first_message_index: temp_first_index } =
client.new_keypair(&seed).await?;
Ok(Self {
client,
temp_write_cap,
temp_index: temp_first_index,
total_boxes: 0,
buffer: Vec::new(),
})
}
pub async fn add_payload(
&mut self,
payload: &[u8],
dest_write_cap: &[u8],
dest_start_index: &[u8],
is_last: bool,
) -> Result<usize> {
let is_start = self.total_boxes == 0;
let result = self
.client
.create_courier_envelopes_from_payload(payload, dest_write_cap, dest_start_index, is_start, is_last)
.await?;
let chunk_count = result.envelopes.len();
for chunk in result.envelopes {
let encrypt_result = self
.client
.encrypt_write(&chunk, &self.temp_write_cap, &self.temp_index)
.await?;
self.client
.start_resending_encrypted_message(
None,
Some(&self.temp_write_cap),
None,
Some(0),
&encrypt_result.envelope_descriptor,
&encrypt_result.message_ciphertext,
&encrypt_result.envelope_hash,
)
.await?;
self.temp_index = encrypt_result.next_message_box_index;
}
self.total_boxes += chunk_count;
Ok(chunk_count)
}
pub async fn add_multi_payload(
&mut self,
destinations: Vec<(&[u8], &[u8], &[u8])>,
is_last: bool,
) -> Result<usize> {
if destinations.is_empty() {
return Ok(0);
}
let is_start = self.total_boxes == 0;
let buf = if self.buffer.is_empty() { None } else { Some(self.buffer.clone()) };
let result = self
.client
.create_courier_envelopes_from_multi_payload(destinations, is_start, is_last, buf)
.await?;
let chunk_count = result.envelopes.len();
self.buffer = result.buffer;
for chunk in result.envelopes {
let encrypt_result = self
.client
.encrypt_write(&chunk, &self.temp_write_cap, &self.temp_index)
.await?;
self.client
.start_resending_encrypted_message(
None,
Some(&self.temp_write_cap),
None,
Some(0),
&encrypt_result.envelope_descriptor,
&encrypt_result.message_ciphertext,
&encrypt_result.envelope_hash,
)
.await?;
self.temp_index = encrypt_result.next_message_box_index;
}
self.total_boxes += chunk_count;
Ok(chunk_count)
}
pub async fn finish(self) -> Result<usize> {
self.client
.start_resending_copy_command(&self.temp_write_cap, None, None)
.await?;
Ok(self.total_boxes)
}
pub async fn finish_with_courier(
self,
courier_identity_hash: &[u8],
courier_queue_id: &[u8],
) -> Result<usize> {
self.client
.start_resending_copy_command(
&self.temp_write_cap,
Some(courier_identity_hash),
Some(courier_queue_id),
)
.await?;
Ok(self.total_boxes)
}
pub fn temp_write_cap(&self) -> &[u8] {
&self.temp_write_cap
}
pub fn buffer(&self) -> &[u8] {
&self.buffer
}
}
#[allow(dead_code)]
fn _ensure_error_in_scope(_e: PigeonholeDbError) {}