use std::{
io::Read,
os::raw::{c_char, c_long, c_ulong},
path::PathBuf,
};
use libc::c_void;
use log::{info, trace};
use plist_plus::Plist;
use super::lockdownd::LockdowndService;
use crate::{bindings as unsafe_bindings, error::MobileImageMounterError, idevice::Device};
pub struct MobileImageMounter<'a> {
pub(crate) pointer: unsafe_bindings::mobile_image_mounter_client_t,
pub(crate) phantom: std::marker::PhantomData<&'a Device>,
}
unsafe impl Send for MobileImageMounter<'_> {}
unsafe impl Sync for MobileImageMounter<'_> {}
impl MobileImageMounter<'_> {
pub fn new(
device: &Device,
descriptor: LockdowndService,
) -> Result<Self, MobileImageMounterError> {
let mut client = unsafe { std::mem::zeroed() };
let result = unsafe {
unsafe_bindings::mobile_image_mounter_new(
device.pointer,
descriptor.pointer,
&mut client,
)
}
.into();
if result != MobileImageMounterError::Success {
return Err(result);
}
Ok(MobileImageMounter {
pointer: client,
phantom: std::marker::PhantomData,
})
}
pub fn start_service(
device: &Device,
label: impl Into<String>,
) -> Result<Self, MobileImageMounterError> {
let mut client = unsafe { std::mem::zeroed() };
let result = unsafe {
unsafe_bindings::mobile_image_mounter_start_service(
device.pointer,
&mut client,
label.into().as_ptr() as *const c_char,
)
}
.into();
if result != MobileImageMounterError::Success {
return Err(result);
}
Ok(MobileImageMounter {
pointer: client,
phantom: std::marker::PhantomData,
})
}
pub fn upload_image(
&self,
image_path: impl Into<String>,
image_type: impl Into<String>,
signature_path: impl Into<String>,
) -> Result<(), MobileImageMounterError> {
let image_path = image_path.into();
let image_type = image_type.into();
let signature_path = signature_path.into();
let dmg_size = match std::fs::File::open(image_path.clone()) {
Ok(mut file) => {
let mut temp_buf = vec![];
file.read_to_end(&mut temp_buf).unwrap();
temp_buf.len()
}
Err(_) => return Err(MobileImageMounterError::DmgNotFound),
};
let signature_size = match std::fs::File::open(signature_path.clone()) {
Ok(mut file) => {
let mut temp_buf = vec![];
file.read_to_end(&mut temp_buf).unwrap();
temp_buf.len()
}
Err(_) => return Err(MobileImageMounterError::SignatureNotFound),
};
let image_path_c_str = &mut std::ffi::CString::new(image_path).unwrap();
let mode_c_str = &mut std::ffi::CString::new("rb").unwrap();
info!("Opening image file");
let image_buffer = unsafe { libc::fopen(image_path_c_str.as_ptr(), mode_c_str.as_ptr()) };
let signature_path_c_str = &mut std::ffi::CString::new(signature_path).unwrap();
info!("Reading signature file");
let signature_buffer =
unsafe { libc::fopen(signature_path_c_str.as_ptr(), mode_c_str.as_ptr()) };
let image_type_c_str = std::ffi::CString::new(image_type.clone()).unwrap();
let image_type_c_str = if image_type == *"" {
std::ptr::null()
} else {
image_type_c_str.as_ptr()
};
info!("Uploading image");
let result = unsafe {
unsafe_bindings::mobile_image_mounter_upload_image(
self.pointer,
image_type_c_str,
dmg_size as c_ulong,
signature_buffer as *const c_char,
signature_size as u16,
Some(image_mounter_callback),
image_buffer as *mut c_void,
)
}
.into();
unsafe {
libc::fclose(image_buffer);
libc::fclose(signature_buffer);
}
if result != MobileImageMounterError::Success {
return Err(result);
}
Ok(())
}
pub fn mount_image(
&self,
image_path: impl Into<String>,
image_type: impl Into<String>,
signature_path: impl Into<String>,
) -> Result<Plist, MobileImageMounterError> {
let image_path = image_path.into();
let image_type = image_type.into();
let signature_path = signature_path.into();
let image_path: PathBuf = image_path.into();
if !image_path.exists() {
return Err(MobileImageMounterError::DmgNotFound);
}
let image_path = match image_path.canonicalize() {
Ok(path) => path.display().to_string(),
Err(_) => return Err(MobileImageMounterError::DmgNotFound),
};
let mut signature_buffer = Vec::new();
let file = match std::fs::File::open(signature_path) {
Ok(file) => file,
Err(_) => return Err(MobileImageMounterError::SignatureNotFound),
};
let mut reader = std::io::BufReader::new(file);
match reader.read_to_end(&mut signature_buffer) {
Ok(_) => (),
Err(_) => return Err(MobileImageMounterError::SignatureNotFound),
};
let image_type_c_str = std::ffi::CString::new(image_type.clone()).unwrap();
let image_type_c_str = if image_type == *"" {
std::ptr::null()
} else {
image_type_c_str.as_ptr()
};
let mut plist: unsafe_bindings::plist_t = unsafe { std::mem::zeroed() };
info!("Mounting image");
let result = unsafe {
unsafe_bindings::mobile_image_mounter_mount_image(
self.pointer,
image_path.as_ptr() as *const c_char,
signature_buffer.as_ptr() as *const c_char,
signature_buffer.len() as u16,
image_type_c_str,
&mut plist,
)
}
.into();
if result != MobileImageMounterError::Success {
return Err(result);
}
Ok(plist.into())
}
pub fn lookup_image(
&self,
image_type: impl Into<String>,
) -> Result<Plist, MobileImageMounterError> {
let image_type = image_type.into();
let image_type_c_str = std::ffi::CString::new(image_type.clone()).unwrap();
let image_type_c_str = if image_type == *"" {
std::ptr::null()
} else {
image_type_c_str.as_ptr()
};
let mut plist: unsafe_bindings::plist_t = unsafe { std::mem::zeroed() };
info!("Looking up image");
let result = unsafe {
unsafe_bindings::mobile_image_mounter_lookup_image(
self.pointer,
image_type_c_str,
&mut plist,
)
}
.into();
if result != MobileImageMounterError::Success {
return Err(result);
}
Ok(plist.into())
}
}
extern "C" fn image_mounter_callback(a: *mut c_void, b: c_ulong, c: *mut c_void) -> c_long {
trace!("image_mounter_callback called");
unsafe { libc::fread(a, 1, b as usize, c as *mut libc::FILE) as c_long }
}
impl Drop for MobileImageMounter<'_> {
fn drop(&mut self) {
info!("Dropping MobileImageMounter");
unsafe {
unsafe_bindings::mobile_image_mounter_free(self.pointer);
}
}
}