iris-chat 0.1.57

Iris Chat command line client and shared encrypted chat core
Documentation
use super::CHUNK;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "camelCase", deny_unknown_fields)]
pub(super) enum Control {
    Accept { id: String, token: String },
    Decline { id: String, token: String },
    Done,
    Received,
    Cancel,
}

pub(super) fn validate_claim(id: &str, token: &str) -> Result<(), String> {
    if !crate::direct_files::valid_transfer_id(id) {
        return Err("Invalid file offer".into());
    }
    if token.len() != 64 || !token.bytes().all(|b| b.is_ascii_hexdigit()) {
        return Err("Invalid file offer".into());
    }
    Ok(())
}
fn frame(kind: u8, data: &[u8]) -> Vec<u8> {
    let mut bytes = Vec::with_capacity(data.len() + 5);
    bytes.extend_from_slice(&((data.len() + 1) as u32).to_be_bytes());
    bytes.push(kind);
    bytes.extend_from_slice(data);
    bytes
}
pub(super) fn control(value: Control) -> Result<Vec<u8>, String> {
    let data = serde_json::to_vec(&value).map_err(|_| "Could not encode file request")?;
    Ok(frame(0, &data))
}
pub(super) fn data(bytes: Vec<u8>) -> Vec<u8> {
    frame(1, &bytes)
}
pub(super) fn parse_control(packet: &[u8]) -> Result<Control, String> {
    if packet.first() != Some(&0) || packet.len() > 1024 {
        return Err("Invalid file transfer frame".into());
    }
    let body = packet.get(1..).ok_or("Invalid file transfer frame")?;
    serde_json::from_slice(body).map_err(|_| "Invalid file transfer frame".into())
}

#[derive(Default)]
pub(super) struct Reader {
    bytes: Vec<u8>,
}
impl Reader {
    pub(super) fn push(&mut self, bytes: &[u8]) -> Result<Vec<Vec<u8>>, String> {
        self.bytes.extend_from_slice(bytes);
        let mut consumed = 0;
        let mut packets = Vec::new();
        while self.bytes.len() - consumed >= 4 {
            let header = self
                .bytes
                .get(consumed..consumed + 4)
                .ok_or("Invalid file frame header")?;
            let length =
                u32::from_be_bytes(header.try_into().map_err(|_| "Invalid file frame header")?)
                    as usize;
            if length == 0 || length > CHUNK + 1 {
                return Err("Invalid file transfer frame size".into());
            }
            let end = consumed + 4 + length;
            if end > self.bytes.len() {
                break;
            }
            packets.push(
                self.bytes
                    .get(consumed + 4..end)
                    .ok_or("Invalid file frame data")?
                    .to_vec(),
            );
            consumed = end;
        }
        self.bytes.drain(..consumed);
        Ok(packets)
    }
}