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)]
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 hostname: Arc<Mutex<String>>,
pub users: Arc<Mutex<HashMap<SocketAddr,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())),
hostname: Arc::new(Mutex::new("plankircd".to_string())),
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);
let mut user;
{
user = self.users.lock().unwrap().get_mut(addr).cloned();
}
match user {
Some(mut user) => {
if !user.is_valid() || user.is_registered() {
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);
if self.users.lock().unwrap().iter().any(|(k, v)| k != addr && v.nickname == user.nickname) {
return Err(());
}
user.register(); // TODO: This is done on the cloned user -- won't save
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 nick_exists(&self, nick: &str) -> bool {
self.users.lock().unwrap().values().any(|user| user.nickname == nick)
}
pub fn is_unreg(&self, addr: &SocketAddr) -> bool {
self.unregistered_users.lock().unwrap().contains_key(addr)
pub fn send_message_to(&self, addr: &SocketAddr, msg: IrcMessage) -> Result<(), SendError<String>> {
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>> {
match self.connections.lock().unwrap().get(to) {
Some(tx) => tx.unbounded_send(string),
None => Ok(()), // TODO: proper error
pub fn is_registered(&self, addr: &SocketAddr) -> bool {
if let Some(user) = self.users.lock().unwrap().get(addr) {
user.is_registered()
}
else {
false
}
}
pub fn send_message_to(&self, to: &SocketAddr, message: IrcMessage) -> Result<(),SendError<String>> {
self.send_string_to(to, message.to_string())
pub fn connect(&mut self, addr: SocketAddr, send: UnboundedSender<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 (tx, rx) = futures::sync::mpsc::unbounded();
{
context.connections.lock().unwrap().insert(addr, tx);
context.unregistered_users.lock().unwrap().insert(addr, user::User::new(addr));
}
context.connect(addr, tx);
let context_inner = context.clone();
@ -65,70 +62,85 @@ fn main() {
})
.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;
}
for l in line.lines() {
println!("{} -> {}", addr, l.trim());
}
let res = IrcMessage::from_str(&line);
if let Err(e) = res {
eprintln!("error parsing command: {:?}", e);
return reader;
}
let msg = res.unwrap();
use models::IrcCommand::*;
match msg.command {
NICK => {
if msg.params.len() < 1 {
context.send_message_to(&addr, IrcMessage::new(Some("plankircd"), Num(431), vec!["*", "No nickname given"]));
}
else {
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())),
};
if let Err(e) = res {
eprintln!("error handling command {:?}", e);
USER => {
if msg.params.len() < 4 {
}
else {
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
})
})
.map_err(|_|());
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())
.map(|(writer, _)| writer)
.map_err(|_| ())

View file

@ -1,3 +1,5 @@
use std::fmt;
#[derive(Debug, PartialEq, Eq)]
pub enum IrcCommand {
USER,
@ -14,6 +16,7 @@ pub enum IrcCommand {
PING,
PONG,
ERROR,
Num(u16),
}
impl IrcCommand {
@ -34,7 +37,19 @@ impl IrcCommand {
"PING" => Some(PING),
"PONG" => Some(PONG),
"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 left = args.next().unwrap().split(' ');
@ -67,11 +82,17 @@ impl IrcMessage {
prefix = Some(&s[1..]);
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 {
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 {
Some(c) => Some(IrcMessage::new(prefix, c, params)),
None => None,
Some(c) => Ok(IrcMessage::new(prefix, c, params)),
None => Err("Not enough arguments".to_string()),
}
}
@ -96,8 +117,8 @@ impl IrcMessage {
// doesn't have any spaces
match self.prefix {
Some(ref prefix) => format!(":{} {:?} {}\r\n", prefix, self.command, param.join(" ")),
None => format!("{:?} {}\r\n", self.command, param.join(" "))
Some(ref prefix) => format!(":{} {} {}\r\n", prefix, 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");
}
#[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)]
pub struct User {
@ -6,41 +8,64 @@ pub struct User {
pub username: String,
pub hostname: String,
pub realname: String,
pub address: SocketAddr,
nick_set: bool,
user_set: bool,
sendchannel: UnboundedSender<String>,
registered: bool,
}
impl User {
pub fn new(addr: SocketAddr) -> User {
pub fn new(send: UnboundedSender<String>) -> User {
User {
nickname: String::new(),
username: String::new(),
hostname: String::new(),
realname: String::new(),
address: addr,
nick_set: false,
user_set: false,
sendchannel: send,
registered: 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) {
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
pub fn is_valid(&self) -> bool {
!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]))
}
}