view src/libpam/environ.rs @ 99:8840fa6534f6

Streamline dependencies and rename to openpam-extensions.
author Paul Fisher <paul@pfish.zone>
date Tue, 24 Jun 2025 14:54:47 -0400
parents b87100c5eed4
children 3f11b8d30f63
line wrap: on
line source

#![allow(unused_variables)] // for now
use crate::environ::EnvironMapMut;
use crate::libpam::memory::CHeapString;
use crate::{EnvironMap, LibPamHandle};
use std::ffi::{c_char, OsStr, OsString};
use std::os::unix::ffi::OsStrExt;
use std::ptr::NonNull;
use std::{iter, ptr};
use crate::libpam::memory;

pub struct LibPamEnviron<'a> {
    source: &'a LibPamHandle,
}

pub struct LibPamEnvironMut<'a> {
    source: &'a mut LibPamHandle,
}

impl<'a> LibPamEnviron<'a> {
    pub fn new(source: &'a LibPamHandle) -> Self {
        Self { source }
    }
}

impl<'a> LibPamEnvironMut<'a> {
    pub fn new(source: &'a mut LibPamHandle) -> Self {
        Self { source }
    }

    fn immut(&self) -> LibPamEnviron {
        LibPamEnviron {
            source: self.source,
        }
    }
}

impl EnvironMap for LibPamEnviron<'_> {
    fn get(&self, val: &OsStr) -> Option<&OsStr> {
        todo!()
    }

    fn iter(&self) -> impl Iterator<Item = (&OsStr, &OsStr)> {
        iter::from_fn(|| todo!())
    }
}

impl EnvironMap for LibPamEnvironMut<'_> {
    fn get(&self, val: &OsStr) -> Option<&OsStr> {
        todo!()
    }

    fn iter(&self) -> impl Iterator<Item = (&OsStr, &OsStr)> {
        iter::from_fn(|| todo!())
    }
}

impl EnvironMapMut for LibPamEnvironMut<'_> {
    fn insert(&mut self, key: &OsStr, val: &OsStr) -> Option<OsString> {
        todo!()
    }

    fn remove(&mut self, key: &OsStr) -> Option<OsString> {
        todo!()
    }
}

struct EnvList {
    /// Pointer to the start of the environment variable list.
    ///
    /// This can't be a `CHeapBox` because it's not just a single
    /// `Option<EnvVar>`.
    vars: NonNull<Option<EnvVar>>,
}

impl EnvList {
    unsafe fn from_ptr(ptr: NonNull<*mut c_char>) -> Self {
        Self{vars: ptr.cast()}
    }

    fn iter(&self) -> impl Iterator<Item = (&OsStr, &OsStr)> {
        let mut current = self.vars;
        iter::from_fn(move || {
            match unsafe {current.as_ref()} {
                None => None,
                Some(item) => {
                    let ret = item.as_kv();
                    current = unsafe {current.add(1) };
                    Some(ret)
                }
            }
        })
    }
}

impl Drop for EnvList {
    fn drop(&mut self) {
        // SAFETY: We own this pointer, and we know it's valid environment data
        // from PAM.
        unsafe {
            let mut var = self.vars;
            while let Some(var_ref) = var.as_mut() {
                ptr::drop_in_place(var_ref as *mut EnvVar);
                var = var.add(1);
            }
            memory::free(self.vars.as_ptr())
        }
    }
}

struct EnvVar(CHeapString);

impl EnvVar {
    fn as_kv(&self) -> (&OsStr, &OsStr) {
        let bytes = self.0.to_bytes();
        let mut split = bytes.splitn(2, |&b| b == b'=');
        (
            OsStr::from_bytes(split.next().unwrap_or_default()),
            OsStr::from_bytes(split.next().unwrap_or_default()),
        )
    }
}

#[cfg(test)]
mod tests {
    use crate::libpam::memory::CHeapBox;
    use super::*;

    #[test]
    fn test_split_kv() {
        fn test(input: &str, key: &str, value: &str) {
            let data = CHeapString::new(input).unwrap();
            let key = OsStr::from_bytes(key.as_bytes());
            let value = OsStr::from_bytes(value.as_bytes());

            assert_eq!(EnvVar(data).as_kv(), (key, value));
        }
        test("THIS=that", "THIS", "that");
        test("THESE=those, no one=knows", "THESE", "those, no one=knows");
        test("HERE=", "HERE", "");
        test("SOME", "SOME", "");
        test("", "", "");
    }

    #[test]
    fn test_iter() {
        let bx = CHeapBox::new([
            Some("ONE=two"),
            Some("BIRDS=birds=birds"),
            Some("me"),
            Some("you="),
            None,
        ].map(|txt| txt.map(|txt| CHeapString::new(txt).unwrap()))).unwrap();
        let env_ptr = CHeapBox::into_ptr(bx);

        let envs = unsafe {EnvList::from_ptr(env_ptr.cast())};
        let bytes = |data: &'static str| OsStr::from_bytes(data.as_ref());
        let result: Vec<_> = envs.iter().collect();
        assert_eq!(vec![
            (bytes("ONE"), bytes("two")),
            (bytes("BIRDS"), bytes("birds=birds")),
            (bytes("me"), bytes("")),
            (bytes("you"), bytes("")),
        ],
        result);
    }
}