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_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.
($($plist:tt)+) => {
$crate::plist_internal!($($plist)+)

View File

@@ -143,12 +143,13 @@ impl<R: ReadWrite> AppServiceClient<R> {
internal_apps: bool,
default_apps: bool,
) -> Result<Vec<AppListEntry>, IdeviceError> {
let mut options = plist::Dictionary::new();
options.insert("includeAppClips".into(), app_clips.into());
options.insert("includeRemovableApps".into(), removable_apps.into());
options.insert("includeHiddenApps".into(), hidden_apps.into());
options.insert("includeInternalApps".into(), internal_apps.into());
options.insert("includeDefaultApps".into(), default_apps.into());
let options = crate::plist!(dict {
"includeAppClips": app_clips,
"includeRemovableApps": removable_apps,
"includeHiddenApps": hidden_apps,
"includeInternalApps": internal_apps,
"includeDefaultApps": default_apps,
});
let res = self
.inner
.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:"
.into(),
);
let mut options = Dictionary::new();
options.insert("StartSuspendedKey".into(), start_suspended.into());
options.insert("KillExisting".into(), kill_existing.into());
let options = crate::plist!(dict {
"StartSuspendedKey": start_suspended,
"KillExisting": kill_existing
});
let env_vars = match env_vars {
Some(e) => e,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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