mirror of
https://github.com/nab138/isideload.git
synced 2026-03-02 06:26:16 +01:00
continue implimenting sideloading
This commit is contained in:
38
Cargo.lock
generated
38
Cargo.lock
generated
@@ -392,6 +392,21 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
@@ -399,6 +414,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -407,6 +423,23 @@ version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
@@ -436,10 +469,13 @@ version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
@@ -733,6 +769,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4031af51250d2f22f61a0d7fb7ea71ba8b6144b2b9dd3b7ee4a931fccbd1ec0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"futures",
|
||||
"plist",
|
||||
"plist-macro",
|
||||
"rustls",
|
||||
@@ -924,6 +961,7 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
name = "minimal"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"idevice",
|
||||
"isideload",
|
||||
"plist",
|
||||
"plist-macro",
|
||||
|
||||
@@ -10,3 +10,4 @@ plist-macro = "0.1.3"
|
||||
tokio = { version = "1.49.0", features = ["rt-multi-thread", "macros"] }
|
||||
tracing = "0.1.44"
|
||||
tracing-subscriber = "0.3.22"
|
||||
idevice = { version = "0.1.52", features = ["usbmuxd"]}
|
||||
@@ -1,12 +1,11 @@
|
||||
use std::env;
|
||||
use std::{env, path::PathBuf};
|
||||
|
||||
use idevice::usbmuxd::{UsbmuxdAddr, UsbmuxdConnection};
|
||||
use isideload::{
|
||||
anisette::remote_v3::RemoteV3AnisetteProvider,
|
||||
auth::apple_account::AppleAccount,
|
||||
dev::{
|
||||
certificates::CertificatesApi,
|
||||
developer_session::{DeveloperSession, TeamsApi},
|
||||
},
|
||||
dev::developer_session::DeveloperSession,
|
||||
sideload::{SideloadConfiguration, TeamSelection, sideload_app},
|
||||
};
|
||||
|
||||
use tracing::Level;
|
||||
@@ -21,14 +20,15 @@ async fn main() {
|
||||
tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
// let _app_path = PathBuf::from(
|
||||
// args.get(1)
|
||||
// .expect("Please provide the path to the app to install"),
|
||||
// );
|
||||
|
||||
let apple_id = args
|
||||
.get(1)
|
||||
.expect("Please provide the Apple ID to use for installation");
|
||||
let apple_password = args.get(2).expect("Please provide the Apple ID password");
|
||||
let app_path = PathBuf::from(
|
||||
args.get(3)
|
||||
.expect("Please provide the path to the app to install"),
|
||||
);
|
||||
|
||||
let get_2fa_code = || {
|
||||
let mut code = String::new();
|
||||
@@ -53,25 +53,46 @@ async fn main() {
|
||||
.await
|
||||
.expect("Failed to create developer session");
|
||||
|
||||
let teams = dev_session
|
||||
.list_teams()
|
||||
.await
|
||||
.expect("Failed to list teams");
|
||||
let usbmuxd = UsbmuxdConnection::default().await;
|
||||
if usbmuxd.is_err() {
|
||||
panic!("Failed to connect to usbmuxd: {:?}", usbmuxd.err());
|
||||
}
|
||||
let mut usbmuxd = usbmuxd.unwrap();
|
||||
|
||||
let team = teams
|
||||
.get(0)
|
||||
.expect("No developer teams available for this account");
|
||||
let devs = usbmuxd.get_devices().await.unwrap();
|
||||
if devs.is_empty() {
|
||||
panic!("No devices found");
|
||||
}
|
||||
|
||||
// let app_ids = dev_session
|
||||
// .list_app_ids(team, None)
|
||||
// .await
|
||||
// .expect("Failed to add appid");
|
||||
// let app_id = app_ids.app_ids.get(0).cloned().unwrap();
|
||||
let provider = devs
|
||||
.iter()
|
||||
.next()
|
||||
.unwrap()
|
||||
.to_provider(UsbmuxdAddr::from_env_var().unwrap(), "isideload-demo");
|
||||
|
||||
let res = dev_session
|
||||
.list_all_development_certs(team, None)
|
||||
.await
|
||||
.expect("Failed to list dev certs");
|
||||
let sideload_config =
|
||||
SideloadConfiguration::builder().team_selection(TeamSelection::Prompt(|teams| {
|
||||
println!("Please select a team:");
|
||||
for (index, team) in teams.iter().enumerate() {
|
||||
println!(
|
||||
"{}: {} ({})",
|
||||
index + 1,
|
||||
team.name.as_deref().unwrap_or("<Unnamed>"),
|
||||
team.team_id
|
||||
);
|
||||
}
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
let selection = input.trim().parse::<usize>().ok()?;
|
||||
if selection == 0 || selection > teams.len() {
|
||||
return None;
|
||||
}
|
||||
Some(teams[selection - 1].team_id.clone())
|
||||
}));
|
||||
|
||||
println!("{:?}", res);
|
||||
let result = sideload_app(&provider, &mut dev_session, app_path, &sideload_config).await;
|
||||
match result {
|
||||
Ok(_) => println!("App installed successfully"),
|
||||
Err(e) => panic!("Failed to install app: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ default = ["install"]
|
||||
install = ["dep:idevice"]
|
||||
|
||||
[dependencies]
|
||||
idevice = { version = "0.1.51", optional = true }
|
||||
idevice = { version = "0.1.52", optional = true }
|
||||
plist = "1.8"
|
||||
plist-macro = "0.1.3"
|
||||
reqwest = { version = "0.13.1", features = ["json", "gzip"] }
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::dev::{
|
||||
use plist_macro::plist;
|
||||
use rootcause::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use tracing::info;
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -59,6 +60,27 @@ pub trait DevicesApi {
|
||||
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
// TODO: This can be skipped if we know the device is already registered
|
||||
/// Check if the device is a development device, and add it if not
|
||||
async fn ensure_device_registered(
|
||||
&mut self,
|
||||
team: &DeveloperTeam,
|
||||
name: &str,
|
||||
udid: &str,
|
||||
device_type: impl Into<Option<DeveloperDeviceType>> + Send,
|
||||
) -> Result<(), Report> {
|
||||
let device_type = device_type.into();
|
||||
let devices = self.list_devices(team, device_type.clone()).await?;
|
||||
|
||||
if !devices.iter().any(|d| d.device_number == udid) {
|
||||
info!("Registering development device");
|
||||
self.add_device(team, name, udid, device_type).await?;
|
||||
}
|
||||
info!("Device is a development device");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DevicesApi for DeveloperSession {
|
||||
|
||||
46
isideload/src/sideload/config.rs
Normal file
46
isideload/src/sideload/config.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::dev::teams::DeveloperTeam;
|
||||
|
||||
/// Configuration for selecting a developer team during sideloading
|
||||
///
|
||||
/// If there is only one team, it will be selected automatically regardless of this setting.
|
||||
/// If there are multiple teams, the behavior will depend on this setting.
|
||||
pub enum TeamSelection {
|
||||
/// Select the first team automatically
|
||||
First,
|
||||
/// Prompt the user to select a team
|
||||
Prompt(fn(&Vec<DeveloperTeam>) -> Option<String>),
|
||||
}
|
||||
|
||||
impl Display for TeamSelection {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TeamSelection::First => write!(f, "first team"),
|
||||
TeamSelection::Prompt(_) => write!(f, "prompting for team"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SideloadConfiguration {
|
||||
pub team_selection: TeamSelection,
|
||||
}
|
||||
|
||||
impl Default for SideloadConfiguration {
|
||||
fn default() -> Self {
|
||||
SideloadConfiguration {
|
||||
team_selection: TeamSelection::First,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SideloadConfiguration {
|
||||
pub fn builder() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn team_selection(mut self, selection: TeamSelection) -> Self {
|
||||
self.team_selection = selection;
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,50 @@ use std::path::PathBuf;
|
||||
|
||||
use idevice::provider::IdeviceProvider;
|
||||
use rootcause::prelude::*;
|
||||
use tracing::info;
|
||||
|
||||
use crate::dev::developer_session::DeveloperSession;
|
||||
use crate::dev::teams::TeamsApi;
|
||||
use crate::dev::{developer_session::DeveloperSession, devices::DevicesApi};
|
||||
use crate::util::device::IdeviceInfo;
|
||||
|
||||
pub mod config;
|
||||
pub use config::{SideloadConfiguration, TeamSelection};
|
||||
|
||||
pub async fn sideload_app(
|
||||
device_provider: &impl IdeviceProvider,
|
||||
dev_session: &DeveloperSession,
|
||||
dev_session: &mut DeveloperSession,
|
||||
app_path: PathBuf,
|
||||
config: &SideloadConfiguration,
|
||||
) -> Result<(), Report> {
|
||||
let device_info = IdeviceInfo::from_device(device_provider).await?;
|
||||
|
||||
let teams = dev_session.list_teams().await?;
|
||||
let team = match teams.len() {
|
||||
0 => {
|
||||
bail!("No developer teams available")
|
||||
}
|
||||
1 => &teams[0],
|
||||
_ => {
|
||||
info!(
|
||||
"Multiple developer teams found, {} as per configuration",
|
||||
config.team_selection
|
||||
);
|
||||
match &config.team_selection {
|
||||
TeamSelection::First => &teams[0],
|
||||
TeamSelection::Prompt(prompt_fn) => {
|
||||
let selection = prompt_fn(&teams).ok_or_else(|| report!("No team selected"))?;
|
||||
teams
|
||||
.iter()
|
||||
.find(|t| t.team_id == selection)
|
||||
.ok_or_else(|| report!("No team found with ID {}", selection))?
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dev_session
|
||||
.ensure_device_registered(team, &device_info.name, &device_info.udid, None)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user