view src/conv.rs @ 58:868a278a362c

Added tag v0.0.4 for changeset 2a5c83d04b93
author Paul Fisher <paul@pfish.zone>
date Mon, 05 May 2025 00:16:04 -0400
parents daa2cde64601
children 3f4a77aa88be
line wrap: on
line source

use libc::{c_char, c_int};
use std::ffi::{CStr, CString};
use std::ptr;

use crate::constants::MessageStyle;
use crate::constants::PamResult;
use crate::constants::ErrorCode;
use crate::items::Item;

#[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
}

#[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 `Conv`ersation channel with the user.
///
/// Communication is mediated by the PAM client (the application that invoked
/// pam).  Messages sent will be relayed to the user by the client, and response
/// will be relayed back.
pub struct Conv<'a>(&'a Inner);

impl Conv<'_> {
    /// Sends a message to the pam client.
    ///
    /// This will typically result in the user seeing a message or a prompt.
    /// There are several message styles available:
    ///
    /// - PAM_PROMPT_ECHO_OFF
    /// - PAM_PROMPT_ECHO_ON
    /// - PAM_ERROR_MSG
    /// - PAM_TEXT_INFO
    /// - PAM_RADIO_TYPE
    /// - PAM_BINARY_PROMPT
    ///
    /// Note that the user experience will depend on how the client implements
    /// these message styles - and not all applications implement all message
    /// styles.
    pub fn send(&self, style: MessageStyle, msg: &str) -> PamResult<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(),
        };
        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 Conv<'_> {
    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 _
    }
}