whoami-1.4.1/Cargo.toml.orig010064400017500001750000000024001444620007700141110ustar0000000000000000[package] name = "whoami" version = "1.4.1" edition = "2018" license = "Apache-2.0 OR BSL-1.0 OR MIT" documentation = "https://docs.rs/whoami" homepage = "https://github.com/ardaku/whoami/blob/stable/CHANGELOG.md" repository = "https://github.com/ardaku/whoami" readme = "README.md" description = "Retrieve the current user and environment." keywords = ["user", "username", "whoami", "platform", "detect"] categories = [ "os", "wasm", "internationalization", "localization", "value-formatting", ] include = [ "LICENSE_APACHE", "LICENSE_BOOST", "LICENSE_MIT", "README.md", "src/*", ] rust-version = "1.40" # Target-specific dependency required to work with wasm-bindgen [target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi"), not(target_os = "daku")))'.dependencies.web-sys] version = "0.3" features = ["Navigator", "Document", "Window", "Location"] optional = true [target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi"), not(target_os = "daku")))'.dependencies.wasm-bindgen] version = "0.2" optional = true [features] default = ["web"] # Enabling this feature indicates that the wasm32-unknown-unknown target should # be assumed to be in a web environment where it can call DOM APIs. web = ["web-sys", "wasm-bindgen"] whoami-1.4.1/Cargo.toml0000644000000030011444620077100104160ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "whoami" version = "1.4.1" include = ["LICENSE_APACHE", "LICENSE_BOOST", "LICENSE_MIT", "README.md", "src/*"] description = "Retrieve the current user and environment." homepage = "https://github.com/ardaku/whoami/blob/stable/CHANGELOG.md" documentation = "https://docs.rs/whoami" readme = "README.md" keywords = ["user", "username", "whoami", "platform", "detect"] categories = ["os", "wasm", "internationalization", "localization", "value-formatting"] license = "Apache-2.0 OR BSL-1.0 OR MIT" repository = "https://github.com/ardaku/whoami" [features] default = ["web"] web = ["web-sys", "wasm-bindgen"] [target."cfg(all(target_arch = \"wasm32\", not(target_os = \"wasi\"), not(target_os = \"daku\")))".dependencies.wasm-bindgen] version = "0.2" optional = true [target."cfg(all(target_arch = \"wasm32\", not(target_os = \"wasi\"), not(target_os = \"daku\")))".dependencies.web-sys] version = "0.3" features = ["Navigator", "Document", "Window", "Location"] optional = true whoami-1.4.1/LICENSE_APACHE010064400017500001750000000227731444615727300132600ustar0000000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS whoami-1.4.1/LICENSE_BOOST010064400017500001750000000024721444615727300132170ustar0000000000000000Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. whoami-1.4.1/LICENSE_MIT010064400017500001750000000020141444615727300127520ustar0000000000000000MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. whoami-1.4.1/README.md010064400017500001750000000100201444620007700124760ustar0000000000000000![WhoAmI Logo](https://raw.githubusercontent.com/ardaku/whoami/stable/res/icon.svg) #### [Changelog][3] | [Source][4] | [Getting Started][5] [![tests](https://github.com/ardaku/whoami/actions/workflows/ci.yml/badge.svg)](https://github.com/ardaku/whoami/actions/workflows/ci.yml) [![GitHub commit activity](https://img.shields.io/github/commit-activity/y/ardaku/whoami)](https://github.com/ardaku/whoami/) [![GitHub contributors](https://img.shields.io/github/contributors/ardaku/whoami)](https://github.com/ardaku/whoami/graphs/contributors) [![Crates.io](https://img.shields.io/crates/v/whoami)](https://crates.io/crates/whoami) [![Crates.io](https://img.shields.io/crates/d/whoami)](https://crates.io/crates/whoami) [![Crates.io (recent)](https://img.shields.io/crates/dr/whoami)](https://crates.io/crates/whoami) [![Crates.io](https://img.shields.io/crates/l/whoami)](https://github.com/ardaku/whoami/search?l=Text&q=license) [![Docs.rs](https://docs.rs/whoami/badge.svg)](https://docs.rs/whoami/) Retrieve the current user and environment through simple functions. Check out the [documentation][0] for examples. ### Features - Get the user's full name - Get the user's username - Get the user's preferred language(s) - Get the devices's hostname - Get the devices's "pretty" or "fancy" name - Get the devices's desktop environment - Get the devices's OS name and version - Get the devices's platform name - Get the devices's CPU architecture and its width ### Supported Platforms WhoAmI targets all platforms that can run Rust, including: - Linux - Windows - Mac OS - BSD variants (FreeBSD, others) - [Web Assembly](https://github.com/ardaku/whoami/blob/stable/WASM.md) - Mock implementation - Web Browser - DOM - WASI (Wasite/Quantii, others) **mock implementation, full implementation planned later** - Daku (Ardaku/Quantii, others) **mock implementation, full implementation planned later** - Illumos variants (SmartOS, OmniOS, others) **may partially or fully work - but untested** - Android **may partially or fully work - but untested, planned later** - iOS **planned later** - Redox **planned later** - Fuchsia **planned later** - Various game consoles **planned later** - Others? (make a PR or open an issue) ## MSRV WhoAmI 1.x.y targets Rust 1.40.0 stable and later, and the 1.x.y track will be maintained at least until the release of the Rust 2024 edition. The MSRV will only be updated on major version bumps, and version 2.0.0 will target Rust 1.65.0 and later to make use of the `let else` syntax. ## Binary [whome](https://crates.io/crates/whome): `whoami` command RiR (Re-written in Rust) that depends on this crate. ## License Copyright © 2017-2023 The WhoAmI Contributors. Licensed under any of - Apache License, Version 2.0, ([LICENSE_APACHE][7] or [https://www.apache.org/licenses/LICENSE-2.0][8]) - Boost Software License, Version 1.0, ([LICENSE_BOOST][11] or [https://www.boost.org/LICENSE_1_0.txt][12]) - MIT License, ([LICENSE_MIT][9] or [https://mit-license.org/][10]) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be licensed as described above, without any additional terms or conditions. ## Help If you want help using or contributing to this library, feel free to send me an email at [aldaronlau@gmail.com][13]. [0]: https://docs.rs/whoami [1]: https://crates.io/crates/whoami [2]: https://github.com/ardaku/whoami/actions?query=workflow%3Atests [3]: https://github.com/ardaku/whoami/blob/stable/CHANGELOG.md [4]: https://github.com/ardaku/whoami/ [5]: https://docs.rs/whoami#getting-started [6]: https://aldaronlau.com/ [7]: https://github.com/ardaku/whoami/blob/stable/LICENSE_APACHE [8]: https://www.apache.org/licenses/LICENSE-2.0 [9]: https://github.com/ardaku/whoami/blob/stable/LICENSE_MIT [10]: https://mit-license.org/ [11]: https://github.com/ardaku/whoami/blob/stable/LICENSE_BOOST [12]: https://www.boost.org/LICENSE_1_0.txt [13]: mailto:aldaronlau@gmail.com whoami-1.4.1/src/fake.rs010064400017500001750000000027331444620007700132760ustar0000000000000000//! Currently used for WebAssembly unknown (non-web) only #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] compile_error!("Unexpected pointer width for target platform"); use std::ffi::OsString; use crate::{Arch, DesktopEnv, Platform}; #[inline(always)] pub(crate) fn lang() -> impl Iterator { std::iter::once("en-US".to_string()) } #[inline(always)] pub(crate) fn username_os() -> OsString { username().into() } #[inline(always)] pub(crate) fn realname_os() -> OsString { realname().into() } #[inline(always)] pub(crate) fn devicename_os() -> OsString { devicename().into() } #[inline(always)] pub(crate) fn distro_os() -> Option { distro().map(|a| a.into()) } #[inline(always)] pub(crate) fn username() -> String { "anonymous".to_string() } #[inline(always)] pub(crate) fn realname() -> String { "Anonymous".to_string() } #[inline(always)] pub(crate) fn devicename() -> String { "Unknown".to_string() } #[inline(always)] pub(crate) fn hostname() -> String { "localhost".to_string() } #[inline(always)] pub(crate) fn distro() -> Option { None } #[inline(always)] pub(crate) fn desktop_env() -> DesktopEnv { DesktopEnv::Unknown("WebAssembly".to_string()) } pub(crate) fn platform() -> Platform { Platform::Unknown("Unknown".to_string()) } pub(crate) fn arch() -> Arch { if cfg!(target_pointer_width = "64") { Arch::Wasm64 } else { Arch::Wasm32 } } whoami-1.4.1/src/lib.rs010064400017500001750000000323671444620007700131440ustar0000000000000000//! Crate for getting the user's username, realname and environment. //! //! ## Getting Started //! Using the whoami crate is super easy! All of the public items are simple //! functions with no parameters that return [`String`]s or [`OsString`]s (with //! the exception of [`desktop_env()`], [`platform()`], and [`arch()`], which //! return enums, and [`lang()`] that returns an iterator of [`String`]s). The //! following example shows how to use all of the functions (except those that //! return [`OsString`]): //! //! ```rust //! fn main() { //! println!( //! "User's Name whoami::realname(): {}", //! whoami::realname(), //! ); //! println!( //! "User's Username whoami::username(): {}", //! whoami::username(), //! ); //! println!( //! "User's Language whoami::lang(): {:?}", //! whoami::lang().collect::>(), //! ); //! println!( //! "Device's Pretty Name whoami::devicename(): {}", //! whoami::devicename(), //! ); //! println!( //! "Device's Hostname whoami::hostname(): {}", //! whoami::hostname(), //! ); //! println!( //! "Device's Platform whoami::platform(): {}", //! whoami::platform(), //! ); //! println!( //! "Device's OS Distro whoami::distro(): {}", //! whoami::distro(), //! ); //! println!( //! "Device's Desktop Env. whoami::desktop_env(): {}", //! whoami::desktop_env(), //! ); //! println!( //! "Device's CPU Arch whoami::arch(): {}", //! whoami::arch(), //! ); //! } //! ``` #![warn( anonymous_parameters, missing_copy_implementations, missing_debug_implementations, missing_docs, nonstandard_style, rust_2018_idioms, single_use_lifetimes, trivial_casts, trivial_numeric_casts, unreachable_pub, unused_extern_crates, unused_qualifications, variant_size_differences, unsafe_code )] #![doc( html_logo_url = "https://raw.githubusercontent.com/ardaku/whoami/stable/res/icon.svg", html_favicon_url = "https://raw.githubusercontent.com/ardaku/whoami/stable/res/icon.svg" )] #[allow(unsafe_code)] // Unix #[cfg_attr( not(any(target_os = "windows", target_arch = "wasm32")), path = "unix.rs" )] // Wasm32 (Daku) - FIXME: Currently routes to fake.rs #[cfg_attr(all(target_arch = "wasm32", target_os = "daku"), path = "fake.rs")] // Wasm32 (Wasi) - FIXME: Currently routes to fake.rs #[cfg_attr(all(target_arch = "wasm32", target_os = "wasi"), path = "fake.rs")] // Wasm32 (Web) #[cfg_attr( all( target_arch = "wasm32", not(target_os = "wasi"), not(target_os = "daku"), feature = "web", ), path = "web.rs" )] // Wasm32 (Unknown) #[cfg_attr( all( target_arch = "wasm32", not(target_os = "wasi"), not(target_os = "daku"), not(feature = "web"), ), path = "fake.rs" )] // Windows #[cfg_attr( all(target_os = "windows", not(target_arch = "wasm32")), path = "windows.rs" )] mod platform; use std::{ ffi::OsString, fmt::{self, Display, Formatter}, io::{Error, ErrorKind}, }; /// This crate's convenience type alias for [`Result`](std::result::Result)s pub type Result = std::result::Result; // FIXME: V2: Move `Unknown` variants to the top of the enum. /// The desktop environment of a system #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum DesktopEnv { /// Popular GTK-based desktop environment on Linux Gnome, /// One of the desktop environments for a specific version of Windows Windows, /// Linux desktop environment optimized for low resource requirements Lxde, /// Stacking window manager for X Windows on Linux Openbox, /// Desktop environment for Linux, BSD and Illumos Mate, /// Lightweight desktop enivornment for unix-like operating systems Xfce, /// KDE Plasma desktop enviroment // FIXME: Rename to 'Plasma' in whoami 2.0.0 Kde, /// Default desktop environment on Linux Mint Cinnamon, /// Tiling window manager for Linux I3, /// Desktop environment for MacOS Aqua, /// Desktop environment for iOS Ios, /// Desktop environment for Android Android, /// Running as Web Assembly on a web page WebBrowser, /// A desktop environment for a video game console Console, /// Ubuntu-branded GNOME Ubuntu, /// Default shell for Fuchsia Ermine, /// Default desktop environment for Redox Orbital, /// Unknown desktop environment Unknown(String), } impl Display for DesktopEnv { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let desktop_env = match self { DesktopEnv::Gnome => "Gnome", DesktopEnv::Windows => "Windows", DesktopEnv::Lxde => "LXDE", DesktopEnv::Openbox => "Openbox", DesktopEnv::Mate => "Mate", DesktopEnv::Xfce => "XFCE", DesktopEnv::Kde => "KDE", DesktopEnv::Cinnamon => "Cinnamon", DesktopEnv::I3 => "I3", DesktopEnv::Aqua => "Aqua", DesktopEnv::Ios => "IOS", DesktopEnv::Android => "Android", DesktopEnv::WebBrowser => "Web Browser", DesktopEnv::Console => "Console", DesktopEnv::Ubuntu => "Ubuntu", DesktopEnv::Ermine => "Ermine", DesktopEnv::Orbital => "Orbital", DesktopEnv::Unknown(a) => { f.write_str("Unknown: ")?; a } }; f.write_str(desktop_env) } } /// The underlying platform for a system #[allow(missing_docs)] #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Platform { Linux, Bsd, Windows, // FIXME: Non-standard casing; Rename to 'Mac' rather than 'MacOs' in // whoami 2.0.0 MacOS, Illumos, Ios, Android, Nintendo, Xbox, PlayStation, Fuchsia, Redox, Unknown(String), } impl Display for Platform { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let platform = match self { Platform::Linux => "Linux", Platform::Bsd => "BSD", Platform::Windows => "Windows", Platform::MacOS => "Mac OS", Platform::Illumos => "Illumos", Platform::Ios => "iOS", Platform::Android => "Android", Platform::Nintendo => "Nintendo", Platform::Xbox => "XBox", Platform::PlayStation => "PlayStation", Platform::Fuchsia => "Fuchsia", Platform::Redox => "Redox", Platform::Unknown(a) => { f.write_str("Unknown: ")?; a } }; f.write_str(platform) } } /// The architecture of a CPU #[non_exhaustive] #[derive(Debug, PartialEq, Eq, Clone)] pub enum Arch { /// ARMv5 ArmV5, /// ARMv6 (Sometimes just referred to as ARM) ArmV6, /// ARMv7 (May or may not support Neon/Thumb) ArmV7, /// ARM64 (aarch64) Arm64, /// i386 (x86) I386, /// i586 (x86) I586, /// i686 (x86) I686, /// X86_64 / Amd64 X64, /// MIPS Mips, /// MIPS (LE) MipsEl, /// MIPS64 Mips64, /// MIPS64 (LE) Mips64El, /// PowerPC PowerPc, /// PowerPC64 PowerPc64, /// PowerPC64LE PowerPc64Le, /// 32-bit RISC-V Riscv32, /// 64-bit RISC-V Riscv64, /// S390x S390x, /// SPARC Sparc, /// SPARC64 Sparc64, /// 32-bit Web Assembly Wasm32, /// 64-bit Web Assembly Wasm64, /// Unknown Architecture Unknown(String), } impl Display for Arch { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let arch = match self { Arch::ArmV5 => "armv5", Arch::ArmV6 => "armv6", Arch::ArmV7 => "armv7", Arch::Arm64 => "arm64", Arch::I386 => "i386", Arch::I586 => "i586", Arch::I686 => "i686", Arch::Mips => "mips", Arch::MipsEl => "mipsel", Arch::Mips64 => "mips64", Arch::Mips64El => "mips64el", Arch::PowerPc => "powerpc", Arch::PowerPc64 => "powerpc64", Arch::PowerPc64Le => "powerpc64le", Arch::Riscv32 => "riscv32", Arch::Riscv64 => "riscv64", Arch::S390x => "s390x", Arch::Sparc => "sparc", Arch::Sparc64 => "sparc64", Arch::Wasm32 => "wasm32", Arch::Wasm64 => "wasm64", Arch::X64 => "x86_64", Arch::Unknown(arch) => { f.write_str("Unknown: ")?; arch } }; f.write_str(arch) } } /// The address width of a CPU architecture #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[non_exhaustive] pub enum Width { /// 32 bits Bits32, /// 64 bits Bits64, } impl Display for Width { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let bits = match self { Width::Bits32 => "32 bits", Width::Bits64 => "64 bits", }; f.write_str(bits) } } impl Arch { /// Get the width of this architecture. pub fn width(&self) -> Result { match self { Arch::ArmV5 | Arch::ArmV6 | Arch::ArmV7 | Arch::I386 | Arch::I586 | Arch::I686 | Arch::Mips | Arch::MipsEl | Arch::PowerPc | Arch::Riscv32 | Arch::Sparc | Arch::Wasm32 => Ok(Width::Bits32), Arch::Arm64 | Arch::Mips64 | Arch::Mips64El | Arch::PowerPc64 | Arch::PowerPc64Le | Arch::Riscv64 | Arch::S390x | Arch::Sparc64 | Arch::Wasm64 | Arch::X64 => Ok(Width::Bits64), Arch::Unknown(unknown_arch) => Err(Error::new( ErrorKind::InvalidData, format!( "Tried getting width of unknown arch ({})", unknown_arch ), )), } } } /// Get the CPU Architecture. #[inline(always)] pub fn arch() -> Arch { platform::arch() } /// Get the user's username. /// /// On unix-systems this differs from [`realname()`] most notably in that spaces /// are not allowed. #[inline(always)] pub fn username() -> String { platform::username() } /// Get the user's username. /// /// On unix-systems this differs from [`realname()`] most notably in that spaces /// are not allowed. #[inline(always)] pub fn username_os() -> OsString { platform::username_os() } /// Get the user's real (full) name. #[inline(always)] pub fn realname() -> String { platform::realname() } /// Get the user's real (full) name. #[inline(always)] pub fn realname_os() -> OsString { platform::realname_os() } /// Get the device name (also known as "Pretty Name"). /// /// Often used to identify device for bluetooth pairing. #[inline(always)] pub fn devicename() -> String { platform::devicename() } /// Get the device name (also known as "Pretty Name"). /// /// Often used to identify device for bluetooth pairing. #[inline(always)] pub fn devicename_os() -> OsString { platform::devicename_os() } /// Get the host device's hostname. /// /// Limited to a-z (case insensitve), 0-9, and dashes. This limit also applies /// to `devicename()` when targeting Windows. Since the hostname is /// case-insensitive, this method normalizes to lowercase (unlike /// [`devicename()`]). #[inline(always)] pub fn hostname() -> String { let mut hostname = platform::hostname(); hostname.make_ascii_lowercase(); hostname } /// Get the host device's hostname. /// /// Limited to a-z (case insensitve), 0-9, and dashes. This limit also applies /// to `devicename()` when targeting Windows. Since the hostname is /// case-insensitive, this method normalizes to lowercase (unlike /// [`devicename()`]). #[inline(always)] pub fn hostname_os() -> OsString { hostname().into() } /// Get the name of the operating system distribution and (possibly) version. /// /// Example: "Windows 10" or "Fedora 26 (Workstation Edition)" #[inline(always)] pub fn distro() -> String { platform::distro().unwrap_or_else(|| format!("Unknown {}", platform())) } /// Get the name of the operating system distribution and (possibly) version. /// /// Example: "Windows 10" or "Fedora 26 (Workstation Edition)" #[inline(always)] pub fn distro_os() -> OsString { platform::distro_os() .unwrap_or_else(|| format!("Unknown {}", platform()).into()) } /// Get the desktop environment. /// /// Example: "gnome" or "windows" #[inline(always)] pub fn desktop_env() -> DesktopEnv { platform::desktop_env() } /// Get the platform. #[inline(always)] pub fn platform() -> Platform { platform::platform() } /// Get the user's preferred language(s). /// /// Returned as iterator of two letter language codes (lowercase), optionally /// followed by a dash (-) and a two letter region code (uppercase). The most /// preferred language is returned first, followed by next preferred, and so on. #[inline(always)] pub fn lang() -> impl Iterator { platform::lang() } whoami-1.4.1/src/unix.rs010064400017500001750000000445041444620007700133550ustar0000000000000000use std::{ borrow::Cow, ffi::{c_void, CStr, OsString}, mem, os::{ raw::{c_char, c_int}, unix::ffi::OsStringExt, }, }; #[cfg(target_os = "macos")] use std::{ os::{ raw::{c_long, c_uchar}, unix::ffi::OsStrExt, }, ptr::null_mut, }; use crate::{Arch, DesktopEnv, Platform}; #[repr(C)] struct PassWd { pw_name: *const c_void, pw_passwd: *const c_void, pw_uid: u32, pw_gid: u32, #[cfg(any( target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", target_os = "openbsd", target_os = "netbsd" ))] pw_change: isize, #[cfg(any( target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", target_os = "openbsd", target_os = "netbsd" ))] pw_class: *const c_void, pw_gecos: *const c_void, pw_dir: *const c_void, pw_shell: *const c_void, #[cfg(any( target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", target_os = "openbsd", target_os = "netbsd" ))] pw_expire: isize, #[cfg(any( target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", target_os = "openbsd", target_os = "netbsd" ))] pw_fields: i32, } extern "system" { fn getpwuid_r( uid: u32, pwd: *mut PassWd, buf: *mut c_void, buflen: usize, result: *mut *mut PassWd, ) -> i32; fn geteuid() -> u32; fn gethostname(name: *mut c_void, len: usize) -> i32; } #[cfg(target_os = "macos")] #[link(name = "CoreFoundation", kind = "framework")] #[link(name = "SystemConfiguration", kind = "framework")] extern "system" { fn CFStringGetCString( the_string: *mut c_void, buffer: *mut u8, buffer_size: c_long, encoding: u32, ) -> c_uchar; fn CFStringGetLength(the_string: *mut c_void) -> c_long; fn CFStringGetMaximumSizeForEncoding( length: c_long, encoding: u32, ) -> c_long; fn SCDynamicStoreCopyComputerName( store: *mut c_void, encoding: *mut u32, ) -> *mut c_void; fn CFRelease(cf: *const c_void); } unsafe fn strlen(cs: *const c_void) -> usize { let mut len = 0; let mut cs: *const u8 = cs.cast(); while *cs != 0 { len += 1; cs = cs.offset(1); } len } unsafe fn strlen_gecos(cs: *const c_void) -> usize { let mut len = 0; let mut cs: *const u8 = cs.cast(); while *cs != 0 && *cs != b',' { len += 1; cs = cs.offset(1); } len } // Convert an OsString into a String fn string_from_os(string: OsString) -> String { match string.into_string() { Ok(string) => string, Err(string) => string.to_string_lossy().to_string(), } } fn os_from_cstring_gecos(string: *const c_void) -> Option { if string.is_null() { return None; } // Get a byte slice of the c string. let slice = unsafe { let length = strlen_gecos(string); if length == 0 { return None; } std::slice::from_raw_parts(string as *const u8, length) }; // Turn byte slice into Rust String. Some(OsString::from_vec(slice.to_vec())) } fn os_from_cstring(string: *const c_void) -> OsString { if string.is_null() { return "".to_string().into(); } // Get a byte slice of the c string. let slice = unsafe { let length = strlen(string); std::slice::from_raw_parts(string as *const u8, length) }; // Turn byte slice into Rust String. OsString::from_vec(slice.to_vec()) } #[cfg(target_os = "macos")] fn os_from_cfstring(string: *mut c_void) -> OsString { if string.is_null() { return "".to_string().into(); } unsafe { let len = CFStringGetLength(string); let capacity = CFStringGetMaximumSizeForEncoding(len, 134_217_984 /* UTF8 */) + 1; let mut out = Vec::with_capacity(capacity as usize); if CFStringGetCString( string, out.as_mut_ptr(), capacity, 134_217_984, /* UTF8 */ ) != 0 { out.set_len(strlen(out.as_ptr().cast())); // Remove trailing NUL byte out.shrink_to_fit(); CFRelease(string); OsString::from_vec(out) } else { CFRelease(string); "".to_string().into() } } } // This function must allocate, because a slice or Cow would still // reference `passwd` which is dropped when this function returns. #[inline(always)] fn getpwuid(real: bool) -> OsString { const BUF_SIZE: usize = 16_384; // size from the man page let mut buffer = mem::MaybeUninit::<[u8; BUF_SIZE]>::uninit(); let mut passwd = mem::MaybeUninit::::uninit(); let mut _passwd = mem::MaybeUninit::<*mut PassWd>::uninit(); // Get PassWd `struct`. let passwd = unsafe { let ret = getpwuid_r( geteuid(), passwd.as_mut_ptr(), buffer.as_mut_ptr() as *mut c_void, BUF_SIZE, _passwd.as_mut_ptr(), ); if ret != 0 { return "Unknown".to_string().into(); } let _passwd = _passwd.assume_init(); if _passwd.is_null() { return "Unknown".to_string().into(); } passwd.assume_init() }; // Extract names. if real { let string = os_from_cstring_gecos(passwd.pw_gecos); let result = if let Some(string) = string { Ok(string) } else { Err(os_from_cstring(passwd.pw_name)) }; fancy_fallback_os(result) } else { os_from_cstring(passwd.pw_name) } } pub(crate) fn username() -> String { string_from_os(username_os()) } pub(crate) fn username_os() -> OsString { getpwuid(false) } fn fancy_fallback(result: Result<&str, String>) -> String { let mut cap = true; let iter = match result { Ok(a) => a.chars(), Err(ref b) => b.chars(), }; let mut new = String::new(); for c in iter { match c { '.' | '-' | '_' => { new.push(' '); cap = true; } a => { if cap { cap = false; for i in a.to_uppercase() { new.push(i); } } else { new.push(a); } } } } new } fn fancy_fallback_os(result: Result) -> OsString { match result { Ok(success) => success, Err(fallback) => { let cs = match fallback.to_str() { Some(a) => Ok(a), None => Err(fallback.to_string_lossy().to_string()), }; fancy_fallback(cs).into() } } } pub(crate) fn realname() -> String { string_from_os(realname_os()) } pub(crate) fn realname_os() -> OsString { getpwuid(true) } #[cfg(not(target_os = "macos"))] pub(crate) fn devicename_os() -> OsString { devicename().into() } #[cfg(not(any(target_os = "macos", target_os = "illumos")))] pub(crate) fn devicename() -> String { let mut distro = String::new(); if let Ok(program) = std::fs::read_to_string("/etc/machine-info") { let program = program.into_bytes(); distro.push_str(&String::from_utf8_lossy(&program)); for i in distro.split('\n') { let mut j = i.split('='); if j.next() == Some("PRETTY_HOSTNAME") { if let Some(value) = j.next() { return value.trim_matches('"').to_string(); } } } } fancy_fallback(Err(hostname())) } #[cfg(target_os = "macos")] pub(crate) fn devicename() -> String { string_from_os(devicename_os()) } #[cfg(target_os = "macos")] pub(crate) fn devicename_os() -> OsString { let out = os_from_cfstring(unsafe { SCDynamicStoreCopyComputerName(null_mut(), null_mut()) }); let computer = if out.as_bytes().is_empty() { Err(hostname_os()) } else { Ok(out) }; fancy_fallback_os(computer) } #[cfg(target_os = "illumos")] pub(crate) fn devicename() -> String { let mut nodename = String::new(); if let Ok(program) = std::fs::read_to_string("/etc/nodename") { let program = program.into_bytes(); nodename.push_str(&String::from_utf8_lossy(&program)); nodename.pop(); // Remove the trailing newline } fancy_fallback(Err(hostname())) } pub(crate) fn hostname() -> String { string_from_os(hostname_os()) } fn hostname_os() -> OsString { // Maximum hostname length = 255, plus a NULL byte. let mut string = Vec::::with_capacity(256); unsafe { gethostname(string.as_mut_ptr() as *mut c_void, 255); string.set_len(strlen(string.as_ptr() as *const c_void)); }; OsString::from_vec(string) } #[cfg(target_os = "macos")] fn distro_xml(data: String) -> Option { let mut product_name = None; let mut user_visible_version = None; if let Some(start) = data.find("") { if let Some(end) = data.find("") { let mut set_product_name = false; let mut set_user_visible_version = false; for line in data[start + "".len()..end].lines() { let line = line.trim(); if line.starts_with("") { match line["".len()..].trim_end_matches("") { "ProductName" => set_product_name = true, "ProductUserVisibleVersion" => { set_user_visible_version = true } "ProductVersion" => { if user_visible_version.is_none() { set_user_visible_version = true } } _ => {} } } else if line.starts_with("") { if set_product_name { product_name = Some( line["".len()..] .trim_end_matches(""), ); set_product_name = false; } else if set_user_visible_version { user_visible_version = Some( line["".len()..] .trim_end_matches(""), ); set_user_visible_version = false; } } } } } if let Some(product_name) = product_name { if let Some(user_visible_version) = user_visible_version { Some(format!("{} {}", product_name, user_visible_version)) } else { Some(product_name.to_string()) } } else { user_visible_version.map(|v| format!("Mac OS (Unknown) {}", v)) } } #[cfg(target_os = "macos")] pub(crate) fn distro_os() -> Option { distro().map(|a| a.into()) } #[cfg(target_os = "macos")] pub(crate) fn distro() -> Option { if let Ok(data) = std::fs::read_to_string( "/System/Library/CoreServices/ServerVersion.plist", ) { distro_xml(data) } else if let Ok(data) = std::fs::read_to_string( "/System/Library/CoreServices/SystemVersion.plist", ) { distro_xml(data) } else { None } } #[cfg(not(target_os = "macos"))] pub(crate) fn distro_os() -> Option { distro().map(|a| a.into()) } #[cfg(not(target_os = "macos"))] pub(crate) fn distro() -> Option { let mut distro = String::new(); let program = std::fs::read_to_string("/etc/os-release") .ok()? .into_bytes(); distro.push_str(&String::from_utf8_lossy(&program)); let mut fallback = None; for i in distro.split('\n') { let mut j = i.split('='); match j.next()? { "PRETTY_NAME" => { return Some(j.next()?.trim_matches('"').to_string()); } "NAME" => fallback = Some(j.next()?.trim_matches('"').to_string()), _ => {} } } fallback } #[cfg(target_os = "macos")] #[inline(always)] pub(crate) const fn desktop_env() -> DesktopEnv { DesktopEnv::Aqua } #[cfg(not(target_os = "macos"))] #[inline(always)] pub(crate) fn desktop_env() -> DesktopEnv { match std::env::var_os("DESKTOP_SESSION") .map(|env| env.to_string_lossy().to_string()) { Some(env_orig) => { let env = env_orig.to_uppercase(); if env.contains("GNOME") { DesktopEnv::Gnome } else if env.contains("LXDE") { DesktopEnv::Lxde } else if env.contains("OPENBOX") { DesktopEnv::Openbox } else if env.contains("I3") { DesktopEnv::I3 } else if env.contains("UBUNTU") { DesktopEnv::Ubuntu } else if env.contains("PLASMA5") { DesktopEnv::Kde } else { DesktopEnv::Unknown(env_orig) } } // TODO: Other Linux Desktop Environments None => DesktopEnv::Unknown("Unknown".to_string()), } } #[cfg(target_os = "macos")] #[inline(always)] pub(crate) const fn platform() -> Platform { Platform::MacOS } #[cfg(not(any( target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", target_os = "openbsd", target_os = "netbsd", target_os = "illumos" )))] #[inline(always)] pub(crate) const fn platform() -> Platform { Platform::Linux } #[cfg(any( target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", target_os = "openbsd", target_os = "netbsd" ))] #[inline(always)] pub(crate) const fn platform() -> Platform { Platform::Bsd } #[cfg(target_os = "illumos")] #[inline(always)] pub(crate) const fn platform() -> Platform { Platform::Illumos } struct LangIter { array: String, index: Option, } impl Iterator for LangIter { type Item = String; fn next(&mut self) -> Option { if self.index? && self.array.contains('-') { self.index = Some(false); let mut temp = self.array.split('-').next()?.to_string(); mem::swap(&mut temp, &mut self.array); Some(temp) } else { self.index = None; let mut temp = String::new(); mem::swap(&mut temp, &mut self.array); Some(temp) } } } #[inline(always)] pub(crate) fn lang() -> impl Iterator { const DEFAULT_LANG: &str = "en_US"; let array = std::env::var("LANG") .unwrap_or_default() .split('.') .next() .unwrap_or(DEFAULT_LANG) .to_string(); let array = if array == "C" { DEFAULT_LANG.to_string() } else { array }; LangIter { array: array.replace('_', "-"), index: Some(true), } } #[repr(C)] #[cfg(any( target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd", target_os = "illumos" ))] struct UtsName { #[cfg(not(target_os = "dragonfly"))] sysname: [c_char; 256], #[cfg(target_os = "dragonfly")] sysname: [c_char; 32], #[cfg(not(target_os = "dragonfly"))] nodename: [c_char; 256], #[cfg(target_os = "dragonfly")] nodename: [c_char; 32], #[cfg(not(target_os = "dragonfly"))] release: [c_char; 256], #[cfg(target_os = "dragonfly")] release: [c_char; 32], #[cfg(not(target_os = "dragonfly"))] version: [c_char; 256], #[cfg(target_os = "dragonfly")] version: [c_char; 32], #[cfg(not(target_os = "dragonfly"))] machine: [c_char; 256], #[cfg(target_os = "dragonfly")] machine: [c_char; 32], } #[repr(C)] #[cfg(any( target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "redox" ))] struct UtsName { sysname: [c_char; 65], nodename: [c_char; 65], release: [c_char; 65], version: [c_char; 65], machine: [c_char; 65], domainname: [c_char; 65], } // Buffer initialization impl Default for UtsName { fn default() -> Self { unsafe { mem::zeroed() } } } extern "C" { #[cfg(not(target_os = "freebsd"))] fn uname(buf: *mut UtsName) -> c_int; #[cfg(target_os = "freebsd")] fn __xuname(nmln: c_int, buf: *mut c_void) -> c_int; } #[inline] #[cfg(target_os = "freebsd")] unsafe extern "C" fn uname(buf: *mut UtsName) -> c_int { __xuname(256, buf.cast()) } impl Arch { fn from_str(s: Cow<'_, str>) -> Self { let arch_str = match s { Cow::Borrowed(str) => str, Cow::Owned(ref str) => str, }; match arch_str { "aarch64" | "arm64" | "aarch64_be" | "armv8b" | "armv8l" => { Arch::Arm64 } "armv5" => Arch::ArmV5, "armv6" | "arm" => Arch::ArmV6, "armv7" => Arch::ArmV7, "i386" => Arch::I386, "i586" => Arch::I586, "i686" | "i686-AT386" => Arch::I686, "mips" => Arch::Mips, "mipsel" => Arch::MipsEl, "mips64" => Arch::Mips64, "mips64el" => Arch::Mips64El, "powerpc" | "ppc" | "ppcle" => Arch::PowerPc, "powerpc64" | "ppc64" | "ppc64le" => Arch::PowerPc64, "powerpc64le" => Arch::PowerPc64Le, "riscv32" => Arch::Riscv32, "riscv64" => Arch::Riscv64, "s390x" => Arch::S390x, "sparc" => Arch::Sparc, "sparc64" => Arch::Sparc64, "wasm32" => Arch::Wasm32, "wasm64" => Arch::Wasm64, "x86_64" | "amd64" => Arch::X64, _ => Arch::Unknown(arch_str.to_owned()), } } } pub(crate) fn arch() -> Arch { let mut buf = UtsName::default(); let result = unsafe { uname(&mut buf) }; if result == -1 { return Arch::Unknown("uname(2) failed to execute".to_owned()); } let arch_str = unsafe { CStr::from_ptr(buf.machine.as_ptr()) }.to_string_lossy(); Arch::from_str(arch_str) } whoami-1.4.1/src/web.rs010064400017500001750000000141101444620007700131350ustar0000000000000000#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] compile_error!("Unexpected pointer width for target platform"); use std::ffi::OsString; use wasm_bindgen::JsValue; use web_sys::window; use crate::{Arch, DesktopEnv, Platform}; // Get the user agent fn user_agent() -> Option { window()?.navigator().user_agent().ok() } // Get the document domain fn document_domain() -> Option { window()?.document()?.location()?.hostname().ok() } struct LangIter { array: Vec, index: usize, } impl Iterator for LangIter { type Item = String; fn next(&mut self) -> Option { if let Some(value) = self.array.get(self.index) { self.index += 1; if let Some(lang) = value.as_string() { Some(lang) } else { self.next() } } else { None } } } #[inline(always)] pub(crate) fn lang() -> impl Iterator { let array = if let Some(window) = window() { window.navigator().languages().to_vec() } else { Vec::new() }; let index = 0; LangIter { array, index } } #[inline(always)] pub(crate) fn username_os() -> OsString { username().into() } #[inline(always)] pub(crate) fn realname_os() -> OsString { realname().into() } #[inline(always)] pub(crate) fn devicename_os() -> OsString { devicename().into() } #[inline(always)] pub(crate) fn distro_os() -> Option { distro().map(|a| a.into()) } #[inline(always)] pub(crate) fn username() -> String { "anonymous".to_string() } #[inline(always)] pub(crate) fn realname() -> String { "Anonymous".to_string() } pub(crate) fn devicename() -> String { let orig_string = user_agent().unwrap_or_default(); let start = if let Some(s) = orig_string.rfind(' ') { s } else { return "Unknown Browser".to_string(); }; let string = orig_string .get(start + 1..) .unwrap_or("Unknown Browser") .replace('/', " "); if let Some(s) = string.rfind("Safari") { if let Some(s) = orig_string.rfind("Chrome") { if let Some(e) = orig_string.get(s..).unwrap_or_default().find(' ') { orig_string .get(s..) .unwrap_or("Chrome") .get(..e) .unwrap_or("Chrome") .replace('/', " ") } else { "Chrome".to_string() } } else if orig_string.contains("Linux") { "GNOME Web".to_string() } else { string.get(s..).unwrap_or("Safari").replace('/', " ") } } else if string.contains("Edg ") { string.replace("Edg ", "Edge ") } else if string.contains("OPR ") { string.replace("OPR ", "Opera ") } else { string } } #[inline(always)] pub(crate) fn hostname() -> String { document_domain() .filter(|x| !x.is_empty()) .unwrap_or_else(|| "localhost".to_string()) } pub(crate) fn distro() -> Option { let string = user_agent()?; let begin = string.find('(')?; let end = string.find(')')?; let string = &string[begin + 1..end]; if string.contains("Win32") || string.contains("Win64") { let begin = if let Some(b) = string.find("NT") { b } else { return Some("Windows".to_string()); }; let end = if let Some(e) = string.find('.') { e } else { return Some("Windows".to_string()); }; let string = &string[begin + 3..end]; Some(format!("Windows {}", string)) } else if string.contains("Linux") { let string = if string.contains("X11") || string.contains("Wayland") { let begin = if let Some(b) = string.find(';') { b } else { return Some("Unknown Linux".to_string()); }; &string[begin + 2..] } else { string }; if string.starts_with("Linux") { Some("Unknown Linux".to_string()) } else { let end = if let Some(e) = string.find(';') { e } else { return Some("Unknown Linux".to_string()); }; Some(string[..end].to_string()) } } else if let Some(begin) = string.find("Mac OS X") { Some(if let Some(end) = string[begin..].find(';') { string[begin..begin + end].to_string() } else { string[begin..].to_string().replace('_', ".") }) } else { // TODO: // Platform::FreeBsd, // Platform::Ios, // Platform::Android, // Platform::Nintendo, // Platform::Xbox, // Platform::PlayStation, // Platform::Dive, // Platform::Fuchsia, // Platform::Redox Some(string.to_string()) } } pub(crate) const fn desktop_env() -> DesktopEnv { DesktopEnv::WebBrowser } pub(crate) fn platform() -> Platform { let string = user_agent().unwrap_or_default(); let begin = if let Some(b) = string.find('(') { b } else { return Platform::Unknown("Unknown".to_string()); }; let end = if let Some(e) = string.find(')') { e } else { return Platform::Unknown("Unknown".to_string()); }; let string = &string[begin + 1..end]; if string.contains("Win32") || string.contains("Win64") { Platform::Windows } else if string.contains("Linux") { Platform::Linux } else if string.contains("Mac OS X") { Platform::MacOS } else { // TODO: // Platform::FreeBsd, // Platform::Ios, // Platform::Android, // Platform::Nintendo, // Platform::Xbox, // Platform::PlayStation, // Platform::Dive, // Platform::Fuchsia, // Platform::Redox, Platform::Unknown(string.to_string()) } } pub(crate) fn arch() -> Arch { if cfg!(target_pointer_width = "64") { Arch::Wasm64 } else { Arch::Wasm32 } } whoami-1.4.1/src/windows.rs010064400017500001750000000304761444620007700140670ustar0000000000000000use std::{ convert::TryInto, ffi::OsString, mem::MaybeUninit, os::{ raw::{c_char, c_int, c_uchar, c_ulong, c_ushort, c_void}, windows::ffi::OsStringExt, }, ptr, }; use crate::{Arch, DesktopEnv, Platform}; #[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, } // Source: // https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info#syntax #[repr(C)] struct SystemInfo { processor_architecture: c_ushort, reserved: c_ushort, dw_page_size: c_ulong, minimum_application_address: *mut c_void, maximum_application_address: *mut c_void, active_processor_mask: usize, number_of_processors: c_ulong, processor_type: c_ulong, allocation_granularity: c_ulong, processor_level: c_ushort, processor_revision: c_ushort, } #[allow(unused)] #[repr(C)] enum ExtendedNameFormat { Unknown, // Nothing FullyQualifiedDN, // Nothing SamCompatible, // Hostname Followed By Username Display, // Full Name UniqueId, // Nothing Canonical, // Nothing UserPrincipal, // Nothing CanonicalEx, // Nothing ServicePrincipal, // Nothing DnsDomain, // Nothing GivenName, // Nothing Surname, // Nothing } #[allow(unused)] #[repr(C)] enum ComputerNameFormat { NetBIOS, // Same as GetComputerNameW DnsHostname, // Fancy Name DnsDomain, // Nothing DnsFullyQualified, // Fancy Name with, for example, .com PhysicalNetBIOS, // Same as GetComputerNameW PhysicalDnsHostname, // Same as GetComputerNameW PhysicalDnsDomain, // Nothing PhysicalDnsFullyQualified, // Fancy Name with, for example, .com 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 = "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 GetNativeSystemInfo(system_info: *mut SystemInfo); } // Convert an OsString into a String fn string_from_os(string: OsString) -> String { match string.into_string() { Ok(string) => string, Err(string) => string.to_string_lossy().to_string(), } } pub(crate) fn username() -> String { string_from_os(username_os()) } pub(crate) fn username_os() -> OsString { // Step 1. Retreive the entire length of the username let mut size = 0; let success = unsafe { // Ignore error, we know that it will be ERROR_INSUFFICIENT_BUFFER GetUserNameW(ptr::null_mut(), &mut size) == 0 }; assert!(success); // Step 2. Allocate memory to put the Windows (UTF-16) string. let mut name: Vec = Vec::with_capacity(size.try_into().unwrap_or(std::usize::MAX)); size = name.capacity().try_into().unwrap_or(std::u32::MAX); let orig_size = size; let fail = unsafe { GetUserNameW(name.as_mut_ptr().cast(), &mut size) == 0 }; if fail { return "unknown".to_string().into(); } debug_assert_eq!(orig_size, size); unsafe { name.set_len(size.try_into().unwrap_or(std::usize::MAX)); } let terminator = name.pop(); // Remove Trailing Null debug_assert_eq!(terminator, Some(0u16)); // Step 3. Convert to Rust String OsString::from_wide(&name) } #[inline(always)] pub(crate) fn realname() -> String { string_from_os(realname_os()) } #[inline(always)] pub(crate) fn realname_os() -> OsString { // Step 1. Retrieve the entire length of the username let mut buf_size = 0; let success = unsafe { GetUserNameExW( ExtendedNameFormat::Display, ptr::null_mut(), &mut buf_size, ) == 0 }; assert!(success); match unsafe { GetLastError() } { 0x00EA /* more data */ => { /* Success, continue */ } _ /* network error or none mapped */ => { // Fallback to username return username_os(); } } // Step 2. Allocate memory to put the Windows (UTF-16) string. let mut name: Vec = Vec::with_capacity(buf_size.try_into().unwrap_or(std::usize::MAX)); let mut name_len = name.capacity().try_into().unwrap_or(std::u32::MAX); let fail = unsafe { GetUserNameExW( ExtendedNameFormat::Display, name.as_mut_ptr().cast(), &mut name_len, ) == 0 }; if fail { return "Unknown".to_string().into(); } debug_assert_eq!(buf_size, name_len + 1); unsafe { name.set_len(name_len.try_into().unwrap_or(std::usize::MAX)); } // Step 3. Convert to Rust String OsString::from_wide(&name) } #[inline(always)] pub(crate) fn devicename() -> String { string_from_os(devicename_os()) } #[inline(always)] pub(crate) fn devicename_os() -> OsString { // Step 1. Retreive the entire length of the device name let mut size = 0; let success = unsafe { // Ignore error, we know that it will be ERROR_INSUFFICIENT_BUFFER GetComputerNameExW( ComputerNameFormat::DnsHostname, ptr::null_mut(), &mut size, ) == 0 }; assert!(success); // Step 2. Allocate memory to put the Windows (UTF-16) string. let mut name: Vec = Vec::with_capacity(size.try_into().unwrap_or(std::usize::MAX)); size = name.capacity().try_into().unwrap_or(std::u32::MAX); let fail = unsafe { GetComputerNameExW( ComputerNameFormat::DnsHostname, name.as_mut_ptr().cast(), &mut size, ) == 0 }; if fail { return "Unknown".to_string().into(); } unsafe { name.set_len(size.try_into().unwrap_or(std::usize::MAX)); } // Step 3. Convert to Rust String OsString::from_wide(&name) } pub(crate) fn hostname() -> String { string_from_os(hostname_os()) } fn hostname_os() -> OsString { // Step 1. Retreive the entire length of the username let mut size = 0; let fail = unsafe { // Ignore error, we know that it will be ERROR_INSUFFICIENT_BUFFER GetComputerNameExW( ComputerNameFormat::NetBIOS, ptr::null_mut(), &mut size, ) == 0 }; debug_assert!(fail); // Step 2. Allocate memory to put the Windows (UTF-16) string. let mut name: Vec = Vec::with_capacity(size.try_into().unwrap_or(std::usize::MAX)); size = name.capacity().try_into().unwrap_or(std::u32::MAX); let fail = unsafe { GetComputerNameExW( ComputerNameFormat::NetBIOS, name.as_mut_ptr().cast(), &mut size, ) == 0 }; if fail { return "localhost".to_string().into(); } unsafe { name.set_len(size.try_into().unwrap_or(std::usize::MAX)); } // Step 3. Convert to Rust String OsString::from_wide(&name) } pub(crate) fn distro_os() -> Option { distro().map(|a| a.into()) } pub(crate) fn distro() -> Option { // Due to MingW Limitations, we must dynamically load ntdll.dll extern "system" { fn LoadLibraryExW( filename: *mut u16, hfile: isize, dwflags: u32, ) -> isize; fn FreeLibrary(hmodule: isize) -> i32; fn GetProcAddress(hmodule: isize, procname: *mut u8) -> *mut c_void; } let mut path = "ntdll.dll\0".encode_utf16().collect::>(); let path = path.as_mut_ptr(); let inst = unsafe { LoadLibraryExW(path, 0, 0x0000_0800) }; let mut path = "RtlGetVersion\0".bytes().collect::>(); let path = path.as_mut_ptr(); let func = unsafe { GetProcAddress(inst, path) }; if func.is_null() { return Some("Windows (Unknown)".to_string()); } let get_version: unsafe extern "system" fn(a: *mut OsVersionInfoEx) -> u32 = unsafe { std::mem::transmute(func) }; let mut version = MaybeUninit::::zeroed(); let version = unsafe { (*version.as_mut_ptr()).os_version_info_size = std::mem::size_of::() as u32; get_version(version.as_mut_ptr()); FreeLibrary(inst); 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(crate) const fn desktop_env() -> DesktopEnv { DesktopEnv::Windows } #[inline(always)] pub(crate) const fn platform() -> Platform { Platform::Windows } struct LangIter { array: Vec, index: usize, } impl Iterator for LangIter { type Item = String; fn next(&mut self) -> Option { if let Some(value) = self.array.get(self.index) { self.index += 1; Some(value.to_string()) } else { None } } } #[inline(always)] pub(crate) fn lang() -> impl Iterator { let mut num_languages = 0; let mut buffer_size = 0; let mut buffer; unsafe { assert_ne!( GetUserPreferredUILanguages( 0x08, /* MUI_LANGUAGE_NAME */ &mut num_languages, std::ptr::null_mut(), // List of languages. &mut buffer_size, ), 0 ); buffer = Vec::with_capacity(buffer_size as usize); assert_ne!( GetUserPreferredUILanguages( 0x08, /* MUI_LANGUAGE_NAME */ &mut num_languages, buffer.as_mut_ptr(), // List of languages. &mut buffer_size, ), 0 ); buffer.set_len(buffer_size as usize); } // We know it ends in two null characters. 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 } } pub(crate) fn arch() -> Arch { fn proc(processor_type: c_ulong) -> Result { Ok(match processor_type { // PROCESSOR_INTEL_386 386 => Arch::I386, // PROCESSOR_INTEL_486 486 => Arch::Unknown("I486".to_string()), // PROCESSOR_INTEL_PENTIUM 586 => Arch::I586, // PROCESSOR_INTEL_IA64 2200 => Arch::Unknown("IA64".to_string()), // PROCESSOR_AMD_X8664 8664 => Arch::X64, v => return Err(v), }) } let buf: SystemInfo = unsafe { let mut buf = MaybeUninit::uninit(); GetNativeSystemInfo(buf.as_mut_ptr()); buf.assume_init() }; // Supported architectures, source: // https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info#members match buf.processor_architecture { // PROCESSOR_ARCHITECTURE_INTEL 0 => Arch::I686, // PROCESSOR_ARCHITECTURE_ARM 5 => Arch::ArmV6, // PROCESSOR_ARCHITECTURE_IA64 6 => Arch::Unknown("IA64".to_string()), // PROCESSOR_ARCHITECTURE_AMD64 9 => Arch::X64, // PROCESSOR_ARCHITECTURE_ARM64 12 => Arch::Arm64, // PROCESSOR_ARCHITECTURE_UNKNOWN 0xFFFF => proc(buf.processor_type) .unwrap_or_else(|e| Arch::Unknown(format!("Unknown ({})", e))), invalid => proc(buf.processor_type).unwrap_or_else(|e| { Arch::Unknown(format!("Invalid arch: {} ({})", invalid, e)) }), } } whoami-1.4.1/.cargo_vcs_info.json0000644000000001121444620077100124200ustar00{ "git": { "sha1": "e4fa92e708916d1ca6edfebdcd8145f1178e38d2" } } whoami-1.4.1/Cargo.lock0000644000000071641444620077100104110ustar00# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "bumpalo" version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "js-sys" version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "proc-macro2" version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "wasm-bindgen" version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" dependencies = [ "bumpalo", "lazy_static", "log", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" [[package]] name = "web-sys" version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "whoami" version = "1.4.1" dependencies = [ "wasm-bindgen", "web-sys", ]