From 69406cb33b201a8b9309aff8191826c8d3db0514 Mon Sep 17 00:00:00 2001 From: Nikita Vilunov Date: Fri, 10 Feb 2023 19:47:58 +0100 Subject: [PATCH] implement irc client registration --- src/projections/irc.rs | 178 ++++++++++++++++++++++++++++++++++++++- src/protos/irc/server.rs | 71 +++++++++++++++- 2 files changed, 245 insertions(+), 4 deletions(-) diff --git a/src/projections/irc.rs b/src/projections/irc.rs index ac02333..94bb654 100644 --- a/src/projections/irc.rs +++ b/src/projections/irc.rs @@ -3,6 +3,7 @@ use std::net::SocketAddr; use prometheus::{IntCounter, IntGauge, Registry as MetricsRegistry}; use serde::Deserialize; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter}; +use tokio::net::tcp::{ReadHalf, WriteHalf}; use tokio::net::{TcpListener, TcpStream}; use tokio::sync::oneshot::channel; @@ -18,6 +19,13 @@ pub struct ServerConfig { pub server_name: String, } +#[derive(Debug)] +struct RegisteredUser { + nickname: Vec, + username: Vec, + realname: Vec, +} + async fn handle_socket( config: ServerConfig, mut stream: TcpStream, @@ -26,14 +34,180 @@ async fn handle_socket( current_connections: IntGauge, ) { let (reader, writer) = stream.split(); - let mut reader = BufReader::new(reader); + let mut reader: BufReader = BufReader::new(reader); let mut writer = BufWriter::new(writer); + { + let notice = ServerMessage { + tags: vec![], + sender: Some(config.server_name.as_bytes().to_vec()), + body: ServerMessageBody::Notice { + first_target: b"*".to_vec(), + rest_targets: vec![], + text: b"Welcome to my server!".to_vec(), + }, + }; + let mut buffer = vec![]; + notice.write(&mut buffer).unwrap(); + writer.write_all(buffer.as_slice()).await; + writer.flush().await; + } + let mut buffer = vec![]; // let (player_id, mut player_handle) = players.create_player().await; // let mut sub = player_handle.subscribe().await; + let mut future_nickname: Option> = None; + let mut future_username: Option<(Vec, Vec)> = None; + + let registered_user: Result = loop { + let res = reader.read_until(b'\n', &mut buffer).await; + match res { + Ok(len) => { + if len == 0 { + log::info!("Terminating socket"); + break Err(anyhow::Error::msg("EOF")); + } + } + Err(err) => { + log::warn!("Failed to read from socket: {err}"); + break Err(err.into()); + } + } + let parsed = client_message(&buffer[..]); + match parsed { + Ok((rest, msg)) => { + log::info!("Incoming IRC message: {msg:?}"); + match msg { + ClientMessage::Nick { nickname } => { + if let Some((username, realname)) = future_username { + break Ok(RegisteredUser { + nickname, + username, + realname, + }); + } else { + future_nickname = Some(nickname); + } + } + ClientMessage::User { username, realname } => { + if let Some(nickname) = future_nickname { + break Ok(RegisteredUser { + nickname, + username, + realname, + }); + } else { + future_username = Some((username, realname)); + } + } + _ => {} + } + } + Err(err) => { + log::warn!("Failed to parse IRC message: {err}"); + } + } + buffer.clear(); + }; + match registered_user { + Ok(user) => { + handle_registered_socket(config, socket_addr, players, reader, writer, user).await + } + Err(_) => {} + } + + current_connections.dec(); +} + +async fn handle_registered_socket<'a>( + config: ServerConfig, + socket_addr: SocketAddr, + mut players: PlayerRegistry, + mut reader: BufReader>, + mut writer: BufWriter>, + user: RegisteredUser, +) { + let mut buffer = vec![]; + log::info!("Handling registered user: {user:?}"); + + { + let notice = ServerMessage { + tags: vec![], + sender: Some(config.server_name.as_bytes().to_vec()), + body: ServerMessageBody::N001Welcome { + client: user.nickname.clone(), + text: b"Welcome to Kek Server".to_vec(), + }, + }; + let mut buffer = vec![]; + notice.write(&mut buffer).unwrap(); + writer.write_all(buffer.as_slice()).await; + writer.flush().await; + } + + { + let notice = ServerMessage { + tags: vec![], + sender: Some(config.server_name.as_bytes().to_vec()), + body: ServerMessageBody::N002YourHost { + client: user.nickname.clone(), + text: b"Welcome to Kek Server".to_vec(), + }, + }; + let mut buffer = vec![]; + notice.write(&mut buffer).unwrap(); + writer.write_all(buffer.as_slice()).await; + writer.flush().await; + } + + { + let notice = ServerMessage { + tags: vec![], + sender: Some(config.server_name.as_bytes().to_vec()), + body: ServerMessageBody::N003Created { + client: user.nickname.clone(), + text: b"Welcome to Kek Server".to_vec(), + }, + }; + let mut buffer = vec![]; + notice.write(&mut buffer).unwrap(); + writer.write_all(buffer.as_slice()).await; + writer.flush().await; + } + + { + let notice = ServerMessage { + tags: vec![], + sender: Some(config.server_name.as_bytes().to_vec()), + body: ServerMessageBody::N004MyInfo { + client: user.nickname.clone(), + hostname: config.server_name.as_bytes().to_vec(), + softname: b"kek-0.1.alpha.3".to_vec(), + }, + }; + let mut buffer = vec![]; + notice.write(&mut buffer).unwrap(); + writer.write_all(buffer.as_slice()).await; + writer.flush().await; + } + + { + let notice = ServerMessage { + tags: vec![], + sender: Some(config.server_name.as_bytes().to_vec()), + body: ServerMessageBody::N005ISupport { + client: user.nickname.clone(), + params: b"CHANTYPES=#".to_vec(), + }, + }; + let mut buffer = vec![]; + notice.write(&mut buffer).unwrap(); + writer.write_all(buffer.as_slice()).await; + writer.flush().await; + } + loop { select! { biased; @@ -48,7 +222,6 @@ async fn handle_socket( let parsed = client_message(&buffer[..]); match parsed { Ok((rest, msg)) => { - log::info!("Incoming IRC message: {msg:?}"); match msg { ClientMessage::Ping { token } => { let response = ServerMessage { @@ -75,7 +248,6 @@ async fn handle_socket( }, } } - current_connections.dec(); } pub async fn launch( diff --git a/src/protos/irc/server.rs b/src/protos/irc/server.rs index 64396d2..03fb9b0 100644 --- a/src/protos/irc/server.rs +++ b/src/protos/irc/server.rs @@ -6,12 +6,20 @@ pub struct ServerMessage { /// Optional tags section, prefixed with `@` pub tags: Vec, /// Optional server name, prefixed with `:`. - pub sender: Option, + pub sender: Option, pub body: ServerMessageBody, } impl ServerMessage { pub fn write(&self, writer: &mut impl Write) -> std::io::Result<()> { + match &self.sender { + Some(ref sender) => { + writer.write(b":")?; + writer.write(sender.as_slice())?; + writer.write(b" ")?; + } + None => {} + } self.body.write(writer)?; writer.write(b"\n")?; Ok(()) @@ -44,6 +52,28 @@ pub enum ServerMessageBody { from: ByteVec, token: ByteVec, }, + N001Welcome { + client: ByteVec, + text: ByteVec, + }, + N002YourHost { + client: ByteVec, + text: ByteVec, + }, + N003Created { + client: ByteVec, + text: ByteVec, + }, + N004MyInfo { + client: ByteVec, + hostname: ByteVec, + softname: ByteVec, + // TODO user modes, channel modes, channel modes with a parameter + }, + N005ISupport { + client: ByteVec, + params: ByteVec, // TODO make this a datatype + }, } impl ServerMessageBody { @@ -69,6 +99,45 @@ impl ServerMessageBody { writer.write(b" :")?; writer.write(&token)?; } + ServerMessageBody::N001Welcome { client, text } => { + writer.write(b"001 ")?; + writer.write(&client)?; + writer.write(b" :")?; + writer.write(text)?; + } + ServerMessageBody::N002YourHost { client, text } => { + writer.write(b"002 ")?; + writer.write(&client)?; + writer.write(b" :")?; + writer.write(text)?; + } + ServerMessageBody::N003Created { client, text } => { + writer.write(b"003 ")?; + writer.write(&client)?; + writer.write(b" :")?; + writer.write(text)?; + } + ServerMessageBody::N004MyInfo { + client, + hostname, + softname, + } => { + writer.write(b"004 ")?; + writer.write(&client)?; + writer.write(b" ")?; + writer.write(&hostname)?; + writer.write(b" ")?; + writer.write(&softname)?; + writer.write(b" DGMQRSZagiloswz CFILPQbcefgijklmnopqrstvz bkloveqjfI")?; + // TODO remove hardcoded modes + } + ServerMessageBody::N005ISupport { client, params } => { + writer.write(b"005 ")?; + writer.write(&client)?; + writer.write(b" ")?; + writer.write(¶ms)?; + writer.write(b" :are supported by this server")?; + } } Ok(()) }