More progress
This commit is contained in:
parent
c26cd3f881
commit
e7ccea3c8e
4 changed files with 182 additions and 111 deletions
|
@ -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 nick_exists(&self, nick: &str) -> bool {
|
||||
self.users.lock().unwrap().values().any(|user| user.nickname == nick)
|
||||
}
|
||||
|
||||
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 is_registered(&self, addr: &SocketAddr) -> bool {
|
||||
if let Some(user) = self.users.lock().unwrap().get(addr) {
|
||||
user.is_registered()
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
if let Some(user) = self.users.lock().unwrap().remove(addr) {
|
||||
user.send(IrcMessage::new(Some(&self.hostname.lock().unwrap()), IrcCommand::ERROR, vec!["Connection terminated."]));
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
98
src/main.rs
98
src/main.rs
|
@ -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) => {
|
||||
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 => {
|
||||
let mut shouldreg = false;
|
||||
if msg.params.len() < 1 {
|
||||
context.send_message_to(&addr, IrcMessage::new(Some("plankircd"), Num(431), vec!["*", "No nickname given"]));
|
||||
}
|
||||
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;
|
||||
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"]));
|
||||
}
|
||||
}
|
||||
}
|
||||
if shouldreg {
|
||||
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);
|
||||
}
|
||||
Ok(())
|
||||
else {
|
||||
context.send_message_to(&addr, IrcMessage::new(Some(&old), NICK, vec![nick]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
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(|_| ())
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
49
src/user.rs
49
src/user.rs
|
@ -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]))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue