Compare commits

..

3 Commits

Author SHA1 Message Date
d005e8fec9 *SNEEZE* 2025-03-07 19:13:44 +01:00
460a18638f stuff sort-of works now yay 2025-03-07 16:12:40 +01:00
8a6f67cfd2 't was missing CRLF on send lol 2025-03-07 15:06:13 +01:00

View File

@@ -1,4 +1,5 @@
use std::collections::{HashMap, HashSet}; use std::collections::HashMap;
use std::time::Instant;
use std::{io::Cursor, net::SocketAddr, str::FromStr, sync::Arc, time::Duration}; use std::{io::Cursor, net::SocketAddr, str::FromStr, sync::Arc, time::Duration};
use irc_rust::Message; use irc_rust::Message;
@@ -23,7 +24,8 @@ struct Client {
username: Option<String>, username: Option<String>,
realname: Option<String>, realname: Option<String>,
modes: HashSet<u8>, // [byte per mode](https://www.unrealircd.org/docs/User_modes) // modes: HashSet<u8>, // [byte per mode](https://www.unrealircd.org/docs/User_modes)
last_pong: Instant,
c2s_tx: Option<Sender<Message>>, c2s_tx: Option<Sender<Message>>,
s2c_rx: Option<Receiver<Message>>, s2c_rx: Option<Receiver<Message>>,
@@ -42,7 +44,8 @@ impl Client {
username: None, username: None,
realname: None, realname: None,
modes: HashSet::new(), // modes: HashSet::new(),
last_pong: Instant::now(),
c2s_tx: Some(c2s_tx), c2s_tx: Some(c2s_tx),
s2c_rx: Some(s2c_rx), s2c_rx: Some(s2c_rx),
@@ -58,7 +61,7 @@ impl Client {
} }
#[derive(Default, Debug)] #[derive(Default, Debug)]
struct AppState { pub struct AppState {
clients: RwLock<HashMap<SocketAddr, Client>>, clients: RwLock<HashMap<SocketAddr, Client>>,
} }
@@ -80,11 +83,18 @@ impl AppState {
} }
} }
pub async fn nick(&self, peer_addr: SocketAddr) -> Option<String> {
if let Some(client) = self.clients.read().await.get(&peer_addr) {
return client.nick.clone();
}
None
}
pub async fn user( pub async fn user(
&self, &self,
peer_addr: SocketAddr, peer_addr: SocketAddr,
username: &str, username: &str,
mode: u16, // mode: u16,
realname: Option<&str>, realname: Option<&str>,
) { ) {
if let Some(client) = self.clients.write().await.get_mut(&peer_addr) { if let Some(client) = self.clients.write().await.get_mut(&peer_addr) {
@@ -101,10 +111,17 @@ impl AppState {
false false
} }
async fn pong(&self, peer_addr: SocketAddr) {
if let Some(client) = self.clients.write().await.get_mut(&peer_addr) {
client.last_pong = Instant::now();
}
}
async fn quit(&self, peer_addr: SocketAddr, reason: Option<&str>) { async fn quit(&self, peer_addr: SocketAddr, reason: Option<&str>) {
// broadcast user leaving unless invisible flag was set // broadcast user leaving unless invisible flag was set
self.clients.write().await.remove(&peer_addr); self.clients.write().await.remove(&peer_addr);
println!("Bye {}: {}", peer_addr, reason.unwrap_or_default());
} }
// clients still needs mod/op status stuff // clients still needs mod/op status stuff
@@ -142,15 +159,14 @@ impl AppState {
Ok(()) Ok(())
} }
pub async fn broadcast(&self, msg: Message) -> Result<()> { // pub async fn broadcast(&self, msg: Message) -> Result<()> {
let clients = self.clients.read().await; // let clients = self.clients.read().await;
let addresses = clients.keys().cloned(); // let addresses = clients.keys().cloned();
for peer_addr in addresses { // for peer_addr in addresses {
self.send(peer_addr, msg.clone()).await?; // self.send(peer_addr, msg.clone()).await?;
} // }
// Ok(())
Ok(()) // }
}
} }
#[tokio::main] #[tokio::main]
@@ -173,7 +189,7 @@ async fn main() -> Result<()> {
let acceptor = TlsAcceptor::from(Arc::new(config)); let acceptor = TlsAcceptor::from(Arc::new(config));
let listener = TcpListener::bind("0.0.0.0:6697").await?; let listener = TcpListener::bind("0.0.0.0:6697").await?;
// which means, this goes into yet another thread // Connection loop
let state: Arc<AppState> = app_state.clone(); let state: Arc<AppState> = app_state.clone();
tokio::spawn(async move { tokio::spawn(async move {
loop { loop {
@@ -199,12 +215,17 @@ async fn main() -> Result<()> {
} }
}); });
// todo: the "processing" loop goes here... // Processing loop
let state: Arc<AppState> = app_state.clone(); let state: Arc<AppState> = app_state.clone();
tokio::spawn(async move { tokio::spawn(async move {
loop { loop {
let messages = state.get_messages().await; let messages = state.get_messages().await;
// println!("Got {} messages", messages.len()); if messages.is_empty() {
tokio::time::sleep(Duration::from_millis(50)).await;
continue;
}
println!("Got {} messages", messages.len());
for (peer_addr, msg) in messages { for (peer_addr, msg) in messages {
println!("{}: {}", peer_addr, msg); println!("{}: {}", peer_addr, msg);
@@ -212,7 +233,6 @@ async fn main() -> Result<()> {
eprintln!("Error handling command: {:?}", e); eprintln!("Error handling command: {:?}", e);
} }
} }
tokio::time::sleep(Duration::from_millis(1)).await;
} }
}); });
@@ -221,21 +241,33 @@ async fn main() -> Result<()> {
tokio::spawn(async move { tokio::spawn(async move {
loop { loop {
let clients = state.clients.read().await; let clients = state.clients.read().await;
let mut mark_for_delete = vec![];
for (peer_addr, client) in clients.iter() { for (peer_addr, client) in clients.iter() {
if Instant::now().duration_since(client.last_pong).as_secs() >= 110 {
mark_for_delete.push(*peer_addr);
}
println!("PING :{}", client.username.clone().unwrap_or_default()); println!("PING :{}", client.username.clone().unwrap_or_default());
state let _ = state
.send( .send(
*peer_addr, *peer_addr,
Message::from_str(&format!( Message::from_str(&format!(
":localhost PING :{}", "PING :{}",
client.username.clone().unwrap_or_default() client.username.clone().unwrap_or_default()
))?, ))?,
) )
.await?; .await;
} }
drop(clients); drop(clients);
// Drop stale clients, if there are any
if !mark_for_delete.is_empty() {
state
.clients
.write()
.await
.retain(|p, _| !mark_for_delete.contains(p));
}
tokio::time::sleep(Duration::from_secs(30)).await; tokio::time::sleep(Duration::from_secs(30)).await;
} }
@@ -255,7 +287,13 @@ async fn handle(
) -> Result<Option<Message>> { ) -> Result<Option<Message>> {
match msg.command()? { match msg.command()? {
"CAP" => { "CAP" => {
state.send(peer_addr, Message::from_str("NONE")?).await?; dbg!(&msg);
state
.send(
peer_addr,
"CAP * LS :multi-prefix sasl=PLAIN,EXTERNAL".into(),
)
.await?;
} }
"NICK" => { "NICK" => {
let nick = msg.params()?.next().unwrap_or_default(); let nick = msg.params()?.next().unwrap_or_default();
@@ -264,20 +302,28 @@ async fn handle(
"USER" => { "USER" => {
let mut params = msg.params()?; let mut params = msg.params()?;
let username = params.next().unwrap_or_default(); // aviinl let username = params.next().unwrap_or_default(); // aviinl
let mode = params.next().unwrap_or_default().parse::<u16>()?; // mode // let mode = params.next().unwrap_or_default().parse::<u16>()?; // mode
params.next().unwrap_or_default(); // unused // params.next().unwrap_or_default(); // unused
let realname = msg.trailing()?; // realname let realname = msg.trailing()?; // realname
state.user(peer_addr, username, /*mode,*/ realname).await;
dbg!(username, realname); let nick = state.nick(peer_addr).await.unwrap_or(username.to_string());
state
state.user(peer_addr, username, mode, realname).await; .send(
peer_addr,
format!(":localhost 001 {} :Welcome to the IRC server.", nick).into(),
)
.await?;
} }
"PASS" => { "PASS" => {
let mut params = msg.params()?; let mut params = msg.params()?;
let password = params.next().unwrap_or_default(); // aviinl let password = params.next().unwrap_or_default(); // aviinl
state.pass(peer_addr, password).await; state.pass(peer_addr, password).await;
} }
"PONG" => {
state.pong(peer_addr).await;
}
"QUIT" => { "QUIT" => {
let reason = msg.trailing()?; // realname let reason = msg.trailing()?; // realname
state.quit(peer_addr, reason).await; state.quit(peer_addr, reason).await;
@@ -332,7 +378,10 @@ async fn connect(
break 'outer; break 'outer;
} }
} }
continue;
} }
let _ = c2s_tx.send("QUIT :Broken Pipe".into()).await;
break;
} }
println!("{:?} closed", peer_addr); println!("{:?} closed", peer_addr);
}); });
@@ -340,7 +389,8 @@ async fn connect(
// fire off a send loop // fire off a send loop
tokio::spawn(async move { tokio::spawn(async move {
while let Some(i) = s2c_rx.recv().await { while let Some(i) = s2c_rx.recv().await {
if let Err(e) = tx.write(i.to_string().as_bytes()).await { let data = i.to_string() + "\r\n";
if let Err(e) = tx.write(data.as_bytes()).await {
eprintln!("Error sending message: {:?}", e); eprintln!("Error sending message: {:?}", e);
break; break;
} }