Mercurial > crates > nonstick
view src/conv.rs @ 65:8e507c7af9cf default tip
Added tag v0.0.5 for changeset bbe84835d6db
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Thu, 22 May 2025 02:08:10 -0400 |
parents | bbe84835d6db |
children |
line wrap: on
line source
//! The [Conversation] struct, for interacting with the user. //! //! This module is experimental and will probably be rewritten in the future //! to improve the interface for both PAM modules and clients. use crate::constants::MessageStyle; use crate::constants::Result; use crate::constants::{ErrorCode, InvalidEnum}; use crate::items::Item; use libc::{c_char, c_int}; use num_derive::FromPrimitive; use std::ffi::{CStr, CString}; use std::ptr; /// Styles of message that are shown to the user. #[derive(Debug, PartialEq, FromPrimitive)] #[non_exhaustive] // non-exhaustive because C might give us back anything! pub enum MessageStyle { /// Requests information from the user; will be masked when typing. PromptEchoOff = 1, /// Requests information from the user; will not be masked. PromptEchoOn = 2, /// An error message. ErrorMsg = 3, /// An informational message. TextInfo = 4, /// Yes/No/Maybe conditionals. Linux-PAM specific. RadioType = 5, /// For server–client non-human interaction. /// NOT part of the X/Open PAM specification. BinaryPrompt = 7, } impl TryFrom<c_int> for MessageStyle { type Error = InvalidEnum<Self>; fn try_from(value: c_int) -> std::result::Result<Self, Self::Error> { Self::from_i32(value).ok_or(value.into()) } } impl From<MessageStyle> for c_int { fn from(val: MessageStyle) -> Self { val as Self } } #[repr(C)] struct Message { msg_style: MessageStyle, msg: *const c_char, } #[repr(C)] struct Response { resp: *const c_char, resp_retcode: libc::c_int, // Unused - always zero } #[doc(hidden)] #[repr(C)] pub struct Inner { conv: extern "C" fn( num_msg: c_int, pam_message: &&Message, pam_response: &mut *const Response, appdata_ptr: *const libc::c_void, ) -> c_int, appdata_ptr: *const libc::c_void, } /// A communication channel with the user. /// /// Use this to communicate with the user, if needed, beyond the standard /// things you can get/set with `get_user`/`get_authtok` and friends. /// The PAM client (i.e., the application that is logging in) will present /// the messages you send to the user and ask for responses. pub struct Conversation<'a>(&'a Inner); impl Conversation<'_> { /// Sends a message to the PAM client. /// /// This will typically result in the user seeing a message or a prompt. /// For details, see what [MessageStyle]s are available. /// /// Note that the user experience will depend on how each style /// is implemented by the client, and that not all clients /// will implement all message styles. pub fn send(&self, style: MessageStyle, msg: &str) -> Result<Option<&CStr>> { let mut resp_ptr: *const Response = ptr::null(); let msg_cstr = CString::new(msg).unwrap(); let msg = Message { msg_style: style, msg: msg_cstr.as_ptr(), }; // TODO: These need to be freed! let ret = (self.0.conv)(1, &&msg, &mut resp_ptr, self.0.appdata_ptr); ErrorCode::result_from(ret)?; let result = unsafe { match (*resp_ptr).resp { p if p.is_null() => None, p => Some(CStr::from_ptr(p)), } }; Ok(result) } } impl Item for Conversation<'_> { type Raw = Inner; fn type_id() -> crate::items::ItemType { crate::items::ItemType::Conversation } unsafe fn from_raw(raw: *const Self::Raw) -> Self { Self(&*raw) } fn into_raw(self) -> *const Self::Raw { self.0 as _ } }