🏡 index : ~doyle/titanirc.git

author Jordan Doyle <jordan@doyle.la> 2023-01-07 23:41:15.0 +00:00:00
committer Jordan Doyle <jordan@doyle.la> 2023-01-07 23:41:15.0 +00:00:00
commit
51566d7610963b1eaf78b3f3938f95fa661dd93d [patch]
tree
d37598861b7db8c22b6aaad94bf6e32be14db0c7
parent
fb2e694c2f8aad2ba8e15905e247570f63537d06
download
51566d7610963b1eaf78b3f3938f95fa661dd93d.tar.gz

Add config file with MOTD support



Diff

 Cargo.lock             | 25 +++++++++++++++++++++++++
 Cargo.toml             |  2 ++
 config.toml            | 11 +++++++++++
 src/config.rs          | 22 ++++++++++++++++++++++
 src/main.rs            | 13 ++++++++++---
 src/server.rs          | 15 +++++++++++++--
 src/server/response.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 143 insertions(+), 7 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index b2389c9..b9d2640 100644
--- a/Cargo.lock
+++ a/Cargo.lock
@@ -794,6 +794,20 @@
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
dependencies = [
 "serde_derive",
]

[[package]]
name = "serde_derive"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "serde_json"
@@ -927,9 +941,11 @@
 "futures",
 "irc-proto",
 "itertools",
 "serde",
 "tokio",
 "tokio-stream",
 "tokio-util",
 "toml",
 "tracing",
 "tracing-subscriber",
]
@@ -988,6 +1004,15 @@
 "pin-project-lite",
 "tokio",
 "tracing",
]

[[package]]
name = "toml"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
dependencies = [
 "serde",
]

[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 625ecc4..1329604 100644
--- a/Cargo.toml
+++ a/Cargo.toml
@@ -12,8 +12,10 @@
chrono = "0.4"
clap = { version = "4.0", features = ["cargo", "derive", "std", "suggestions", "color"] }
futures = "0.3"
serde = { version = "1.0", features = ["derive"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
toml = "0.5"
tokio = { version = "1.23", features = ["full"] }
tokio-stream = { version = "0.1", features = ["net"] }
tokio-util = { version = "0.7", features = ["codec"] }
diff --git a/config.toml b/config.toml
new file mode 100644
index 0000000..b5aac20 100644
--- /dev/null
+++ a/config.toml
@@ -1,0 +1,11 @@
listen-address = "[::]:6667"

motd = """
Welcome to our IRC server

This network does NOT condone or allow any illegal
activities.

Any such occurrence of this activity will result
in immediate bans and removal from the network.
"""
diff --git a/src/config.rs b/src/config.rs
index 042fdef..90cf2f0 100644
--- a/src/config.rs
+++ a/src/config.rs
@@ -1,4 +1,7 @@
use std::{net::SocketAddr, str::FromStr};

use clap::Parser;
use serde::Deserialize;

#[derive(Parser)]
#[clap(version = clap::crate_version!(), author = clap::crate_authors!())]
@@ -6,4 +9,23 @@
    /// Turn debugging information on

    #[clap(short, long, action = clap::ArgAction::Count)]
    pub verbose: u8,
    #[clap(short, long)]
    pub config: Config,
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Config {
    pub listen_address: SocketAddr,
    pub motd: Option<String>,
}

impl FromStr for Config {
    type Err = std::io::Error;

    fn from_str(path: &str) -> Result<Self, Self::Err> {
        let contents = std::fs::read(path)?;
        toml::from_slice(&contents)
            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
    }
}
diff --git a/src/main.rs b/src/main.rs
index cc9072c..3723f92 100644
--- a/src/main.rs
+++ a/src/main.rs
@@ -44,12 +44,19 @@
        .pretty();
    subscriber.init();

    let server = Server::default().start();
    let listener = TcpListener::bind("127.0.0.1:6697").await?;
    let listen_address = opts.config.listen_address;

    let server = Server {
        channels: HashMap::default(),
        clients: HashMap::default(),
        config: opts.config,
    }
    .start();
    let listener = TcpListener::bind(listen_address).await?;

    actix_rt::spawn(start_tcp_acceptor_loop(listener, server));

    info!("Server listening on 127.0.0.1:6697");
    info!("Server listening on {}", listen_address);

    tokio::signal::ctrl_c().await?;
    System::current().stop();
diff --git a/src/server.rs b/src/server.rs
index 50f0b24..2222d9e 100644
--- a/src/server.rs
+++ a/src/server.rs
@@ -11,19 +11,21 @@
use crate::{
    channel::Channel,
    client::Client,
    config::Config,
    connection::InitiatedConnection,
    messages::{
        Broadcast, ChannelFetchTopic, ChannelJoin, ChannelList, ChannelMemberList,
        FetchClientByNick, ServerDisconnect, UserConnected, UserNickChange,
    },
    server::response::Motd,
    SERVER_NAME,
};

/// The root actor for arbitration between clients and channels.

#[derive(Default)]
pub struct Server {
    channels: HashMap<String, Addr<Channel>>,
    clients: HashMap<Addr<Client>, InitiatedConnection>,
    pub channels: HashMap<String, Addr<Channel>>,
    pub clients: HashMap<Addr<Client>, InitiatedConnection>,
    pub config: Config,
}

/// Received when a user connects to the server, and sends them the server preamble

@@ -72,6 +74,13 @@
                    prefix: Some(Prefix::ServerName(SERVER_NAME.to_string())),
                    command: Command::Response(response, arguments),
                },
            });
        }

        for message in Motd::new(self).into_messages(msg.connection.nick.clone()) {
            msg.handle.do_send(Broadcast {
                span: Span::current(),
                message,
            });
        }

diff --git a/src/server/response.rs b/src/server/response.rs
index 1e8180a..ac743d9 100644
--- a/src/server/response.rs
+++ a/src/server/response.rs
@@ -1,6 +1,66 @@
use irc_proto::{Command, Message, Prefix, Response};

use crate::SERVER_NAME;
use crate::{server::Server, SERVER_NAME};

#[derive(Default)]
pub struct Motd {
    pub motd: Option<String>,
}

impl Motd {
    #[must_use]
    pub fn new(server: &Server) -> Self {
        Self {
            motd: server.config.motd.clone(),
        }
    }

    #[must_use]
    pub fn into_messages(self, for_user: String) -> Vec<Message> {
        if let Some(motd) = self.motd {
            let mut motd_messages = vec![Message {
                tags: None,
                prefix: Some(Prefix::ServerName(SERVER_NAME.to_string())),
                command: Command::Response(
                    Response::RPL_MOTDSTART,
                    vec![
                        for_user.to_string(),
                        format!("- {SERVER_NAME} Message of the day -"),
                    ],
                ),
            }];

            motd_messages.extend(motd.trim().split('\n').map(|v| Message {
                tags: None,
                prefix: Some(Prefix::ServerName(SERVER_NAME.to_string())),
                command: Command::Response(
                    Response::RPL_MOTD,
                    vec![for_user.to_string(), v.to_string()],
                ),
            }));

            motd_messages.push(Message {
                tags: None,
                prefix: Some(Prefix::ServerName(SERVER_NAME.to_string())),
                command: Command::Response(
                    Response::RPL_ENDOFMOTD,
                    vec![for_user, "End of /MOTD command.".to_string()],
                ),
            });

            motd_messages
        } else {
            vec![Message {
                tags: None,
                prefix: Some(Prefix::ServerName(SERVER_NAME.to_string())),
                command: Command::Response(
                    Response::ERR_NOMOTD,
                    vec![for_user, "MOTD File is missing".to_string()],
                ),
            }]
        }
    }
}

#[derive(Default)]
pub struct ChannelList {