e2e-irc 3.0.0

An IRC bouncer that can send encrypted messages
use crate::helpers::bytes_to_privmsg_base64;
use crate::{encryption, helpers, State};
use eyre::Result;
use pgp::{Deserializable, SignedPublicKey};
use std::collections::HashMap;
use std::sync::mpsc::{Receiver, Sender};

#[derive(Debug)]
struct InvalidCommand;

impl std::fmt::Display for InvalidCommand {
    fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        Ok(())
    }
}

impl std::error::Error for InvalidCommand {}

fn parse_bouncer_command(message: String, state: &mut State) -> Result<()> {
    macro_rules! unwrap_option {
        ($t:expr) => {
            match $t {
                Some(val) => val,
                None => return Err(InvalidCommand.into()),
            }
        };
    }

    let mut splitted = message.split(' ');
    match unwrap_option!(splitted.next()) {
        "ALLOW_UNENCRYPTED" => state
            .nicks_without_encryption
            .push(unwrap_option!(splitted.next()).to_string().to_lowercase()),
        _ => return Err(InvalidCommand.into()),
    };
    Ok(())
}

pub fn handle_message_from_client(
    recieved: &str,
    public_key: &Vec<u8>,
    server: &str,
    keys: &mut HashMap<String, SignedPublicKey>,
    writer_channel_tx: &Sender<String>,
    writer_channel_rx: &Receiver<String>,
    listener_channel_tx: &Sender<String>,
    _listener_channel_rx: &Receiver<String>,
    state: &mut State,
) -> Result<()> {
    let mut recieved = recieved.to_string();

    if recieved.split(' ').count() == 1 {
        recieved += " ";
    }

    let parsed = ircparser::parse(&recieved);
    let command = match parsed {
        Ok(val) => val[0].clone(),
        Err(_) => {
            writer_channel_tx.send(recieved)?;
            return Ok(());
        }
    };

    if command.command == "PRIVMSG" && !command.params[0].starts_with('#') {
        if command.params[0] == "BOUNCER" {
            return parse_bouncer_command(command.params[1].clone(), state);
        }
        if state
            .nicks_without_encryption
            .contains(&command.params[0].to_lowercase())
        {
            writer_channel_tx.send(recieved)?;
            return Ok(());
        }
        let other = &command.params[0];

        if !keys.contains_key(other) {
            helpers::send_key(writer_channel_tx, other, public_key)?;
            let key = helpers::recieve_message_base64(
                writer_channel_rx,
                listener_channel_tx,
                "127.0.0.1",
                server,
                other,
                "END_KEY",
            )?;
            let key = SignedPublicKey::from_bytes(key.as_slice())?;
            keys.insert(other.to_string(), key);
        }

        let foreign_key = keys.get(other).unwrap();

        writer_channel_tx.send(format!("PRIVMSG {other} START_MESSAGE\r\n"))?;
        writer_channel_tx.send(bytes_to_privmsg_base64(
            &encryption::encrypt(foreign_key, &command.params[1])?,
            other,
        ))?;
        writer_channel_tx.send(format!("PRIVMSG {other} END_MESSAGE\r\n"))?;
    } else {
        writer_channel_tx.send(recieved.replace("127.0.0.1", server))?;
    }
    Ok(())
}