This commit is contained in:
Adrian Hedqvist 2018-06-20 16:38:12 +02:00
parent 3af1f717f5
commit c26cd3f881
7 changed files with 218 additions and 134 deletions

109
Cargo.lock generated
View file

@ -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"

View file

@ -5,4 +5,4 @@ authors = ["Adrian Hedqvist <adrian@tollyx.net>"]
[dependencies]
tokio = "0.1"
futures = "0.2"
futures = "0.1"

5
src/channel.rs Normal file
View file

@ -0,0 +1,5 @@
pub struct Channel {
name: String,
topic: String,
users: Vec<String>,
}

75
src/context.rs Normal file
View file

@ -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<Mutex<HashMap<SocketAddr, UnboundedSender<String>>>>,
pub unregistered_users: Arc<Mutex<HashMap<SocketAddr,User>>>,
pub users: Arc<Mutex<HashMap<String,User>>>,
pub channels: Arc<Mutex<HashMap<String,Channel>>>,
}
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<String>> {
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<String>> {
self.send_string_to(to, message.to_string())
}
}

View file

@ -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<Mutex<HashMap<,>>>,
users: Arc<Mutex<HashMap<String,user::User>>>,
channels: Arc<Mutex<HashMap<String,channel::Channel>>>,
}
*/
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(())
}))

View file

@ -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,
}
}

View file

@ -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
}
}