// Take a look at the license at the top of the repository in the LICENSE file. use crate::{ common::{Gid, Uid}, User, }; use crate::sys::utils; use libc::{c_char, endpwent, getgrgid, getgrouplist, getpwent, gid_t, setpwent, strlen}; fn get_user_groups(name: *const c_char, group_id: gid_t) -> Vec { let mut add = 0; loop { let mut nb_groups = 256 + add; let mut groups = Vec::with_capacity(nb_groups as _); unsafe { if getgrouplist(name, group_id as _, groups.as_mut_ptr(), &mut nb_groups) == -1 { add += 100; continue; } groups.set_len(nb_groups as _); return groups .into_iter() .filter_map(|g| { let group = getgrgid(g as _); if group.is_null() { return None; } utils::cstr_to_rust((*group).gr_name) }) .collect(); } } } fn endswith(s1: *const c_char, s2: &[u8]) -> bool { if s1.is_null() { return false; } unsafe { let mut len = strlen(s1) as isize - 1; let mut i = s2.len() as isize - 1; while len >= 0 && i >= 0 && *s1.offset(len) == s2[i as usize] as _ { i -= 1; len -= 1; } i == -1 } } fn users_list(filter: F) -> Vec where F: Fn(*const c_char, u32) -> bool, { let mut users = Vec::new(); unsafe { setpwent(); loop { let pw = getpwent(); if pw.is_null() { break; } if !filter((*pw).pw_shell, (*pw).pw_uid) { // This is not a "real" or "local" user. continue; } let groups = get_user_groups((*pw).pw_name, (*pw).pw_gid); let uid = (*pw).pw_uid; let gid = (*pw).pw_gid; if let Some(name) = utils::cstr_to_rust((*pw).pw_name) { users.push(User { uid: Uid(uid), gid: Gid(gid), name, groups, }); } } endpwent(); } users.sort_unstable_by(|x, y| x.name.partial_cmp(&y.name).unwrap()); users.dedup_by(|a, b| a.name == b.name); users } pub(crate) fn get_users_list() -> Vec { users_list(|shell, uid| { !endswith(shell, b"/false") && !endswith(shell, b"/uucico") && uid < 65536 }) } // This was the OSX-based solution. It provides enough information, but what a mess! // pub fn get_users_list() -> Vec { // let mut users = Vec::new(); // let node_name = b"/Local/Default\0"; // unsafe { // let node_name = ffi::CFStringCreateWithCStringNoCopy( // std::ptr::null_mut(), // node_name.as_ptr() as *const c_char, // ffi::kCFStringEncodingMacRoman, // ffi::kCFAllocatorNull as *mut c_void, // ); // let node_ref = ffi::ODNodeCreateWithName( // ffi::kCFAllocatorDefault, // ffi::kODSessionDefault, // node_name, // std::ptr::null_mut(), // ); // let query = ffi::ODQueryCreateWithNode( // ffi::kCFAllocatorDefault, // node_ref, // ffi::kODRecordTypeUsers as _, // kODRecordTypeGroups // std::ptr::null(), // 0, // std::ptr::null(), // std::ptr::null(), // 0, // std::ptr::null_mut(), // ); // if query.is_null() { // return users; // } // let results = ffi::ODQueryCopyResults( // query, // false as _, // std::ptr::null_mut(), // ); // let len = ffi::CFArrayGetCount(results); // for i in 0..len { // let name = match get_user_name(ffi::CFArrayGetValueAtIndex(results, i)) { // Some(n) => n, // None => continue, // }; // let groups = get_user_groups(&name); // users.push(User { name }); // } // ffi::CFRelease(results as *const c_void); // ffi::CFRelease(query as *const c_void); // ffi::CFRelease(node_ref as *const c_void); // ffi::CFRelease(node_name as *const c_void); // } // users.sort_unstable_by(|x, y| x.name.partial_cmp(&y.name).unwrap()); // return users; // } // fn get_user_name(result: *const c_void) -> Option { // let user_name = ffi::ODRecordGetRecordName(result as _); // let ptr = ffi::CFStringGetCharactersPtr(user_name); // String::from_utf16(&if ptr.is_null() { // let len = ffi::CFStringGetLength(user_name); // It returns the len in UTF-16 code pairs. // if len == 0 { // continue; // } // let mut v = Vec::with_capacity(len as _); // for x in 0..len { // v.push(ffi::CFStringGetCharacterAtIndex(user_name, x)); // } // v // } else { // let mut v: Vec = Vec::new(); // let mut x = 0; // loop { // let letter = *ptr.offset(x); // if letter == 0 { // break; // } // v.push(letter); // x += 1; // } // v // }.ok() // }