Skip to main content

ellidri/state/
capabilities.rs

1//! Handler for the CAP command
2//!
3//! Link to the capabilities specification: <https://ircv3.net/specs/core/capability-negotiation>
4
5use crate::client::cap;
6use crate::lines;
7use crate::message::{Command, rpl};
8use super::{CommandContext, HandlerResult as Result};
9
10impl super::StateInner {
11    fn cmd_cap_list(&self, ctx: CommandContext<'_>) -> Result {
12        let client = &self.clients[ctx.addr];
13        client.write_enabled_capabilities(ctx.rb);
14        Ok(())
15    }
16
17    fn cmd_cap_ls(&mut self, ctx: CommandContext<'_>, version: &str) -> Result {
18        let client = self.clients.get_mut(ctx.addr).unwrap();
19        client.set_cap_version(version);
20        let mut msg = ctx.rb.reply(Command::Cap).param("LS");
21        let mut trailing = msg.raw_trailing_param();
22
23        trailing.push_str(cap::LS_COMMON);
24        if self.auth_provider.is_available() {
25            trailing.push_str(" sasl");
26            if client.capabilities.v302 {
27                trailing.push('=');
28                self.auth_provider.write_mechanisms(&mut trailing);
29            }
30        }
31
32        Ok(())
33    }
34
35    fn cmd_cap_req(&mut self, ctx: CommandContext<'_>, capabilities: &str) -> Result {
36        let client = self.clients.get_mut(ctx.addr).unwrap();
37        if !cap::are_supported(capabilities) {
38            ctx.rb.reply(Command::Cap).param("NAK").trailing_param(capabilities);
39            return Err(());
40        }
41        client.update_capabilities(capabilities);
42        ctx.rb.reply(Command::Cap).param("ACK").trailing_param(capabilities);
43        Ok(())
44    }
45
46    pub fn cmd_cap(&mut self, ctx: CommandContext<'_>, params: &[&str]) -> Result {
47        match params[0] {
48            "END" => Ok(()),
49            "LIST" => self.cmd_cap_list(ctx),
50            "LS" => self.cmd_cap_ls(ctx, *params.get(1).unwrap_or(&"")),
51            "REQ" => self.cmd_cap_req(ctx, *params.get(1).unwrap_or(&"")),
52            _ => {
53                log::debug!("{}:     Bad command", ctx.addr);
54                ctx.rb.reply(rpl::ERR_INVALIDCAPCMD)
55                    .param(params[0])
56                    .trailing_param(lines::UNKNOWN_COMMAND);
57                Err(())
58            }
59        }
60    }
61}