diff --git a/src/main.rs b/src/main.rs index fc7db93..852e25a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,180 +17,14 @@ pub mod connect; pub mod config; +pub mod servers; use std::{ env::args, path::PathBuf, - collections::VecDeque, - sync::mpsc::{ - channel, - Sender, - }, + sync::mpsc::channel, thread, }; -use irc_proto::command::{ - CapSubCommand, - Command, -}; -use irc_proto::message::Message; -use irc_stream::{ - IrcRead, - IrcWrite, -}; -use toml::value::Value; - -trait IrcReadWrite: IrcRead + IrcWrite {} -impl IrcReadWrite for T {} - -/// Constructs a VecDeque with the messages -/// required to identify with an IRC server. -/// Uses the credentials set in -`config.toml`. -fn get_irc_identify_messages (config: &Value) - -> Result, Box> { - let mut queue = VecDeque::new(); - - queue.push_back(Message::from( - Command::CAP( - None, - CapSubCommand::END, - None, - None - ))); - - match config.get("password") { - Some(p) => queue.push_back(Message::from( - Command::PASS( - String::from(p.as_str().ok_or("Could not parse password.")?) - ) - )), - None => () - } - - let nick: String; - match config.get("nickname") { - Some(n) => { - nick = String::from(n.as_str().ok_or("Could not parse nickname.")?); - queue.push_back(Message::from(Command::NICK(nick.clone()))); - }, - None => return Err("No nickname supplied!".into()), - } - - match config.get("username") { - Some(u) => queue.push_back(Message::from( - Command::USER( - String::from(u.as_str().ok_or("Could not parse username.")?), - "0".to_owned(), - String::from(u.as_str().ok_or("Could not parse username.")?) - ) - )), - None => queue.push_back(Message::from( - // nick.clone() is only used once because the value - // can be moved the second time - Command::USER(nick.clone(), "0".to_owned(), nick) - )) - } - Ok(queue) -} - -/// Appends a given VecDeque to include the messages -/// used to join the channels defined in `config.toml`. -fn get_irc_join_messages(config: &Value, queue: VecDeque) - -> Result, Box> { - let mut queue = queue; - - match config.get("channels") { - Some(c) => { - for channel in c.as_array().ok_or("Could not parse channels.")? { - queue.push_back(Message::from( - Command::JOIN( - String::from(channel.as_str().ok_or("Could not parse one of the channels")?), - None, - None - ) - )) - } - }, - None => () - } - - Ok(queue) -} - -fn handle_message(message: Message) -> (Option, Option<[String; 3]>) { - let sender = match message.clone().source_nickname() { - Some(s) => String::from(s), - None => String::from("anonymous") - }; - match message.clone().command { - Command::PING(ref data, _) => { - return ( - Some(Message::from( - Command::PONG(data.to_owned(), None) - )), - None - ); - }, - Command::PRIVMSG(ref rec, ref msg) => { - return (None, Some([rec.clone(), sender.clone(), msg.clone()])) - }, - _ => println!("{}", message.clone().to_string()) - } - return (None, None) -} - -fn handle_server(config: Value, tx: Sender<[String; 4]>) - -> Result<(), Box> { - let server_name = config.get("server").unwrap().as_str() - .ok_or("Could not get server adress from config")?; - let mut stream = connect::connect_irc(&config).unwrap(); - let mut message_queue = get_irc_identify_messages(&config).unwrap(); - - while let Some(message) = message_queue.pop_front() { - stream.write(message).unwrap(); - } - - message_queue = get_irc_join_messages(&config, message_queue).unwrap(); - - // Wait for first ping and join channels after sending pong. - loop { - let message = stream.read()?; - match message.command { - Command::PING(ref data, _) => { - stream.write(Message::from( - Command::PONG(data.to_owned(), None) - )).unwrap(); - while let Some(message) = message_queue.pop_front() { - stream.write(message).unwrap(); - } - break; - } - _ => () - } - } - - // Handle all incoming messages after joining the channels. - loop { - let message = stream.read()?; - let (answer, data) = handle_message(message); - match answer { - Some(a) => stream.write(a).unwrap(), - None => () - } - match data { - Some(d) => { - // There must be a better way to do this… - let d = [ - server_name.to_owned(), - d[0].clone(), - d[1].clone(), - d[2].clone() - ]; - tx.send(d)? - }, - None => (), - } - } -} fn main() { let mut server_conf_flag = false; @@ -235,7 +69,7 @@ fn main() { server_threads.push(thread::Builder::new() .name("server name here".to_string()) .spawn(move || { - handle_server(s_conf, t).unwrap(); + servers::handle_server(s_conf, t).unwrap(); }) ); } diff --git a/src/servers.rs b/src/servers.rs new file mode 100644 index 0000000..0e319dc --- /dev/null +++ b/src/servers.rs @@ -0,0 +1,178 @@ +/* Copyright 2021 Daniel Mowitz + * This file is part of Mention2Mail. + * + * Mention2Mail is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Mention2Mail is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Mention2Mail. If not, see . + */ + +use std::{ + collections::VecDeque, + sync::mpsc::Sender, +}; +use irc_proto::command::{ + CapSubCommand, + Command, +}; +use irc_proto::message::Message; +use toml::value::Value; + +/// Constructs a VecDeque with the messages +/// required to identify with an IRC server. +/// Uses the credentials set in -`config.toml`. +fn get_irc_identify_messages (config: &Value) + -> Result, Box> { + let mut queue = VecDeque::new(); + + queue.push_back(Message::from( + Command::CAP( + None, + CapSubCommand::END, + None, + None + ))); + + match config.get("password") { + Some(p) => queue.push_back(Message::from( + Command::PASS( + String::from(p.as_str().ok_or("Could not parse password.")?) + ) + )), + None => () + } + + let nick: String; + match config.get("nickname") { + Some(n) => { + nick = String::from(n.as_str().ok_or("Could not parse nickname.")?); + queue.push_back(Message::from(Command::NICK(nick.clone()))); + }, + None => return Err("No nickname supplied!".into()), + } + + match config.get("username") { + Some(u) => queue.push_back(Message::from( + Command::USER( + String::from(u.as_str().ok_or("Could not parse username.")?), + "0".to_owned(), + String::from(u.as_str().ok_or("Could not parse username.")?) + ) + )), + None => queue.push_back(Message::from( + // nick.clone() is only used once because the value + // can be moved the second time + Command::USER(nick.clone(), "0".to_owned(), nick) + )) + } + Ok(queue) +} + +/// Appends a given VecDeque to include the messages +/// used to join the channels defined in `config.toml`. +fn get_irc_join_messages(config: &Value, queue: VecDeque) + -> Result, Box> { + let mut queue = queue; + + match config.get("channels") { + Some(c) => { + for channel in c.as_array().ok_or("Could not parse channels.")? { + queue.push_back(Message::from( + Command::JOIN( + String::from(channel.as_str().ok_or("Could not parse one of the channels")?), + None, + None + ) + )) + } + }, + None => () + } + + Ok(queue) +} + +fn handle_message(message: Message) -> (Option, Option<[String; 3]>) { + let sender = match message.clone().source_nickname() { + Some(s) => String::from(s), + None => String::from("anonymous") + }; + match message.clone().command { + Command::PING(ref data, _) => { + return ( + Some(Message::from( + Command::PONG(data.to_owned(), None) + )), + None + ); + }, + Command::PRIVMSG(ref rec, ref msg) => { + return (None, Some([rec.clone(), sender.clone(), msg.clone()])) + }, + _ => println!("{}", message.clone().to_string()) + } + return (None, None) +} + +pub fn handle_server(config: Value, tx: Sender<[String; 4]>) + -> Result<(), Box> { + let server_name = config.get("server").unwrap().as_str() + .ok_or("Could not get server adress from config")?; + let mut stream = super::connect::connect_irc(&config).unwrap(); + let mut message_queue = get_irc_identify_messages(&config).unwrap(); + + while let Some(message) = message_queue.pop_front() { + stream.write(message).unwrap(); + } + + message_queue = get_irc_join_messages(&config, message_queue).unwrap(); + + // Wait for first ping and join channels after sending pong. + loop { + let message = stream.read()?; + match message.command { + Command::PING(ref data, _) => { + stream.write(Message::from( + Command::PONG(data.to_owned(), None) + )).unwrap(); + while let Some(message) = message_queue.pop_front() { + stream.write(message).unwrap(); + } + break; + } + _ => () + } + } + + // Handle all incoming messages after joining the channels. + loop { + let message = stream.read()?; + let (answer, data) = handle_message(message); + match answer { + Some(a) => stream.write(a).unwrap(), + None => () + } + match data { + Some(d) => { + // There must be a better way to do this… + let d = [ + server_name.to_owned(), + d[0].clone(), + d[1].clone(), + d[2].clone() + ]; + tx.send(d)? + }, + None => (), + } + } +} +