use std::ffi::{CStr, CString}; use std::io::stdout; use clap::Parser as _; use maildir::Maildir; mod arguments; mod cmd; mod error; mod rfc822; use arguments::{Arguments, Mode}; use cmd::{ count, folders, list, move_mail, open_submaildir, raw, read, search, serialize_to, Return, }; use error::{Error, Result}; fn switch_to_user(sys_user: &str) -> Result<()> { unsafe { *libc::__errno_location() = 0; } let c_sys_user = CString::new(sys_user).map_err(|e| Error::Setuid(format!("nul char in user, {}", e)))?; let user_info: *const libc::passwd = unsafe { libc::getpwnam(c_sys_user.as_ptr()) }; let err = unsafe { *libc::__errno_location() }; if err != 0 { return Err(Error::Setuid(format!( "error calling getpwnam {:?}", unsafe { libc::strerror(err) } ))); }; if user_info.is_null() { return Err(Error::Setuid(format!("user {:?} does not exist", sys_user))); }; let rc = unsafe { libc::setuid((*user_info).pw_uid) }; if rc != 0 { let err = unsafe { *libc::__errno_location() }; return Err(Error::Setuid(format!( "error calling setuid {:?}", unsafe { CStr::from_ptr(libc::strerror(err)) } ))); } Ok(()) } fn main() -> std::io::Result<()> { simplelog::TermLogger::init( simplelog::LevelFilter::Info, simplelog::Config::default(), simplelog::TerminalMode::Stderr, simplelog::ColorChoice::Never, ) .unwrap(); let args = Arguments::parse(); std::env::remove_var("PATH"); if let Err(e) = switch_to_user(&args.sys_user) { serialize_to(Err(e), &stdout())? } let path = args.maildir_path.join(args.mail_user); let res = match args.mode { Mode::Read { subfolder, mid } => { read(&open_submaildir(path, &subfolder), &mid).map(Return::Read) } Mode::Raw { subfolder, mid, mime_path, } => raw(&open_submaildir(path, &subfolder), &mid, &mime_path) .map(|(h, t)| Return::Raw(h, t)), Mode::List { subfolder, start, end, ref sortby, } => list(&open_submaildir(path, &subfolder), start, end, sortby).map(Return::List), Mode::Folders => folders(&Maildir::from(path)).map(Return::Folders), Mode::Count { subfolder } => count(&open_submaildir(path, &subfolder)).map(Return::Count), Mode::Search { pattern, subfolder } => { search(&open_submaildir(path, &subfolder), &pattern).map(Return::Search) } Mode::Move { mid, from, to } => move_mail(path, &mid, &from, &to).map(|()| Return::Move), }; serialize_to(res, &stdout()) }