Port some services to plist macro

This commit is contained in:
Jackson Coxson
2025-08-17 22:31:19 -06:00
parent 47dbab0155
commit f388aaaf2d
11 changed files with 288 additions and 440 deletions

View File

@@ -52,6 +52,23 @@
/// ``` /// ```
#[macro_export] #[macro_export]
macro_rules! plist { macro_rules! plist {
// Force: dictionary out
(dict { $($tt:tt)+ }) => {{
let mut object = plist::Dictionary::new();
$crate::plist_internal!(@object object () ($($tt)+) ($($tt)+));
object
}};
// Force: value out (explicit, though default already does this)
(value { $($tt:tt)+ }) => {
$crate::plist_internal!({ $($tt)+ })
};
// Force: raw vec of plist::Value out
(array [ $($tt:tt)+ ]) => {
$crate::plist_internal!(@array [] $($tt)+)
};
// Hide distracting implementation details from the generated rustdoc. // Hide distracting implementation details from the generated rustdoc.
($($plist:tt)+) => { ($($plist:tt)+) => {
$crate::plist_internal!($($plist)+) $crate::plist_internal!($($plist)+)

View File

@@ -143,12 +143,13 @@ impl<R: ReadWrite> AppServiceClient<R> {
internal_apps: bool, internal_apps: bool,
default_apps: bool, default_apps: bool,
) -> Result<Vec<AppListEntry>, IdeviceError> { ) -> Result<Vec<AppListEntry>, IdeviceError> {
let mut options = plist::Dictionary::new(); let options = crate::plist!(dict {
options.insert("includeAppClips".into(), app_clips.into()); "includeAppClips": app_clips,
options.insert("includeRemovableApps".into(), removable_apps.into()); "includeRemovableApps": removable_apps,
options.insert("includeHiddenApps".into(), hidden_apps.into()); "includeHiddenApps": hidden_apps,
options.insert("includeInternalApps".into(), internal_apps.into()); "includeInternalApps": internal_apps,
options.insert("includeDefaultApps".into(), default_apps.into()); "includeDefaultApps": default_apps,
});
let res = self let res = self
.inner .inner
.invoke("com.apple.coredevice.feature.listapps", Some(options)) .invoke("com.apple.coredevice.feature.listapps", Some(options))

View File

@@ -98,9 +98,10 @@ impl<'a, R: ReadWrite> ProcessControlClient<'a, R> {
"launchSuspendedProcessWithDevicePath:bundleIdentifier:environment:arguments:options:" "launchSuspendedProcessWithDevicePath:bundleIdentifier:environment:arguments:options:"
.into(), .into(),
); );
let mut options = Dictionary::new(); let options = crate::plist!(dict {
options.insert("StartSuspendedKey".into(), start_suspended.into()); "StartSuspendedKey": start_suspended,
options.insert("KillExisting".into(), kill_existing.into()); "KillExisting": kill_existing
});
let env_vars = match env_vars { let env_vars = match env_vars {
Some(e) => e, Some(e) => e,

View File

@@ -90,11 +90,10 @@ impl HeartbeatClient {
/// # Errors /// # Errors
/// Returns `IdeviceError` if the message fails to send /// Returns `IdeviceError` if the message fails to send
pub async fn send_polo(&mut self) -> Result<(), IdeviceError> { pub async fn send_polo(&mut self) -> Result<(), IdeviceError> {
let mut req = plist::Dictionary::new(); let req = crate::plist!({
req.insert("Command".into(), "Polo".into()); "Command": "Polo"
self.idevice });
.send_plist(plist::Value::Dictionary(req.clone())) self.idevice.send_plist(req).await?;
.await?;
Ok(()) Ok(())
} }
} }

View File

@@ -70,22 +70,15 @@ impl InstallationProxyClient {
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"); let application_type = application_type.unwrap_or("Any");
let mut options = plist::Dictionary::new();
if let Some(ids) = bundle_identifiers {
let ids = ids
.into_iter()
.map(plist::Value::String)
.collect::<Vec<plist::Value>>();
options.insert("BundleIDs".into(), ids.into());
}
options.insert("ApplicationType".into(), application_type.into());
let mut req = plist::Dictionary::new(); let req = crate::plist!({
req.insert("Command".into(), "Lookup".into()); "Command": "Lookup",
req.insert("ClientOptions".into(), plist::Value::Dictionary(options)); "ClientOptions": {
self.idevice "ApplicationType": application_type,
.send_plist(plist::Value::Dictionary(req)) "BundleIDs":? bundle_identifiers,
.await?; }
});
self.idevice.send_plist(req).await?;
let mut res = self.idevice.read_plist().await?; let mut res = self.idevice.read_plist().await?;
match res.remove("LookupResult") { match res.remove("LookupResult") {
@@ -155,14 +148,13 @@ impl InstallationProxyClient {
let package_path = package_path.into(); let package_path = package_path.into();
let options = options.unwrap_or(plist::Value::Dictionary(Dictionary::new())); let options = options.unwrap_or(plist::Value::Dictionary(Dictionary::new()));
let mut command = Dictionary::new(); let command = crate::plist!({
command.insert("Command".into(), "Install".into()); "Command": "Install",
command.insert("ClientOptions".into(), options); "ClientOptions": options,
command.insert("PackagePath".into(), package_path.into()); "PackagePath": package_path,
});
self.idevice self.idevice.send_plist(command).await?;
.send_plist(plist::Value::Dictionary(command))
.await?;
self.watch_completion(callback, state).await self.watch_completion(callback, state).await
} }
@@ -220,14 +212,13 @@ impl InstallationProxyClient {
let package_path = package_path.into(); let package_path = package_path.into();
let options = options.unwrap_or(plist::Value::Dictionary(Dictionary::new())); let options = options.unwrap_or(plist::Value::Dictionary(Dictionary::new()));
let mut command = Dictionary::new(); let command = crate::plist!({
command.insert("Command".into(), "Upgrade".into()); "Command": "Upgrade",
command.insert("ClientOptions".into(), options); "ClientOptions": options,
command.insert("PackagePath".into(), package_path.into()); "PackagePath": package_path,
});
self.idevice self.idevice.send_plist(command).await?;
.send_plist(plist::Value::Dictionary(command))
.await?;
self.watch_completion(callback, state).await self.watch_completion(callback, state).await
} }
@@ -285,14 +276,13 @@ impl InstallationProxyClient {
let bundle_id = bundle_id.into(); let bundle_id = bundle_id.into();
let options = options.unwrap_or(plist::Value::Dictionary(Dictionary::new())); let options = options.unwrap_or(plist::Value::Dictionary(Dictionary::new()));
let mut command = Dictionary::new(); let command = crate::plist!({
command.insert("Command".into(), "Uninstall".into()); "Command": "Uninstall",
command.insert("ApplicationIdentifier".into(), bundle_id.into()); "ApplicationIdentifier": bundle_id,
command.insert("ClientOptions".into(), options); "ClientOptions": options,
});
self.idevice self.idevice.send_plist(command).await?;
.send_plist(plist::Value::Dictionary(command))
.await?;
self.watch_completion(callback, state).await self.watch_completion(callback, state).await
} }
@@ -317,14 +307,13 @@ impl InstallationProxyClient {
) -> Result<bool, IdeviceError> { ) -> Result<bool, IdeviceError> {
let options = options.unwrap_or(plist::Value::Dictionary(Dictionary::new())); let options = options.unwrap_or(plist::Value::Dictionary(Dictionary::new()));
let mut command = Dictionary::new(); let command = crate::plist!({
command.insert("Command".into(), "CheckCapabilitiesMatch".into()); "Command": "CheckCapabilitiesMatch",
command.insert("ClientOptions".into(), options); "ClientOptions": options,
command.insert("Capabilities".into(), capabilities.into()); "Capabilities": capabilities
});
self.idevice self.idevice.send_plist(command).await?;
.send_plist(plist::Value::Dictionary(command))
.await?;
let mut res = self.idevice.read_plist().await?; let mut res = self.idevice.read_plist().await?;
if let Some(caps) = res.remove("LookupResult").and_then(|x| x.as_boolean()) { if let Some(caps) = res.remove("LookupResult").and_then(|x| x.as_boolean()) {
@@ -355,13 +344,12 @@ impl InstallationProxyClient {
) -> Result<Vec<plist::Value>, IdeviceError> { ) -> Result<Vec<plist::Value>, IdeviceError> {
let options = options.unwrap_or(plist::Value::Dictionary(Dictionary::new())); let options = options.unwrap_or(plist::Value::Dictionary(Dictionary::new()));
let mut command = Dictionary::new(); let command = crate::plist!({
command.insert("Command".into(), "Browse".into()); "Command": "Browse",
command.insert("ClientOptions".into(), options); "ClientOptions": options,
});
self.idevice self.idevice.send_plist(command).await?;
.send_plist(plist::Value::Dictionary(command))
.await?;
let mut values = Vec::new(); let mut values = Vec::new();
loop { loop {

View File

@@ -93,20 +93,13 @@ impl LockdownClient {
key: Option<&str>, key: Option<&str>,
domain: Option<&str>, domain: Option<&str>,
) -> Result<Value, IdeviceError> { ) -> Result<Value, IdeviceError> {
let mut request = plist::Dictionary::new(); let request = crate::plist!({
request.insert("Label".into(), self.idevice.label.clone().into()); "Label": self.idevice.label.clone(),
request.insert("Request".into(), "GetValue".into()); "Request": "GetValue",
"Key":? key,
if let Some(key) = key { "Domain":? domain
request.insert("Key".into(), key.into()); });
} self.idevice.send_plist(request).await?;
if let Some(domain) = domain {
request.insert("Domain".into(), domain.into());
}
self.idevice
.send_plist(plist::Value::Dictionary(request))
.await?;
let message: plist::Dictionary = self.idevice.read_plist().await?; let message: plist::Dictionary = self.idevice.read_plist().await?;
match message.get("Value") { match message.get("Value") {
Some(m) => Ok(m.to_owned()), Some(m) => Ok(m.to_owned()),
@@ -138,19 +131,15 @@ impl LockdownClient {
) -> Result<(), IdeviceError> { ) -> Result<(), IdeviceError> {
let key = key.into(); let key = key.into();
let mut req = plist::Dictionary::new(); let req = crate::plist!({
req.insert("Label".into(), self.idevice.label.clone().into()); "Label": self.idevice.label.clone(),
req.insert("Request".into(), "SetValue".into()); "Request": "SetValue",
req.insert("Key".into(), key.into()); "Key": key,
req.insert("Value".into(), value); "Value": value,
"Domain":? domain
});
if let Some(domain) = domain { self.idevice.send_plist(req).await?;
req.insert("Domain".into(), domain.into());
}
self.idevice
.send_plist(plist::Value::Dictionary(req))
.await?;
self.idevice.read_plist().await?; self.idevice.read_plist().await?;
Ok(()) Ok(())
@@ -177,28 +166,14 @@ impl LockdownClient {
return Err(IdeviceError::NoEstablishedConnection); return Err(IdeviceError::NoEstablishedConnection);
} }
let mut request = plist::Dictionary::new(); let request = crate::plist!({
request.insert( "Label": self.idevice.label.clone(),
"Label".to_string(), "Request": "StartSession",
plist::Value::String(self.idevice.label.clone()), "HostID": pairing_file.host_id.clone(),
); "SystemBUID": pairing_file.system_buid.clone()
request.insert( });
"Request".to_string(), self.idevice.send_plist(request).await?;
plist::Value::String("StartSession".to_string()),
);
request.insert(
"HostID".to_string(),
plist::Value::String(pairing_file.host_id.clone()),
);
request.insert(
"SystemBUID".to_string(),
plist::Value::String(pairing_file.system_buid.clone()),
);
self.idevice
.send_plist(plist::Value::Dictionary(request))
.await?;
let response = self.idevice.read_plist().await?; let response = self.idevice.read_plist().await?;
match response.get("EnableSessionSSL") { match response.get("EnableSessionSSL") {
@@ -236,12 +211,11 @@ impl LockdownClient {
identifier: impl Into<String>, identifier: impl Into<String>,
) -> Result<(u16, bool), IdeviceError> { ) -> Result<(u16, bool), IdeviceError> {
let identifier = identifier.into(); let identifier = identifier.into();
let mut req = plist::Dictionary::new(); let req = crate::plist!({
req.insert("Request".into(), "StartService".into()); "Request": "StartService",
req.insert("Service".into(), identifier.into()); "Service": identifier,
self.idevice });
.send_plist(plist::Value::Dictionary(req)) self.idevice.send_plist(req).await?;
.await?;
let response = self.idevice.read_plist().await?; let response = self.idevice.read_plist().await?;
let ssl = match response.get("EnableServiceSSL") { let ssl = match response.get("EnableServiceSSL") {
@@ -307,37 +281,29 @@ impl LockdownClient {
}; };
let ca = crate::ca::generate_certificates(&pub_key, None).unwrap(); let ca = crate::ca::generate_certificates(&pub_key, None).unwrap();
let mut pair_record = plist::Dictionary::new(); let mut pair_record = crate::plist!(dict {
pair_record.insert("DevicePublicKey".into(), plist::Value::Data(pub_key)); "DevicePublicKey": pub_key,
pair_record.insert("DeviceCertificate".into(), plist::Value::Data(ca.dev_cert)); "DeviceCertificate": ca.dev_cert,
pair_record.insert( "HostCertificate": ca.host_cert.clone(),
"HostCertificate".into(), "HostID": host_id,
plist::Value::Data(ca.host_cert.clone()), "RootCertificate": ca.host_cert,
); "RootPrivateKey": ca.private_key.clone(),
pair_record.insert("HostID".into(), host_id.into()); "WiFiMACAddress": wifi_mac,
pair_record.insert("RootCertificate".into(), plist::Value::Data(ca.host_cert)); "SystemBUID": system_buid,
pair_record.insert( });
"RootPrivateKey".into(),
plist::Value::Data(ca.private_key.clone()),
);
pair_record.insert("WiFiMACAddress".into(), wifi_mac.into());
pair_record.insert("SystemBUID".into(), system_buid.into());
let mut options = plist::Dictionary::new(); let req = crate::plist!({
options.insert("ExtendedPairingErrors".into(), true.into()); "Label": self.idevice.label.clone(),
"Request": "Pair",
let mut req = plist::Dictionary::new(); "PairRecord": pair_record.clone(),
req.insert("Label".into(), self.idevice.label.clone().into()); "ProtocolVersion": "2",
req.insert("Request".into(), "Pair".into()); "PairingOptions": {
req.insert( "ExtendedPairingErrors": true
"PairRecord".into(), }
plist::Value::Dictionary(pair_record.clone()), });
);
req.insert("ProtocolVersion".into(), "2".into());
req.insert("PairingOptions".into(), plist::Value::Dictionary(options));
loop { loop {
self.idevice.send_plist(req.clone().into()).await?; self.idevice.send_plist(req.clone()).await?;
match self.idevice.read_plist().await { match self.idevice.read_plist().await {
Ok(escrow) => { Ok(escrow) => {
pair_record.insert("HostPrivateKey".into(), plist::Value::Data(ca.private_key)); pair_record.insert("HostPrivateKey".into(), plist::Value::Data(ca.private_key));

View File

@@ -55,11 +55,10 @@ impl ImageMounter {
/// # Errors /// # Errors
/// Returns `IdeviceError` if communication fails or response is malformed /// Returns `IdeviceError` if communication fails or response is malformed
pub async fn copy_devices(&mut self) -> Result<Vec<plist::Value>, IdeviceError> { pub async fn copy_devices(&mut self) -> Result<Vec<plist::Value>, IdeviceError> {
let mut req = plist::Dictionary::new(); let req = crate::plist!({
req.insert("Command".into(), "CopyDevices".into()); "Command": "CopyDevices"
self.idevice });
.send_plist(plist::Value::Dictionary(req)) self.idevice.send_plist(req).await?;
.await?;
let mut res = self.idevice.read_plist().await?; let mut res = self.idevice.read_plist().await?;
match res.remove("EntryList") { match res.remove("EntryList") {
@@ -83,12 +82,11 @@ impl ImageMounter {
image_type: impl Into<&str>, 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 req = crate::plist!({
req.insert("Command".into(), "LookupImage".into()); "Command": "LookupImage",
req.insert("ImageType".into(), image_type.into()); "ImageType": image_type
self.idevice });
.send_plist(plist::Value::Dictionary(req)) self.idevice.send_plist(req).await?;
.await?;
let res = self.idevice.read_plist().await?; let res = self.idevice.read_plist().await?;
match res.get("ImageSignature") { match res.get("ImageSignature") {
@@ -152,14 +150,13 @@ impl ImageMounter {
} }
}; };
let mut req = plist::Dictionary::new(); let req = crate::plist!({
req.insert("Command".into(), "ReceiveBytes".into()); "Command": "ReceiveBytes",
req.insert("ImageType".into(), image_type.into()); "ImageType": image_type,
req.insert("ImageSize".into(), image_size.into()); "ImageSize": image_size,
req.insert("ImageSignature".into(), plist::Value::Data(signature)); "ImageSignature": signature,
self.idevice });
.send_plist(plist::Value::Dictionary(req)) self.idevice.send_plist(req).await?;
.await?;
let res = self.idevice.read_plist().await?; let res = self.idevice.read_plist().await?;
match res.get("Status") { match res.get("Status") {
@@ -210,19 +207,14 @@ impl ImageMounter {
) -> Result<(), IdeviceError> { ) -> Result<(), IdeviceError> {
let image_type = image_type.into(); let image_type = image_type.into();
let mut req = plist::Dictionary::new(); let req = crate::plist!({
req.insert("Command".into(), "MountImage".into()); "Command": "MountImage",
req.insert("ImageType".into(), image_type.into()); "ImageType": image_type,
req.insert("ImageSignature".into(), plist::Value::Data(signature)); "ImageSignature": signature,
if let Some(trust_cache) = trust_cache { "ImageTrustCache":? trust_cache,
req.insert("ImageTrustCache".into(), plist::Value::Data(trust_cache)); "ImageInfoPlist":? info_plist,
} });
if let Some(info_plist) = info_plist { self.idevice.send_plist(req).await?;
req.insert("ImageInfoPlist".into(), info_plist);
}
self.idevice
.send_plist(plist::Value::Dictionary(req))
.await?;
let res = self.idevice.read_plist().await?; let res = self.idevice.read_plist().await?;
@@ -253,12 +245,11 @@ impl ImageMounter {
mount_path: impl Into<String>, mount_path: impl Into<String>,
) -> Result<(), IdeviceError> { ) -> Result<(), IdeviceError> {
let mount_path = mount_path.into(); let mount_path = mount_path.into();
let mut req = plist::Dictionary::new(); let req = crate::plist!({
req.insert("Command".into(), "UnmountImage".into()); "Command": "UnmountImage",
req.insert("MountPath".into(), mount_path.into()); "MountPath": mount_path,
self.idevice });
.send_plist(plist::Value::Dictionary(req)) self.idevice.send_plist(req).await?;
.await?;
let res = self.idevice.read_plist().await?; let res = self.idevice.read_plist().await?;
match res.get("Status") { match res.get("Status") {
@@ -288,14 +279,13 @@ impl ImageMounter {
) -> 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 req = crate::plist!({
req.insert("Command".into(), "QueryPersonalizationManifest".into()); "Command": "QueryPersonalizationManifest",
req.insert("PersonalizedImageType".into(), image_type.clone().into()); "PersonalizedImageType": image_type.clone(),
req.insert("ImageType".into(), image_type.into()); "ImageType": image_type,
req.insert("ImageSignature".into(), plist::Value::Data(signature)); "ImageSignature": signature
self.idevice });
.send_plist(plist::Value::Dictionary(req)) self.idevice.send_plist(req).await?;
.await?;
let mut res = self.idevice.read_plist().await?; let mut res = self.idevice.read_plist().await?;
match res.remove("ImageSignature") { match res.remove("ImageSignature") {
@@ -312,11 +302,10 @@ impl ImageMounter {
/// # Errors /// # Errors
/// Returns `IdeviceError` if query fails /// Returns `IdeviceError` if query fails
pub async fn query_developer_mode_status(&mut self) -> Result<bool, IdeviceError> { pub async fn query_developer_mode_status(&mut self) -> Result<bool, IdeviceError> {
let mut req = plist::Dictionary::new(); let req = crate::plist!({
req.insert("Command".into(), "QueryDeveloperModeStatus".into()); "Command": "QueryDeveloperModeStatus"
self.idevice });
.send_plist(plist::Value::Dictionary(req)) self.idevice.send_plist(req).await?;
.await?;
let res = self.idevice.read_plist().await?; let res = self.idevice.read_plist().await?;
match res.get("DeveloperModeStatus") { match res.get("DeveloperModeStatus") {
@@ -339,14 +328,11 @@ impl ImageMounter {
&mut self, &mut self,
personalized_image_type: Option<&str>, personalized_image_type: Option<&str>,
) -> Result<Vec<u8>, IdeviceError> { ) -> Result<Vec<u8>, IdeviceError> {
let mut req = plist::Dictionary::new(); let req = crate::plist!({
req.insert("Command".into(), "QueryNonce".into()); "Command": "QueryNonce",
if let Some(image_type) = personalized_image_type { "PersonalizedImageType":? personalized_image_type,
req.insert("PersonalizedImageType".into(), image_type.into()); });
} self.idevice.send_plist(req).await?;
self.idevice
.send_plist(plist::Value::Dictionary(req))
.await?;
let res = self.idevice.read_plist().await?; let res = self.idevice.read_plist().await?;
match res.get("PersonalizationNonce") { match res.get("PersonalizationNonce") {
@@ -369,14 +355,11 @@ impl ImageMounter {
&mut self, &mut self,
image_type: Option<&str>, image_type: Option<&str>,
) -> Result<plist::Dictionary, IdeviceError> { ) -> Result<plist::Dictionary, IdeviceError> {
let mut req = plist::Dictionary::new(); let req = crate::plist!({
req.insert("Command".into(), "QueryPersonalizationIdentifiers".into()); "Command": "QueryPersonalizationIdentifiers",
if let Some(image_type) = image_type { "PersonalizedImageType":? image_type,
req.insert("PersonalizedImageType".into(), image_type.into()); });
} self.idevice.send_plist(req).await?;
self.idevice
.send_plist(plist::Value::Dictionary(req))
.await?;
let res = self.idevice.read_plist().await?; let res = self.idevice.read_plist().await?;
match res.get("PersonalizationIdentifiers") { match res.get("PersonalizationIdentifiers") {
@@ -390,11 +373,10 @@ impl ImageMounter {
/// # Errors /// # Errors
/// Returns `IdeviceError` if operation fails /// Returns `IdeviceError` if operation fails
pub async fn roll_personalization_nonce(&mut self) -> Result<(), IdeviceError> { pub async fn roll_personalization_nonce(&mut self) -> Result<(), IdeviceError> {
let mut req = plist::Dictionary::new(); let req = crate::plist!({
req.insert("Command".into(), "RollPersonalizationNonce".into()); "Command": "RollPersonalizationNonce"
self.idevice });
.send_plist(plist::Value::Dictionary(req)) self.idevice.send_plist(req).await?;
.await?;
Ok(()) Ok(())
} }
@@ -404,11 +386,10 @@ impl ImageMounter {
/// # Errors /// # Errors
/// Returns `IdeviceError` if operation fails /// Returns `IdeviceError` if operation fails
pub async fn roll_cryptex_nonce(&mut self) -> Result<(), IdeviceError> { pub async fn roll_cryptex_nonce(&mut self) -> Result<(), IdeviceError> {
let mut req = plist::Dictionary::new(); let req = crate::plist!({
req.insert("Command".into(), "RollCryptexNonce".into()); "Command": "RollCryptexNonce"
self.idevice });
.send_plist(plist::Value::Dictionary(req)) self.idevice.send_plist(req).await?;
.await?;
Ok(()) Ok(())
} }
@@ -669,11 +650,12 @@ impl ImageMounter {
} }
}; };
let mut parameters = plist::Dictionary::new(); let parameters = crate::plist!(dict {
parameters.insert("ApProductionMode".into(), true.into()); "ApProductionMode": true,
parameters.insert("ApSecurityDomain".into(), 1.into()); "ApSecurityMode": 1,
parameters.insert("ApSecurityMode".into(), true.into()); "ApSecurityMode": true,
parameters.insert("ApSupportsImg4".into(), true.into()); "ApSupportsImg4": true
});
for (key, manifest_item) in manifest { for (key, manifest_item) in manifest {
println!("{key}, {manifest_item:?}"); println!("{key}, {manifest_item:?}");

View File

@@ -185,31 +185,14 @@ impl RestoreOptions {
} }
pub fn to_plist(&self) -> Dictionary { pub fn to_plist(&self) -> Dictionary {
let mut opts = Dictionary::new(); crate::plist!(dict {
opts.insert( "RestoreShouldReboot": self.reboot,
"RestoreShouldReboot".into(), "RestoreDontCopyBackup": !self.copy,
plist::Value::Boolean(self.reboot), "RestorePreserveSettings": self.preserve_settings,
); "RestoreSystemFiles": self.system_files,
opts.insert( "RemoveItemsNotRestored": self.remove_items_not_restored,
"RestoreDontCopyBackup".into(), "Password":? self.password.clone()
plist::Value::Boolean(!self.copy), })
);
opts.insert(
"RestorePreserveSettings".into(),
plist::Value::Boolean(self.preserve_settings),
);
opts.insert(
"RestoreSystemFiles".into(),
plist::Value::Boolean(self.system_files),
);
opts.insert(
"RemoveItemsNotRestored".into(),
plist::Value::Boolean(self.remove_items_not_restored),
);
if let Some(pw) = &self.password {
opts.insert("Password".into(), plist::Value::String(pw.clone()));
}
opts
} }
} }
@@ -297,12 +280,11 @@ impl MobileBackup2Client {
debug!("Starting mobilebackup2 version exchange"); debug!("Starting mobilebackup2 version exchange");
// Send supported protocol versions (matching libimobiledevice) // Send supported protocol versions (matching libimobiledevice)
let mut hello_dict = Dictionary::new(); let hello_dict = crate::plist!(dict {
let versions = vec![plist::Value::Real(2.0), plist::Value::Real(2.1)]; "SupportedProtocolVersions": [
hello_dict.insert( 2.0, 2.1
"SupportedProtocolVersions".into(), ]
plist::Value::Array(versions), });
);
self.send_device_link_message("Hello", Some(hello_dict)) self.send_device_link_message("Hello", Some(hello_dict))
.await?; .await?;
@@ -349,25 +331,15 @@ impl MobileBackup2Client {
message_name: &str, message_name: &str,
options: Option<Dictionary>, options: Option<Dictionary>,
) -> Result<(), IdeviceError> { ) -> Result<(), IdeviceError> {
// Create DLMessageProcessMessage array format
let mut message_array = Vec::new();
message_array.push(plist::Value::String("DLMessageProcessMessage".into()));
// Create the actual message dictionary // Create the actual message dictionary
let mut message_dict = Dictionary::new(); let message_dict = crate::plist!(dict {
message_dict.insert("MessageName".into(), message_name.into()); "MessageName": message_name,
:<? options,
if let Some(opts) = options { });
for (key, value) in opts {
message_dict.insert(key, value);
}
}
message_array.push(plist::Value::Dictionary(message_dict));
debug!("Sending device link message: {message_name}"); debug!("Sending device link message: {message_name}");
self.idevice self.idevice
.send_bplist(plist::Value::Array(message_array)) .send_bplist(crate::plist!(["DLMessageProcessMessage", message_dict]))
.await .await
} }
@@ -453,17 +425,12 @@ impl MobileBackup2Client {
source_identifier: Option<&str>, source_identifier: Option<&str>,
options: Option<Dictionary>, options: Option<Dictionary>,
) -> Result<(), IdeviceError> { ) -> Result<(), IdeviceError> {
let mut dict = Dictionary::new(); let dict = crate::plist!(dict {
if let Some(t) = target_identifier { "TargetIdentifier":? target_identifier,
dict.insert("TargetIdentifier".into(), t.into()); "SourceIdentifier":? source_identifier,
} "Options":? options,
if let Some(s) = source_identifier {
dict.insert("SourceIdentifier".into(), s.into());
}
if let Some(opts) = options {
dict.insert("Options".into(), plist::Value::Dictionary(opts));
// Special cases like Unback/EnableCloudBackup are handled by caller if needed // Special cases like Unback/EnableCloudBackup are handled by caller if needed
} });
self.send_device_link_message(request, Some(dict)).await self.send_device_link_message(request, Some(dict)).await
} }
@@ -1090,17 +1057,10 @@ impl MobileBackup2Client {
.ok_or(IdeviceError::InvalidHostID)?; .ok_or(IdeviceError::InvalidHostID)?;
self.assert_backup_exists(backup_root, source)?; self.assert_backup_exists(backup_root, source)?;
let mut dict = Dictionary::new(); let dict = crate::plist!(dict {
dict.insert( "TargetIdentifier": target_udid.unwrap(),
"TargetIdentifier".into(), "SourceIdentifier":? source_identifier,
plist::Value::String(target_udid.unwrap().to_string()), });
);
if let Some(src) = source_identifier {
dict.insert(
"SourceIdentifier".into(),
plist::Value::String(src.to_string()),
);
}
self.send_device_link_message("Info", Some(dict)).await?; self.send_device_link_message("Info", Some(dict)).await?;
match self.process_restore_dl_loop(backup_root).await? { match self.process_restore_dl_loop(backup_root).await? {
@@ -1121,16 +1081,11 @@ impl MobileBackup2Client {
.ok_or(IdeviceError::InvalidHostID)?; .ok_or(IdeviceError::InvalidHostID)?;
self.assert_backup_exists(backup_root, source)?; self.assert_backup_exists(backup_root, source)?;
let mut dict = Dictionary::new(); let dict = crate::plist!(dict {
dict.insert("MessageName".into(), plist::Value::String("List".into())); "MessageName": "List",
dict.insert( "TargetIdentifier": target_udid.unwrap(),
"TargetIdentifier".into(), "SourceIdentifier": source,
plist::Value::String(target_udid.unwrap().to_string()), });
);
dict.insert(
"SourceIdentifier".into(),
plist::Value::String(source.to_string()),
);
self.send_device_link_message("List", Some(dict)).await?; self.send_device_link_message("List", Some(dict)).await?;
match self.process_restore_dl_loop(backup_root).await? { match self.process_restore_dl_loop(backup_root).await? {
@@ -1151,20 +1106,12 @@ impl MobileBackup2Client {
.or(target_udid) .or(target_udid)
.ok_or(IdeviceError::InvalidHostID)?; .ok_or(IdeviceError::InvalidHostID)?;
self.assert_backup_exists(backup_root, source)?; self.assert_backup_exists(backup_root, source)?;
let dict = crate::plist!(dict {
let mut dict = Dictionary::new(); "TargetIdentifier": target_udid.unwrap(),
dict.insert( "MessageName": "Unback",
"TargetIdentifier".into(), "SourceIdentifier": source,
plist::Value::String(target_udid.unwrap().to_string()), "Password":? password
); });
dict.insert("MessageName".into(), plist::Value::String("Unback".into()));
dict.insert(
"SourceIdentifier".into(),
plist::Value::String(source.to_string()),
);
if let Some(pw) = password {
dict.insert("Password".into(), plist::Value::String(pw.to_string()));
}
self.send_device_link_message("Unback", Some(dict)).await?; self.send_device_link_message("Unback", Some(dict)).await?;
let _ = self.process_restore_dl_loop(backup_root).await?; let _ = self.process_restore_dl_loop(backup_root).await?;
Ok(()) Ok(())
@@ -1184,28 +1131,14 @@ impl MobileBackup2Client {
.or(target_udid) .or(target_udid)
.ok_or(IdeviceError::InvalidHostID)?; .ok_or(IdeviceError::InvalidHostID)?;
self.assert_backup_exists(backup_root, source)?; self.assert_backup_exists(backup_root, source)?;
let dict = crate::plist!(dict {
let mut dict = Dictionary::new(); "MessageName": "Extract",
dict.insert("MessageName".into(), plist::Value::String("Extract".into())); "TargetIdentifier": target_udid.unwrap(),
dict.insert( "DomainName": domain_name,
"TargetIdentifier".into(), "RelativePath": relative_path,
plist::Value::String(target_udid.unwrap().to_string()), "SourceIdentifier": source,
); "Password":? password,
dict.insert( });
"DomainName".into(),
plist::Value::String(domain_name.to_string()),
);
dict.insert(
"RelativePath".into(),
plist::Value::String(relative_path.to_string()),
);
dict.insert(
"SourceIdentifier".into(),
plist::Value::String(source.to_string()),
);
if let Some(pw) = password {
dict.insert("Password".into(), plist::Value::String(pw.to_string()));
}
self.send_device_link_message("Extract", Some(dict)).await?; self.send_device_link_message("Extract", Some(dict)).await?;
let _ = self.process_restore_dl_loop(backup_root).await?; let _ = self.process_restore_dl_loop(backup_root).await?;
Ok(()) Ok(())
@@ -1219,21 +1152,12 @@ impl MobileBackup2Client {
new: Option<&str>, new: Option<&str>,
) -> Result<(), IdeviceError> { ) -> Result<(), IdeviceError> {
let target_udid = self.idevice.udid(); let target_udid = self.idevice.udid();
let mut dict = Dictionary::new(); let dict = crate::plist!(dict {
dict.insert( "MessageName": "ChangePassword",
"MessageName".into(), "TargetIdentifier": target_udid.ok_or(IdeviceError::InvalidHostID)?,
plist::Value::String("ChangePassword".into()), "OldPassword":? old,
); "NewPassword":? new
dict.insert( });
"TargetIdentifier".into(),
plist::Value::String(target_udid.ok_or(IdeviceError::InvalidHostID)?.to_string()),
);
if let Some(o) = old {
dict.insert("OldPassword".into(), plist::Value::String(o.to_string()));
}
if let Some(n) = new {
dict.insert("NewPassword".into(), plist::Value::String(n.to_string()));
}
self.send_device_link_message("ChangePassword", Some(dict)) self.send_device_link_message("ChangePassword", Some(dict))
.await?; .await?;
let _ = self.process_restore_dl_loop(backup_root).await?; let _ = self.process_restore_dl_loop(backup_root).await?;
@@ -1243,15 +1167,10 @@ impl MobileBackup2Client {
/// Erase device via mobilebackup2 /// Erase device via mobilebackup2
pub async fn erase_device_from_path(&mut self, backup_root: &Path) -> Result<(), IdeviceError> { pub async fn erase_device_from_path(&mut self, backup_root: &Path) -> Result<(), IdeviceError> {
let target_udid = self.idevice.udid(); let target_udid = self.idevice.udid();
let mut dict = Dictionary::new(); let dict = crate::plist!(dict {
dict.insert( "MessageName": "EraseDevice",
"MessageName".into(), "TargetIdentifier": target_udid.ok_or(IdeviceError::InvalidHostID)?
plist::Value::String("EraseDevice".into()), });
);
dict.insert(
"TargetIdentifier".into(),
plist::Value::String(target_udid.ok_or(IdeviceError::InvalidHostID)?.to_string()),
);
self.send_device_link_message("EraseDevice", Some(dict)) self.send_device_link_message("EraseDevice", Some(dict))
.await?; .await?;
let _ = self.process_restore_dl_loop(backup_root).await?; let _ = self.process_restore_dl_loop(backup_root).await?;
@@ -1291,10 +1210,10 @@ impl MobileBackup2Client {
/// Returns `IdeviceError` if disconnection fails /// Returns `IdeviceError` if disconnection fails
pub async fn disconnect(&mut self) -> Result<(), IdeviceError> { pub async fn disconnect(&mut self) -> Result<(), IdeviceError> {
// Send DLMessageDisconnect array per DeviceLink protocol // Send DLMessageDisconnect array per DeviceLink protocol
let arr = vec![ let arr = crate::plist!(array [
plist::Value::String("DLMessageDisconnect".into()), "DLMessageDisconnect",
plist::Value::String("___EmptyParameterString___".into()), "___EmptyParameterString___"
]; ]);
self.send_dl_array(arr).await?; self.send_dl_array(arr).await?;
debug!("Disconnected from backup service"); debug!("Disconnected from backup service");
Ok(()) Ok(())

View File

@@ -4,7 +4,6 @@
//! https://github.com/doronz88/pymobiledevice3/blob/master/pymobiledevice3/services/os_trace.py //! https://github.com/doronz88/pymobiledevice3/blob/master/pymobiledevice3/services/os_trace.py
use chrono::{DateTime, NaiveDateTime}; use chrono::{DateTime, NaiveDateTime};
use plist::Dictionary;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use crate::{Idevice, IdeviceError, IdeviceService, obf}; use crate::{Idevice, IdeviceError, IdeviceService, obf};
@@ -70,15 +69,14 @@ impl OsTraceRelayClient {
Some(p) => p as i64, Some(p) => p as i64,
None => -1, None => -1,
}; };
let mut req = Dictionary::new(); let req = crate::plist!({
req.insert("Request".into(), "StartActivity".into()); "Request": "StartActivity",
req.insert("Pid".into(), Into::into(pid)); "Pid": pid,
req.insert("MessageFilter".into(), Into::into(65_535)); "MessageFilter": 65_535,
req.insert("StreamFlags".into(), Into::into(60)); "StreamFlags": 60
});
self.idevice self.idevice.send_bplist(req).await?;
.send_bplist(plist::Value::Dictionary(req))
.await?;
// Read a single byte // Read a single byte
self.idevice.read_raw(1).await?; self.idevice.read_raw(1).await?;
@@ -100,12 +98,11 @@ impl OsTraceRelayClient {
/// Get the list of available PIDs /// Get the list of available PIDs
pub async fn get_pid_list(&mut self) -> Result<Vec<u64>, IdeviceError> { pub async fn get_pid_list(&mut self) -> Result<Vec<u64>, IdeviceError> {
let mut req = Dictionary::new(); let req = crate::plist!({
req.insert("Request".into(), "PidList".into()); "Request": "PidList"
});
self.idevice self.idevice.send_bplist(req).await?;
.send_bplist(plist::Value::Dictionary(req))
.await?;
// Read a single byte // Read a single byte
self.idevice.read_raw(1).await?; self.idevice.read_raw(1).await?;
@@ -133,24 +130,14 @@ impl OsTraceRelayClient {
age_limit: Option<u64>, age_limit: Option<u64>,
start_time: Option<u64>, start_time: Option<u64>,
) -> Result<(), IdeviceError> { ) -> Result<(), IdeviceError> {
let mut req = Dictionary::new(); let req = crate::plist!({
req.insert("Request".into(), "CreateArchive".into()); "Request": "CreateArchive",
"SizeLimit":? size_limit,
"AgeLimit":? age_limit,
"StartTime":? start_time,
});
if let Some(size) = size_limit { self.idevice.send_bplist(req).await?;
req.insert("SizeLimit".into(), size.into());
}
if let Some(age) = age_limit {
req.insert("AgeLimit".into(), age.into());
}
if let Some(time) = start_time {
req.insert("StartTime".into(), time.into());
}
self.idevice
.send_bplist(plist::Value::Dictionary(req))
.await?;
// Read a single byte // Read a single byte
if self.idevice.read_raw(1).await?[0] != 1 { if self.idevice.read_raw(1).await?[0] != 1 {

View File

@@ -35,12 +35,11 @@ impl RestoreServiceClient {
/// Enter recovery /// Enter recovery
pub async fn enter_recovery(&mut self) -> Result<(), IdeviceError> { pub async fn enter_recovery(&mut self) -> Result<(), IdeviceError> {
let mut req = Dictionary::new(); let req = crate::plist!({
req.insert("command".into(), "recovery".into()); "command": "recovery"
});
self.stream self.stream.send_object(req, true).await?;
.send_object(plist::Value::Dictionary(req), true)
.await?;
let res = self.stream.recv().await?; let res = self.stream.recv().await?;
let mut res = match res { let mut res = match res {
@@ -69,12 +68,10 @@ impl RestoreServiceClient {
/// Reboot /// Reboot
pub async fn reboot(&mut self) -> Result<(), IdeviceError> { pub async fn reboot(&mut self) -> Result<(), IdeviceError> {
let mut req = Dictionary::new(); let req = crate::plist!({
req.insert("command".into(), "reboot".into()); "command": "reboot"
});
self.stream self.stream.send_object(req, true).await?;
.send_object(plist::Value::Dictionary(req), true)
.await?;
let res = self.stream.recv().await?; let res = self.stream.recv().await?;
let mut res = match res { let mut res = match res {
@@ -103,12 +100,10 @@ impl RestoreServiceClient {
/// Get preflightinfo /// Get preflightinfo
pub async fn get_preflightinfo(&mut self) -> Result<Dictionary, IdeviceError> { pub async fn get_preflightinfo(&mut self) -> Result<Dictionary, IdeviceError> {
let mut req = Dictionary::new(); let req = crate::plist!({
req.insert("command".into(), "getpreflightinfo".into()); "command": "getpreflightinfo"
});
self.stream self.stream.send_object(req, true).await?;
.send_object(plist::Value::Dictionary(req), true)
.await?;
let res = self.stream.recv().await?; let res = self.stream.recv().await?;
let mut res = match res { let mut res = match res {
@@ -133,12 +128,10 @@ impl RestoreServiceClient {
/// Get nonces /// Get nonces
/// Doesn't seem to work /// Doesn't seem to work
pub async fn get_nonces(&mut self) -> Result<Dictionary, IdeviceError> { pub async fn get_nonces(&mut self) -> Result<Dictionary, IdeviceError> {
let mut req = Dictionary::new(); let req = crate::plist!({
req.insert("command".into(), "getnonces".into()); "command": "getnonces"
});
self.stream self.stream.send_object(req, true).await?;
.send_object(plist::Value::Dictionary(req), true)
.await?;
let res = self.stream.recv().await?; let res = self.stream.recv().await?;
let mut res = match res { let mut res = match res {
@@ -163,12 +156,10 @@ impl RestoreServiceClient {
/// Get app parameters /// Get app parameters
/// Doesn't seem to work /// Doesn't seem to work
pub async fn get_app_parameters(&mut self) -> Result<Dictionary, IdeviceError> { pub async fn get_app_parameters(&mut self) -> Result<Dictionary, IdeviceError> {
let mut req = Dictionary::new(); let req = crate::plist!({
req.insert("command".into(), "getappparameters".into()); "command": "getappparameters"
});
self.stream self.stream.send_object(req, true).await?;
.send_object(plist::Value::Dictionary(req), true)
.await?;
let res = self.stream.recv().await?; let res = self.stream.recv().await?;
let mut res = match res { let mut res = match res {
@@ -195,13 +186,11 @@ impl RestoreServiceClient {
pub async fn restore_lang(&mut self, language: impl Into<String>) -> Result<(), IdeviceError> { pub async fn restore_lang(&mut self, language: impl Into<String>) -> Result<(), IdeviceError> {
let language = language.into(); let language = language.into();
let mut req = Dictionary::new(); let req = crate::plist!({
req.insert("command".into(), "restorelang".into()); "command": "restorelang",
req.insert("argument".into(), language.into()); "argument": language,
});
self.stream self.stream.send_object(req, true).await?;
.send_object(plist::Value::Dictionary(req), true)
.await?;
let res = self.stream.recv().await?; let res = self.stream.recv().await?;
let mut res = match res { let mut res = match res {

View File

@@ -57,12 +57,11 @@ impl SpringBoardServicesClient {
&mut self, &mut self,
bundle_identifier: String, bundle_identifier: String,
) -> Result<Vec<u8>, IdeviceError> { ) -> Result<Vec<u8>, IdeviceError> {
let mut req = plist::Dictionary::new(); let req = crate::plist!({
req.insert("command".into(), "getIconPNGData".into()); "command": "getIconPNGData",
req.insert("bundleId".into(), bundle_identifier.into()); "bundleId": bundle_identifier,
self.idevice });
.send_plist(plist::Value::Dictionary(req)) self.idevice.send_plist(req).await?;
.await?;
let mut res = self.idevice.read_plist().await?; let mut res = self.idevice.read_plist().await?;
match res.remove("pngData") { match res.remove("pngData") {