From c26cd3f881c0d55dae1ac98457432888f0c72159 Mon Sep 17 00:00:00 2001 From: Adrian Hedqvist Date: Wed, 20 Jun 2018 16:38:12 +0200 Subject: [PATCH] Progress --- Cargo.lock | 109 +------------------------------------------- Cargo.toml | 2 +- src/channel.rs | 5 +++ src/context.rs | 75 +++++++++++++++++++++++++++++++ src/main.rs | 119 +++++++++++++++++++++++++++++++++++++++---------- src/models.rs | 6 +++ src/user.rs | 36 ++++++++++++++- 7 files changed, 218 insertions(+), 134 deletions(-) create mode 100644 src/channel.rs create mode 100644 src/context.rs diff --git a/Cargo.lock b/Cargo.lock index 73b1a7e..f9e86e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,11 +60,6 @@ dependencies = [ "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "either" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -84,98 +79,6 @@ name = "futures" version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "futures" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-async-runtime 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-stable 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures-async-runtime" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-stable 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures-channel" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures-core" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures-executor" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-channel 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures-io" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures-sink" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures-stable" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures-util" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "iovec" version = "0.1.2" @@ -278,7 +181,7 @@ dependencies = [ name = "plankircd" version = "0.1.0" dependencies = [ - "futures 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -466,19 +369,9 @@ dependencies = [ "checksum crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fe8153ef04a7594ded05b427ffad46ddeaf22e63fd48d42b3e1e3bb4db07cae7" "checksum crossbeam-epoch 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2af0e75710d6181e234c8ecc79f14a97907850a541b13b0be1dd10992f2e4620" "checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b" -"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c" -"checksum futures 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d81ccac876e9e5efad47774552d1ecc05828886ad64468bc716595659bd078cb" -"checksum futures-async-runtime 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bab8d196f9bcbc3b33148960602889bf44c74afe5bea20ee970aaa08df2db2f5" -"checksum futures-channel 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bbb37ec6418c577b25f5b129c0f4456ad7ce8714ec43c59712aa7e4cd2cb6b85" -"checksum futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7455c91eb2eae38f33b013f77ebe766c75761af333efd9d550e154045c63e225" -"checksum futures-executor 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5db1dd3979745f5e50b28fd604602f2715f9d5a28ab835a5f9686a9d84cd1315" -"checksum futures-io 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6a0470fdba9dc87c27a3564ad6d5cc04e080f3afa26c93549728cce46ab21a2" -"checksum futures-sink 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a8a93a7c480876b8e02cdd70022e7eb9c8423575ea6a25a0b749b18834c16412" -"checksum futures-stable 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a222f540db94a09c275be08a406b5cd82f4311422b940a684795cdaef3d02e81" -"checksum futures-util 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cf12a3fc1ccaf1bc2901ec6e0ed6ed407a4f16eaa20dd838f40cabf5f7b31f1" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739" diff --git a/Cargo.toml b/Cargo.toml index 744b12c..7b73476 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,4 @@ authors = ["Adrian Hedqvist "] [dependencies] tokio = "0.1" -futures = "0.2" \ No newline at end of file +futures = "0.1" \ No newline at end of file diff --git a/src/channel.rs b/src/channel.rs new file mode 100644 index 0000000..270e20c --- /dev/null +++ b/src/channel.rs @@ -0,0 +1,5 @@ +pub struct Channel { + name: String, + topic: String, + users: Vec, +} \ No newline at end of file diff --git a/src/context.rs b/src/context.rs new file mode 100644 index 0000000..004fabd --- /dev/null +++ b/src/context.rs @@ -0,0 +1,75 @@ +use futures::sync::mpsc::UnboundedSender; +use futures::sync::mpsc::SendError; +use std::net::SocketAddr; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; +use user::User; +use channel::Channel; +use models::IrcMessage; +use models::IrcCommand; + +#[derive(Clone)] +pub struct Context { + pub connections: Arc>>>, + pub unregistered_users: Arc>>, + pub users: Arc>>, + pub channels: Arc>>, +} + +impl Context { + pub fn new() -> Context { + Context { + connections: Arc::new(Mutex::new(HashMap::new())), + unregistered_users: Arc::new(Mutex::new(HashMap::new())), + users: Arc::new(Mutex::new(HashMap::new())), + channels: Arc::new(Mutex::new(HashMap::new())), + } + } + + pub fn register_user(&mut self, addr: &SocketAddr) -> Result<(), ()> { + let mut users = self.users.lock().unwrap(); + let mut unreg = self.unregistered_users.lock().unwrap(); + let remove = unreg.remove(addr); + match remove { + Some(user) => { + if users.contains_key(&user.nickname) { + self.send_string_to(&addr, format!(":plankircd 433 {} :Nickname is already in use", user.nickname)); + unreg.insert(*addr, user); + return Err(()); + } + let mut msg = format!(":plankircd 001 {} :Welcome to IRC!\r\n", user.nickname); + msg += &format!(":plankircd 002 {} :Your host is plankircd, running version 0.0.1\r\n", user.nickname); + msg += &format!(":plankircd 003 {} :This server was created tomorrow\r\n", user.nickname); + msg += &format!(":plankircd 004 {} plankircd plankircd-0.0.1 o o\r\n", user.nickname); + msg += &format!(":plankircd 422 {} :MOTD File is missing\r\n", user.nickname); + let res = self.send_string_to(&addr, msg); + println!("{:?}",res); + users.insert(user.nickname.clone(), user); + Ok(()) + }, + None => Err(()) + } + } + + pub fn disconnect(&mut self, addr: &SocketAddr) { + self.send_message_to(addr, IrcMessage::new(Some("plankircd"), IrcCommand::ERROR, vec!["Connection terminated."])); + self.connections.lock().unwrap().remove(addr); + self.unregistered_users.lock().unwrap().remove(addr); + self.users.lock().unwrap().retain(|_, u| u.address != *addr); + } + + pub fn is_unreg(&self, addr: &SocketAddr) -> bool { + self.unregistered_users.lock().unwrap().contains_key(addr) + } + + pub fn send_string_to(&self, to: &SocketAddr, string: String) -> Result<(),SendError> { + match self.connections.lock().unwrap().get(to) { + Some(tx) => tx.unbounded_send(string), + None => Ok(()), // TODO: proper error + } + } + + pub fn send_message_to(&self, to: &SocketAddr, message: IrcMessage) -> Result<(),SendError> { + self.send_string_to(to, message.to_string()) + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 6a08c70..deabc06 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,9 @@ extern crate tokio; extern crate futures; +//use futures::prelude::*; +use futures::sync::mpsc::UnboundedSender; +use std::net::SocketAddr; use tokio::prelude::*; use tokio::io; use std::iter; @@ -11,36 +14,37 @@ use std::sync::{Arc, Mutex}; pub mod models; pub mod user; -/* -struct Context { - connections: Arc>>, - users: Arc>>, - channels: Arc>>, -} -*/ +pub mod channel; +pub mod context; + +use context::Context; + fn main() { let addr = "127.0.0.1:6667".parse().unwrap(); let listener = TcpListener::bind(&addr) .expect("unable to bind TCP listener"); - let mut connections = Arc::new(Mutex::new(HashMap::new())); - //let mut users = Arc::new(Mutex::new(HashMap::new())); - //let mut channels = Arc::new(Mutex::new(HashMap::new())); + let mut context = Context::new(); let server = listener.incoming() .map_err(|e| eprintln!("accept failed = {:?}", e)) - .for_each(|sock| { + .for_each(move |sock| { let addr = sock.peer_addr().unwrap(); println!("{} connected", addr); let (reader, writer) = sock.split(); - let (tx, rx) = futures::channel::mpsc::unbounded(); + let (tx, rx) = futures::sync::mpsc::unbounded(); - connections.lock().unwrap().insert(addr, tx); + { + context.connections.lock().unwrap().insert(addr, tx); + context.unregistered_users.lock().unwrap().insert(addr, user::User::new(addr)); + } - let bufreader = BufReader::new(reader); + let context_inner = context.clone(); + let reader = BufReader::new(reader); let iter = stream::iter_ok::<_, io::Error>(iter::repeat(())); - let socket_reader = iter.fold(bufreader, move |reader, _| { + let socket_reader = iter.fold(reader, move |reader, _| { + let mut context = context_inner.clone(); io::read_until(reader, b'\n', vec![]) .and_then(|(reader, vec)| { if vec.len() == 0 { @@ -50,23 +54,90 @@ fn main() { Ok((reader, vec)) } }) - .map(move |(reader, vec)| { - let line = String::from_utf8(vec).unwrap(); - let msg = models::IrcMessage::from_str(&line); - match msg { - Some(msg) => { - println!("{} -> {}", addr, msg.to_string().trim()); - + .map(|(reader, vec)| { + match String::from_utf8(vec) { + Ok(s) => (reader, s), + Err(e) => { + eprintln!("failed converting data: {:?}", e); + (reader, "ERROR".to_string()) }, - None => eprintln!("Unhandled message: {} -> {}", addr, line.trim()), + } + }) + .map(move |(reader, line)| { + use models::IrcMessage; + let msg = IrcMessage::from_str(&line); + let res = match msg { + Some(msg) => { + use models::IrcCommand::*; + match msg.command { + NICK => { + let mut shouldreg = false; + if msg.params.len() < 1 { + } + else { + if context.is_unreg(&addr) { + let mut lock = context.unregistered_users.lock().unwrap(); + let mut unreg = lock.get_mut(&addr).unwrap(); + let wasreg = unreg.is_registered(); + unreg.set_nick(&msg.params[0]); + if !wasreg && unreg.is_registered() { + shouldreg = true; + } + } + } + if shouldreg { + context.register_user(&addr); + } + Ok(()) + }, + USER => { + let mut shouldreg = false; + if msg.params.len() < 4 { + } + else { + if context.is_unreg(&addr) { + let mut lock = context.unregistered_users.lock().unwrap(); + let mut unreg = lock.get_mut(&addr).unwrap(); + let wasreg = unreg.is_registered(); + unreg.set_user(&msg.params[0], &msg.params[2], &msg.params[3]); + if !wasreg && unreg.is_registered() { + shouldreg = true; + } + } + } + if shouldreg { + context.register_user(&addr); + } + Ok(()) + }, + PING => context.send_message_to(&addr, IrcMessage::new(Some("plankircd"), PONG, vec!["plankircd", &msg.params[0]])), + QUIT => Ok(context.disconnect(&addr)), + _ => Ok(println!("Unhandled command: {} -> {}", addr, line.trim())), + } + }, + None => Ok(eprintln!("Unrecognized command: {} -> {}", addr, line.trim())), }; + + if let Err(e) = res { + eprintln!("error handling command {:?}", e); + } + reader }) + }) + .map_err(|_|()); + + let socket_writer = rx.fold(writer, move |writer, msg| { + println!("{} <- {}", addr, msg); + io::write_all(writer, msg.into_bytes()) + .map(|(writer, _)| writer) + .map_err(|_| ()) }); + let connection = socket_reader.map(|_|()).select(socket_writer.map(|_|())); // Spawn the future as a concurrent task. - tokio::spawn(socket_reader.then(move |_| { + tokio::spawn(connection.then(move |_| { println!("Connection {} closed", addr); Ok(()) })) diff --git a/src/models.rs b/src/models.rs index 8bf4a16..b208ea3 100644 --- a/src/models.rs +++ b/src/models.rs @@ -11,6 +11,9 @@ pub enum IrcCommand { CAP, PASS, QUIT, + PING, + PONG, + ERROR, } impl IrcCommand { @@ -28,6 +31,9 @@ impl IrcCommand { "QUIT" => Some(QUIT), "LIST" => Some(LIST), "PASS" => Some(PASS), + "PING" => Some(PING), + "PONG" => Some(PONG), + "ERROR" => Some(ERROR), _ => None, } } diff --git a/src/user.rs b/src/user.rs index 231d748..2f913ef 100644 --- a/src/user.rs +++ b/src/user.rs @@ -1,12 +1,46 @@ +use std::net::SocketAddr; + +#[derive(Debug, Clone)] pub struct User { pub nickname: String, pub username: String, pub hostname: String, pub realname: String, + pub address: SocketAddr, + nick_set: bool, + user_set: bool, } impl User { - fn to_string(&self) -> String { + pub fn new(addr: SocketAddr) -> User { + User { + nickname: String::new(), + username: String::new(), + hostname: String::new(), + realname: String::new(), + address: addr, + nick_set: false, + user_set: false, + } + } + + pub fn set_nick(&mut self, nick: &str) { + self.nickname = nick.to_string(); + self.nick_set = true; + } + + pub fn set_user(&mut self, user: &str, host: &str, real: &str) { + self.username = user.to_string(); + self.hostname = host.to_string(); + self.realname = real.to_string(); + self.user_set = true; + } + + pub fn to_string(&self) -> String { format!("{}!{}@{}", self.nickname, self.username, self.hostname) } + + pub fn is_registered(&self) -> bool { + self.nick_set && self.user_set + } }