🏡 index : ~doyle/titanirc.git

author Jordan Doyle <jordan@doyle.la> 2024-01-29 23:29:58.0 +00:00:00
committer Jordan Doyle <jordan@doyle.la> 2024-01-29 23:29:58.0 +00:00:00
commit
722ae31a9122a6c6b260559ca3c3b65e45bec504 [patch]
tree
7eda0dbf3331957d3927161c4779b51ad5d21b92
parent
0a5ef8e8dca23d85c35af640c6910422fdca1149
download
722ae31a9122a6c6b260559ca3c3b65e45bec504.tar.gz

Implement WALLOPS



Diff

 rustfmt.toml      |  4 ++--
 src/client.rs     | 14 ++++++++------
 src/connection.rs | 29 +++++++++++++++++++++++------
 src/messages.rs   |  7 +++++++
 src/server.rs     | 25 ++++++++++++++++++++++++-
 5 files changed, 64 insertions(+), 15 deletions(-)

diff --git a/rustfmt.toml b/rustfmt.toml
index d440511..51fbc7e 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -1,7 +1,7 @@
edition = "2021"
## not yet supported on stable
#imports_granularity = "Crate"
imports_granularity = "Crate"
newline_style = "Unix"
## not yet supported on stable
#group_imports = "StdExternalCrate"
group_imports = "StdExternalCrate"
use_field_init_shorthand = true
diff --git a/src/client.rs b/src/client.rs
index 0104b84..d452aa0 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -18,7 +18,7 @@ use crate::{
    channel::Channel,
    connection::{
        sasl::SaslAlreadyAuthenticated, Capability, InitiatedConnection, MessageSink,
        NickNotOwnedByUser,
        NickNotOwnedByUser, UserMode,
    },
    messages::{
        Broadcast, ChannelFetchTopic, ChannelFetchWhoList, ChannelInvite, ChannelJoin,
@@ -26,7 +26,7 @@ use crate::{
        ChannelSetMode, ChannelUpdateTopic, ClientAway, ConnectedChannels, FetchClientDetails,
        FetchUserPermission, FetchWhoList, FetchWhois, MessageKind, PrivateMessage,
        ServerAdminInfo, ServerDisconnect, ServerFetchMotd, ServerListUsers, UserKickedFromChannel,
        UserNickChange, UserNickChangeInternal,
        UserNickChange, UserNickChangeInternal, Wallops,
    },
    persistence::{
        events::{
@@ -624,9 +624,6 @@ impl StreamHandler<Result<irc_proto::Message, ProtocolError>> for Client {
        // https://modern.ircdocs.horse/
        #[allow(clippy::match_same_arms)]
        match item.command {
            Command::USER(_, _, _) | Command::PASS(_) | Command::CAP(_, _, _, _) => {
                // these were already handled by `negotiate_client_connection`
            }
            Command::NICK(new_nick) => {
                ctx.notify(UserNickChangeInternal {
                    old_nick: self.connection.nick.to_string(),
@@ -883,7 +880,12 @@ impl StreamHandler<Result<irc_proto::Message, ProtocolError>> for Client {
            Command::DIE => {}
            Command::RESTART => {}
            Command::USERS(_) => {}
            Command::WALLOPS(_) => {}
            Command::WALLOPS(message) if self.connection.mode.contains(UserMode::OPER) => {
                self.server.do_send(Wallops {
                    span: Span::current(),
                    message,
                });
            }
            Command::USERHOST(_) => {}
            Command::SAJOIN(_, _) => {}
            Command::SAMODE(_, _, _) => {}
diff --git a/src/connection.rs b/src/connection.rs
index 2181b42..e37fc9a 100644
--- a/src/connection.rs
+++ b/src/connection.rs
@@ -44,7 +44,6 @@ pub struct ConnectionRequest {
    host: Option<SocketAddr>,
    nick: Option<String>,
    user: Option<String>,
    mode: Option<String>,
    real_name: Option<String>,
    user_id: Option<UserId>,
    capabilities: Capability,
@@ -55,7 +54,7 @@ pub struct InitiatedConnection {
    pub host: SocketAddr,
    pub nick: String,
    pub user: String,
    pub mode: String,
    pub mode: UserMode,
    pub real_name: String,
    pub user_id: UserId,
    pub capabilities: Capability,
@@ -82,7 +81,6 @@ impl TryFrom<ConnectionRequest> for InitiatedConnection {
            host: Some(host),
            nick: Some(nick),
            user: Some(user),
            mode: Some(mode),
            real_name: Some(real_name),
            user_id: Some(user_id),
            capabilities,
@@ -95,7 +93,7 @@ impl TryFrom<ConnectionRequest> for InitiatedConnection {
            host,
            nick,
            user,
            mode,
            mode: UserMode::empty(),
            real_name,
            user_id,
            capabilities,
@@ -138,9 +136,8 @@ pub async fn negotiate_client_connection(
        match msg.command {
            Command::PASS(_) => {}
            Command::NICK(nick) => request.nick = Some(nick),
            Command::USER(_user, mode, real_name) => {
            Command::USER(_user, _mode, real_name) => {
                // we ignore the user here, as it will be set by the AUTHENTICATE command
                request.mode = Some(mode);
                request.real_name = Some(real_name);
            }
            Command::CAP(_, CapSubCommand::LIST | CapSubCommand::LS, _, _) => {
@@ -279,6 +276,26 @@ bitflags! {
        const USERHOST_IN_NAMES = 0b0000_0000_0000_0000_0000_0000_0000_0001;
        const SERVER_TIME       = 0b0000_0000_0000_0000_0000_0000_0000_0010;
    }

    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
    pub struct UserMode: u32 {
        /// a - user is flagged as away
        const AWAY           = 0b0000_0000_0000_0000_0000_0000_0000_0001;
        /// i - marks a users as invisible
        const INVISIBLE      = 0b0000_0000_0000_0000_0000_0000_0000_0010;
        /// w - user receives wallops
        const WALLOPS        = 0b0000_0000_0000_0000_0000_0000_0000_0100;
        /// r - restricted user connection
        const RESTRICTED     = 0b0000_0000_0000_0000_0000_0000_0000_1000;
        /// o - operator flag
        const OPER           = 0b0000_0000_0000_0000_0000_0000_0001_0000;
        /// O - local operator flag
        const LOCAL_OPER     = 0b0000_0000_0000_0000_0000_0000_0010_0000;
        /// s - marks a user for receipt of server notices
        const SERVER_NOTICES = 0b0000_0000_0000_0000_0000_0000_0100_0000;
        /// x - masked hostname
        const MASKED_HOST    = 0b0000_0000_0000_0000_0000_0000_1000_0000;
    }
}

impl Capability {
diff --git a/src/messages.rs b/src/messages.rs
index 33d7158..7a575d3 100644
--- a/src/messages.rs
+++ b/src/messages.rs
@@ -47,6 +47,13 @@ pub struct UserNickChange {
    pub span: Span,
}

#[derive(Message, Clone)]
#[rtype(result = "()")]
pub struct Wallops {
    pub message: String,
    pub span: Span,
}

/// List all the channels a user is connected to
#[derive(Message, Clone)]
#[rtype(result = "Vec<(crate::channel::permissions::Permission, String)>")]
diff --git a/src/server.rs b/src/server.rs
index 7eaf60f..cc0ec2f 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -22,12 +22,13 @@ use crate::{
    channel::{permissions::Permission, Channel, ChannelId},
    client::Client,
    config::Config,
    connection::InitiatedConnection,
    connection::{InitiatedConnection, UserMode},
    messages::{
        Broadcast, ChannelFetchTopic, ChannelFetchWhoList, ChannelJoin, ChannelList,
        ChannelMemberList, ClientAway, ConnectedChannels, FetchClientByNick, FetchWhoList,
        FetchWhois, MessageKind, PrivateMessage, ServerAdminInfo, ServerDisconnect,
        ServerFetchMotd, ServerListUsers, UserConnected, UserNickChange, UserNickChangeInternal,
        Wallops,
    },
    persistence::Persistence,
    server::response::{AdminInfo, ListUsers, Motd, WhoList, Whois},
@@ -135,6 +136,28 @@ impl Handler<UserConnected> for Server {
    }
}

impl Handler<Wallops> for Server {
    type Result = ();

    #[instrument(parent = &msg.span, skip_all)]
    fn handle(&mut self, msg: Wallops, _ctx: &mut Self::Context) -> Self::Result {
        for (handle, conn) in &self.clients {
            if !conn.mode.contains(UserMode::WALLOPS) {
                continue;
            }

            handle.do_send(Broadcast {
                message: Message {
                    tags: None,
                    prefix: Some(Prefix::ServerName(SERVER_NAME.to_string())),
                    command: Command::WALLOPS(msg.message.clone()),
                },
                span: msg.span.clone(),
            });
        }
    }
}

/// Returns the MOTD when requested.
impl Handler<ServerFetchMotd> for Server {
    type Result = MessageResult<ServerFetchMotd>;