KeyBoxen 0.1.0

Standalone secret-service daemon for window managers
// Copyright (C) 2022 KeyBoxen Authors
// SPDX-License-Identifier: GPL-3.0-or-later

//! This module deals with the GUI aspects of the server using
//! pinentry.
//!
//! There are 3 functions for which a GUI is needed:
//!
//! - Prompting user for a password
//! - Prompting user for a confirmation to proceed with an action
//! - To display urgent notifications to the user
//!
//! The best tool for this job is the 'pinentry' program. Pinentry is
//! a part of the modular GnuPG software framework. It is more than
//! likely to be present in a default GNU/Linux install. Pinentry also
//! comes in multiple flavours of toolkits and terminals. This makes
//! it very easy for us to integrate seamlessly with the server with
//! the user environment.
//!
//! The pinentry program communicates with its client (the secret
//! server in our case) using the text-based [Assuan protocol] over std
//! streams. Assuan protocol is very easy to test manually or to
//! implement in a program. [Pinentry] uses the assuan protocol for
//! all its configuration (like name, description and button labels)
//! and operations (like password entry, prompts and messages).
//!
//! [Assuan protocol]: https://www.gnupg.org/documentation/manuals/assuan/Implementation.html#Implementation
//! [Pinentry]: https://info2html.sourceforge.net/cgi-bin/info2html-demo/info2html?%28pinentry%29Protocol

mod commands;
mod controller;
mod responses;

pub use controller::prompt;
use tokio::sync::mpsc;

/// Parameters for pinentry dialog
///
/// The struct is initialized directly and passed to [prompt]
/// function. The struct contains all information necessary for
/// configuring and showing the dialog.
pub struct DialogParams {
    pub dialog_type: DialogType,
    pub title: String,
    pub description: String,
    pub prompt: String,
    pub label_yes: String,
    pub label_cancel: String,
    pub label_no: Option<String>,
}

/// The type of dialog to be presented to the user
pub enum DialogType {
    Password,
    Confirmation,
    Notification,
}

/// Structured command to be passed to the pinentry instance
#[derive(Debug)]
pub enum Command {
    Title(String),
    Description(String),
    Prompt(String),
    OKButton(String),
    CancelButton(String),
    NotOKButton(String),
    TimeOut(i64),
    Data(Vec<u8>),
    End,
    Cancel,
    Password,
    Confirm,
    Notify,
    Exit,
}

/// Parsed response received from the pinentry instance
#[derive(Debug)]
pub enum Response {
    Status(String, String),
    Enquiry(String, String),
    Comment(String),
    Success(String),
    Failure(i64, String),
    DataOK(Vec<u8>, String),
    DataFail(Vec<u8>, i64, String),
    DataBuffered,
}

/// All errors raised by this module
#[derive(Debug, thiserror::Error)]
pub enum ErrorKind {
    #[error(transparent)]
    IO(#[from] std::io::Error),
    #[error(transparent)]
    SendResponse(#[from] mpsc::error::SendError<Response>),
    #[error(transparent)]
    SendCommand(#[from] mpsc::error::SendError<Command>),
    #[error(transparent)]
    ParserError(#[from] pest::error::Error<responses::Rule>),
    #[error("Encountered empty response from pinentry")]
    EmptyResponse,
    #[error("Encountered unexpected response from pinentry: {0}")]
    UnexpectedResponse(String),
    #[error("Attempted command failed")]
    CommandFail,
    #[error("User cancelled operation")]
    UserCancel,
    #[error("User did not confirm")]
    UserNo,
}

/// Result wrapping all errors of this module
pub type Result<T> = std::result::Result<T, ErrorKind>;