diff --git a/idevice/src/utils/installation/helpers.rs b/idevice/src/utils/installation/helpers.rs index d2c3e9d..83a75aa 100644 --- a/idevice/src/utils/installation/helpers.rs +++ b/idevice/src/utils/installation/helpers.rs @@ -11,6 +11,9 @@ use crate::{ provider::IdeviceProvider, }; +#[cfg(feature = "rsd")] +use crate::{RsdService, provider::RsdProvider, rsd}; + pub const PUBLIC_STAGING: &str = "PublicStaging"; pub const IPCC_REMOTE_FILE: &str = "idevice.ipcc"; @@ -222,6 +225,37 @@ async fn upload_file_to_public_staging>( }) } +/// Upload a file to `PublicStaging` over RSD and return its InstallationProxy path +#[cfg(feature = "rsd")] +async fn upload_file_to_public_staging_rsd>( + provider: &mut impl RsdProvider, + handshake: &mut rsd::RsdHandshake, + file: P, +) -> Result { + let mut afc = AfcClient::connect_rsd(provider, handshake).await?; + + ensure_public_staging(&mut afc).await?; + + let file = file.as_ref(); + + let package_type = determine_package_type(&file).await?; + + let remote_path = format!("{PUBLIC_STAGING}/{}", package_type.get_remote_file()?); + + afc_upload_file(&mut afc, file, &remote_path).await?; + + let options = match package_type { + PackageType::Ipcc => plist!({"PackageType": "CarrierBundle"}), + PackageType::Ipa(build_id) => plist!({"CFBundleIdentifier": build_id}), + PackageType::Unknown => plist!({}), + }; + + Ok(InstallPackage { + remote_package_path: remote_path, + options, + }) +} + /// Recursively Upload a directory of file to `PublicStaging` async fn upload_dir_to_public_staging>( provider: &dyn IdeviceProvider, @@ -248,6 +282,34 @@ async fn upload_dir_to_public_staging>( }) } +/// Recursively upload a directory to `PublicStaging` over RSD. +#[cfg(feature = "rsd")] +async fn upload_dir_to_public_staging_rsd>( + provider: &mut impl RsdProvider, + handshake: &mut rsd::RsdHandshake, + file: P, +) -> Result { + let mut afc = AfcClient::connect_rsd(provider, handshake).await?; + + ensure_public_staging(&mut afc).await?; + + let file = file.as_ref(); + let remote_folder_name = file + .iter() + .next_back() + .map(|x| x.to_string_lossy().to_string()) + .unwrap_or(IPA_REMOTE_FILE.to_string()); + + let remote_path = format!("{PUBLIC_STAGING}/{remote_folder_name}"); + + afc_upload_dir(&mut afc, file, &remote_path).await?; + + Ok(InstallPackage { + remote_package_path: remote_path, + options: plist!({"PackageType": "Developer"}), + }) +} + pub async fn prepare_file_upload( provider: &dyn IdeviceProvider, data: impl AsRef<[u8]>, @@ -268,6 +330,28 @@ pub async fn prepare_file_upload( }) } +#[cfg(feature = "rsd")] +pub async fn prepare_file_upload_rsd( + provider: &mut impl RsdProvider, + handshake: &mut rsd::RsdHandshake, + data: impl AsRef<[u8]>, + caller_options: Option, +) -> Result { + let InstallPackage { + remote_package_path, + options, + } = upload_file_to_public_staging_rsd(provider, handshake, data).await?; + let full_options = plist!({ + :, @@ -288,3 +372,26 @@ pub async fn prepare_dir_upload( options: full_options, }) } + +#[cfg(feature = "rsd")] +pub async fn prepare_dir_upload_rsd( + provider: &mut impl RsdProvider, + handshake: &mut rsd::RsdHandshake, + local_path: impl AsRef, + caller_options: Option, +) -> Result { + let InstallPackage { + remote_package_path, + options, + } = upload_dir_to_public_staging_rsd(provider, handshake, &local_path).await?; + + let full_options = plist!({ + :>( install_package_with_callback(provider, local_path, options, |_| async {}, ()).await } +/// Same as `install_package` but using the RSD transport. +#[cfg(feature = "rsd")] +pub async fn install_package_rsd>( + provider: &mut impl RsdProvider, + handshake: &mut rsd::RsdHandshake, + local_path: P, + options: Option, +) -> Result<(), IdeviceError> { + install_package_with_callback_rsd(provider, handshake, local_path, options, |_| async {}, ()) + .await +} + /// Same as `install_package` but providing a callback that receives `(percent_complete, state)` /// updates while InstallationProxy performs the operation. pub async fn install_package_with_callback, Fut, S>( @@ -63,6 +80,38 @@ where } } +/// Same as `install_package` but providing a callback that receives `(percent_complete, state)` +/// updates while InstallationProxy performs the operation. +#[cfg(feature = "rsd")] +pub async fn install_package_with_callback_rsd, Fut, S>( + provider: &mut impl RsdProvider, + handshake: &mut rsd::RsdHandshake, + local_path: P, + options: Option, + callback: impl Fn((u64, S)) -> Fut, + state: S, +) -> Result<(), IdeviceError> +where + Fut: std::future::Future, + S: Clone, +{ + let metadata = tokio::fs::metadata(&local_path).await?; + + if metadata.is_dir() { + let InstallPackage { + remote_package_path, + options, + } = prepare_dir_upload_rsd(provider, handshake, local_path, options).await?; + let mut inst = InstallationProxyClient::connect_rsd(provider, handshake).await?; + + inst.upgrade_with_callback(remote_package_path, Some(options), callback, state) + .await + } else { + let data = tokio::fs::read(&local_path).await?; + install_bytes_with_callback_rsd(provider, handshake, data, options, callback, state).await + } +} + /// Upgrade an application by first uploading the local package and then invoking InstallationProxy. /// /// - Accepts a local file path or directory path. @@ -75,6 +124,18 @@ pub async fn upgrade_package>( upgrade_package_with_callback(provider, local_path, options, |_| async {}, ()).await } +/// Same as `upgrade_package` but using the RSD transport. +#[cfg(feature = "rsd")] +pub async fn upgrade_package_rsd>( + provider: &mut impl RsdProvider, + handshake: &mut rsd::RsdHandshake, + local_path: P, + options: Option, +) -> Result<(), IdeviceError> { + upgrade_package_with_callback_rsd(provider, handshake, local_path, options, |_| async {}, ()) + .await +} + /// Same as `upgrade_package` but providing a callback that receives `(percent_complete, state)` /// updates while InstallationProxy performs the operation. pub async fn upgrade_package_with_callback, Fut, S>( @@ -105,6 +166,37 @@ where } } +/// Same as `upgrade_package_with_callback` but using the RSD transport. +#[cfg(feature = "rsd")] +pub async fn upgrade_package_with_callback_rsd, Fut, S>( + provider: &mut impl RsdProvider, + handshake: &mut rsd::RsdHandshake, + local_path: P, + options: Option, + callback: impl Fn((u64, S)) -> Fut, + state: S, +) -> Result<(), IdeviceError> +where + Fut: std::future::Future, + S: Clone, +{ + let metadata = tokio::fs::metadata(&local_path).await?; + + if metadata.is_dir() { + let InstallPackage { + remote_package_path, + options, + } = prepare_dir_upload_rsd(provider, handshake, local_path, options).await?; + let mut inst = InstallationProxyClient::connect_rsd(provider, handshake).await?; + + inst.upgrade_with_callback(remote_package_path, Some(options), callback, state) + .await + } else { + let data = tokio::fs::read(&local_path).await?; + upgrade_bytes_with_callback_rsd(provider, handshake, data, options, callback, state).await + } +} + /// Install an application from raw bytes by first uploading them to `PublicStaging` and then /// invoking InstallationProxy `Install`. /// @@ -118,6 +210,17 @@ pub async fn install_bytes( install_bytes_with_callback(provider, data, options, |_| async {}, ()).await } +/// Same as `install_bytes` but using the RSD transport. +#[cfg(feature = "rsd")] +pub async fn install_bytes_rsd( + provider: &mut impl RsdProvider, + handshake: &mut rsd::RsdHandshake, + data: impl AsRef<[u8]>, + options: Option, +) -> Result<(), IdeviceError> { + install_bytes_with_callback_rsd(provider, handshake, data, options, |_| async {}, ()).await +} + /// Same as `install_bytes` but providing a callback that receives `(percent_complete, state)` /// updates while InstallationProxy performs the install operation. /// @@ -145,6 +248,30 @@ where .await } +/// Same as `install_bytes_with_callback` but using the RSD transport. +#[cfg(feature = "rsd")] +pub async fn install_bytes_with_callback_rsd( + provider: &mut impl RsdProvider, + handshake: &mut rsd::RsdHandshake, + data: impl AsRef<[u8]>, + options: Option, + callback: impl Fn((u64, S)) -> Fut, + state: S, +) -> Result<(), IdeviceError> +where + Fut: std::future::Future, + S: Clone, +{ + let InstallPackage { + remote_package_path, + options, + } = prepare_file_upload_rsd(provider, handshake, data, options).await?; + let mut inst = InstallationProxyClient::connect_rsd(provider, handshake).await?; + + inst.install_with_callback(remote_package_path, Some(options), callback, state) + .await +} + /// Upgrade an application from raw bytes by first uploading them to `PublicStaging` and then /// invoking InstallationProxy `Upgrade`. /// @@ -158,6 +285,17 @@ pub async fn upgrade_bytes( upgrade_bytes_with_callback(provider, data, options, |_| async {}, ()).await } +/// Same as `upgrade_bytes` but using the RSD transport. +#[cfg(feature = "rsd")] +pub async fn upgrade_bytes_rsd( + provider: &mut impl RsdProvider, + handshake: &mut rsd::RsdHandshake, + data: impl AsRef<[u8]>, + options: Option, +) -> Result<(), IdeviceError> { + upgrade_bytes_with_callback_rsd(provider, handshake, data, options, |_| async {}, ()).await +} + /// Same as `upgrade_bytes` but providing a callback that receives `(percent_complete, state)` /// updates while InstallationProxy performs the upgrade operation. /// @@ -184,3 +322,27 @@ where inst.upgrade_with_callback(remote_package_path, Some(options), callback, state) .await } + +/// Same as `upgrade_bytes_with_callback` but using the RSD transport. +#[cfg(feature = "rsd")] +pub async fn upgrade_bytes_with_callback_rsd( + provider: &mut impl RsdProvider, + handshake: &mut rsd::RsdHandshake, + data: impl AsRef<[u8]>, + options: Option, + callback: impl Fn((u64, S)) -> Fut, + state: S, +) -> Result<(), IdeviceError> +where + Fut: std::future::Future, + S: Clone, +{ + let InstallPackage { + remote_package_path, + options, + } = prepare_file_upload_rsd(provider, handshake, data, options).await?; + let mut inst = InstallationProxyClient::connect_rsd(provider, handshake).await?; + + inst.upgrade_with_callback(remote_package_path, Some(options), callback, state) + .await +}