224 lines
7.6 KiB
Rust
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(())
|
|
}
|
|
}
|