ellidri 2.3.0

Your kawaii IRC server
Documentation
//! Handlers for commands related to the message-tags specification.

use crate::{auth, lines};
use crate::message::rpl;
use super::{CommandContext, HandlerResult as Result};

impl super::StateInner {
    pub fn cmd_authenticate(&mut self, ctx: CommandContext<'_>, payload: &str) -> Result {
        let client = self.clients.get_mut(ctx.addr).unwrap();
        if !client.capabilities.sasl {
            log::debug!("{}:     hasn't negociated sasl", ctx.addr);
            return Err(());
        }
        if client.identity().is_some() {
            log::debug!("{}:     is already logged in", ctx.addr);
            ctx.rb.reply(rpl::ERR_SASLALREADY).trailing_param(lines::SASL_ALREADY);
            client.auth_reset();
            return Err(());
        }
        if payload == "*" && client.auth_id().is_some() {
            ctx.rb.reply(rpl::ERR_SASLABORTED).trailing_param(lines::SASL_ABORTED);
            client.auth_reset();
            return Ok(());
        }
        if let Some(id) = client.auth_id() {
            match client.auth_buffer_push(payload) {
                Ok(true) => {
                    let decoded = match client.auth_buffer_decode() {
                        Ok(decoded) => decoded,
                        Err(err) => {
                            log::debug!("{}:     bad base64: {}", ctx.addr, err);
                            ctx.rb.reply(rpl::ERR_SASLFAIL).trailing_param(lines::SASL_FAILED);
                            client.auth_reset();
                            return Err(());
                        }
                    };
                    let mut challenge = Vec::new();
                    match self.auth_provider.next_challenge(id, &decoded, &mut challenge) {
                        Ok(Some(user)) => {
                            log::debug!("{}:     now authenticated", ctx.addr);
                            let msg = ctx.rb.reply(rpl::LOGGEDIN)
                                .param(client.nick())
                                .param(client.full_name())
                                .param(&user);
                            lines::logged_in(msg, &user);
                            ctx.rb.reply(rpl::SASLSUCCESS).trailing_param(lines::SASL_SUCCESSFUL);
                            client.log_in(user);
                            client.auth_reset();
                        }
                        Ok(None) => {
                            auth::write_buffer(ctx.rb, &challenge);
                        }
                        Err(err) => {
                            log::debug!("{}:     bad response: {:?}", ctx.addr, err);
                            ctx.rb.reply(rpl::ERR_SASLFAIL).trailing_param(lines::SASL_FAILED);
                            client.auth_reset();
                            return Err(());
                        }
                    }
                }
                Ok(false) => {}
                Err(()) => {
                    ctx.rb.reply(rpl::ERR_SASLTOOLONG).trailing_param(lines::SASL_TOO_LONG);
                    log::debug!("{}:     sasl too long", ctx.addr);
                    return Err(());
                }
            }
        } else {
            let mut challenge = Vec::new();
            let id = match self.auth_provider.start_auth(payload, &mut challenge) {
                Ok(id) => id,
                Err(auth::Error::ProviderUnavailable) => {
                    log::debug!("{}:     sasl unavailable for {:?}", ctx.addr, payload);
                    ctx.rb.reply(rpl::ERR_SASLFAIL).trailing_param(lines::SASL_FAILED);
                    return Err(());
                }
                Err(_) => {
                    log::debug!("{}:     unknown mechanism {:?}", ctx.addr, payload);
                    let mut msg = ctx.rb.reply(rpl::SASLMECHS);
                    self.auth_provider.write_mechanisms(msg.raw_param());
                    msg.trailing_param(lines::SASL_MECHS);
                    return Err(());
                }
            };
            auth::write_buffer(ctx.rb, &challenge);
            client.auth_set_id(id);
        }
        Ok(())
    }
}