chore: move client-related code into its own directory
This commit is contained in:
parent
0489cac646
commit
d3c3cd8292
12 changed files with 297 additions and 278 deletions
|
|
@ -1,14 +1,12 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
commands::{IrcAction, IrcHandler},
|
||||
user::User,
|
||||
};
|
||||
use super::{ClientAction, ClientHandler};
|
||||
use crate::user::User;
|
||||
|
||||
pub struct Cap;
|
||||
|
||||
#[async_trait]
|
||||
impl IrcHandler for Cap {
|
||||
impl ClientHandler for Cap {
|
||||
async fn handle(
|
||||
&self,
|
||||
_arguments: Vec<String>,
|
||||
|
|
@ -17,7 +15,7 @@ impl IrcHandler for Cap {
|
|||
_server_outgoing_password: String,
|
||||
_server_incoming_passwords: Vec<String>,
|
||||
_user_passwords: Vec<String>,
|
||||
) -> Vec<super::IrcAction> {
|
||||
vec![IrcAction::DoNothing]
|
||||
) -> Vec<super::ClientAction> {
|
||||
vec![ClientAction::DoNothing]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,12 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
JOINED_CHANNELS,
|
||||
channels::Channel,
|
||||
commands::{IrcAction, IrcHandler},
|
||||
user::User,
|
||||
};
|
||||
use super::{ClientAction, ClientHandler};
|
||||
use crate::{JOINED_CHANNELS, channels::Channel, user::User};
|
||||
|
||||
pub struct Join;
|
||||
|
||||
#[async_trait]
|
||||
impl IrcHandler for Join {
|
||||
impl ClientHandler for Join {
|
||||
async fn handle(
|
||||
&self,
|
||||
arguments: Vec<String>,
|
||||
|
|
@ -19,7 +15,7 @@ impl IrcHandler for Join {
|
|||
_server_outgoing_password: String,
|
||||
_server_incoming_passwords: Vec<String>,
|
||||
_user_passwords: Vec<String>,
|
||||
) -> Vec<super::IrcAction> {
|
||||
) -> Vec<super::ClientAction> {
|
||||
let mut joined_channels = JOINED_CHANNELS.lock().await;
|
||||
let mut channels = Vec::new();
|
||||
|
||||
|
|
@ -31,7 +27,7 @@ impl IrcHandler for Join {
|
|||
}
|
||||
|
||||
if !authenticated {
|
||||
return vec![IrcAction::ErrorAuthenticateFirst];
|
||||
return vec![ClientAction::ErrorAuthenticateFirst];
|
||||
}
|
||||
|
||||
for existing_channel in joined_channels.clone() {
|
||||
|
|
@ -56,6 +52,6 @@ impl IrcHandler for Join {
|
|||
}
|
||||
}
|
||||
|
||||
vec![IrcAction::JoinChannels(channels)]
|
||||
vec![ClientAction::JoinChannels(channels)]
|
||||
}
|
||||
}
|
||||
|
|
@ -5,13 +5,13 @@ use async_trait::async_trait;
|
|||
use tokio::{io::BufWriter, net::TcpStream, sync::broadcast::Sender};
|
||||
use tracing::debug;
|
||||
|
||||
use super::commands::{
|
||||
cap::Cap, join::Join, nick::Nick, pass::Pass, ping::Ping, privmsg::PrivMsg,
|
||||
user::User as UserHandler, who::Who,
|
||||
};
|
||||
use crate::{
|
||||
SENDER,
|
||||
channels::Channel,
|
||||
commands::{
|
||||
cap::Cap, join::Join, nick::Nick, pass::Pass, ping::Ping, privmsg::PrivMsg,
|
||||
user::User as UserHandler, who::Who,
|
||||
},
|
||||
config::ServerInfo,
|
||||
error_structs::CommandExecError,
|
||||
messages::{ChanJoinMessage, Message},
|
||||
|
|
@ -29,17 +29,17 @@ mod user;
|
|||
mod who;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IrcCommand {
|
||||
pub struct ClientCommand {
|
||||
command: String,
|
||||
arguments: Vec<String>,
|
||||
}
|
||||
|
||||
pub struct IrcMessage {
|
||||
pub struct ClientMessage {
|
||||
pub sender: String, // TODO: replace with hostmask
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
pub enum IrcAction {
|
||||
pub enum ClientAction {
|
||||
SendText(IrcResponse),
|
||||
SendMessage(Message),
|
||||
JoinChannels(Vec<Channel>),
|
||||
|
|
@ -55,7 +55,7 @@ pub enum ReturnAction {
|
|||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait IrcHandler: Send + Sync {
|
||||
pub trait ClientHandler: Send + Sync {
|
||||
async fn handle(
|
||||
&self,
|
||||
command: Vec<String>,
|
||||
|
|
@ -64,12 +64,12 @@ pub trait IrcHandler: Send + Sync {
|
|||
server_outgoing_password: String,
|
||||
server_incoming_passwords: Vec<String>,
|
||||
user_passwords: Vec<String>,
|
||||
) -> Vec<IrcAction>;
|
||||
) -> Vec<ClientAction>;
|
||||
}
|
||||
|
||||
pub struct SendMessage(Option<String>);
|
||||
|
||||
impl IrcCommand {
|
||||
impl ClientCommand {
|
||||
pub async fn new(command_with_arguments: String) -> Self {
|
||||
let mut split_command: Vec<&str> = command_with_arguments
|
||||
.split_whitespace()
|
||||
|
|
@ -114,7 +114,7 @@ impl IrcCommand {
|
|||
user_state: &mut User,
|
||||
config: &ServerInfo,
|
||||
) -> Result<Vec<ReturnAction>, CommandExecError> {
|
||||
let mut command_map: HashMap<String, &dyn IrcHandler> = HashMap::new();
|
||||
let mut command_map: HashMap<String, &dyn ClientHandler> = HashMap::new();
|
||||
let broadcast_sender = SENDER.lock().await.clone().unwrap();
|
||||
|
||||
// Command map is defined here
|
||||
|
|
@ -159,7 +159,7 @@ impl IrcCommand {
|
|||
}
|
||||
}
|
||||
|
||||
impl IrcAction {
|
||||
impl ClientAction {
|
||||
pub async fn execute(
|
||||
&self,
|
||||
writer: &mut BufWriter<TcpStream>,
|
||||
|
|
@ -168,11 +168,11 @@ impl IrcAction {
|
|||
sender: Sender<Message>,
|
||||
) -> ReturnAction {
|
||||
match self {
|
||||
IrcAction::SendText(msg) => {
|
||||
ClientAction::SendText(msg) => {
|
||||
msg.send(hostname, writer, false).await.unwrap();
|
||||
}
|
||||
|
||||
IrcAction::JoinChannels(channels) => {
|
||||
ClientAction::JoinChannels(channels) => {
|
||||
for channel in channels {
|
||||
let join_message = ChanJoinMessage {
|
||||
sender: user_state.clone().unwrap_all(),
|
||||
|
|
@ -182,11 +182,11 @@ impl IrcAction {
|
|||
}
|
||||
}
|
||||
|
||||
IrcAction::SendMessage(msg) => {
|
||||
ClientAction::SendMessage(msg) => {
|
||||
sender.send(msg.clone()).unwrap();
|
||||
}
|
||||
|
||||
IrcAction::UpgradeToServerConn => {
|
||||
ClientAction::UpgradeToServerConn => {
|
||||
return ReturnAction::ServerConn;
|
||||
}
|
||||
|
||||
|
|
@ -1,14 +1,12 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
commands::{IrcAction, IrcHandler},
|
||||
user::User,
|
||||
};
|
||||
use super::{ClientAction, ClientHandler};
|
||||
use crate::user::User;
|
||||
|
||||
pub struct Nick;
|
||||
|
||||
#[async_trait]
|
||||
impl IrcHandler for Nick {
|
||||
impl ClientHandler for Nick {
|
||||
async fn handle(
|
||||
&self,
|
||||
command: Vec<String>,
|
||||
|
|
@ -17,7 +15,7 @@ impl IrcHandler for Nick {
|
|||
_server_outgoing_password: String,
|
||||
_server_incoming_passwords: Vec<String>,
|
||||
_user_passwords: Vec<String>,
|
||||
) -> Vec<IrcAction> {
|
||||
) -> Vec<ClientAction> {
|
||||
user_state.nickname = Some({
|
||||
if command[0].len() > 9 {
|
||||
String::from_utf8(
|
||||
|
|
@ -34,6 +32,6 @@ impl IrcHandler for Nick {
|
|||
}
|
||||
});
|
||||
|
||||
vec![IrcAction::DoNothing]
|
||||
vec![ClientAction::DoNothing]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,12 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
commands::{IrcAction, IrcHandler},
|
||||
user::User,
|
||||
};
|
||||
use super::{ClientAction, ClientHandler};
|
||||
use crate::user::User;
|
||||
|
||||
pub struct Pass;
|
||||
|
||||
#[async_trait]
|
||||
impl IrcHandler for Pass {
|
||||
impl ClientHandler for Pass {
|
||||
async fn handle(
|
||||
&self,
|
||||
command: Vec<String>,
|
||||
|
|
@ -17,22 +15,22 @@ impl IrcHandler for Pass {
|
|||
server_outgoing_password: String,
|
||||
server_incoming_passwords: Vec<String>,
|
||||
_user_passwords: Vec<String>,
|
||||
) -> Vec<IrcAction> {
|
||||
) -> Vec<ClientAction> {
|
||||
// XXX
|
||||
|
||||
if server_incoming_passwords.contains(&command[0]) {
|
||||
vec![
|
||||
IrcAction::SendText(crate::sender::IrcResponse {
|
||||
ClientAction::SendText(crate::sender::IrcResponse {
|
||||
sender: None,
|
||||
command: "PASS".to_owned(),
|
||||
receiver: None,
|
||||
arguments: Vec::new(),
|
||||
message: server_outgoing_password.clone(),
|
||||
}),
|
||||
IrcAction::UpgradeToServerConn,
|
||||
ClientAction::UpgradeToServerConn,
|
||||
]
|
||||
} else {
|
||||
vec![IrcAction::DoNothing]
|
||||
vec![ClientAction::DoNothing]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,12 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
commands::{IrcAction, IrcHandler},
|
||||
sender::IrcResponse,
|
||||
user::User,
|
||||
};
|
||||
use super::{ClientAction, ClientHandler};
|
||||
use crate::{sender::IrcResponse, user::User};
|
||||
|
||||
pub struct Ping;
|
||||
|
||||
#[async_trait]
|
||||
impl IrcHandler for Ping {
|
||||
impl ClientHandler for Ping {
|
||||
async fn handle(
|
||||
&self,
|
||||
command: Vec<String>,
|
||||
|
|
@ -18,9 +15,9 @@ impl IrcHandler for Ping {
|
|||
_server_outgoing_password: String,
|
||||
_server_incoming_passwords: Vec<String>,
|
||||
_user_passwords: Vec<String>,
|
||||
) -> Vec<IrcAction> {
|
||||
) -> Vec<ClientAction> {
|
||||
if authenticated {
|
||||
vec![IrcAction::SendText(IrcResponse {
|
||||
vec![ClientAction::SendText(IrcResponse {
|
||||
sender: None,
|
||||
command: "PONG".into(),
|
||||
arguments: Vec::new(),
|
||||
|
|
@ -28,7 +25,7 @@ impl IrcHandler for Ping {
|
|||
message: format!(":{}", command[0].clone()),
|
||||
})]
|
||||
} else {
|
||||
vec![IrcAction::DoNothing]
|
||||
vec![ClientAction::DoNothing]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use super::{ClientAction, ClientHandler};
|
||||
use crate::{
|
||||
commands::{IrcAction, IrcHandler},
|
||||
messages::{Message, PrivMessage, Receiver},
|
||||
user::User,
|
||||
};
|
||||
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
pub struct PrivMsg;
|
||||
|
||||
#[async_trait]
|
||||
impl IrcHandler for PrivMsg {
|
||||
impl ClientHandler for PrivMsg {
|
||||
async fn handle(
|
||||
&self,
|
||||
command: Vec<String>,
|
||||
|
|
@ -18,9 +18,9 @@ impl IrcHandler for PrivMsg {
|
|||
_server_outgoing_password: String,
|
||||
_server_incoming_passwords: Vec<String>,
|
||||
_user_passwords: Vec<String>,
|
||||
) -> Vec<IrcAction> {
|
||||
) -> Vec<ClientAction> {
|
||||
if !authenticated {
|
||||
return vec![IrcAction::ErrorAuthenticateFirst];
|
||||
return vec![ClientAction::ErrorAuthenticateFirst];
|
||||
}
|
||||
|
||||
let receiver = if command[0].clone().starts_with("#") {
|
||||
|
|
@ -35,6 +35,6 @@ impl IrcHandler for PrivMsg {
|
|||
text: command[1].clone(),
|
||||
};
|
||||
|
||||
vec![IrcAction::SendMessage(Message::PrivMessage(message))]
|
||||
vec![ClientAction::SendMessage(Message::PrivMessage(message))]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,12 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
commands::{IrcAction, IrcHandler},
|
||||
user::User as UserState,
|
||||
};
|
||||
use super::{ClientAction, ClientHandler};
|
||||
use crate::user::User as UserState;
|
||||
|
||||
pub struct User;
|
||||
|
||||
#[async_trait]
|
||||
impl IrcHandler for User {
|
||||
impl ClientHandler for User {
|
||||
async fn handle(
|
||||
&self,
|
||||
command: Vec<String>,
|
||||
|
|
@ -17,9 +15,9 @@ impl IrcHandler for User {
|
|||
_server_outgoing_password: String,
|
||||
_server_incoming_passwords: Vec<String>,
|
||||
_user_passwords: Vec<String>,
|
||||
) -> Vec<IrcAction> {
|
||||
) -> Vec<ClientAction> {
|
||||
if command.len() < 4 {
|
||||
return vec![IrcAction::DoNothing]; // XXX: return an error
|
||||
return vec![ClientAction::DoNothing]; // XXX: return an error
|
||||
}
|
||||
|
||||
// oh my god this is a mess
|
||||
|
|
@ -40,6 +38,6 @@ impl IrcHandler for User {
|
|||
});
|
||||
user_state.realname = Some(command[3].clone());
|
||||
|
||||
vec![IrcAction::DoNothing]
|
||||
vec![ClientAction::DoNothing]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,12 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
commands::{IrcAction, IrcHandler},
|
||||
user::User,
|
||||
};
|
||||
use super::{ClientAction, ClientHandler};
|
||||
use crate::user::User;
|
||||
|
||||
pub struct Who;
|
||||
|
||||
#[async_trait]
|
||||
impl IrcHandler for Who {
|
||||
impl ClientHandler for Who {
|
||||
async fn handle(
|
||||
&self,
|
||||
_arguments: Vec<String>,
|
||||
|
|
@ -17,7 +15,7 @@ impl IrcHandler for Who {
|
|||
_server_outgoing_password: String,
|
||||
_server_incoming_passwords: Vec<String>,
|
||||
_user_passwords: Vec<String>,
|
||||
) -> Vec<super::IrcAction> {
|
||||
vec![IrcAction::DoNothing] // TODO
|
||||
) -> Vec<super::ClientAction> {
|
||||
vec![ClientAction::DoNothing] // TODO
|
||||
}
|
||||
}
|
||||
229
src/client/mod.rs
Normal file
229
src/client/mod.rs
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
use std::{
|
||||
net::TcpStream,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
CONNECTED_USERS, JOINED_CHANNELS, SENDER, TcpListenerResult,
|
||||
config::ServerInfo,
|
||||
error_structs::{self, ListenerError},
|
||||
login::send_motd,
|
||||
messages::{Message, NetJoinMessage, Receiver as MsgReceiver},
|
||||
sender::{IrcResponse, IrcResponseCodes},
|
||||
ts6::structs::{ServerId, UserId},
|
||||
user::User,
|
||||
userid_gen,
|
||||
};
|
||||
|
||||
use tokio::{
|
||||
io::{AsyncBufReadExt, BufReader as TokioBufReader, BufWriter as TokioBufWriter},
|
||||
net::TcpStream as TokioTcpStream,
|
||||
spawn,
|
||||
sync::{
|
||||
Mutex,
|
||||
broadcast::{self, Receiver, Sender},
|
||||
},
|
||||
time::sleep,
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
mod commands;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Client {}
|
||||
|
||||
impl Client {
|
||||
pub async fn tcp_listener(
|
||||
&self,
|
||||
stream: &TcpStream,
|
||||
mut user_state: User,
|
||||
info: &ServerInfo,
|
||||
reader: &mut TokioBufReader<TokioTcpStream>,
|
||||
our_sid: ServerId,
|
||||
) -> Result<TcpListenerResult, ListenerError> {
|
||||
let mut buffer = String::new();
|
||||
|
||||
let mut writer = TokioBufWriter::new(TokioTcpStream::from_std(stream.try_clone()?)?);
|
||||
|
||||
match reader.read_line(&mut buffer).await {
|
||||
Ok(0) => return Err(ListenerError::ConnectionError),
|
||||
Ok(_) => {}
|
||||
|
||||
Err(_) => {
|
||||
let mut conneted_users = CONNECTED_USERS.lock().await;
|
||||
let _ = conneted_users.remove(&user_state.clone().unwrap_all());
|
||||
|
||||
return Err(ListenerError::ConnectionError);
|
||||
}
|
||||
}
|
||||
|
||||
let command = commands::ClientCommand::new(buffer.clone()).await;
|
||||
match command
|
||||
.execute(&mut writer, &info.server_hostname, &mut user_state, info)
|
||||
.await
|
||||
{
|
||||
Ok(return_actions) => {
|
||||
for return_action in return_actions {
|
||||
match return_action {
|
||||
commands::ReturnAction::ServerConn => {
|
||||
return Ok(TcpListenerResult::ServerConnectionInit);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(error) => match error {
|
||||
error_structs::CommandExecError::NonexistantCommand => {
|
||||
let error_string = format!("error processing your command: {error:#?}\n");
|
||||
let error = IrcResponseCodes::UnknownCommand;
|
||||
|
||||
error
|
||||
.into_irc_response("*".into(), error_string.into())
|
||||
.send(&info.server_hostname, &mut writer, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if !user_state.identified && user_state.is_populated_without_uid() {
|
||||
let id = userid_gen::increase_user_id()
|
||||
.await
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join("");
|
||||
let user_id = format!("{our_sid}{id}");
|
||||
|
||||
user_state.identified = true;
|
||||
user_state.user_id = Some(UserId::try_from(user_id).unwrap()); // XXX: error handling
|
||||
user_state.timestamp = Some(SystemTime::now());
|
||||
|
||||
send_motd(info.clone(), user_state.clone(), &mut writer).await?;
|
||||
|
||||
let broadcast_sender = SENDER.lock().await.clone().unwrap();
|
||||
|
||||
broadcast_sender
|
||||
.send(Message::NetJoinMessage(NetJoinMessage {
|
||||
user: user_state.clone().unwrap_all(),
|
||||
server_id: our_sid.clone(),
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
CONNECTED_USERS
|
||||
.lock()
|
||||
.await
|
||||
.insert(user_state.clone().unwrap_all());
|
||||
}
|
||||
|
||||
Ok(TcpListenerResult::UpdatedUser(user_state))
|
||||
}
|
||||
|
||||
pub async fn message_listener(
|
||||
&self,
|
||||
user_wrapped: &User,
|
||||
receiver: &mut Receiver<Message>,
|
||||
writer: &mut TokioBufWriter<TokioTcpStream>,
|
||||
hostname: &str,
|
||||
) -> Result<(), ListenerError> {
|
||||
if !user_wrapped.is_populated() {
|
||||
sleep(Duration::from_millis(250)).await; // avoid immediate returns b'cuz they result in high
|
||||
// cpu usage
|
||||
return Err(ListenerError::UserIsUnidentified);
|
||||
}
|
||||
|
||||
let user = user_wrapped.clone().unwrap_all();
|
||||
let message: Message = receiver.recv().await.unwrap();
|
||||
let joined_channels = JOINED_CHANNELS.lock().await;
|
||||
|
||||
let mut channel_name: Option<String> = None;
|
||||
|
||||
debug!("new message in the message stream: {message:?}");
|
||||
|
||||
match message {
|
||||
Message::PrivMessage(message) => {
|
||||
for channel in joined_channels.clone() {
|
||||
if let MsgReceiver::ChannelName(channelname) = message.clone().receiver
|
||||
&& channelname == channel.name
|
||||
&& channel.joined_users.contains(user_wrapped)
|
||||
{
|
||||
channel_name = Some(channel.name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
dbg!(&message);
|
||||
|
||||
if match message.clone().receiver {
|
||||
MsgReceiver::UserId(userid) => {
|
||||
debug!("{userid} ?= {}", user.user_id);
|
||||
if userid == user.user_id { true } else { false }
|
||||
}
|
||||
|
||||
MsgReceiver::Username(username) => {
|
||||
if username.to_lowercase() == user.username.to_lowercase() {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
_ => false,
|
||||
} {
|
||||
IrcResponse {
|
||||
sender: Some(message.sender.hostmask()),
|
||||
command: "PRIVMSG".into(),
|
||||
arguments: Vec::new(),
|
||||
message: message.text,
|
||||
receiver: Some(user.username.clone()),
|
||||
}
|
||||
.send("", writer, true)
|
||||
.await?;
|
||||
} else if let Some(channel_name) = channel_name {
|
||||
if message.sender != user {
|
||||
IrcResponse {
|
||||
sender: Some(message.sender.hostmask()),
|
||||
command: "PRIVMSG".into(),
|
||||
arguments: Vec::new(),
|
||||
message: message.text,
|
||||
receiver: Some(channel_name),
|
||||
}
|
||||
.send("", writer, true)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Message::ChanJoinMessage(message) => {
|
||||
if message.channel.joined_users.contains(user_wrapped) || message.sender == user {
|
||||
let channel = message.channel.clone();
|
||||
|
||||
IrcResponse {
|
||||
sender: Some(message.sender.hostmask().clone()),
|
||||
command: "JOIN".into(),
|
||||
arguments: Vec::new(),
|
||||
message: message.channel.name.clone(),
|
||||
receiver: None,
|
||||
}
|
||||
.send("", writer, true)
|
||||
.await?;
|
||||
|
||||
channel
|
||||
.send_topic(user_wrapped.clone(), writer, hostname)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
channel
|
||||
.names_list_send(user_wrapped.clone(), &channel, writer, hostname)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Message::NetJoinMessage(_) => {} // we don't care about these here :)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
206
src/main.rs
206
src/main.rs
|
|
@ -23,6 +23,7 @@ use tracing::{debug, instrument};
|
|||
|
||||
use crate::{
|
||||
channels::Channel,
|
||||
client::Client,
|
||||
config::ServerInfo,
|
||||
error_structs::{HandlerError, ListenerError},
|
||||
logging::LogLevel,
|
||||
|
|
@ -37,7 +38,7 @@ use crate::{
|
|||
};
|
||||
|
||||
mod channels;
|
||||
mod commands;
|
||||
mod client;
|
||||
mod config;
|
||||
mod error_structs;
|
||||
mod logging;
|
||||
|
|
@ -93,9 +94,7 @@ async fn main() -> Result<(), AnyhowError> {
|
|||
let tx_thread = tx.clone();
|
||||
let info = info.clone();
|
||||
|
||||
spawn(handle_connection(
|
||||
stream, info, /*&mut rx_thread,*/ tx_thread,
|
||||
));
|
||||
spawn(handle_connection(stream, info, tx_thread));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -117,12 +116,13 @@ async fn handle_connection(
|
|||
|
||||
let hostname = info.server_hostname.clone();
|
||||
|
||||
// TODO: generate randomally and allow overriding from config
|
||||
// TODO: generate randomly and allow overriding from config
|
||||
let my_server_id = ServerId::try_from("000".to_owned()).unwrap();
|
||||
let client_status = Client::default();
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
result = tcp_listener(&stream_tcp, state.clone(), &info, &mut tcp_reader, my_server_id.clone()) => {
|
||||
result = client_status.tcp_listener(&stream_tcp, state.clone(), &info, &mut tcp_reader, my_server_id.clone()) => {
|
||||
match result {
|
||||
Ok(tcp_listener_result) => {
|
||||
match tcp_listener_result {
|
||||
|
|
@ -141,7 +141,7 @@ async fn handle_connection(
|
|||
}
|
||||
}
|
||||
},
|
||||
result = message_listener(&state, &mut message_receiver, &mut tcp_writer, &hostname) => {
|
||||
result = client_status.message_listener(&state, &mut message_receiver, &mut tcp_writer, &hostname) => {
|
||||
match result {
|
||||
Ok(_) => {},
|
||||
Err(err) => {
|
||||
|
|
@ -191,198 +191,6 @@ async fn handle_connection(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn tcp_listener(
|
||||
stream: &TcpStream,
|
||||
mut user_state: User,
|
||||
info: &ServerInfo,
|
||||
reader: &mut TokioBufReader<TokioTcpStream>,
|
||||
our_sid: ServerId,
|
||||
) -> Result<TcpListenerResult, ListenerError> {
|
||||
let mut buffer = String::new();
|
||||
|
||||
let mut writer = TokioBufWriter::new(TokioTcpStream::from_std(stream.try_clone()?)?);
|
||||
|
||||
match reader.read_line(&mut buffer).await {
|
||||
Ok(0) => return Err(ListenerError::ConnectionError),
|
||||
Ok(_) => {}
|
||||
|
||||
Err(_) => {
|
||||
let mut conneted_users = CONNECTED_USERS.lock().await;
|
||||
let _ = conneted_users.remove(&user_state.clone().unwrap_all());
|
||||
|
||||
return Err(ListenerError::ConnectionError);
|
||||
}
|
||||
}
|
||||
|
||||
let command = commands::IrcCommand::new(buffer.clone()).await;
|
||||
match command
|
||||
.execute(&mut writer, &info.server_hostname, &mut user_state, info)
|
||||
.await
|
||||
{
|
||||
Ok(return_actions) => {
|
||||
for return_action in return_actions {
|
||||
match return_action {
|
||||
commands::ReturnAction::ServerConn => {
|
||||
return Ok(TcpListenerResult::ServerConnectionInit);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(error) => match error {
|
||||
error_structs::CommandExecError::NonexistantCommand => {
|
||||
let error_string = format!("error processing your command: {error:#?}\n");
|
||||
let error = IrcResponseCodes::UnknownCommand;
|
||||
|
||||
error
|
||||
.into_irc_response("*".into(), error_string.into())
|
||||
.send(&info.server_hostname, &mut writer, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if !user_state.identified && user_state.is_populated_without_uid() {
|
||||
let id = userid_gen::increase_user_id()
|
||||
.await
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join("");
|
||||
let user_id = format!("{our_sid}{id}");
|
||||
|
||||
user_state.identified = true;
|
||||
user_state.user_id = Some(UserId::try_from(user_id).unwrap()); // XXX: error handling
|
||||
user_state.timestamp = Some(SystemTime::now());
|
||||
|
||||
send_motd(info.clone(), user_state.clone(), &mut writer).await?;
|
||||
|
||||
let broadcast_sender = SENDER.lock().await.clone().unwrap();
|
||||
|
||||
broadcast_sender
|
||||
.send(Message::NetJoinMessage(NetJoinMessage {
|
||||
user: user_state.clone().unwrap_all(),
|
||||
server_id: our_sid.clone(),
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
CONNECTED_USERS
|
||||
.lock()
|
||||
.await
|
||||
.insert(user_state.clone().unwrap_all());
|
||||
}
|
||||
|
||||
Ok(TcpListenerResult::UpdatedUser(user_state))
|
||||
}
|
||||
|
||||
async fn message_listener(
|
||||
user_wrapped: &User,
|
||||
receiver: &mut Receiver<Message>,
|
||||
writer: &mut TokioBufWriter<TokioTcpStream>,
|
||||
hostname: &str,
|
||||
) -> Result<(), ListenerError> {
|
||||
if !user_wrapped.is_populated() {
|
||||
sleep(Duration::from_millis(250)).await; // avoid immediate returns b'cuz they result in high
|
||||
// cpu usage
|
||||
return Err(ListenerError::UserIsUnidentified);
|
||||
}
|
||||
|
||||
let user = user_wrapped.clone().unwrap_all();
|
||||
let message: Message = receiver.recv().await.unwrap();
|
||||
let joined_channels = JOINED_CHANNELS.lock().await;
|
||||
|
||||
let mut channel_name: Option<String> = None;
|
||||
|
||||
debug!("new message in the message stream: {message:?}");
|
||||
|
||||
match message {
|
||||
Message::PrivMessage(message) => {
|
||||
for channel in joined_channels.clone() {
|
||||
if let MsgReceiver::ChannelName(channelname) = message.clone().receiver
|
||||
&& channelname == channel.name
|
||||
&& channel.joined_users.contains(user_wrapped)
|
||||
{
|
||||
channel_name = Some(channel.name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
dbg!(&message);
|
||||
|
||||
if match message.clone().receiver {
|
||||
MsgReceiver::UserId(userid) => {
|
||||
debug!("{userid} ?= {}", user.user_id);
|
||||
if userid == user.user_id { true } else { false }
|
||||
}
|
||||
|
||||
MsgReceiver::Username(username) => {
|
||||
if username.to_lowercase() == user.username.to_lowercase() {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
_ => false,
|
||||
} {
|
||||
IrcResponse {
|
||||
sender: Some(message.sender.hostmask()),
|
||||
command: "PRIVMSG".into(),
|
||||
arguments: Vec::new(),
|
||||
message: message.text,
|
||||
receiver: Some(user.username.clone()),
|
||||
}
|
||||
.send("", writer, true)
|
||||
.await?;
|
||||
} else if let Some(channel_name) = channel_name {
|
||||
if message.sender != user {
|
||||
IrcResponse {
|
||||
sender: Some(message.sender.hostmask()),
|
||||
command: "PRIVMSG".into(),
|
||||
arguments: Vec::new(),
|
||||
message: message.text,
|
||||
receiver: Some(channel_name),
|
||||
}
|
||||
.send("", writer, true)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Message::ChanJoinMessage(message) => {
|
||||
if message.channel.joined_users.contains(user_wrapped) || message.sender == user {
|
||||
let channel = message.channel.clone();
|
||||
|
||||
IrcResponse {
|
||||
sender: Some(message.sender.hostmask().clone()),
|
||||
command: "JOIN".into(),
|
||||
arguments: Vec::new(),
|
||||
message: message.channel.name.clone(),
|
||||
receiver: None,
|
||||
}
|
||||
.send("", writer, true)
|
||||
.await?;
|
||||
|
||||
channel
|
||||
.send_topic(user_wrapped.clone(), writer, hostname)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
channel
|
||||
.names_list_send(user_wrapped.clone(), &channel, writer, hostname)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Message::NetJoinMessage(_) => {} // we don't care about these here :)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::userid_gen;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ use std::collections::HashMap;
|
|||
|
||||
use crate::{
|
||||
SENDER,
|
||||
commands::IrcMessage,
|
||||
messages::Message,
|
||||
sender::IrcResponse,
|
||||
ts6::{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue