Use option<&str> instead of owned option string

This commit is contained in:
Jackson Coxson
2025-08-08 10:18:31 -06:00
parent 21584f4190
commit d59f028251
10 changed files with 76 additions and 164 deletions

View File

@@ -109,11 +109,14 @@ pub unsafe extern "C" fn installation_proxy_get_apps(
let app_type = if application_type.is_null() {
None
} else {
Some(unsafe {
std::ffi::CStr::from_ptr(application_type)
.to_string_lossy()
.into_owned()
})
Some(
match unsafe { std::ffi::CStr::from_ptr(application_type) }.to_str() {
Ok(a) => a,
Err(_) => {
return ffi_err!(IdeviceError::InvalidCString);
}
},
)
};
let bundle_ids = if bundle_identifiers.is_null() {
@@ -125,9 +128,9 @@ pub unsafe extern "C" fn installation_proxy_get_apps(
.map(|&s| {
unsafe { std::ffi::CStr::from_ptr(s) }
.to_string_lossy()
.into_owned()
.to_string()
})
.collect(),
.collect::<Vec<String>>(),
)
};

View File

@@ -3,7 +3,7 @@
use std::ptr::null_mut;
use idevice::{IdeviceError, IdeviceService, lockdown::LockdownClient, provider::IdeviceProvider};
use plist_ffi::{PlistWrapper, plist_t};
use plist_ffi::plist_t;
use crate::{
IdeviceFfiError, IdeviceHandle, IdevicePairingFile, RUNTIME, ffi_err,
@@ -183,18 +183,26 @@ pub unsafe extern "C" fn lockdownd_get_value(
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let value = unsafe { std::ffi::CStr::from_ptr(key) }
.to_string_lossy()
.into_owned();
let value = if key.is_null() {
None
} else {
Some(match unsafe { std::ffi::CStr::from_ptr(key) }.to_str() {
Ok(v) => v,
Err(_) => {
return ffi_err!(IdeviceError::InvalidCString);
}
})
};
let domain = if domain.is_null() {
None
} else {
Some(
unsafe { std::ffi::CStr::from_ptr(domain) }
.to_string_lossy()
.into_owned(),
)
Some(match unsafe { std::ffi::CStr::from_ptr(domain) }.to_str() {
Ok(v) => v,
Err(_) => {
return ffi_err!(IdeviceError::InvalidCString);
}
})
};
let res: Result<plist::Value, IdeviceError> = RUNTIME.block_on(async move {
@@ -213,54 +221,6 @@ pub unsafe extern "C" fn lockdownd_get_value(
}
}
/// Gets all values from lockdownd
///
/// # Arguments
/// * `client` - A valid LockdowndClient handle
/// * `out_plist` - Pointer to store the returned plist dictionary
///
/// # Returns
/// An IdeviceFfiError on error, null on success
///
/// # Safety
/// `client` must be a valid pointer to a handle allocated by this library
/// `out_plist` must be a valid pointer to store the plist
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lockdownd_get_all_values(
client: *mut LockdowndClientHandle,
domain: *const libc::c_char,
out_plist: *mut plist_t,
) -> *mut IdeviceFfiError {
if out_plist.is_null() {
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let domain = if domain.is_null() {
None
} else {
Some(
unsafe { std::ffi::CStr::from_ptr(domain) }
.to_string_lossy()
.into_owned(),
)
};
let res: Result<plist::Dictionary, IdeviceError> = RUNTIME.block_on(async move {
let client_ref = unsafe { &mut (*client).0 };
client_ref.get_all_values(domain).await
});
match res {
Ok(dict) => {
unsafe {
*out_plist = PlistWrapper::new_node(plist::Value::Dictionary(dict)).into_ptr();
}
null_mut()
}
Err(e) => ffi_err!(e),
}
}
/// Frees a LockdowndClient handle
///
/// # Arguments

View File

@@ -514,7 +514,7 @@ pub unsafe extern "C" fn image_mounter_query_nonce(
let image_type = if !personalized_image_type.is_null() {
let image_type_cstr = unsafe { std::ffi::CStr::from_ptr(personalized_image_type) };
match image_type_cstr.to_str() {
Ok(s) => Some(s.to_string()),
Ok(s) => Some(s),
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
}
} else {
@@ -566,7 +566,7 @@ pub unsafe extern "C" fn image_mounter_query_personalization_identifiers(
let image_type = if !image_type.is_null() {
let image_type_cstr = unsafe { std::ffi::CStr::from_ptr(image_type) };
match image_type_cstr.to_str() {
Ok(s) => Some(s.to_string()),
Ok(s) => Some(s),
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
}
} else {

View File

@@ -98,10 +98,10 @@ impl InstallationProxyClient {
/// ```
pub async fn get_apps(
&mut self,
application_type: Option<String>,
application_type: Option<&str>,
bundle_identifiers: Option<Vec<String>>,
) -> Result<HashMap<String, plist::Value>, IdeviceError> {
let application_type = application_type.unwrap_or("Any".to_string());
let application_type = application_type.unwrap_or("Any");
let mut options = plist::Dictionary::new();
if let Some(ids) = bundle_identifiers {
let ids = ids

View File

@@ -86,16 +86,16 @@ impl LockdownClient {
/// ```
pub async fn get_value(
&mut self,
key: impl Into<String>,
domain: Option<String>,
key: Option<&str>,
domain: Option<&str>,
) -> Result<Value, IdeviceError> {
let key = key.into();
let mut request = plist::Dictionary::new();
request.insert("Label".into(), self.idevice.label.clone().into());
request.insert("Request".into(), "GetValue".into());
request.insert("Key".into(), key.into());
if let Some(key) = key {
request.insert("Key".into(), key.into());
}
if let Some(domain) = domain {
request.insert("Domain".into(), domain.into());
}
@@ -110,43 +110,6 @@ impl LockdownClient {
}
}
/// Retrieves all available values from the device
///
/// # Returns
/// A dictionary containing all device values
///
/// # Errors
/// Returns `IdeviceError` if:
/// - Communication fails
/// - The response is malformed
///
/// # Example
/// ```rust
/// let all_values = client.get_all_values().await?;
/// for (key, value) in all_values {
/// println!("{}: {:?}", key, value);
/// }
/// ```
pub async fn get_all_values(
&mut self,
domain: Option<String>,
) -> Result<plist::Dictionary, IdeviceError> {
let mut request = plist::Dictionary::new();
request.insert("Label".into(), self.idevice.label.clone().into());
request.insert("Request".into(), "GetValue".into());
if let Some(domain) = domain {
request.insert("Domain".into(), domain.into());
}
let message = plist::to_value(&request)?;
self.idevice.send_plist(message).await?;
let message: plist::Dictionary = self.idevice.read_plist().await?;
match message.get("Value") {
Some(m) => Ok(plist::from_value(m)?),
None => Err(IdeviceError::UnexpectedResponse),
}
}
/// Sets a value on the device
///
/// # Arguments
@@ -167,7 +130,7 @@ impl LockdownClient {
&mut self,
key: impl Into<String>,
value: Value,
domain: Option<String>,
domain: Option<&str>,
) -> Result<(), IdeviceError> {
let key = key.into();
@@ -321,7 +284,7 @@ impl LockdownClient {
let host_id = host_id.into();
let system_buid = system_buid.into();
let pub_key = self.get_value("DevicePublicKey", None).await?;
let pub_key = self.get_value(Some("DevicePublicKey"), None).await?;
let pub_key = match pub_key.as_data().map(|x| x.to_vec()) {
Some(p) => p,
None => {
@@ -330,7 +293,7 @@ impl LockdownClient {
}
};
let wifi_mac = self.get_value("WiFiAddress", None).await?;
let wifi_mac = self.get_value(Some("WiFiAddress"), None).await?;
let wifi_mac = match wifi_mac.as_string() {
Some(w) => w,
None => {

View File

@@ -113,7 +113,7 @@ impl ImageMounter {
/// Returns `IdeviceError::NotFound` if image doesn't exist
pub async fn lookup_image(
&mut self,
image_type: impl Into<String>,
image_type: impl Into<&str>,
) -> Result<Vec<u8>, IdeviceError> {
let image_type = image_type.into();
let mut req = plist::Dictionary::new();
@@ -370,7 +370,7 @@ impl ImageMounter {
/// Returns `IdeviceError` if query fails
pub async fn query_nonce(
&mut self,
personalized_image_type: Option<String>,
personalized_image_type: Option<&str>,
) -> Result<Vec<u8>, IdeviceError> {
let mut req = plist::Dictionary::new();
req.insert("Command".into(), "QueryNonce".into());
@@ -400,7 +400,7 @@ impl ImageMounter {
/// Returns `IdeviceError` if query fails
pub async fn query_personalization_identifiers(
&mut self,
image_type: Option<String>,
image_type: Option<&str>,
) -> Result<plist::Dictionary, IdeviceError> {
let mut req = plist::Dictionary::new();
req.insert("Command".into(), "QueryPersonalizationIdentifiers".into());
@@ -626,10 +626,7 @@ impl ImageMounter {
request.insert("ApECID", unique_chip_id);
request.insert(
"ApNonce",
plist::Value::Data(
self.query_nonce(Some("DeveloperDiskImage".to_string()))
.await?,
),
plist::Value::Data(self.query_nonce(Some("DeveloperDiskImage")).await?),
);
request.insert("ApProductionMode", true);
request.insert("ApSecurityDomain", 1);

View File

@@ -67,7 +67,9 @@ async fn main() {
println!(
"{:?}",
lockdown_client.get_value("ProductVersion", None).await
lockdown_client
.get_value(Some("ProductVersion"), None)
.await
);
println!(
@@ -82,5 +84,5 @@ async fn main() {
.await
);
println!("{:?}", lockdown_client.idevice.get_type().await.unwrap());
println!("{:#?}", lockdown_client.get_all_values(None).await);
println!("{:#?}", lockdown_client.get_value(None, None).await);
}

View File

@@ -69,10 +69,7 @@ async fn main() {
.await
.expect("Unable to connect to instproxy");
if matches.subcommand_matches("lookup").is_some() {
let apps = instproxy_client
.get_apps(Some("User".to_string()), None)
.await
.unwrap();
let apps = instproxy_client.get_apps(Some("User"), None).await.unwrap();
for app in apps.keys() {
println!("{app}");
}

View File

@@ -39,12 +39,7 @@ async fn main() {
.subcommand(
Command::new("get")
.about("Gets a value")
.arg(arg!(-v --value <STRING> "the value to get").required(true))
.arg(arg!(-d --domain <STRING> "the domain to get in").required(false)),
)
.subcommand(
Command::new("get_all")
.about("Gets all")
.arg(arg!(-v --value <STRING> "the value to get").required(false))
.arg(arg!(-d --domain <STRING> "the domain to get in").required(false)),
)
.subcommand(
@@ -86,8 +81,8 @@ async fn main() {
match matches.subcommand() {
Some(("get", sub_m)) => {
let key = sub_m.get_one::<String>("value").unwrap();
let domain = sub_m.get_one::<String>("domain").cloned();
let key = sub_m.get_one::<String>("value").map(|x| x.as_str());
let domain = sub_m.get_one::<String>("domain").map(|x| x.as_str());
match lockdown_client.get_value(key, domain).await {
Ok(value) => {
@@ -98,27 +93,18 @@ async fn main() {
}
}
}
Some(("get_all", sub_m)) => {
let domain = sub_m.get_one::<String>("domain").cloned();
match lockdown_client.get_all_values(domain).await {
Ok(value) => {
println!("{}", pretty_print_plist(&plist::Value::Dictionary(value)));
}
Err(e) => {
eprintln!("Error getting value: {e}");
}
}
}
Some(("set", sub_m)) => {
let key = sub_m.get_one::<String>("key").unwrap();
let value_str = sub_m.get_one::<String>("value").unwrap();
let domain = sub_m.get_one::<String>("domain").cloned();
let domain = sub_m.get_one::<String>("domain");
let value = Value::String(value_str.clone());
match lockdown_client.set_value(key, value, domain).await {
match lockdown_client
.set_value(key, value, domain.map(|x| x.as_str()))
.await
{
Ok(()) => println!("Successfully set"),
Err(e) => eprintln!("Error setting value: {e}"),
}

View File

@@ -89,7 +89,10 @@ async fn main() {
.await
.expect("Unable to connect to lockdown");
let product_version = match lockdown_client.get_value("ProductVersion", None).await {
let product_version = match lockdown_client
.get_value(Some("ProductVersion"), None)
.await
{
Ok(p) => p,
Err(_) => {
lockdown_client
@@ -97,7 +100,7 @@ async fn main() {
.await
.unwrap();
lockdown_client
.get_value("ProductVersion", None)
.get_value(Some("ProductVersion"), None)
.await
.unwrap()
}
@@ -182,21 +185,22 @@ async fn main() {
.await
.expect("Unable to read signature");
let unique_chip_id = match lockdown_client.get_value("UniqueChipID", None).await {
Ok(u) => u,
Err(_) => {
lockdown_client
.start_session(&provider.get_pairing_file().await.unwrap())
.await
.expect("Unable to start session");
lockdown_client
.get_value("UniqueChipID", None)
.await
.expect("Unable to get UniqueChipID")
let unique_chip_id =
match lockdown_client.get_value(Some("UniqueChipID"), None).await {
Ok(u) => u,
Err(_) => {
lockdown_client
.start_session(&provider.get_pairing_file().await.unwrap())
.await
.expect("Unable to start session");
lockdown_client
.get_value(Some("UniqueChipID"), None)
.await
.expect("Unable to get UniqueChipID")
}
}
}
.as_unsigned_integer()
.expect("Unexpected value for chip IP");
.as_unsigned_integer()
.expect("Unexpected value for chip IP");
mounter_client
.mount_personalized_with_callback(