use crate::{DesktopEnv, Platform};
use std::{
convert::TryInto,
ffi::OsString,
os::{
raw::{c_char, c_int, c_uchar, c_ulong},
windows::ffi::OsStringExt,
},
ptr,
};
#[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;
}
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> {
extern "system" {
fn GetVersion() -> usize;
}
let bits = unsafe { GetVersion() } as u32;
let mut out = "Windows ".to_string();
let major: u8 = (bits & 0b00000000_00000000_00000000_11111111) as u8;
let minor: u8 = ((bits & 0b00000000_00000000_11111111_00000000) >> 8) as u8;
let build: u16 =
((bits & 0b11111111_11111111_00000000_00000000) >> 16) as u16;
match major {
5 => out.push_str("XP"),
6 => match minor {
0 => out.push_str("Vista"),
1 => out.push_str("7"),
2 => match build {
9200 => out.push_str("10"),
_ => out.push_str("8"),
},
_ => out.push_str("8"),
},
_ => out.push_str("Unknown"),
}
Some(out)
}
#[inline(always)]
pub const fn desktop_env() -> DesktopEnv {
DesktopEnv::Windows
}
#[inline(always)]
pub const fn platform() -> Platform {
Platform::Windows
}