use tracing::{debug, warn};
use crate::{Idevice, IdeviceError, IdeviceService, afc::AfcClient, lockdown::LockdownClient, obf};
#[derive(Debug)]
pub struct CrashReportCopyMobileClient {
pub afc_client: AfcClient,
}
impl IdeviceService for CrashReportCopyMobileClient {
fn service_name() -> std::borrow::Cow<'static, str> {
obf!("com.apple.crashreportcopymobile")
}
async fn from_stream(idevice: Idevice) -> Result<Self, crate::IdeviceError> {
Ok(Self::new(idevice))
}
}
impl CrashReportCopyMobileClient {
pub fn new(idevice: Idevice) -> Self {
Self {
afc_client: AfcClient::new(idevice),
}
}
pub async fn ls(&mut self, dir_path: Option<&str>) -> Result<Vec<String>, IdeviceError> {
let path = dir_path.unwrap_or("/");
let mut res = self.afc_client.list_dir(path).await?;
if res.len() > 2 {
if &res[0] == "." {
res.swap_remove(0);
}
if &res[1] == ".." {
res.swap_remove(1);
}
}
Ok(res)
}
pub async fn pull(&mut self, log: impl Into<String>) -> Result<Vec<u8>, IdeviceError> {
let log = log.into();
let mut f = self
.afc_client
.open(format!("/{log}"), crate::afc::opcode::AfcFopenMode::RdOnly)
.await?;
f.read_entire().await
}
pub async fn remove(&mut self, log: impl Into<String>) -> Result<(), IdeviceError> {
let log = log.into();
self.afc_client.remove(format!("/{log}")).await
}
pub fn to_afc_client(self) -> AfcClient {
self.afc_client
}
}
const EXPECTED_FLUSH: [u8; 4] = [0x70, 0x69, 0x6E, 0x67];
pub async fn flush_reports(
provider: &dyn crate::provider::IdeviceProvider,
) -> Result<(), IdeviceError> {
let mut lockdown = LockdownClient::connect(provider).await?;
let legacy = lockdown
.get_value(Some("ProductVersion"), None)
.await
.ok()
.as_ref()
.and_then(|x| x.as_string())
.and_then(|x| x.split(".").next())
.and_then(|x| x.parse::<u8>().ok())
.map(|x| x < 5)
.unwrap_or(false);
lockdown
.start_session(&provider.get_pairing_file().await?)
.await?;
let (port, ssl) = lockdown
.start_service(obf!("com.apple.crashreportmover"))
.await?;
let mut idevice = provider.connect(port).await?;
if ssl {
idevice
.start_session(&provider.get_pairing_file().await?, legacy)
.await?;
}
let res = idevice.read_raw(4).await?;
debug!(
"Flush reports response: {:?}",
String::from_utf8_lossy(&res)
);
if res[..4] == EXPECTED_FLUSH {
Ok(())
} else {
warn!("crashreportmover sent wrong bytes: {res:02X?}");
Err(IdeviceError::CrashReportMoverBadResponse(res))
}
}
#[cfg(feature = "rsd")]
impl crate::RsdService for CrashReportCopyMobileClient {
fn rsd_service_name() -> std::borrow::Cow<'static, str> {
crate::obf!("com.apple.crashreportcopymobile.shim.remote")
}
async fn from_stream(stream: Box<dyn crate::ReadWrite>) -> Result<Self, crate::IdeviceError> {
let mut idevice = crate::Idevice::new(stream, "");
idevice.rsd_checkin().await?;
Ok(Self::new(idevice))
}
}