More progress

This commit is contained in:
Adrian Hedqvist 2018-06-21 16:33:13 +02:00
parent c26cd3f881
commit e7ccea3c8e
4 changed files with 182 additions and 111 deletions

View file

@ -10,66 +10,68 @@ use models::IrcCommand;
#[derive(Clone)] #[derive(Clone)]
pub struct Context { pub struct Context {
pub connections: Arc<Mutex<HashMap<SocketAddr, UnboundedSender<String>>>>, pub hostname: Arc<Mutex<String>>,
pub unregistered_users: Arc<Mutex<HashMap<SocketAddr,User>>>, pub users: Arc<Mutex<HashMap<SocketAddr,User>>>,
pub users: Arc<Mutex<HashMap<String,User>>>,
pub channels: Arc<Mutex<HashMap<String,Channel>>>, pub channels: Arc<Mutex<HashMap<String,Channel>>>,
} }
impl Context { impl Context {
pub fn new() -> Context { pub fn new() -> Context {
Context { Context {
connections: Arc::new(Mutex::new(HashMap::new())), hostname: Arc::new(Mutex::new("plankircd".to_string())),
unregistered_users: Arc::new(Mutex::new(HashMap::new())),
users: Arc::new(Mutex::new(HashMap::new())), users: Arc::new(Mutex::new(HashMap::new())),
channels: Arc::new(Mutex::new(HashMap::new())), channels: Arc::new(Mutex::new(HashMap::new())),
} }
} }
pub fn register_user(&mut self, addr: &SocketAddr) -> Result<(), ()> { pub fn register_user(&mut self, addr: &SocketAddr) -> Result<(), ()> {
let mut users = self.users.lock().unwrap(); let mut user;
let mut unreg = self.unregistered_users.lock().unwrap(); {
let remove = unreg.remove(addr); user = self.users.lock().unwrap().get_mut(addr).cloned();
match remove { }
Some(user) => { match user {
if users.contains_key(&user.nickname) { Some(mut user) => {
self.send_string_to(&addr, format!(":plankircd 433 {} :Nickname is already in use", user.nickname)); if !user.is_valid() || user.is_registered() {
unreg.insert(*addr, user);
return Err(()); return Err(());
} }
let mut msg = format!(":plankircd 001 {} :Welcome to IRC!\r\n", user.nickname); if self.users.lock().unwrap().iter().any(|(k, v)| k != addr && v.nickname == user.nickname) {
msg += &format!(":plankircd 002 {} :Your host is plankircd, running version 0.0.1\r\n", user.nickname); return Err(());
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); user.register(); // TODO: This is done on the cloned user -- won't save
let res = self.send_string_to(&addr, msg);
println!("{:?}",res);
users.insert(user.nickname.clone(), user);
Ok(()) Ok(())
}, },
None => Err(()) None => Err(())
} }
} }
pub fn disconnect(&mut self, addr: &SocketAddr) { pub fn nick_exists(&self, nick: &str) -> bool {
self.send_message_to(addr, IrcMessage::new(Some("plankircd"), IrcCommand::ERROR, vec!["Connection terminated."])); self.users.lock().unwrap().values().any(|user| user.nickname == nick)
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 { pub fn send_message_to(&self, addr: &SocketAddr, msg: IrcMessage) -> Result<(), SendError<String>> {
self.unregistered_users.lock().unwrap().contains_key(addr) if let Some(user) = self.users.lock().unwrap().get(addr) {
user.send(msg)
}
else { Ok(()) }
} }
pub fn send_string_to(&self, to: &SocketAddr, string: String) -> Result<(),SendError<String>> { pub fn is_registered(&self, addr: &SocketAddr) -> bool {
match self.connections.lock().unwrap().get(to) { if let Some(user) = self.users.lock().unwrap().get(addr) {
Some(tx) => tx.unbounded_send(string), user.is_registered()
None => Ok(()), // TODO: proper error }
else {
false
} }
} }
pub fn send_message_to(&self, to: &SocketAddr, message: IrcMessage) -> Result<(),SendError<String>> { pub fn connect(&mut self, addr: SocketAddr, send: UnboundedSender<String>) {
self.send_string_to(to, message.to_string()) self.users.lock().unwrap().insert(addr, User::new(send));
}
pub fn disconnect(&mut self, addr: &SocketAddr) {
if let Some(user) = self.users.lock().unwrap().remove(addr) {
user.send(IrcMessage::new(Some(&self.hostname.lock().unwrap()), IrcCommand::ERROR, vec!["Connection terminated."]));
}
} }
} }

View file

@ -34,10 +34,7 @@ fn main() {
let (reader, writer) = sock.split(); let (reader, writer) = sock.split();
let (tx, rx) = futures::sync::mpsc::unbounded(); let (tx, rx) = futures::sync::mpsc::unbounded();
{ context.connect(addr, tx);
context.connections.lock().unwrap().insert(addr, tx);
context.unregistered_users.lock().unwrap().insert(addr, user::User::new(addr));
}
let context_inner = context.clone(); let context_inner = context.clone();
@ -65,70 +62,85 @@ fn main() {
}) })
.map(move |(reader, line)| { .map(move |(reader, line)| {
use models::IrcMessage; use models::IrcMessage;
let msg = IrcMessage::from_str(&line); for l in line.lines() {
let res = match msg { println!("{} -> {}", addr, l.trim());
Some(msg) => { }
use models::IrcCommand::*; let res = IrcMessage::from_str(&line);
match msg.command { if let Err(e) = res {
NICK => { eprintln!("error parsing command: {:?}", e);
let mut shouldreg = false; return reader;
if msg.params.len() < 1 { }
} let msg = res.unwrap();
else { use models::IrcCommand::*;
if context.is_unreg(&addr) { match msg.command {
let mut lock = context.unregistered_users.lock().unwrap(); NICK => {
let mut unreg = lock.get_mut(&addr).unwrap(); if msg.params.len() < 1 {
let wasreg = unreg.is_registered(); context.send_message_to(&addr, IrcMessage::new(Some("plankircd"), Num(431), vec!["*", "No nickname given"]));
unreg.set_nick(&msg.params[0]); }
if !wasreg && unreg.is_registered() { else {
shouldreg = true; let nick = &msg.params[0];
} if context.nick_exists(nick) {
context.send_message_to(&addr, IrcMessage::new(Some("plankircd"), Num(443), vec!["*", nick, "Nickname is already in use"]));
}
else {
let (valid, old) = {
let mut lock = context.users.lock().unwrap();
let mut user = lock.get_mut(&addr).unwrap();
let old = user.nickname.clone();
user.set_nick(nick);
(user.is_valid(), old)
};
if valid {
if !context.is_registered(&addr) {
context.register_user(&addr);
}
else {
context.send_message_to(&addr, IrcMessage::new(Some(&old), NICK, vec![nick]));
} }
} }
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())), USER => {
}; if msg.params.len() < 4 {
}
if let Err(e) = res { else {
eprintln!("error handling command {:?}", e); let valid = {
let mut lock = context.users.lock().unwrap();
let mut user = lock.get_mut(&addr).unwrap();
user.set_user(&msg.params[0], &msg.params[2], &msg.params[3]);
user.is_valid()
};
if valid && !context.is_registered(&addr) {
context.register_user(&addr);
}
}
},
PING => {context.send_message_to(&addr, IrcMessage::new(Some("plankircd"), PONG, vec!["plankircd", &msg.params[0]]));},
PRIVMSG => {
if msg.params[0].starts_with('#') {
}
else {
let users = context.users.lock().unwrap();
let from = &users[&addr];
if let Some(user) = users.values().find(|u| u.nickname == msg.params[0]) {
user.privmsg(from, &msg.params[1]);
}
}
}
QUIT => context.disconnect(&addr),
_ => println!("Unhandled command: {}", msg.command),
} }
reader reader
}) })
}) })
.map_err(|_|()); .map_err(|_|());
let socket_writer = rx.fold(writer, move |writer, msg| { let socket_writer = rx.fold(writer, move |writer, msg| {
println!("{} <- {}", addr, msg); for l in msg.lines() {
println!("{} <- {}", addr, l.trim());
}
io::write_all(writer, msg.into_bytes()) io::write_all(writer, msg.into_bytes())
.map(|(writer, _)| writer) .map(|(writer, _)| writer)
.map_err(|_| ()) .map_err(|_| ())

View file

@ -1,3 +1,5 @@
use std::fmt;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum IrcCommand { pub enum IrcCommand {
USER, USER,
@ -14,6 +16,7 @@ pub enum IrcCommand {
PING, PING,
PONG, PONG,
ERROR, ERROR,
Num(u16),
} }
impl IrcCommand { impl IrcCommand {
@ -34,7 +37,19 @@ impl IrcCommand {
"PING" => Some(PING), "PING" => Some(PING),
"PONG" => Some(PONG), "PONG" => Some(PONG),
"ERROR" => Some(ERROR), "ERROR" => Some(ERROR),
_ => None, s => if let Ok(i) = s.parse::<u16>() {
Some(Num(i))
} else { None },
}
}
}
impl fmt::Display for IrcCommand {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::IrcCommand::*;
match self {
Num(i) => write!(f, "{:03}", i),
cmd => write!(f, "{:?}", cmd),
} }
} }
} }
@ -55,7 +70,7 @@ impl IrcMessage {
} }
} }
pub fn from_str(msg: &str) -> Option<IrcMessage> { // TODO: Wrap in Result<> pub fn from_str(msg: &str) -> Result<IrcMessage, String> { // TODO: Wrap in Result<>
let mut args = msg.trim().splitn(2, " :"); let mut args = msg.trim().splitn(2, " :");
let mut left = args.next().unwrap().split(' '); let mut left = args.next().unwrap().split(' ');
@ -67,11 +82,17 @@ impl IrcMessage {
prefix = Some(&s[1..]); prefix = Some(&s[1..]);
if let Some(c) = left.next() { if let Some(c) = left.next() {
cmd = IrcCommand::from_str(c); cmd = match IrcCommand::from_str(c) {
Some(c) => Some(c),
None => return Err(format!("Unknown command: {}", s)),
};
}; };
} }
else { else {
cmd = IrcCommand::from_str(s); cmd = match IrcCommand::from_str(s) {
Some(c) => Some(c),
None => return Err(format!("Unknown command: {}", s)),
};
} }
} }
@ -82,8 +103,8 @@ impl IrcMessage {
}; };
match cmd { match cmd {
Some(c) => Some(IrcMessage::new(prefix, c, params)), Some(c) => Ok(IrcMessage::new(prefix, c, params)),
None => None, None => Err("Not enough arguments".to_string()),
} }
} }
@ -96,8 +117,8 @@ impl IrcMessage {
// doesn't have any spaces // doesn't have any spaces
match self.prefix { match self.prefix {
Some(ref prefix) => format!(":{} {:?} {}\r\n", prefix, self.command, param.join(" ")), Some(ref prefix) => format!(":{} {} {}\r\n", prefix, self.command, param.join(" ")),
None => format!("{:?} {}\r\n", self.command, param.join(" ")) None => format!("{} {}\r\n", self.command, param.join(" "))
} }
} }
} }
@ -160,4 +181,15 @@ mod tests {
assert_eq!(&msg.to_string(), "PRIVMSG supes :u suk lol\r\n"); assert_eq!(&msg.to_string(), "PRIVMSG supes :u suk lol\r\n");
} }
#[test]
fn message_to_string_numeric() {
let msg = IrcMessage {
prefix: Some("plankircd".to_string()),
command: IrcCommand::Num(4),
params: vec!["supes".to_string(), "u suk lol".to_string()]
};
assert_eq!(&msg.to_string(), ":plankircd 004 supes :u suk lol\r\n");
}
} }

