1
0
Files
wfassoc/wfassoc/src/lowlevel/ext_key.rs

224 lines
7.6 KiB
Rust

use super::{
Error, LosseProgId, OpenKeyPurpose, OpenKeyTerritory, OpenedKey, PERM_R, PERM_RW, Result,
Scope, View, check_privilege,
};
use crate::win32::{concept, regext};
use winreg::RegKey;
use winreg::enums::{HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ExtKey {
ext: concept::Ext,
}
impl ExtKey {
pub fn new(inner: concept::Ext) -> Self {
Self { ext: inner }
}
pub fn inner(&self) -> &concept::Ext {
&self.ext
}
}
impl ExtKey {
const FULL_CLASSES: &str = "Software\\Classes";
const PARTIAL_CLASSES: &str = "";
fn open_key(&self, territory: OpenKeyTerritory, purpose: OpenKeyPurpose) -> Result<OpenedKey> {
// check privilege
check_privilege(territory, purpose)?;
// Fetch the permission
let perms = purpose.to_permission();
// navigate to extension container
let hk = match territory {
OpenKeyTerritory::User => RegKey::predef(HKEY_CURRENT_USER),
OpenKeyTerritory::System => RegKey::predef(HKEY_LOCAL_MACHINE),
OpenKeyTerritory::Hybrid => RegKey::predef(HKEY_CLASSES_ROOT),
};
let classes = match territory {
OpenKeyTerritory::User | OpenKeyTerritory::System => {
hk.open_subkey_with_flags(Self::FULL_CLASSES, perms)?
}
OpenKeyTerritory::Hybrid => hk.open_subkey_with_flags(Self::PARTIAL_CLASSES, perms)?,
};
// open extension key if possible
let this_ext = regext::try_open_subkey_with_flags(
&classes,
regext::blank_path_guard(self.ext.dotted_inner())?,
perms,
)?;
// okey
Ok(OpenedKey::new(classes, this_ext))
}
fn open_view_for_read(&self, view: View) -> Result<OpenedKey> {
self.open_key(view.into(), OpenKeyPurpose::Read)
}
fn open_scope_for_write(&self, scope: Scope) -> Result<OpenedKey> {
self.open_key(scope.into(), OpenKeyPurpose::ReadWrite)
}
pub fn is_exist(&self, view: View) -> Result<bool> {
let key = self.open_view_for_read(view)?.this_key;
Ok(key.is_some())
}
/// Ensure this file extension key is presented in Classes key.
///
/// Return true if we newly create this key,
/// otherwise false indicating there already is an existing key.
pub fn ensure(&mut self, scope: Scope) -> Result<bool> {
let key = self.open_scope_for_write(scope)?;
if let None = key.this_key {
let _ = key.parent_key.create_subkey_with_flags(
regext::blank_path_guard(self.ext.dotted_inner())?,
PERM_RW,
)?;
Ok(true)
} else {
Ok(false)
}
}
/// Delete this file extension key from Classes key.
///
/// Return true if we successfully delete this key,
/// otherwise false indicating there is no such key (already deleted).
pub fn delete(&mut self, scope: Scope) -> Result<bool> {
let key = self.open_scope_for_write(scope)?;
Ok(regext::arbitrarily_delete_subkey_all(
&key.parent_key,
regext::blank_path_guard(self.ext.dotted_inner())?,
)?)
}
// YYC MARK:
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/fa-file-types#setting-optional-subkeys-and-file-type-extension-attributes
// TODO:
// We do not support "Content Type" and "PerceivedType"
// because current interface are enough to use,
// and these types has not been made as concept struct in Rust.
// We may expand these in future.
fn open_view_for_getter(&self, view: View) -> Result<RegKey> {
self.open_view_for_read(view)?
.this_key
.ok_or(Error::InexistantKey)
}
fn open_scope_for_setter(&self, scope: Scope) -> Result<RegKey> {
self.open_scope_for_write(scope)?
.this_key
.ok_or(Error::InexistantKey)
}
const NAMEOF_DEFAULT: &str = "";
pub fn get_default(&self, view: View) -> Result<Option<LosseProgId>> {
let key = self.open_view_for_getter(view)?;
// Get value of it
let value = regext::try_get_value::<String, _>(&key, Self::NAMEOF_DEFAULT)?;
// Transform it as result
Ok(value.map(|v| LosseProgId::from(v.as_str())))
}
pub fn set_default(&mut self, scope: Scope, pid: Option<&LosseProgId>) -> Result<()> {
let key = self.open_scope_for_setter(scope)?;
match pid {
Some(pid) => {
// Set value for this key
key.set_value(Self::NAMEOF_DEFAULT, &pid.to_string())?;
}
None => {
// Delete this key
regext::arbitrarily_delete_value(&key, Self::NAMEOF_DEFAULT)?;
}
}
Ok(())
}
const NAMEOF_OPEN_WITH_PROGIDS: &str = "OpenWithProgIds";
pub fn get_open_with_progids(&self, view: View) -> Result<Option<Vec<LosseProgId>>> {
let key = self.open_view_for_getter(view)?;
// Get OpenWithProgIds subkey
let open_with_progids_key =
match regext::try_open_subkey_with_flags(&key, Self::NAMEOF_OPEN_WITH_PROGIDS, PERM_R)?
{
Some(key) => key,
None => return Ok(None),
};
// Fetch all sub-values
let key_names = regext::get_all_string_subkey_names(&open_with_progids_key)?;
// Map the result
let progids = key_names
.into_iter()
.map(|name| LosseProgId::from(name.as_str()))
.collect();
// Return value
Ok(Some(progids))
}
///
///
/// If there is no "OpenWithProgIds" subkey, this function return false.
pub fn is_in_open_with_progids(&self, view: View, pid: &LosseProgId) -> Result<bool> {
let key = self.open_view_for_getter(view)?;
// Get OpenWithProgIds subkey
let open_with_progids_key =
match regext::try_open_subkey_with_flags(&key, Self::NAMEOF_OPEN_WITH_PROGIDS, PERM_R)?
{
Some(key) => key,
None => return Ok(false),
};
// Check whether there is given ProgId
Ok(regext::try_get_value::<String, _>(
&open_with_progids_key,
regext::blank_path_guard(pid.to_string())?,
)?
.is_some())
}
///
///
/// If there is no "OpenWithProgIds" subkey, this function will create it first,
/// then add your given ProgId into it.
pub fn add_into_open_with_progids(&mut self, scope: Scope, pid: &LosseProgId) -> Result<()> {
let key = self.open_scope_for_setter(scope)?;
// Get subkey
let (open_with_progids_key, _) =
key.create_subkey_with_flags(Self::NAMEOF_OPEN_WITH_PROGIDS, PERM_RW)?;
// Add value
open_with_progids_key.set_value(regext::blank_path_guard(pid.to_string())?, &"")?;
Ok(())
}
///
///
/// If there is no "OpenWithProgIds" subkey, this function do nothing.
pub fn remove_from_open_with_progids(&mut self, scope: Scope, pid: &LosseProgId) -> Result<()> {
let key = self.open_scope_for_setter(scope)?;
// Try get subkey
let open_with_progids_key = match regext::try_open_subkey_with_flags(
&key,
Self::NAMEOF_OPEN_WITH_PROGIDS,
PERM_RW,
)? {
Some(key) => key,
None => return Ok(()),
};
// Remove given key
regext::arbitrarily_delete_value(
&open_with_progids_key,
regext::blank_path_guard(pid.to_string())?,
)?;
Ok(())
}
}