|
| 1 | +#![allow(non_snake_case)] |
| 2 | + |
| 3 | +use std::cell::{Ref, RefCell}; |
| 4 | +use std::convert::TryInto; |
| 5 | +use std::io; |
| 6 | + |
| 7 | +use super::os; |
| 8 | +use crate::function::OptionalArg; |
| 9 | +use crate::obj::objstr::PyStringRef; |
| 10 | +use crate::obj::objtype::PyClassRef; |
| 11 | +use crate::pyobject::{PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject}; |
| 12 | +use crate::VirtualMachine; |
| 13 | + |
| 14 | +use winapi::shared::winerror; |
| 15 | +use winreg::{enums::RegType, RegKey, RegValue}; |
| 16 | + |
| 17 | +#[pyclass] |
| 18 | +#[derive(Debug)] |
| 19 | +struct PyHKEY { |
| 20 | + key: RefCell<RegKey>, |
| 21 | +} |
| 22 | +type PyHKEYRef = PyRef<PyHKEY>; |
| 23 | + |
| 24 | +impl PyValue for PyHKEY { |
| 25 | + fn class(vm: &VirtualMachine) -> PyClassRef { |
| 26 | + vm.class("winreg", "HKEYType") |
| 27 | + } |
| 28 | +} |
| 29 | + |
| 30 | +#[pyimpl] |
| 31 | +impl PyHKEY { |
| 32 | + fn new(key: RegKey) -> Self { |
| 33 | + Self { |
| 34 | + key: RefCell::new(key), |
| 35 | + } |
| 36 | + } |
| 37 | + |
| 38 | + fn key(&self) -> Ref<RegKey> { |
| 39 | + self.key.borrow() |
| 40 | + } |
| 41 | + |
| 42 | + #[pymethod] |
| 43 | + fn Close(&self) { |
| 44 | + let null_key = RegKey::predef(0 as winreg::HKEY); |
| 45 | + let key = self.key.replace(null_key); |
| 46 | + drop(key); |
| 47 | + } |
| 48 | + #[pymethod] |
| 49 | + fn Detach(&self) -> usize { |
| 50 | + let null_key = RegKey::predef(0 as winreg::HKEY); |
| 51 | + let key = self.key.replace(null_key); |
| 52 | + let handle = key.raw_handle(); |
| 53 | + std::mem::forget(key); |
| 54 | + handle as usize |
| 55 | + } |
| 56 | + |
| 57 | + #[pymethod(magic)] |
| 58 | + fn bool(&self) -> bool { |
| 59 | + !self.key().raw_handle().is_null() |
| 60 | + } |
| 61 | + #[pymethod(magic)] |
| 62 | + fn enter(zelf: PyRef<Self>) -> PyRef<Self> { |
| 63 | + zelf |
| 64 | + } |
| 65 | + #[pymethod(magic)] |
| 66 | + fn exit(&self, _cls: PyObjectRef, _exc: PyObjectRef, _tb: PyObjectRef) { |
| 67 | + self.Close(); |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +enum Hkey { |
| 72 | + PyHKEY(PyHKEYRef), |
| 73 | + Constant(winreg::HKEY), |
| 74 | +} |
| 75 | +impl TryFromObject for Hkey { |
| 76 | + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> { |
| 77 | + obj.downcast() |
| 78 | + .map(Self::PyHKEY) |
| 79 | + .or_else(|o| usize::try_from_object(vm, o).map(|i| Self::Constant(i as winreg::HKEY))) |
| 80 | + } |
| 81 | +} |
| 82 | +impl Hkey { |
| 83 | + fn with_key<R>(&self, f: impl FnOnce(&RegKey) -> R) -> R { |
| 84 | + match self { |
| 85 | + Self::PyHKEY(py) => f(&py.key()), |
| 86 | + Self::Constant(hkey) => { |
| 87 | + let k = RegKey::predef(*hkey); |
| 88 | + let res = f(&k); |
| 89 | + std::mem::forget(k); |
| 90 | + res |
| 91 | + } |
| 92 | + } |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +fn winreg_OpenKey( |
| 97 | + key: Hkey, |
| 98 | + subkey: Option<PyStringRef>, |
| 99 | + reserved: OptionalArg<i32>, |
| 100 | + access: OptionalArg<u32>, |
| 101 | + vm: &VirtualMachine, |
| 102 | +) -> PyResult<PyHKEY> { |
| 103 | + let reserved = reserved.unwrap_or(0); |
| 104 | + let access = access.unwrap_or(winreg::enums::KEY_READ); |
| 105 | + if reserved != 0 { |
| 106 | + // RegKey::open_subkey* doesn't have a reserved param, so this'll do |
| 107 | + return Err(vm.new_value_error("reserved param must be 0".to_owned())); |
| 108 | + } |
| 109 | + |
| 110 | + let subkey = subkey.as_ref().map_or("", |s| s.as_str()); |
| 111 | + let key = key |
| 112 | + .with_key(|k| k.open_subkey_with_flags(subkey, access)) |
| 113 | + .map_err(|e| os::convert_io_error(vm, e))?; |
| 114 | + |
| 115 | + Ok(PyHKEY::new(key)) |
| 116 | +} |
| 117 | + |
| 118 | +fn winreg_QueryValue( |
| 119 | + key: Hkey, |
| 120 | + subkey: Option<PyStringRef>, |
| 121 | + vm: &VirtualMachine, |
| 122 | +) -> PyResult<String> { |
| 123 | + let subkey = subkey.as_ref().map_or("", |s| s.as_str()); |
| 124 | + key.with_key(|k| k.get_value(subkey)) |
| 125 | + .map_err(|e| os::convert_io_error(vm, e)) |
| 126 | +} |
| 127 | + |
| 128 | +fn winreg_QueryValueEx( |
| 129 | + key: Hkey, |
| 130 | + subkey: Option<PyStringRef>, |
| 131 | + vm: &VirtualMachine, |
| 132 | +) -> PyResult<(PyObjectRef, usize)> { |
| 133 | + let subkey = subkey.as_ref().map_or("", |s| s.as_str()); |
| 134 | + key.with_key(|k| k.get_raw_value(subkey)) |
| 135 | + .map_err(|e| os::convert_io_error(vm, e)) |
| 136 | + .and_then(|regval| { |
| 137 | + let ty = regval.vtype.clone() as usize; |
| 138 | + Ok((reg_to_py(regval, vm)?, ty)) |
| 139 | + }) |
| 140 | +} |
| 141 | + |
| 142 | +fn winreg_EnumKey(key: Hkey, index: u32, vm: &VirtualMachine) -> PyResult<String> { |
| 143 | + key.with_key(|k| k.enum_keys().nth(index as usize)) |
| 144 | + .unwrap_or_else(|| { |
| 145 | + Err(io::Error::from_raw_os_error( |
| 146 | + winerror::ERROR_NO_MORE_ITEMS as i32, |
| 147 | + )) |
| 148 | + }) |
| 149 | + .map_err(|e| os::convert_io_error(vm, e)) |
| 150 | +} |
| 151 | + |
| 152 | +fn winreg_EnumValue( |
| 153 | + key: Hkey, |
| 154 | + index: u32, |
| 155 | + vm: &VirtualMachine, |
| 156 | +) -> PyResult<(String, PyObjectRef, usize)> { |
| 157 | + key.with_key(|k| k.enum_values().nth(index as usize)) |
| 158 | + .unwrap_or_else(|| { |
| 159 | + Err(io::Error::from_raw_os_error( |
| 160 | + winerror::ERROR_NO_MORE_ITEMS as i32, |
| 161 | + )) |
| 162 | + }) |
| 163 | + .map_err(|e| os::convert_io_error(vm, e)) |
| 164 | + .and_then(|(name, value)| { |
| 165 | + let ty = value.vtype.clone() as usize; |
| 166 | + Ok((name, reg_to_py(value, vm)?, ty)) |
| 167 | + }) |
| 168 | +} |
| 169 | + |
| 170 | +fn winreg_CloseKey(key: Hkey) { |
| 171 | + match key { |
| 172 | + Hkey::PyHKEY(py) => py.Close(), |
| 173 | + Hkey::Constant(hkey) => drop(RegKey::predef(hkey)), |
| 174 | + } |
| 175 | +} |
| 176 | + |
| 177 | +fn reg_to_py(value: RegValue, vm: &VirtualMachine) -> PyResult { |
| 178 | + macro_rules! bytes_to_int { |
| 179 | + ($int:ident, $f:ident, $name:ident) => {{ |
| 180 | + let i = if value.bytes.is_empty() { |
| 181 | + Ok(0 as $int) |
| 182 | + } else { |
| 183 | + (&*value.bytes).try_into().map($int::$f).map_err(|_| { |
| 184 | + vm.new_value_error(format!("{} value is wrong length", stringify!(name))) |
| 185 | + }) |
| 186 | + }; |
| 187 | + i.map(|i| vm.new_int(i)) |
| 188 | + }}; |
| 189 | + }; |
| 190 | + let bytes_to_wide = |b: &[u8]| -> Option<&[u16]> { |
| 191 | + if b.len() % 2 == 0 { |
| 192 | + Some(unsafe { std::slice::from_raw_parts(b.as_ptr().cast(), b.len() / 2) }) |
| 193 | + } else { |
| 194 | + None |
| 195 | + } |
| 196 | + }; |
| 197 | + match value.vtype { |
| 198 | + RegType::REG_DWORD => bytes_to_int!(u32, from_ne_bytes, REG_DWORD), |
| 199 | + RegType::REG_DWORD_BIG_ENDIAN => bytes_to_int!(u32, from_be_bytes, REG_DWORD_BIG_ENDIAN), |
| 200 | + RegType::REG_QWORD => bytes_to_int!(u64, from_ne_bytes, REG_DWORD), |
| 201 | + // RegType::REG_QWORD_BIG_ENDIAN => bytes_to_int!(u64, from_be_bytes, REG_DWORD_BIG_ENDIAN), |
| 202 | + RegType::REG_SZ | RegType::REG_EXPAND_SZ => { |
| 203 | + let wide_slice = bytes_to_wide(&value.bytes).ok_or_else(|| { |
| 204 | + vm.new_value_error("REG_SZ string doesn't have an even byte length".to_owned()) |
| 205 | + })?; |
| 206 | + let nul_pos = wide_slice |
| 207 | + .iter() |
| 208 | + .position(|w| *w == 0) |
| 209 | + .unwrap_or_else(|| wide_slice.len()); |
| 210 | + let s = String::from_utf16_lossy(&wide_slice[..nul_pos]); |
| 211 | + Ok(vm.new_str(s)) |
| 212 | + } |
| 213 | + RegType::REG_MULTI_SZ => { |
| 214 | + if value.bytes.is_empty() { |
| 215 | + return Ok(vm.ctx.new_list(vec![])); |
| 216 | + } |
| 217 | + let wide_slice = bytes_to_wide(&value.bytes).ok_or_else(|| { |
| 218 | + vm.new_value_error( |
| 219 | + "REG_MULTI_SZ string doesn't have an even byte length".to_owned(), |
| 220 | + ) |
| 221 | + })?; |
| 222 | + let wide_slice = if let Some((0, rest)) = wide_slice.split_last() { |
| 223 | + rest |
| 224 | + } else { |
| 225 | + wide_slice |
| 226 | + }; |
| 227 | + let strings = wide_slice |
| 228 | + .split(|c| *c == 0) |
| 229 | + .map(|s| vm.new_str(String::from_utf16_lossy(s))) |
| 230 | + .collect(); |
| 231 | + Ok(vm.ctx.new_list(strings)) |
| 232 | + } |
| 233 | + RegType::REG_BINARY | _ => { |
| 234 | + if value.bytes.is_empty() { |
| 235 | + Ok(vm.get_none()) |
| 236 | + } else { |
| 237 | + Ok(vm.ctx.new_bytes(value.bytes)) |
| 238 | + } |
| 239 | + } |
| 240 | + } |
| 241 | +} |
| 242 | + |
| 243 | +pub fn make_module(vm: &VirtualMachine) -> PyObjectRef { |
| 244 | + let ctx = &vm.ctx; |
| 245 | + let hkey_type = PyHKEY::make_class(ctx); |
| 246 | + let module = py_module!(vm, "winreg", { |
| 247 | + "HKEYType" => hkey_type, |
| 248 | + "OpenKey" => ctx.new_function(winreg_OpenKey), |
| 249 | + "QueryValue" => ctx.new_function(winreg_QueryValue), |
| 250 | + "QueryValueEx" => ctx.new_function(winreg_QueryValueEx), |
| 251 | + "EnumKey" => ctx.new_function(winreg_EnumKey), |
| 252 | + "EnumValue" => ctx.new_function(winreg_EnumValue), |
| 253 | + "CloseKey" => ctx.new_function(winreg_CloseKey), |
| 254 | + }); |
| 255 | + |
| 256 | + macro_rules! add_constants { |
| 257 | + (hkey, $($name:ident),*$(,)?) => { |
| 258 | + extend_module!(vm, module, { |
| 259 | + $((stringify!($name)) => ctx.new_int(winreg::enums::$name as usize)),* |
| 260 | + }) |
| 261 | + }; |
| 262 | + (winnt, $($name:ident),*$(,)?) => { |
| 263 | + extend_module!(vm, module, { |
| 264 | + $((stringify!($name)) => ctx.new_int(winapi::um::winnt::$name)),* |
| 265 | + }) |
| 266 | + }; |
| 267 | + } |
| 268 | + |
| 269 | + add_constants!( |
| 270 | + hkey, |
| 271 | + HKEY_CLASSES_ROOT, |
| 272 | + HKEY_CURRENT_USER, |
| 273 | + HKEY_LOCAL_MACHINE, |
| 274 | + HKEY_USERS, |
| 275 | + HKEY_PERFORMANCE_DATA, |
| 276 | + HKEY_CURRENT_CONFIG, |
| 277 | + HKEY_DYN_DATA, |
| 278 | + ); |
| 279 | + add_constants!( |
| 280 | + winnt, |
| 281 | + // access rights |
| 282 | + KEY_ALL_ACCESS, |
| 283 | + KEY_WRITE, |
| 284 | + KEY_READ, |
| 285 | + KEY_EXECUTE, |
| 286 | + KEY_QUERY_VALUE, |
| 287 | + KEY_SET_VALUE, |
| 288 | + KEY_CREATE_SUB_KEY, |
| 289 | + KEY_ENUMERATE_SUB_KEYS, |
| 290 | + KEY_NOTIFY, |
| 291 | + KEY_CREATE_LINK, |
| 292 | + KEY_WOW64_64KEY, |
| 293 | + KEY_WOW64_32KEY, |
| 294 | + // value types |
| 295 | + REG_BINARY, |
| 296 | + REG_DWORD, |
| 297 | + REG_DWORD_LITTLE_ENDIAN, |
| 298 | + REG_DWORD_BIG_ENDIAN, |
| 299 | + REG_EXPAND_SZ, |
| 300 | + REG_LINK, |
| 301 | + REG_MULTI_SZ, |
| 302 | + REG_NONE, |
| 303 | + REG_QWORD, |
| 304 | + REG_QWORD_LITTLE_ENDIAN, |
| 305 | + REG_RESOURCE_LIST, |
| 306 | + REG_FULL_RESOURCE_DESCRIPTOR, |
| 307 | + REG_RESOURCE_REQUIREMENTS_LIST, |
| 308 | + REG_SZ, |
| 309 | + ); |
| 310 | + |
| 311 | + module |
| 312 | +} |
0 commit comments