megalock 0.1.0

X11 xlock replacement
mod pam;
pub mod statics;

use crate::{utils::get_username, wm::pam::authenticate_password};
use anyhow::Result;
use std::sync::{Arc, Mutex};
use tracing::debug;

#[derive(Debug, Clone)]
pub enum Call {
    SelectFocusedWindow,
    OpenFullscreenWindow(String),
    GrabPointerAndKeyboard,
    UnlockSleep,
    RaiseWindow,
    RedrawScreen,
    FlushCommands,
}

pub trait Broker {
    fn set_pam_return(&mut self, pam: Arc<Mutex<Option<std::sync::mpsc::Receiver<()>>>>);
    fn pam_return(&self) -> Arc<Mutex<Option<std::sync::mpsc::Receiver<()>>>>;
    fn set_pam(&mut self, pam: std::sync::mpsc::Sender<()>);
    fn pam(&self) -> Option<std::sync::mpsc::Sender<()>>;
    fn set_receiver(&mut self, call: Arc<Mutex<Option<std::sync::mpsc::Receiver<Call>>>>);
    fn receiver(&self) -> Arc<Mutex<Option<std::sync::mpsc::Receiver<Call>>>>;
    fn listen(&mut self) -> Result<()>;
}

pub trait Client {
    fn pam_return(&self) -> Arc<Mutex<std::sync::mpsc::Sender<()>>>;
    fn pam(&self) -> Arc<Mutex<std::sync::mpsc::Receiver<()>>>;
    fn call(&self) -> Option<std::sync::mpsc::Sender<Call>>;
    fn clear_password(&mut self);
    fn password(&self) -> String;

    fn loop_until_pam(&mut self, name: &str) -> Result<()> {
        while let Ok(_) = self.pam().lock().unwrap().recv() {
            debug!(
                "PAM authentication attempt: username: '{}', password: '{}'",
                get_username()?,
                self.password()
            );

            match authenticate_password(name) {
                Ok(_) => {
                    debug!("PAM authentication successful");
                    return Ok(());
                }
                Err(e) => {
                    debug!("PAM authentication failed: {}", e);
                    self.clear_password();
                }
            }

            self.pam_return().lock().unwrap().send(()).unwrap();
        }

        Ok(())
    }

    fn select_focused_window(&self) -> Result<()> {
        self.call().unwrap().send(Call::SelectFocusedWindow)?;
        Ok(())
    }

    fn open_fullscreen_window(&self, program_name: String) -> Result<()> {
        self.call()
            .unwrap()
            .send(Call::OpenFullscreenWindow(program_name))?;
        Ok(())
    }

    fn grab_pointer_and_keyboard(&self) -> Result<()> {
        self.call().unwrap().send(Call::GrabPointerAndKeyboard)?;
        Ok(())
    }

    fn unlock_sleep(&self) -> Result<()> {
        self.call().unwrap().send(Call::UnlockSleep)?;
        Ok(())
    }

    fn raise_window(&self) -> Result<()> {
        self.call().unwrap().send(Call::RaiseWindow)?;
        Ok(())
    }

    fn redraw_screen(&self) -> Result<()> {
        self.call().unwrap().send(Call::RedrawScreen)?;
        Ok(())
    }

    fn flush(&self) -> Result<()> {
        self.call().unwrap().send(Call::FlushCommands)?;
        Ok(())
    }
}