pub mod remote_v3;
use crate::auth::grandslam::GrandSlam;
use plist::Dictionary;
use plist_macro::plist;
use reqwest::header::HeaderMap;
use rootcause::prelude::*;
use serde::Deserialize;
use std::{collections::HashMap, sync::Arc, time::SystemTime};
use tokio::sync::RwLock;
use tracing::warn;
#[derive(Deserialize, Debug, Clone)]
pub struct AnisetteClientInfo {
pub client_info: String,
pub user_agent: String,
}
#[derive(Debug, Clone)]
pub struct AnisetteData {
machine_id: String,
one_time_password: String,
pub routing_info: String,
_device_description: String,
device_unique_identifier: String,
_local_user_id: String,
generated_at: SystemTime,
}
impl AnisetteData {
pub fn get_headers(&self) -> HashMap<String, String> {
HashMap::from_iter([
(
"X-Mme-Device-Id".to_string(),
self.device_unique_identifier.clone(),
),
("X-Apple-I-MD".to_string(), self.one_time_password.clone()),
("X-Apple-I-MD-M".to_string(), self.machine_id.clone()),
])
}
pub fn get_header_map(&self) -> Result<HeaderMap, Report> {
let headers_map = self.get_headers();
let mut header_map = HeaderMap::new();
for (key, value) in headers_map {
header_map.insert(
reqwest::header::HeaderName::from_bytes(key.as_bytes())?,
reqwest::header::HeaderValue::from_str(&value)?,
);
}
Ok(header_map)
}
pub fn get_client_provided_data(&self) -> Dictionary {
let headers = self.get_headers();
let mut cpd = plist!(dict {
"bootstrap": "true",
"icscrec": "true",
"loc": "en_US",
"pbe": "false",
"prkgen": "true",
"svct": "iCloud"
});
for (key, value) in headers {
cpd.insert(key.to_string(), plist::Value::String(value));
}
cpd
}
pub fn needs_refresh(&self) -> bool {
let elapsed = self.generated_at.elapsed();
match elapsed {
Ok(elapsed) => elapsed.as_secs() > 60,
Err(_) => {
warn!("Unable to determine anisette data age, treating as expired");
true
}
}
}
}
#[async_trait::async_trait]
pub trait AnisetteProvider {
async fn get_anisette_data(&self) -> Result<AnisetteData, Report>;
async fn get_client_info(&mut self) -> Result<AnisetteClientInfo, Report>;
async fn provision(&mut self, gs: Arc<GrandSlam>) -> Result<(), Report>;
fn needs_provisioning(&self) -> Result<bool, Report>;
}
#[derive(Clone)]
pub struct AnisetteDataGenerator {
provider: Arc<RwLock<dyn AnisetteProvider + Send + Sync>>,
data: Option<Arc<AnisetteData>>,
}
impl AnisetteDataGenerator {
pub fn new(provider: Arc<RwLock<dyn AnisetteProvider + Send + Sync>>) -> Self {
AnisetteDataGenerator {
provider,
data: None,
}
}
pub async fn get_anisette_data(
&mut self,
gs: Arc<GrandSlam>,
) -> Result<Arc<AnisetteData>, Report> {
if let Some(data) = &self.data
&& !data.needs_refresh()
{
return Ok(data.clone());
}
let provider = self.provider.read().await;
if provider.needs_provisioning()? {
drop(provider);
let mut provider_write = self.provider.write().await;
provider_write.provision(gs).await?;
drop(provider_write);
let provider = self.provider.read().await;
let data = provider.get_anisette_data().await?;
let arc_data = Arc::new(data);
self.data = Some(arc_data.clone());
Ok(arc_data)
} else {
let data = provider.get_anisette_data().await?;
let arc_data = Arc::new(data);
self.data = Some(arc_data.clone());
Ok(arc_data)
}
}
pub async fn get_client_info(&self) -> Result<AnisetteClientInfo, Report> {
let mut provider = self.provider.write().await;
provider.get_client_info().await
}
}