1 // WhoAmI
2 // Copyright © 2017-2021 Jeron Aldaron Lau.
3 //
4 // Licensed under any of:
5 // - Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
6 // - MIT License (https://mit-license.org/)
7 // - Boost Software License, Version 1.0 (https://www.boost.org/LICENSE_1_0.txt)
8 // At your choosing (See accompanying files LICENSE_APACHE_2_0.txt,
9 // LICENSE_MIT.txt and LICENSE_BOOST_1_0.txt).
10
11 use crate::{DesktopEnv, Platform};
12
13 use std::convert::TryInto;
14 use std::ffi::OsString;
15 use std::os::raw::{c_char, c_int, c_uchar, c_ulong, c_ushort};
16 use std::os::windows::ffi::OsStringExt;
17 use std::ptr;
18
19 #[repr(C)]
20 struct OsVersionInfoEx {
21 os_version_info_size: c_ulong,
22 major_version: c_ulong,
23 minor_version: c_ulong,
24 build_number: c_ulong,
25 platform_id: c_ulong,
26 sz_csd_version: [u16; 128],
27 service_pack_major: c_ushort,
28 service_pack_minor: c_ushort,
29 suite_mask: c_ushort,
30 product_type: c_uchar,
31 reserved: c_uchar,
32 }
33
34 #[allow(unused)]
35 #[repr(C)]
36 enum ExtendedNameFormat {
37 Unknown, // Nothing
38 FullyQualifiedDN, // Nothing
39 SamCompatible, // Hostname Followed By Username
40 Display, // Full Name
41 UniqueId, // Nothing
42 Canonical, // Nothing
43 UserPrincipal, // Nothing
44 CanonicalEx, // Nothing
45 ServicePrincipal, // Nothing
46 DnsDomain, // Nothing
47 GivenName, // Nothing
48 Surname, // Nothing
49 }
50
51 #[allow(unused)]
52 #[repr(C)]
53 enum ComputerNameFormat {
54 NetBIOS, // Same as GetComputerNameW
55 DnsHostname, // Fancy Name
56 DnsDomain, // Nothing
57 DnsFullyQualified, // Fancy Name with, for example, .com
58 PhysicalNetBIOS, // Same as GetComputerNameW
59 PhysicalDnsHostname, // Same as GetComputerNameW
60 PhysicalDnsDomain, // Nothing
61 PhysicalDnsFullyQualified, // Fancy Name with, for example, .com
62 Max,
63 }
64
65 #[link(name = "secur32")]
66 extern "system" {
GetLastError() -> c_ulong67 fn GetLastError() -> c_ulong;
GetUserNameExW( a: ExtendedNameFormat, b: *mut c_char, c: *mut c_ulong, ) -> c_uchar68 fn GetUserNameExW(
69 a: ExtendedNameFormat,
70 b: *mut c_char,
71 c: *mut c_ulong,
72 ) -> c_uchar;
GetUserNameW(a: *mut c_char, b: *mut c_ulong) -> c_int73 fn GetUserNameW(a: *mut c_char, b: *mut c_ulong) -> c_int;
GetComputerNameExW( a: ComputerNameFormat, b: *mut c_char, c: *mut c_ulong, ) -> c_int74 fn GetComputerNameExW(
75 a: ComputerNameFormat,
76 b: *mut c_char,
77 c: *mut c_ulong,
78 ) -> c_int;
79 }
80
81 #[link(name = "ntdll")]
82 extern "system" {
RtlGetVersion(a: *mut OsVersionInfoEx) -> u3283 fn RtlGetVersion(a: *mut OsVersionInfoEx) -> u32;
84 }
85
86 #[link(name = "kernel32")]
87 extern "system" {
GetUserPreferredUILanguages( dw_flags: c_ulong, pul_num_languages: *mut c_ulong, pwsz_languages_buffer: *mut u16, pcch_languages_buffer: *mut c_ulong, ) -> c_int88 fn GetUserPreferredUILanguages(
89 dw_flags: c_ulong,
90 pul_num_languages: *mut c_ulong,
91 pwsz_languages_buffer: *mut u16,
92 pcch_languages_buffer: *mut c_ulong,
93 ) -> c_int;
94 }
95
96 // Convert an OsString into a String
string_from_os(string: OsString) -> String97 fn string_from_os(string: OsString) -> String {
98 match string.into_string() {
99 Ok(string) => string,
100 Err(string) => string.to_string_lossy().to_string(),
101 }
102 }
103
username() -> String104 pub fn username() -> String {
105 string_from_os(username_os())
106 }
107
username_os() -> OsString108 pub fn username_os() -> OsString {
109 // Step 1. Retreive the entire length of the username
110 let mut size = 0;
111 let success = unsafe {
112 // Ignore error, we know that it will be ERROR_INSUFFICIENT_BUFFER
113 GetUserNameW(ptr::null_mut(), &mut size) == 0
114 };
115 assert!(success);
116
117 // Step 2. Allocate memory to put the Windows (UTF-16) string.
118 let mut name: Vec<u16> = Vec::with_capacity(size.try_into().unwrap_or(usize::MAX));
119 size = name.capacity().try_into().unwrap_or(c_ulong::MAX);
120 let orig_size = size;
121 let fail =
122 unsafe { GetUserNameW(name.as_mut_ptr().cast(), &mut size) == 0 };
123 if fail {
124 return "unknown".to_string().into();
125 }
126 debug_assert_eq!(orig_size, size);
127 unsafe {
128 name.set_len(size.try_into().unwrap_or(usize::MAX));
129 }
130 let terminator = name.pop(); // Remove Trailing Null
131 debug_assert_eq!(terminator, Some(0u16));
132
133 // Step 3. Convert to Rust String
134 OsString::from_wide(&name)
135 }
136
137 #[inline(always)]
realname() -> String138 pub fn realname() -> String {
139 string_from_os(realname_os())
140 }
141
142 #[inline(always)]
realname_os() -> OsString143 pub fn realname_os() -> OsString {
144 // Step 1. Retrieve the entire length of the username
145 let mut buf_size = 0;
146 let success = unsafe {
147 GetUserNameExW(
148 ExtendedNameFormat::Display,
149 ptr::null_mut(),
150 &mut buf_size,
151 ) == 0
152 };
153 assert!(success);
154 match unsafe { GetLastError() } {
155 0x00EA /* more data */ => { /* Success, continue */ }
156 _ /* network error or none mapped */ => {
157 // Fallback to username
158 return username_os();
159 }
160 }
161
162 // Step 2. Allocate memory to put the Windows (UTF-16) string.
163 let mut name: Vec<u16> = Vec::with_capacity(buf_size.try_into().unwrap_or(usize::MAX));
164 let mut name_len = name.capacity().try_into().unwrap_or(c_ulong::MAX);
165 let fail = unsafe {
166 GetUserNameExW(
167 ExtendedNameFormat::Display,
168 name.as_mut_ptr().cast(),
169 &mut name_len,
170 ) == 0
171 };
172 if fail {
173 return "Unknown".to_string().into();
174 }
175 debug_assert_eq!(buf_size, name_len + 1);
176 unsafe {
177 name.set_len(name_len.try_into().unwrap_or(usize::MAX));
178 }
179
180 // Step 3. Convert to Rust String
181 OsString::from_wide(&name)
182 }
183
184 #[inline(always)]
devicename() -> String185 pub fn devicename() -> String {
186 string_from_os(devicename_os())
187 }
188
189 #[inline(always)]
devicename_os() -> OsString190 pub fn devicename_os() -> OsString {
191 // Step 1. Retreive the entire length of the device name
192 let mut size = 0;
193 let success = unsafe {
194 // Ignore error, we know that it will be ERROR_INSUFFICIENT_BUFFER
195 GetComputerNameExW(
196 ComputerNameFormat::DnsHostname,
197 ptr::null_mut(),
198 &mut size,
199 ) == 0
200 };
201 assert!(success);
202
203 // Step 2. Allocate memory to put the Windows (UTF-16) string.
204 let mut name: Vec<u16> = Vec::with_capacity(size.try_into().unwrap_or(usize::MAX));
205 size = name.capacity().try_into().unwrap_or(c_ulong::MAX);
206 let fail = unsafe {
207 GetComputerNameExW(
208 ComputerNameFormat::DnsHostname,
209 name.as_mut_ptr().cast(),
210 &mut size,
211 ) == 0
212 };
213 if fail {
214 return "Unknown".to_string().into();
215 }
216 unsafe {
217 name.set_len(size.try_into().unwrap_or(usize::MAX));
218 }
219
220 // Step 3. Convert to Rust String
221 OsString::from_wide(&name)
222 }
223
hostname() -> String224 pub fn hostname() -> String {
225 string_from_os(hostname_os())
226 }
227
hostname_os() -> OsString228 pub fn hostname_os() -> OsString {
229 // Step 1. Retreive the entire length of the username
230 let mut size = 0;
231 let fail = unsafe {
232 // Ignore error, we know that it will be ERROR_INSUFFICIENT_BUFFER
233 GetComputerNameExW(
234 ComputerNameFormat::NetBIOS,
235 ptr::null_mut(),
236 &mut size,
237 ) == 0
238 };
239 debug_assert!(fail);
240
241 // Step 2. Allocate memory to put the Windows (UTF-16) string.
242 let mut name: Vec<u16> = Vec::with_capacity(size.try_into().unwrap_or(usize::MAX));
243 size = name.capacity().try_into().unwrap_or(c_ulong::MAX);
244 let fail = unsafe {
245 GetComputerNameExW(
246 ComputerNameFormat::NetBIOS,
247 name.as_mut_ptr().cast(),
248 &mut size,
249 ) == 0
250 };
251 if fail {
252 return "localhost".to_string().into();
253 }
254 unsafe {
255 name.set_len(size.try_into().unwrap_or(usize::MAX));
256 }
257
258 // Step 3. Convert to Rust String
259 OsString::from_wide(&name)
260 }
261
distro_os() -> Option<OsString>262 pub fn distro_os() -> Option<OsString> {
263 distro().map(|a| a.into())
264 }
265
distro() -> Option<String>266 pub fn distro() -> Option<String> {
267 let mut version = std::mem::MaybeUninit::<OsVersionInfoEx>::zeroed();
268
269 let version = unsafe {
270 (*version.as_mut_ptr()).os_version_info_size =
271 std::mem::size_of::<OsVersionInfoEx>() as u32;
272 RtlGetVersion(version.as_mut_ptr());
273 version.assume_init()
274 };
275
276 let product = match version.product_type {
277 1 => "Workstation",
278 2 => "Domain Controller",
279 3 => "Server",
280 _ => "Unknown",
281 };
282
283 let out = format!(
284 "Windows {}.{}.{} ({})",
285 version.major_version,
286 version.minor_version,
287 version.build_number,
288 product
289 );
290
291 Some(out)
292 }
293
294 #[inline(always)]
desktop_env() -> DesktopEnv295 pub const fn desktop_env() -> DesktopEnv {
296 DesktopEnv::Windows
297 }
298
299 #[inline(always)]
platform() -> Platform300 pub const fn platform() -> Platform {
301 Platform::Windows
302 }
303
304 struct LangIter {
305 array: Vec<String>,
306 index: usize,
307 }
308
309 impl Iterator for LangIter {
310 type Item = String;
311
next(&mut self) -> Option<Self::Item>312 fn next(&mut self) -> Option<Self::Item> {
313 if let Some(value) = self.array.get(self.index) {
314 self.index += 1;
315 Some(value.to_string())
316 } else {
317 None
318 }
319 }
320 }
321
322 #[inline(always)]
lang() -> impl Iterator<Item = String>323 pub fn lang() -> impl Iterator<Item = String> {
324 let mut num_languages = 0;
325 let mut buffer_size = 0;
326 let mut buffer;
327
328 unsafe {
329 assert_ne!(
330 GetUserPreferredUILanguages(
331 0x08, /* MUI_LANGUAGE_NAME */
332 &mut num_languages,
333 std::ptr::null_mut(), // List of languages.
334 &mut buffer_size,
335 ),
336 0
337 );
338
339 buffer = Vec::with_capacity(buffer_size as usize);
340
341 assert_ne!(
342 GetUserPreferredUILanguages(
343 0x08, /* MUI_LANGUAGE_NAME */
344 &mut num_languages,
345 buffer.as_mut_ptr(), // List of languages.
346 &mut buffer_size,
347 ),
348 0
349 );
350
351 buffer.set_len(buffer_size as usize);
352 }
353
354 // We know it ends in two null characters.
355 buffer.pop();
356 buffer.pop();
357
358 //
359 let array = String::from_utf16_lossy(&buffer)
360 .split('\0')
361 .map(|x| x.to_string())
362 .collect();
363 let index = 0;
364
365 LangIter { array, index }
366 }
367