View file

@ -1,4 +1,6 @@
use std::net::SocketAddr; use models::{IrcMessage, IrcCommand};
use futures::sync::mpsc::UnboundedSender;
use futures::sync::mpsc::SendError;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct User { pub struct User {
@ -6,41 +8,64 @@ pub struct User {
pub username: String, pub username: String,
pub hostname: String, pub hostname: String,
pub realname: String, pub realname: String,
pub address: SocketAddr, sendchannel: UnboundedSender<String>,
nick_set: bool, registered: bool,
user_set: bool,
} }
impl User { impl User {
pub fn new(addr: SocketAddr) -> User { pub fn new(send: UnboundedSender<String>) -> User {
User { User {
nickname: String::new(), nickname: String::new(),
username: String::new(), username: String::new(),
hostname: String::new(), hostname: String::new(),
realname: String::new(), realname: String::new(),
address: addr, sendchannel: send,
nick_set: false, registered: false,
user_set: false,
} }
} }
pub fn register(&mut self) {
if self.registered { return; }
self.registered = true;
let mut msg = format!(":{0} 001 {1} :Welcome to IRC!\r\n:{0} 002 {1} :Your host is plankircd, running version 0.0.1\r\n:{0} 003 {1} :This server was created tomorrow\r\n:{0} 004 {1} plankircd plankircd-0.0.1 o o\r\n:{0} 422 {1} :MOTD File is missing\r\n", self.hostname, self.nickname);
let res = self.send_raw(msg);
println!("{:?}",res);
}
pub fn is_registered(&self) -> bool {
self.registered
}
pub fn set_nick(&mut self, nick: &str) { pub fn set_nick(&mut self, nick: &str) {
self.nickname = nick.to_string(); self.nickname = nick.to_string();
self.nick_set = true;
} }
pub fn set_user(&mut self, user: &str, host: &str, real: &str) { pub fn set_user(&mut self, user: &str, host: &str, real: &str) {
self.username = user.to_string(); self.username = user.to_string();
self.hostname = host.to_string(); self.hostname = host.to_string();
self.realname = real.to_string(); self.realname = real.to_string();
self.user_set = true;
} }
pub fn to_string(&self) -> String { pub fn to_string(&self) -> String {
format!("{}!{}@{}", self.nickname, self.username, self.hostname) format!("{}!{}@{}", self.nickname, self.username, self.hostname)
} }
pub fn is_registered(&self) -> bool { pub fn is_valid(&self) -> bool {
self.nick_set && self.user_set !self.nickname.is_empty() && !self.username.is_empty() && !self.hostname.is_empty()
}
pub fn send_raw(&self, msg: String) -> Result<(),SendError<String>> {
self.sendchannel.unbounded_send(msg)
}
pub fn send(&self, msg: IrcMessage) -> Result<(),SendError<String>> {
self.sendchannel.unbounded_send(msg.to_string())
}
pub fn privmsg(&self, from: &User, msg: &str) -> Result<(),SendError<String>> {
self.send(IrcMessage::new(Some(&from.nickname), IrcCommand::PRIVMSG, vec![&self.nickname, msg]))
} }
} }