Git · dadoum.zone
Repositories
Help
Report an Issue
apple-crates
Code
Commits
Branches
Tags
Search
Tree:
a74924e
Branches
Tags
main
apple-crates
itunes
src
itunes.rs
Refactor and unify a lot of things for another project and implement airdrop support
Dadoum
commited
a74924e
at 2026-10-25 22:15:58
itunes.rs
Blame
History
Raw
use adi::proxy::ADIError; use base64::Engine; use base64::prelude::BASE64_STANDARD; use grandslam::bundle_information::BundleInformation; use grandslam::device::Device; use grandslam::http_session::{ AnisetteHTTPSession, AppleError, BasicHTTPSession, HTTPSession, HTTPSessionCreationError, URLBag, URLBagError, }; use plist::{Dictionary, Value}; use plist_macros::dict; use reqwest::Method; use thiserror::Error; use uuid::Uuid; pub async fn http_session<'lt>( device: Device, client_bundle_info: BundleInformation<'lt>, ) -> Result<HTTPSession<'lt>, HTTPSessionCreationError> { let http_session = BasicHTTPSession::new(device, client_bundle_info, None)?; // https://init.itunes.apple.com/WebObjects/MZInit.woa/wa/initiateSession let url_bag_response = http_session .request_builder(Method::GET, "https://init.itunes.apple.com/bag.xml") .query(&[("ix", "6")]) .header("Content-Type", "application/x-apple-plist") .send() .await? .error_for_status()? .bytes() .await?; let url_bag = parse_url_bag_response(&url_bag_response) .map_err(|_| HTTPSessionCreationError::InvalidURLBagResponseStructure)?; HTTPSession::new(http_session, url_bag) } pub fn parse_url_bag_response(url_bag_response: &[u8]) -> Result<URLBag, URLBagError> { plist::from_bytes(url_bag_response).map_err(|_| URLBagError::Parsing) } pub async fn setup_sap(http_session: &HTTPSession<'_>) -> Result<(), AuthError> { let sign_sap_setup_url = http_session .url_bag() .get("sign-sap-setup") .and_then(Value::as_string) .ok_or(AuthError::InvalidURLBag)?; let sap_certificate_response = http_session .request_builder(Method::GET, sign_sap_setup_url) .send() .await? .error_for_status()? .bytes() .await?; let sap_certificate_response: Dictionary = plist::from_bytes(&sap_certificate_response).map_err(|_| AuthError::Parsing)?; let _sap_certificate = sap_certificate_response .get("sign-sap-setup-buffer") .and_then(Value::as_data) .ok_or(AuthError::Parsing)?; // TODO: exchange the certificate with FairPlay. todo!() } #[derive(Debug, Error)] pub enum AuthError { #[error("Could not log-in to Apple servers: {0}")] Apple(#[from] AppleError), #[error("Cannot generate device authentication data: {0}")] Anisette(#[from] ADIError), #[error("Network error: {0}")] Network(#[from] reqwest::Error), #[error("Cannot find the endpoint in the URL bag")] InvalidURLBag, #[error("Cannot parse HTTP response")] Parsing, } /// Log in to the iTunes Store. The password can be a PET, and attempts are counted from 1. pub async fn login( http_session: &AnisetteHTTPSession<'_, '_>, apple_id: &str, password: &str, attempt: i32, ) -> Result<(), AuthError> { let authenticate_url = http_session .url_bag() .get("authenticateAccount") .and_then(Value::as_string) .ok_or(AuthError::InvalidURLBag)?; let accounts: Vec<Value> = http_session .adi_proxy() .get_all_provisioned_accounts()? .iter() .map(|account| { Value::Dictionary(dict! { "dsid": account.ds_id, "mid": BASE64_STANDARD.encode(&account.mid), "otp": BASE64_STANDARD.encode(&account.otp), }) }) .collect(); let request_plist = dict! { "appleId": apple_id, "attempt": attempt, "auth-mid-otp": accounts, "createSession": true, "guid": "A0DE61112331", // TODO "machineName": "MacBook", // TODO "password": password, "passwordSettings": dict! { "free": "", "paid": "" }, // "kbsync": vec![], "uuid": Uuid::new_v4().to_string().to_uppercase(), "why": "signIn", }; // UNWRAP safety: like plist_to_body, I don't see how serializing our own plist could fail. let mut request_body = Vec::new(); plist::to_writer_xml(&mut request_body, &request_plist).expect("Failed to serialize plist?"); // TODO: check if we need to sign the request following the sign-sap-request url bag key. let _signature = sign_sap_request(&request_body); let response = http_session .anisette_request_builder(Method::POST, authenticate_url)? .body(request_body) .header("Content-Type", "application/x-apple-plist") // .header("X-Apple-ActionSignature", signature) .send() .await? .error_for_status()? .bytes() .await?; let _response: Dictionary = plist::from_bytes(&response).map_err(|_| AuthError::Parsing)?; // TODO: handle the response. todo!() } fn sign_sap_request(_request_body: &[u8]) -> Vec<u8> { // TODO: sign with FairPlay, once setup_sap works. todo!() }