diff --git a/idevice/src/lib.rs b/idevice/src/lib.rs index 1f181a4..644add5 100644 --- a/idevice/src/lib.rs +++ b/idevice/src/lib.rs @@ -28,6 +28,8 @@ use std::io::{self, BufWriter}; use thiserror::Error; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +pub use util::{pretty_print_dictionary, pretty_print_plist}; + pub trait ReadWrite: AsyncRead + AsyncWrite + Unpin + Send + Sync + std::fmt::Debug {} impl ReadWrite for T {} @@ -69,7 +71,7 @@ impl Idevice { /// Sends a plist to the socket async fn send_plist(&mut self, message: plist::Value) -> Result<(), IdeviceError> { if let Some(socket) = &mut self.socket { - debug!("Sending plist: {message:?}"); + debug!("Sending plist: {}", pretty_print_plist(&message)); let buf = Vec::new(); let mut writer = BufWriter::new(buf); @@ -88,7 +90,7 @@ impl Idevice { /// Sends raw bytes to the socket async fn send_raw(&mut self, message: &[u8]) -> Result<(), IdeviceError> { if let Some(socket) = &mut self.socket { - let message_parts = message.chunks(2048); + let message_parts = message.chunks(1024 * 64); let part_len = message_parts.len(); let mut err = 5; @@ -140,11 +142,11 @@ impl Idevice { let mut buf = vec![0; len as usize]; socket.read_exact(&mut buf).await?; let res: plist::Dictionary = plist::from_bytes(&buf)?; - debug!("Received plist: {res:#?}"); + debug!("Received plist: {}", pretty_print_dictionary(&res)); if let Some(e) = res.get("Error") { let e: String = plist::from_value(e)?; - if let Some(e) = IdeviceError::from_device_error_type(e.as_str()) { + if let Some(e) = IdeviceError::from_device_error_type(e.as_str(), &res) { return Err(e); } else { return Err(IdeviceError::UnknownErrorType(e)); @@ -240,22 +242,40 @@ pub enum IdeviceError { #[error("bad build manifest")] BadBuildManifest, + #[error("image not mounted")] + ImageNotMounted, #[cfg(feature = "tss")] #[error("http reqwest error")] Reqwest(#[from] reqwest::Error), + #[error("internal error")] + InternalError(String), + #[error("unknown error `{0}` returned from device")] UnknownErrorType(String), } impl IdeviceError { - fn from_device_error_type(e: &str) -> Option { + fn from_device_error_type(e: &str, context: &plist::Dictionary) -> Option { match e { "GetProhibited" => Some(Self::GetProhibited), "InvalidHostID" => Some(Self::InvalidHostID), "SessionInactive" => Some(Self::SessionInactive), "DeviceLocked" => Some(Self::DeviceLocked), + "InternalError" => { + let detailed_error = context + .get("DetailedError") + .and_then(|d| d.as_string()) + .unwrap_or("No context") + .to_string(); + + if detailed_error.contains("There is no matching entry in the device map for") { + Some(Self::ImageNotMounted) + } else { + Some(Self::InternalError(detailed_error)) + } + } _ => None, } } diff --git a/idevice/src/tss.rs b/idevice/src/tss.rs index fdcbfb2..3333548 100644 --- a/idevice/src/tss.rs +++ b/idevice/src/tss.rs @@ -33,7 +33,10 @@ impl TSSRequest { } pub async fn send(&self) -> Result { - debug!("Sending TSS request: {:#?}", self.inner); + debug!( + "Sending TSS request: {}", + crate::pretty_print_dictionary(&self.inner) + ); let client = reqwest::Client::new(); let res = client diff --git a/idevice/src/usbmuxd/mod.rs b/idevice/src/usbmuxd/mod.rs index 32b8f19..da19493 100644 --- a/idevice/src/usbmuxd/mod.rs +++ b/idevice/src/usbmuxd/mod.rs @@ -256,7 +256,7 @@ impl UsbmuxdConnection { self.socket.read_exact(&mut body_buffer).await?; let res = plist::from_bytes(&body_buffer)?; - debug!("Read from muxer: {res:#?}"); + debug!("Read from muxer: {}", crate::pretty_print_dictionary(&res)); Ok(res) } diff --git a/idevice/src/util.rs b/idevice/src/util.rs index 620aeb0..d17988f 100644 --- a/idevice/src/util.rs +++ b/idevice/src/util.rs @@ -1,5 +1,7 @@ // Jackson Coxson +use plist::Value; + pub fn plist_to_bytes(p: &plist::Dictionary) -> Vec { let buf = Vec::new(); let mut writer = std::io::BufWriter::new(buf); @@ -7,3 +9,59 @@ pub fn plist_to_bytes(p: &plist::Dictionary) -> Vec { writer.into_inner().unwrap() } + +pub fn pretty_print_plist(p: &Value) -> String { + print_plist(p, 0) +} + +pub fn pretty_print_dictionary(dict: &plist::Dictionary) -> String { + let items: Vec = dict + .iter() + .map(|(k, v)| format!("{}: {}", k, print_plist(v, 2))) + .collect(); + format!("{{\n{}\n}}", items.join(",\n")) +} + +fn print_plist(p: &Value, indentation: usize) -> String { + let indent = " ".repeat(indentation); + match p { + Value::Array(vec) => { + let items: Vec = vec + .iter() + .map(|v| print_plist(v, indentation + 2)) + .collect(); + format!("[\n{}\n{}]", items.join(",\n"), indent) + } + Value::Dictionary(dict) => { + let items: Vec = dict + .iter() + .map(|(k, v)| { + format!( + "{}{}: {}", + " ".repeat(indentation + 2), + k, + print_plist(v, indentation + 2) + ) + }) + .collect(); + format!("{}{{\n{}\n{}}}", indent, items.join(",\n"), indent) + } + Value::Boolean(b) => format!("{}", b), + Value::Data(vec) => { + let len = vec.len(); + let preview: String = vec + .iter() + .take(20) + .map(|b| format!("{:02X}", b)) + .collect::>() + .join(" "); + format!("Data({}... Len: {})", preview, len) + } + Value::Date(date) => format!("Date({})", date.to_xml_format()), + Value::Real(f) => format!("{}", f), + Value::Integer(i) => format!("{}", i), + Value::String(s) => format!("\"{}\"", s), + Value::Uid(_uid) => "Uid(?)".to_string(), + _ => "Unknown".to_string(), + } +}