e2e-irc 3.0.0

An IRC bouncer that can send encrypted messages
use base64::{engine::general_purpose, Engine as _};
use eyre::Result;
use std::sync::mpsc::{self, Receiver, Sender};

#[derive(Debug)]
pub struct IrcParseError;

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

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

static MAX_LENGTH: usize = 300;

#[macro_export]
macro_rules! unwrap_or_return_result {
    ($e:expr) => {
        match $e {
            Ok(val) => val,
            Err(_) => return Ok(()),
        }
    };
}

#[macro_export]
macro_rules! unwrap_or_return_option {
    ($e:expr) => {
        match $e {
            Some(val) => val,
            None => return Ok(()),
        }
    };
}

pub struct State {
    pub nicks_without_encryption: Vec<String>,
}

impl State {
    pub fn new() -> Self {
        State {
            nicks_without_encryption: vec![
                "nickserv".to_string(),
                "chanserv".to_string(),
                "hostserv".to_string(),
            ],
        }
    }
}

fn forward(
    message: String,
    stream: &Sender<String>,
    server_local: &str,
    server_forward: &str,
) -> Result<(), mpsc::SendError<String>> {
    match ircparser::parse(&message) {
        Ok(val) => match val[0].command.as_str() {
            "PRIVMSG" => stream.send(message),
            _ => stream.send(message.replace(server_local, server_forward)),
        },
        Err(_) => stream.send(message.replace(server_local, server_forward)),
    }
}

pub fn bytes_to_privmsg_base64(message: &Vec<u8>, reciever: &str) -> String {
    let message_length = MAX_LENGTH - format!("PRIVMSG {reciever} :\r\n").len();
    let encoded = general_purpose::STANDARD.encode(message);

    let mut command = String::new();
    for line in encoded
        .chars()
        .collect::<Vec<char>>()
        .chunks(message_length)
        .map(|c| c.iter().collect::<String>())
    {
        dbg!(&line);
        command.push_str(&format!("PRIVMSG {reciever} :{line}\r\n"));
    }

    println!("{}", encoded);
    println!("{}", command);
    command
}

pub fn send_key(sender: &Sender<String>, reciever: &str, key: &Vec<u8>) -> Result<()> {
    sender.send(format!("PRIVMSG {reciever} :START_KEY\r\n"))?;
    sender.send(bytes_to_privmsg_base64(key, reciever))?;
    sender.send(format!("PRIVMSG {reciever} :END_KEY\r\n"))?;

    Ok(())
}

pub fn get_nick(userstring: &str) -> Option<String> {
    let userstring = userstring.chars().collect::<Vec<char>>();
    let start_pos = userstring.iter().position(|&x| x == ':')? + 1;
    let end_pos = userstring.iter().position(|&x| x == '!')?;
    Some(userstring[start_pos..end_pos].iter().collect::<String>())
}

pub fn recieve_message_base64(
    writer_channel_rx: &Receiver<String>,
    forward_stream: &Sender<String>,
    server_local: &str,
    server_forward: &str,
    sender: &str,
    end: &str,
) -> Result<Vec<u8>> {
    let mut message: Vec<String> = Vec::new();

    while !message.contains(&end.to_string()) {
        let recieved_raw = writer_channel_rx.recv()?;

        let parse_result = ircparser::parse(&recieved_raw);

        let recieved = match parse_result {
            Ok(mut val) => val.pop_back().unwrap(),
            Err(_) => return Err(IrcParseError.into()),
        };

        let begin_source_reciever = format!(":{sender}!");
        if recieved.command != "PRIVMSG"
            || !recieved
                .source
                .clone()
                .unwrap_or("".to_string())
                .starts_with(&begin_source_reciever)
            || recieved.params[0].starts_with('#')
        {
            forward(recieved_raw, forward_stream, server_local, server_forward)?;
            continue;
        }

        message.push(recieved.params[1].clone());
    }
    message.pop();

    let foreign_key = dbg!(message.concat());
    let foreign_key = general_purpose::STANDARD.decode(foreign_key)?;
    Ok(foreign_key)
}