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

View File

@@ -3,7 +3,7 @@
use std::ptr::null_mut; use std::ptr::null_mut;
use idevice::{IdeviceError, IdeviceService, lockdown::LockdownClient, provider::IdeviceProvider}; use idevice::{IdeviceError, IdeviceService, lockdown::LockdownClient, provider::IdeviceProvider};
use plist_ffi::{PlistWrapper, plist_t}; use plist_ffi::plist_t;
use crate::{ use crate::{
IdeviceFfiError, IdeviceHandle, IdevicePairingFile, RUNTIME, ffi_err, IdeviceFfiError, IdeviceHandle, IdevicePairingFile, RUNTIME, ffi_err,
@@ -183,18 +183,26 @@ pub unsafe extern "C" fn lockdownd_get_value(
return ffi_err!(IdeviceError::FfiInvalidArg); return ffi_err!(IdeviceError::FfiInvalidArg);
} }
let value = unsafe { std::ffi::CStr::from_ptr(key) } let value = if key.is_null() {
.to_string_lossy() None
.into_owned(); } 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() { let domain = if domain.is_null() {
None None
} else { } else {
Some( Some(match unsafe { std::ffi::CStr::from_ptr(domain) }.to_str() {
unsafe { std::ffi::CStr::from_ptr(domain) } Ok(v) => v,
.to_string_lossy() Err(_) => {
.into_owned(), return ffi_err!(IdeviceError::InvalidCString);
) }
})
}; };
let res: Result<plist::Value, IdeviceError> = RUNTIME.block_on(async move { 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 /// Frees a LockdowndClient handle
/// ///
/// # Arguments /// # 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 = if !personalized_image_type.is_null() {
let image_type_cstr = unsafe { std::ffi::CStr::from_ptr(personalized_image_type) }; let image_type_cstr = unsafe { std::ffi::CStr::from_ptr(personalized_image_type) };
match image_type_cstr.to_str() { match image_type_cstr.to_str() {
Ok(s) => Some(s.to_string()), Ok(s) => Some(s),
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg), Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
} }
} else { } 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 = if !image_type.is_null() {
let image_type_cstr = unsafe { std::ffi::CStr::from_ptr(image_type) }; let image_type_cstr = unsafe { std::ffi::CStr::from_ptr(image_type) };
match image_type_cstr.to_str() { match image_type_cstr.to_str() {
Ok(s) => Some(s.to_string()), Ok(s) => Some(s),
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg), Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
} }
} else { } else {

View File

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

View File

@@ -86,16 +86,16 @@ impl LockdownClient {
/// ``` /// ```
pub async fn get_value( pub async fn get_value(
&mut self, &mut self,
key: impl Into<String>, key: Option<&str>,
domain: Option<String>, domain: Option<&str>,
) -> Result<Value, IdeviceError> { ) -> Result<Value, IdeviceError> {
let key = key.into();
let mut request = plist::Dictionary::new(); let mut request = plist::Dictionary::new();
request.insert("Label".into(), self.idevice.label.clone().into()); request.insert("Label".into(), self.idevice.label.clone().into());
request.insert("Request".into(), "GetValue".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 { if let Some(domain) = domain {
request.insert("Domain".into(), domain.into()); 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 /// Sets a value on the device
/// ///
/// # Arguments /// # Arguments
@@ -167,7 +130,7 @@ impl LockdownClient {
&mut self, &mut self,
key: impl Into<String>, key: impl Into<String>,
value: Value, value: Value,
domain: Option<String>, domain: Option<&str>,
) -> Result<(), IdeviceError> { ) -> Result<(), IdeviceError> {
let key = key.into(); let key = key.into();
@@ -321,7 +284,7 @@ impl LockdownClient {
let host_id = host_id.into(); let host_id = host_id.into();
let system_buid = system_buid.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()) { let pub_key = match pub_key.as_data().map(|x| x.to_vec()) {
Some(p) => p, Some(p) => p,
None => { 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() { let wifi_mac = match wifi_mac.as_string() {
Some(w) => w, Some(w) => w,
None => { None => {

View File

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

View File

@@ -67,7 +67,9 @@ async fn main() {
println!( println!(
"{:?}", "{:?}",
lockdown_client.get_value("ProductVersion", None).await lockdown_client
.get_value(Some("ProductVersion"), None)
.await
); );
println!( println!(
@@ -82,5 +84,5 @@ async fn main() {
.await .await
); );
println!("{:?}", lockdown_client.idevice.get_type().await.unwrap()); 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 .await
.expect("Unable to connect to instproxy"); .expect("Unable to connect to instproxy");
if matches.subcommand_matches("lookup").is_some() { if matches.subcommand_matches("lookup").is_some() {
let apps = instproxy_client let apps = instproxy_client.get_apps(Some("User"), None).await.unwrap();
.get_apps(Some("User".to_string()), None)
.await
.unwrap();
for app in apps.keys() { for app in apps.keys() {
println!("{app}"); println!("{app}");
} }

View File

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

View File

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