install-framework-base 1.0.0

[Install Framework] Official generic interface implementation
Documentation
// Copyright 2021 Yuri6037

// 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.

use std::vec::Vec;
use std::ptr::null_mut;
use std::ptr::null;
use std::ffi::OsString;
use std::ffi::OsStr;
use std::os::windows::ffi::OsStringExt;
use std::os::windows::ffi::OsStrExt;
use winapi::um::winreg::RegOpenKeyExW;
use winapi::shared::minwindef::HKEY;
use winapi::um::winnt::KEY_READ;
use winapi::shared::winerror::ERROR_SUCCESS;
use winapi::shared::winerror::ERROR_FILE_NOT_FOUND;
use winapi::shared::winerror::ERROR_ACCESS_DENIED;
use winapi::um::winreg::RegGetValueW;
use winapi::um::winreg::RegSetValueExW;
use winapi::um::winreg::RRF_RT_ANY;
use winapi::shared::minwindef::DWORD;
use winapi::um::winnt::REG_EXPAND_SZ;
use winapi::um::winreg::HKEY_CURRENT_USER;
use winapi::um::winreg::HKEY_LOCAL_MACHINE;

#[derive(Clone, Copy, Debug)]
pub enum Error
{
    NoFound,
    AccessDenied,
    Other(u32)
}

#[derive(Clone, Copy, Debug)]
pub enum RootKey
{
    /// HKEY_CURRENT_USER
    CurrentUser,

    /// HKEY_LOCAL_MACHINE
    LocalMachine
}

fn root_key_to_hkey(root: RootKey) -> HKEY
{
    match root
    {
        RootKey::CurrentUser => HKEY_CURRENT_USER,
        RootKey::LocalMachine => HKEY_LOCAL_MACHINE
    }
}

fn handle_error(errcode: DWORD) -> Error
{
    match errcode
    {
        ERROR_FILE_NOT_FOUND => return Error::NoFound,
        ERROR_ACCESS_DENIED => return Error::AccessDenied,
        v => return Error::Other(v)
    };
}

/// Gets a registry value as a string
/// 
/// # Arguments
///
/// * `root` - root HKEY
/// * `path` - path without root HKEY prefix and using backslashes instead of slashes
/// * `name` - name of value to read
///
/// # Returns
///
/// * string value as an OsString
/// * type of value as given by WinAPI (DWORD)
pub fn get_registry_string(root: RootKey, path: &str, name: &str) -> Result<(OsString, DWORD), Error>
{
    let mut wpath: Vec<u16> = OsString::from(path).encode_wide().collect();
    let mut wname: Vec<u16> = OsString::from(name).encode_wide().collect();
    wpath.push(0);
    wname.push(0);
    unsafe
    {
        let root_key = root_key_to_hkey(root);
        let mut hkey: HKEY = null_mut();
        let err = RegOpenKeyExW(root_key, wpath.as_ptr(), 0, KEY_READ, &mut hkey as _);
        if err as DWORD != ERROR_SUCCESS
        {
            println!("err1");
            return Err(handle_error(err as DWORD));
        }
        let mut size: DWORD = 0;
        let err = RegGetValueW(hkey, null(), wname.as_ptr(), RRF_RT_ANY, null_mut(), null_mut(), &mut size as _);
        if err as DWORD != ERROR_SUCCESS
        {
            println!("err2");
            return Err(handle_error(err as DWORD));
        }
        let mut buf: Vec<u16> = vec![0; size as usize];
        let mut typeasf: DWORD = 0;
        let err = RegGetValueW(hkey, null(), wname.as_ptr(), RRF_RT_ANY, &mut typeasf as _, buf.as_mut_ptr() as _, &mut size as _);
        if err as DWORD != ERROR_SUCCESS
        {
            println!("err3");
            return Err(handle_error(err as DWORD));
        }
        let len = buf.iter().take_while(|&&c| c != 0).count();
        return Ok((OsString::from_wide(&buf[..len]), typeasf));
    }
}

/// Gets a registry value as a string
/// 
/// # Arguments
///
/// * `root` - root HKEY
/// * `path` - path without root HKEY prefix and using backslashes instead of slashes
/// * `name` - name of value to read
/// * `value` - value string as an OsString
/// * `typeasf` - type identifier as DWORD (obtained from a call to get_registry_value)
pub fn set_registry_string(root: RootKey, path: &str, name: &str, value: &OsStr, typeasf: DWORD) -> Result<(), Error>
{
    let mut wpath: Vec<u16> = OsString::from(path).encode_wide().collect();
    let mut wname: Vec<u16> = OsString::from(name).encode_wide().collect();
    let mut wvalue: Vec<u16> = value.encode_wide().collect();
    wpath.push(0);
    wname.push(0);
    wvalue.push(0);
    unsafe
    {
        let root_key = root_key_to_hkey(root);
        let mut hkey: HKEY = null_mut();
        let err = RegOpenKeyExW(root_key, wpath.as_ptr(), 0, KEY_READ, &mut hkey as _);
        if err as DWORD != ERROR_SUCCESS
        {
            return Err(handle_error(err as DWORD));
        }
        let err = RegSetValueExW(hkey, wname.as_ptr(), 0, typeasf, wvalue.as_ptr() as _, (wvalue.len() * 2) as DWORD);
        if err as DWORD != ERROR_SUCCESS
        {
            return Err(handle_error(err as DWORD));
        }
    }
    return Ok(());
}

const REG_PATH_USER: &'static str = "Environment";
const REG_PATH_SYS: &'static str = "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment";
const REG_PATH_NAME: &'static str = "Path";

/// Gets PATH variable
/// 
/// # Arguments
///
/// * `user` - wether to read the local user PATH or the system (all user) PATH
/// 
/// # Returns
///
/// path list as a Vec<OsString>
pub fn get_path(user: bool) -> Result<Vec<OsString>, Error>
{
    let mut v = Vec::new();
    let (data, _) = match user
    {
        true => get_registry_string(RootKey::CurrentUser, REG_PATH_USER, REG_PATH_NAME)?,
        false => get_registry_string(RootKey::LocalMachine, REG_PATH_SYS, REG_PATH_NAME)?,
    };
    let wdata: Vec<u16> = data.encode_wide().collect();
    let mut word = Vec::new();

    for c in wdata
    {
        if c == 0x003b
        {
            if word.len() > 0
            {
                v.push(OsString::from_wide(&word));
                word.clear();
            }
        }
        else
        {
            word.push(c);
        }
    }
    return Ok(v);
}

/// Sets PATH variable
/// 
/// # Arguments
///
/// * `user` - wether to write the local user PATH or the system (all user) PATH
/// * `new_path` - the new path list
pub fn set_path(user: bool, new_path: Vec<OsString>) -> Result<(), Error>
{
    let mut s = OsString::new();

    for p in new_path
    {
        s.push(p);
        s.push(";");
    }
    if user
    {
        return set_registry_string(RootKey::CurrentUser, REG_PATH_USER, REG_PATH_NAME, &s, REG_EXPAND_SZ);
    }
    else
    {
        return set_registry_string(RootKey::LocalMachine, REG_PATH_SYS, REG_PATH_NAME, &s, REG_EXPAND_SZ);
    }
}