1use std::ffi::{CStr, CString};
2use std::io;
3use std::os::raw::*;
4
5pub enum AccountStatus {
7 Active(String),
9 NoPassword,
11 NoLogin,
13 Unknown,
15}
16impl AccountStatus {
17 pub fn to_string(&self) -> String {
19 match self {
20 Self::Unknown => "Unknown password".to_string(),
21 Self::Active(p) => p.to_string(),
22 Self::NoLogin => "Disabled account".to_string(),
23 Self::NoPassword => "No password".to_string(),
24 }
25 }
26}
27
28pub struct Shadow {
30 pub name: String,
32 pub passwd: AccountStatus,
34
35 pub last_chage: c_long,
37 pub min: c_long,
39 pub max: c_long,
41
42 pub warn: c_long,
44 pub inactive: c_long,
46 pub expires: c_long,
48}
49
50#[repr(C)]
52struct Spwd {
53 sp_namp: *const c_char,
54 sp_pwdp: *const c_char,
55
56 sp_lstchg: c_long,
57 sp_min: c_long,
58 sp_max: c_long,
59 sp_warn: c_long,
60
61 sp_inact: c_long,
62 sp_expire: c_long,
63
64 sp_flag: c_ulong,
65}
66extern "C" {
67 fn getspnam(name: *const c_char) -> *const Spwd;
68}
69
70impl Shadow {
71 unsafe fn convert(raw: *const Spwd) -> io::Result<Self> {
72 if raw == std::ptr::null() {
73 return Err(io::Error::last_os_error());
74 }
75
76 let name = match CStr::from_ptr((*raw).sp_namp).to_str() {
77 Ok(o) => o.to_string(),
78 Err(_) => return Err(io::Error::from(io::ErrorKind::InvalidData)),
79 };
80
81 let passwd: AccountStatus = {
82 match CStr::from_ptr((*raw).sp_pwdp).to_str() {
83 Err(_) => AccountStatus::Unknown,
84 Ok(o) => {
85 if o.eq("*") {
86 AccountStatus::NoLogin
87 } else if o.eq("!") {
88 AccountStatus::NoPassword
89 } else {
90 AccountStatus::Active(o.to_string())
91 }
92 }
93 }
94 };
95
96 Ok(Self {
97 name,
98 passwd,
99 last_chage: (*raw).sp_lstchg,
100 min: (*raw).sp_min,
101 max: (*raw).sp_max,
102 warn: (*raw).sp_warn,
103 inactive: (*raw).sp_inact,
104 expires: (*raw).sp_expire,
105 })
106 }
107
108 pub fn new_from_username(name: &str) -> io::Result<Self> {
110 unsafe {
111 let ptr = match CString::new(name) {
112 Err(_) => return Err(io::Error::from(io::ErrorKind::InvalidData)),
113 Ok(o) => o,
114 };
115
116 Self::convert(getspnam(ptr.as_ptr()))
117 }
118 }
119}