mirror of
https://github.com/Devolutions/IronRDP.git
synced 2025-08-04 15:18:17 +00:00
WIP
This commit is contained in:
parent
194ed07630
commit
cd3973e0de
11 changed files with 394 additions and 0 deletions
18
Cargo.lock
generated
18
Cargo.lock
generated
|
@ -2429,6 +2429,13 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ironrdp-cfg"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ironrdp-propertyset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ironrdp-client"
|
||||
version = "0.1.0"
|
||||
|
@ -2628,6 +2635,10 @@ dependencies = [
|
|||
name = "ironrdp-pdu-generators"
|
||||
version = "0.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "ironrdp-propertyset"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "ironrdp-rdcleanpath"
|
||||
version = "0.1.3"
|
||||
|
@ -2659,6 +2670,13 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ironrdp-rdpfile"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ironrdp-propertyset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ironrdp-rdpsnd"
|
||||
version = "0.4.0"
|
||||
|
|
23
crates/ironrdp-cfg/Cargo.toml
Normal file
23
crates/ironrdp-cfg/Cargo.toml
Normal file
|
@ -0,0 +1,23 @@
|
|||
[package]
|
||||
name = "ironrdp-cfg"
|
||||
version = "0.1.0"
|
||||
readme = "README.md"
|
||||
description = "IronRDP utilities for ironrdp-cfgstore"
|
||||
publish = false # Temporary.
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
authors.workspace = true
|
||||
keywords.workspace = true
|
||||
categories.workspace = true
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
test = false
|
||||
|
||||
[dependencies]
|
||||
ironrdp-propertyset = { path = "../ironrdp-propertyset", version = "0.1" } # public
|
||||
|
||||
[lints]
|
||||
workspace = true
|
7
crates/ironrdp-cfg/README.md
Normal file
7
crates/ironrdp-cfg/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# IronRDP Configuration
|
||||
|
||||
IronRDP-related utilities for ironrdp-propertyset.
|
||||
|
||||
This crate is part of the [IronRDP] project.
|
||||
|
||||
[IronRDP]: https://github.com/Devolutions/IronRDP
|
38
crates/ironrdp-cfg/src/lib.rs
Normal file
38
crates/ironrdp-cfg/src/lib.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
// QUESTION: consider auto-generating this file based on a reference file?
|
||||
// https://gist.github.com/awakecoding/838c7fe2ed3a6208e3ca5d8af25363f6
|
||||
|
||||
use ironrdp_propertyset::{ExtractFrom, PropertySet, Value};
|
||||
|
||||
pub trait PropertySetExt {
|
||||
fn read<'a, V: ExtractFrom<&'a Value>>(&'a self, key: &str) -> Option<V>;
|
||||
|
||||
fn full_address(&self) -> Option<&str> {
|
||||
self.read::<&str>("full address")
|
||||
}
|
||||
|
||||
fn alternate_full_address(&self) -> Option<&str> {
|
||||
self.read::<&str>("alternate full address")
|
||||
}
|
||||
|
||||
fn gateway_hostname(&self) -> Option<&str> {
|
||||
self.read::<&str>("gatewayhostname")
|
||||
}
|
||||
|
||||
fn remote_application_name(&self) -> Option<&str> {
|
||||
self.read::<&str>("remoteapplicationname")
|
||||
}
|
||||
|
||||
fn remote_application_program(&self) -> Option<&str> {
|
||||
self.read::<&str>("remoteapplicationprogram")
|
||||
}
|
||||
|
||||
fn kdc_proxy_url(&self) -> Option<&str> {
|
||||
self.read::<&str>("kdcproxyurl")
|
||||
}
|
||||
}
|
||||
|
||||
impl PropertySetExt for PropertySet {
|
||||
fn read<'a, V: ExtractFrom<&'a Value>>(&'a self, key: &str) -> Option<V> {
|
||||
todo!("should we log directly from ironrdp-propertyset?");
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::boxed::Box;
|
||||
use core::fmt;
|
||||
|
|
20
crates/ironrdp-propertyset/Cargo.toml
Normal file
20
crates/ironrdp-propertyset/Cargo.toml
Normal file
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "ironrdp-propertyset"
|
||||
version = "0.1.0"
|
||||
readme = "README.md"
|
||||
description = "A key-value store for configuration options"
|
||||
publish = false # Temporary.
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
authors.workspace = true
|
||||
keywords.workspace = true
|
||||
categories.workspace = true
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
test = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
7
crates/ironrdp-propertyset/README.md
Normal file
7
crates/ironrdp-propertyset/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# IronRDP PropertySet
|
||||
|
||||
The main type is `PropertySet`, a key-value store for configuration options.
|
||||
|
||||
This crate is part of the [IronRDP] project.
|
||||
|
||||
[IronRDP]: https://github.com/Devolutions/IronRDP
|
129
crates/ironrdp-propertyset/src/lib.rs
Normal file
129
crates/ironrdp-propertyset/src/lib.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::string::String;
|
||||
|
||||
pub type Key = Cow<'static, str>;
|
||||
|
||||
/// Key-value store for configuration keys.
|
||||
#[derive(Clone, Default, PartialEq, Eq)]
|
||||
pub struct PropertySet {
|
||||
inner: BTreeMap<Key, Value>,
|
||||
}
|
||||
|
||||
impl PropertySet {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, key: impl Into<Key>, value: impl Into<Value>) -> Option<Value> {
|
||||
self.inner.insert(key.into(), value.into())
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, key: &str) -> Option<Value> {
|
||||
self.inner.remove(key)
|
||||
}
|
||||
|
||||
pub fn get<'a, V: ExtractFrom<&'a Value>>(&'a self, key: &str) -> Option<V> {
|
||||
self.inner.get(key).and_then(|val| V::extract_from(val, private::Token))
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Key, &Value)> {
|
||||
self.inner.iter()
|
||||
}
|
||||
|
||||
pub fn into_iter(self) -> impl Iterator<Item = (Key, Value)> {
|
||||
self.inner.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_from {
|
||||
($from:ty => $enum:ident :: $variant:ident) => {
|
||||
impl From<$from> for $enum {
|
||||
fn from(value: $from) -> Self {
|
||||
Self::$variant(value.into())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_extract_from {
|
||||
(ref $enum:ident :: as_int => $to:ty) => {
|
||||
impl ExtractFrom<&$enum> for $to {
|
||||
fn extract_from(value: &$enum, _token: private::Token) -> Option<Self> {
|
||||
value.as_int().and_then(|v| v.try_into().ok())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub trait ExtractFrom<Value>: Sized {
|
||||
fn extract_from(value: Value, _token: private::Token) -> Option<Self>;
|
||||
}
|
||||
|
||||
/// Represents a value of any type of the .RDP file format.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub enum Value {
|
||||
/// Numerical value.
|
||||
Int(i64),
|
||||
/// String value.
|
||||
Str(String),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
if let Self::Str(value) = self {
|
||||
Some(value.as_str())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_int(&self) -> Option<i64> {
|
||||
if let Self::Int(value) = self {
|
||||
Some(*value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_from!(String => Value::Str);
|
||||
impl_from!(&str => Value::Str);
|
||||
impl_from!(u8 => Value::Int);
|
||||
impl_from!(u16 => Value::Int);
|
||||
impl_from!(u32 => Value::Int);
|
||||
impl_from!(i8 => Value::Int);
|
||||
impl_from!(i16 => Value::Int);
|
||||
impl_from!(i32 => Value::Int);
|
||||
impl_from!(i64 => Value::Int);
|
||||
impl_from!(bool => Value::Int);
|
||||
|
||||
impl_extract_from!(ref Value::as_int => u8);
|
||||
impl_extract_from!(ref Value::as_int => u16);
|
||||
impl_extract_from!(ref Value::as_int => u32);
|
||||
impl_extract_from!(ref Value::as_int => i8);
|
||||
impl_extract_from!(ref Value::as_int => i16);
|
||||
impl_extract_from!(ref Value::as_int => i32);
|
||||
impl_extract_from!(ref Value::as_int => i64);
|
||||
|
||||
impl<'a> ExtractFrom<&'a Value> for &'a str {
|
||||
fn extract_from(value: &'a Value, _token: private::Token) -> Option<Self> {
|
||||
value.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtractFrom<&Value> for bool {
|
||||
fn extract_from(value: &Value, _token: private::Token) -> Option<Self> {
|
||||
value.as_int().map(|value| value != 0)
|
||||
}
|
||||
}
|
||||
|
||||
mod private {
|
||||
pub struct Token;
|
||||
}
|
23
crates/ironrdp-rdpfile/Cargo.toml
Normal file
23
crates/ironrdp-rdpfile/Cargo.toml
Normal file
|
@ -0,0 +1,23 @@
|
|||
[package]
|
||||
name = "ironrdp-rdpfile"
|
||||
version = "0.1.0"
|
||||
readme = "README.md"
|
||||
description = "Parser and writer for .RDP file format"
|
||||
publish = false # Temporary.
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
authors.workspace = true
|
||||
keywords.workspace = true
|
||||
categories.workspace = true
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
test = false
|
||||
|
||||
[dependencies]
|
||||
ironrdp-propertyset = { path = "../ironrdp-propertyset", version = "0.1" } # public
|
||||
|
||||
[lints]
|
||||
workspace = true
|
7
crates/ironrdp-rdpfile/README.md
Normal file
7
crates/ironrdp-rdpfile/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# IronRDP .RDP file
|
||||
|
||||
Loader and writer for the .RDP file format.
|
||||
|
||||
This crate is part of the [IronRDP] project.
|
||||
|
||||
[IronRDP]: https://github.com/Devolutions/IronRDP
|
121
crates/ironrdp-rdpfile/src/lib.rs
Normal file
121
crates/ironrdp-rdpfile/src/lib.rs
Normal file
|
@ -0,0 +1,121 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec::Vec;
|
||||
use ironrdp_propertyset::{PropertySet, Value};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ErrorKind {
|
||||
UnknownType { ty: String },
|
||||
InvalidValue { ty: String, value: String },
|
||||
MalformedLine { line: String },
|
||||
DuplicatedKey { key: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Error {
|
||||
pub kind: ErrorKind,
|
||||
pub line: usize,
|
||||
}
|
||||
|
||||
impl core::error::Error for Error {}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let line_number = self.line;
|
||||
|
||||
match &self.kind {
|
||||
ErrorKind::UnknownType { ty } => write!(f, "unknown type at line {line_number} ({ty})"),
|
||||
ErrorKind::InvalidValue { ty, value } => {
|
||||
write!(f, "invalid value at line {line_number} for type {ty} ({value})")
|
||||
}
|
||||
ErrorKind::MalformedLine { line } => write!(f, "malformed line at line {line_number} ({line})"),
|
||||
ErrorKind::DuplicatedKey { key } => write!(f, "duplicated key at line {line_number} ({key})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ParseResult {
|
||||
pub store: PropertySet,
|
||||
pub errors: Vec<Error>,
|
||||
}
|
||||
|
||||
pub fn parse(input: &str) -> ParseResult {
|
||||
let mut store = PropertySet::new();
|
||||
let mut errors = Vec::new();
|
||||
|
||||
for (idx, line) in input.lines().enumerate() {
|
||||
let mut split = line.splitn(2, ':');
|
||||
|
||||
if let (Some(key), Some(ty), Some(value)) = (split.next(), split.next(), split.next()) {
|
||||
let is_duplicated = match ty {
|
||||
"i" => {
|
||||
if let Ok(value) = value.parse::<i64>() {
|
||||
store.insert(key.to_owned(), value).is_some()
|
||||
} else {
|
||||
errors.push(Error {
|
||||
kind: ErrorKind::InvalidValue {
|
||||
ty: ty.to_owned(),
|
||||
value: value.to_owned(),
|
||||
},
|
||||
line: idx,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
"s" => store.insert(key.to_owned(), value).is_some(),
|
||||
_ => {
|
||||
errors.push(Error {
|
||||
kind: ErrorKind::UnknownType { ty: ty.to_owned() },
|
||||
line: idx,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if is_duplicated {
|
||||
errors.push(Error {
|
||||
kind: ErrorKind::DuplicatedKey { key: key.to_owned() },
|
||||
line: idx,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
errors.push(Error {
|
||||
kind: ErrorKind::MalformedLine { line: line.to_owned() },
|
||||
line: idx,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ParseResult { store, errors }
|
||||
}
|
||||
|
||||
pub fn write(store: &PropertySet) -> String {
|
||||
let mut buf = String::new();
|
||||
|
||||
for (key, value) in store.iter() {
|
||||
buf.push_str(key);
|
||||
|
||||
match value {
|
||||
Value::Int(value) => {
|
||||
buf.push_str(":i:");
|
||||
buf.push_str(&value.to_string());
|
||||
}
|
||||
Value::Str(value) => {
|
||||
buf.push_str(":s:");
|
||||
buf.push_str(value);
|
||||
}
|
||||
}
|
||||
|
||||
buf.push('\n');
|
||||
}
|
||||
|
||||
buf
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue