Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions idevice/src/utils/installation/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -222,6 +225,37 @@ async fn upload_file_to_public_staging<P: AsRef<[u8]>>(
})
}

/// Upload a file to `PublicStaging` over RSD and return its InstallationProxy path
#[cfg(feature = "rsd")]
async fn upload_file_to_public_staging_rsd<P: AsRef<[u8]>>(
provider: &mut impl RsdProvider,
handshake: &mut rsd::RsdHandshake,
file: P,
) -> Result<InstallPackage, IdeviceError> {
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<P: AsRef<Path>>(
provider: &dyn IdeviceProvider,
Expand All @@ -248,6 +282,34 @@ async fn upload_dir_to_public_staging<P: AsRef<Path>>(
})
}

/// Recursively upload a directory to `PublicStaging` over RSD.
#[cfg(feature = "rsd")]
async fn upload_dir_to_public_staging_rsd<P: AsRef<Path>>(
provider: &mut impl RsdProvider,
handshake: &mut rsd::RsdHandshake,
file: P,
) -> Result<InstallPackage, IdeviceError> {
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]>,
Expand All @@ -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<plist::Value>,
) -> Result<InstallPackage, IdeviceError> {
let InstallPackage {
remote_package_path,
options,
} = upload_file_to_public_staging_rsd(provider, handshake, data).await?;
let full_options = plist!({
:<? caller_options,
:< options,
});

Ok(InstallPackage {
remote_package_path,
options: full_options,
})
}

pub async fn prepare_dir_upload(
provider: &dyn IdeviceProvider,
local_path: impl AsRef<Path>,
Expand All @@ -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<Path>,
caller_options: Option<plist::Value>,
) -> Result<InstallPackage, IdeviceError> {
let InstallPackage {
remote_package_path,
options,
} = upload_dir_to_public_staging_rsd(provider, handshake, &local_path).await?;

let full_options = plist!({
:<? caller_options,
:< options,
});

Ok(InstallPackage {
remote_package_path,
options: full_options,
})
}
162 changes: 162 additions & 0 deletions idevice/src/utils/installation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ use crate::{
services::installation_proxy::InstallationProxyClient,
};

#[cfg(feature = "rsd")]
use crate::{RsdService, provider::RsdProvider, rsd};
#[cfg(feature = "rsd")]
use helpers::{prepare_dir_upload_rsd, prepare_file_upload_rsd};

/// Install an application by first uploading the local package and then invoking InstallationProxy.
///
/// - Accepts a local file path or directory path.
Expand All @@ -33,6 +38,18 @@ pub async fn install_package<P: AsRef<Path>>(
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<P: AsRef<Path>>(
provider: &mut impl RsdProvider,
handshake: &mut rsd::RsdHandshake,
local_path: P,
options: Option<plist::Value>,
) -> 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<P: AsRef<Path>, Fut, S>(
Expand Down Expand Up @@ -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<P: AsRef<Path>, Fut, S>(
provider: &mut impl RsdProvider,
handshake: &mut rsd::RsdHandshake,
local_path: P,
options: Option<plist::Value>,
callback: impl Fn((u64, S)) -> Fut,
state: S,
) -> Result<(), IdeviceError>
where
Fut: std::future::Future<Output = ()>,
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.
Expand All @@ -75,6 +124,18 @@ pub async fn upgrade_package<P: AsRef<Path>>(
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<P: AsRef<Path>>(
provider: &mut impl RsdProvider,
handshake: &mut rsd::RsdHandshake,
local_path: P,
options: Option<plist::Value>,
) -> 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<P: AsRef<Path>, Fut, S>(
Expand Down Expand Up @@ -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<P: AsRef<Path>, Fut, S>(
provider: &mut impl RsdProvider,
handshake: &mut rsd::RsdHandshake,
local_path: P,
options: Option<plist::Value>,
callback: impl Fn((u64, S)) -> Fut,
state: S,
) -> Result<(), IdeviceError>
where
Fut: std::future::Future<Output = ()>,
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`.
///
Expand All @@ -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<plist::Value>,
) -> 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.
///
Expand Down Expand Up @@ -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<Fut, S>(
provider: &mut impl RsdProvider,
handshake: &mut rsd::RsdHandshake,
data: impl AsRef<[u8]>,
options: Option<plist::Value>,
callback: impl Fn((u64, S)) -> Fut,
state: S,
) -> Result<(), IdeviceError>
where
Fut: std::future::Future<Output = ()>,
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`.
///
Expand All @@ -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<plist::Value>,
) -> 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.
///
Expand All @@ -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<Fut, S>(
provider: &mut impl RsdProvider,
handshake: &mut rsd::RsdHandshake,
data: impl AsRef<[u8]>,
options: Option<plist::Value>,
callback: impl Fn((u64, S)) -> Fut,
state: S,
) -> Result<(), IdeviceError>
where
Fut: std::future::Future<Output = ()>,
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
}
Loading