use crate::{DesktopEnv, Platform};
use std::convert::TryInto;
use std::ffi::OsString;
use std::os::raw::{c_char, c_int, c_uchar, c_ulong, c_ushort};
use std::os::windows::ffi::OsStringExt;
use std::ptr;
#[repr(C)]
struct OsVersionInfoEx {
os_version_info_size: c_ulong,
major_version: c_ulong,
minor_version: c_ulong,
build_number: c_ulong,
platform_id: c_ulong,
sz_csd_version: [u16; 128],
service_pack_major: c_ushort,
service_pack_minor: c_ushort,
suite_mask: c_ushort,
product_type: c_uchar,
reserved: c_uchar,
}
#[allow(unused)]
#[repr(C)]
enum ExtendedNameFormat {
Unknown, FullyQualifiedDN, SamCompatible, Display, UniqueId, Canonical, UserPrincipal, CanonicalEx, ServicePrincipal, DnsDomain, GivenName, Surname, }
#[allow(unused)]
#[repr(C)]
enum ComputerNameFormat {
NetBIOS, DnsHostname, DnsDomain, DnsFullyQualified, PhysicalNetBIOS, PhysicalDnsHostname, PhysicalDnsDomain, PhysicalDnsFullyQualified, Max,
}
#[link(name = "secur32")]
extern "system" {
fn GetLastError() -> c_ulong;
fn GetUserNameExW(
a: ExtendedNameFormat,
b: *mut c_char,
c: *mut c_ulong,
) -> c_uchar;
fn GetUserNameW(a: *mut c_char, b: *mut c_ulong) -> c_int;
fn GetComputerNameExW(
a: ComputerNameFormat,
b: *mut c_char,
c: *mut c_ulong,
) -> c_int;
}
#[link(name = "ntdll")]
extern "system" {
fn RtlGetVersion(a: *mut OsVersionInfoEx) -> u32;
}
#[link(name = "kernel32")]
extern "system" {
fn GetUserPreferredUILanguages(
dw_flags: c_ulong,
pul_num_languages: *mut c_ulong,
pwsz_languages_buffer: *mut u16,
pcch_languages_buffer: *mut c_ulong,
) -> c_int;
}
fn string_from_os(string: OsString) -> String {
match string.into_string() {
Ok(string) => string,
Err(string) => string.to_string_lossy().to_string(),
}
}
pub fn username() -> String {
string_from_os(username_os())
}
pub fn username_os() -> OsString {
let mut size = 0;
let fail = unsafe {
GetUserNameW(ptr::null_mut(), &mut size) == 0
};
debug_assert_eq!(fail, true);
let mut name: Vec<u16> = Vec::with_capacity(size.try_into().unwrap());
let orig_size = size;
let fail =
unsafe { GetUserNameW(name.as_mut_ptr().cast(), &mut size) == 0 };
if fail {
panic!(
"Failed to get username: {}, report at https://github.com/libcala/whoami/issues",
unsafe { GetLastError() }
);
}
debug_assert_eq!(orig_size, size);
unsafe {
name.set_len(size.try_into().unwrap());
}
let terminator = name.pop(); debug_assert_eq!(terminator, Some(0u16));
OsString::from_wide(&name)
}
#[inline(always)]
pub fn realname() -> String {
string_from_os(realname_os())
}
#[inline(always)]
pub fn realname_os() -> OsString {
let mut buf_size = 0;
let fail = unsafe {
GetUserNameExW(
ExtendedNameFormat::Display,
ptr::null_mut(),
&mut buf_size,
) == 0
};
debug_assert_eq!(fail, true);
match unsafe { GetLastError() } {
0x00EA => { }
0x054B => {
return "Unknown".into()
}
0x0534 => {
return username_os();
}
u => {
eprintln!("Unknown error code: {}, report at https://github.com/libcala/whoami/issues", u);
unreachable!();
}
}
let mut name: Vec<u16> = Vec::with_capacity(buf_size.try_into().unwrap());
let mut name_len = buf_size;
let fail = unsafe {
GetUserNameExW(
ExtendedNameFormat::Display,
name.as_mut_ptr().cast(),
&mut name_len,
) == 0
};
if fail {
panic!(
"Failed to get username: {}, report at https://github.com/libcala/whoami/issues",
unsafe { GetLastError() }
);
}
debug_assert_eq!(buf_size, name_len + 1);
unsafe {
name.set_len(name_len.try_into().unwrap());
}
OsString::from_wide(&name)
}
#[inline(always)]
pub fn devicename() -> String {
string_from_os(devicename_os())
}
#[inline(always)]
pub fn devicename_os() -> OsString {
let mut size = 0;
let fail = unsafe {
GetComputerNameExW(
ComputerNameFormat::DnsHostname,
ptr::null_mut(),
&mut size,
) == 0
};
debug_assert_eq!(fail, true);
let mut name: Vec<u16> = Vec::with_capacity(size.try_into().unwrap());
let fail = unsafe {
GetComputerNameExW(
ComputerNameFormat::DnsHostname,
name.as_mut_ptr().cast(),
&mut size,
) == 0
};
if fail {
panic!(
"Failed to get computer name: {}, report at https://github.com/libcala/whoami/issues",
unsafe { GetLastError() }
);
}
unsafe {
name.set_len(size.try_into().unwrap());
}
OsString::from_wide(&name)
}
pub fn hostname() -> String {
string_from_os(hostname_os())
}
pub fn hostname_os() -> OsString {
let mut size = 0;
let fail = unsafe {
GetComputerNameExW(
ComputerNameFormat::NetBIOS,
ptr::null_mut(),
&mut size,
) == 0
};
debug_assert_eq!(fail, true);
let mut name: Vec<u16> = Vec::with_capacity(size.try_into().unwrap());
let fail = unsafe {
GetComputerNameExW(
ComputerNameFormat::NetBIOS,
name.as_mut_ptr().cast(),
&mut size,
) == 0
};
if fail {
panic!(
"Failed to get computer name: {}, report at https://github.com/libcala/whoami/issues",
unsafe { GetLastError() }
);
}
unsafe {
name.set_len(size.try_into().unwrap());
}
OsString::from_wide(&name)
}
pub fn distro_os() -> Option<OsString> {
distro().map(|a| a.into())
}
pub fn distro() -> Option<String> {
let mut version = std::mem::MaybeUninit::<OsVersionInfoEx>::zeroed();
let version = unsafe {
(*version.as_mut_ptr()).os_version_info_size =
std::mem::size_of::<OsVersionInfoEx>() as u32;
RtlGetVersion(version.as_mut_ptr());
version.assume_init()
};
let product = match version.product_type {
1 => "Workstation",
2 => "Domain Controller",
3 => "Server",
_ => "Unknown",
};
let out = format!(
"Windows {}.{}.{} ({})",
version.major_version,
version.minor_version,
version.build_number,
product
);
Some(out)
}
#[inline(always)]
pub const fn desktop_env() -> DesktopEnv {
DesktopEnv::Windows
}
#[inline(always)]
pub const fn platform() -> Platform {
Platform::Windows
}
struct LangIter {
array: Vec<String>,
index: usize,
}
impl Iterator for LangIter {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
if let Some(value) = self.array.get(self.index) {
self.index += 1;
Some(value.to_string())
} else {
None
}
}
}
#[inline(always)]
pub fn lang() -> impl Iterator<Item = String> {
let mut num_languages = 0;
let mut buffer_size = 0;
let mut buffer;
unsafe {
assert_ne!(
GetUserPreferredUILanguages(
0x08,
&mut num_languages,
std::ptr::null_mut(), &mut buffer_size,
),
0
);
buffer = Vec::with_capacity(buffer_size as usize);
assert_ne!(
GetUserPreferredUILanguages(
0x08,
&mut num_languages,
buffer.as_mut_ptr(), &mut buffer_size,
),
0
);
buffer.set_len(buffer_size as usize);
}
buffer.pop();
buffer.pop();
let array = String::from_utf16_lossy(&buffer)
.split('\0')
.map(|x| x.to_string())
.collect();
let index = 0;
LangIter { array, index }
}