use std::{convert::TryFrom, ffi::CStr, os::raw::c_char};
use log::warn;
use crate::{
bindings as unsafe_bindings, error::AfcError, idevice::Device,
services::house_arrest::HouseArrest, services::lockdownd::LockdowndService,
};
pub struct AfcClient<'a> {
pub(crate) pointer: unsafe_bindings::afc_client_t,
phantom: std::marker::PhantomData<&'a Device>,
}
impl AfcClient<'_> {
pub fn new(device: &Device) -> Result<(Self, LockdowndService), String> {
let mut pointer = unsafe { std::mem::zeroed() };
let mut client_pointer = unsafe { std::mem::zeroed() };
let result = unsafe {
unsafe_bindings::afc_client_new(device.pointer, &mut pointer, &mut client_pointer)
};
if result != 0 {
return Err(format!("afc_client_new failed: {}", result));
}
Ok((
AfcClient {
pointer: client_pointer,
phantom: std::marker::PhantomData,
},
LockdowndService {
pointer: &mut pointer,
port: pointer.port as u32,
phantom: std::marker::PhantomData,
},
))
}
pub fn start_service(
device: &Device,
service_name: impl Into<String>,
) -> Result<Self, AfcError> {
let service_name = service_name.into();
let mut pointer = unsafe { std::mem::zeroed() };
let result = unsafe {
unsafe_bindings::afc_client_start_service(
device.pointer,
&mut pointer,
service_name.as_ptr() as *const c_char,
)
}
.into();
if result != AfcError::Success {
return Err(result);
}
Ok(AfcClient {
pointer,
phantom: std::marker::PhantomData,
})
}
pub fn get_device_info(&self) -> Result<String, AfcError> {
let mut info = unsafe { std::mem::zeroed() };
let mut info_ptr: *mut *mut c_char = &mut info;
let result =
unsafe { unsafe_bindings::afc_get_device_info(self.pointer, &mut info_ptr) }.into();
if result != AfcError::Success {
return Err(result);
}
Ok(unsafe { CStr::from_ptr(info) }
.to_string_lossy()
.into_owned())
}
pub fn read_directory(&self, directory: impl Into<String>) -> Result<Vec<String>, AfcError> {
let directory = directory.into();
if directory.is_empty() {
warn!("Cannot use empty string as directory");
return Err(AfcError::InvalidArg);
}
let directory_ptr: *const c_char = directory.as_ptr() as *const c_char;
let mut list: *mut *mut libc::c_char = std::ptr::null_mut::<*mut libc::c_char>();
let result =
unsafe { unsafe_bindings::afc_read_directory(self.pointer, directory_ptr, &mut list) }
.into();
if result != AfcError::Success {
return Err(result);
}
let mut list_vec: Vec<String> = Vec::new();
let mut list_ptr: *mut *mut libc::c_char = list;
while !list_ptr.is_null() {
if unsafe { *list_ptr }.is_null() {
break;
}
let list_str = unsafe { CStr::from_ptr(*list_ptr).to_string_lossy().into_owned() };
list_vec.push(list_str);
list_ptr = unsafe { list_ptr.offset(1) };
}
unsafe { unsafe_bindings::afc_dictionary_free(list) };
Ok(list_vec)
}
pub fn get_file_info(&self, path: impl Into<String>) -> Result<String, AfcError> {
let path_ptr: *const c_char = path.into().as_ptr() as *const c_char;
let mut info = Vec::new();
let mut info_ptr: *mut *mut libc::c_char = &mut info.as_mut_ptr();
let result =
unsafe { unsafe_bindings::afc_get_file_info(self.pointer, path_ptr, &mut info_ptr) }
.into();
if result != AfcError::Success {
return Err(result);
}
println!("got here 2: {:?}", info);
println!("ptr: {:?}", info_ptr);
todo!();
}
pub fn file_open(&self, path: impl Into<String>, mode: AfcFileMode) -> Result<u64, AfcError> {
let file_name_ptr: *const c_char = path.into().as_ptr() as *const c_char;
let mut handle = unsafe { std::mem::zeroed() };
let result = unsafe {
unsafe_bindings::afc_file_open(self.pointer, file_name_ptr, mode.into(), &mut handle)
}
.into();
if result != AfcError::Success {
return Err(result);
}
Ok(handle)
}
pub fn file_close(&self, handle: u64) -> Result<(), AfcError> {
let result = unsafe { unsafe_bindings::afc_file_close(self.pointer, handle) }.into();
if result != AfcError::Success {
return Err(result);
}
Ok(())
}
pub fn file_lock(&self, handle: u64, lock_type: AfcLockOp) -> Result<(), AfcError> {
let result =
unsafe { unsafe_bindings::afc_file_lock(self.pointer, handle, lock_type.into()) }
.into();
if result != AfcError::Success {
return Err(result);
}
Ok(())
}
pub fn file_read(&self, handle: u64, length: u32) -> Result<Vec<i8>, AfcError> {
let mut buffer = unsafe { std::mem::zeroed() };
let mut bytes_written = unsafe { std::mem::zeroed() };
let result = unsafe {
unsafe_bindings::afc_file_read(
self.pointer,
handle,
&mut buffer,
length,
&mut bytes_written,
)
}
.into();
if result != AfcError::Success {
return Err(result);
}
let vec = unsafe {
Vec::from_raw_parts(
buffer as *mut i8,
bytes_written as usize,
bytes_written as usize,
)
};
Ok(vec)
}
pub fn file_write(&self, handle: u64, data: impl Into<String>) -> Result<(), AfcError> {
let data = data.into();
let data_ptr: *const c_char = data.as_ptr() as *const c_char;
let mut bytes_written = unsafe { std::mem::zeroed() };
let result = unsafe {
unsafe_bindings::afc_file_write(
self.pointer,
handle,
data_ptr,
data.len() as u32,
&mut bytes_written,
)
}
.into();
if result != AfcError::Success {
return Err(result);
}
Ok(())
}
pub fn file_seek(&self, handle: u64, offset: i64, whence: u8) -> Result<(), AfcError> {
let result =
unsafe { unsafe_bindings::afc_file_seek(self.pointer, handle, offset, whence.into()) }
.into();
if result != AfcError::Success {
return Err(result);
}
Ok(())
}
pub fn file_tell(&self, handle: u64) -> Result<u64, AfcError> {
let mut position = unsafe { std::mem::zeroed() };
let result =
unsafe { unsafe_bindings::afc_file_tell(self.pointer, handle, &mut position) }.into();
if result != AfcError::Success {
return Err(result);
}
Ok(position)
}
pub fn file_truncate(&self, handle: u64, length: u64) -> Result<(), AfcError> {
let result =
unsafe { unsafe_bindings::afc_file_truncate(self.pointer, handle, length) }.into();
if result != AfcError::Success {
return Err(result);
}
Ok(())
}
pub fn remove_path(&self, path: impl Into<String>) -> Result<(), AfcError> {
let path_ptr: *const c_char = path.into().as_ptr() as *const c_char;
let result = unsafe { unsafe_bindings::afc_remove_path(self.pointer, path_ptr) }.into();
if result != AfcError::Success {
return Err(result);
}
Ok(())
}
pub fn rename_path(
&self,
old_path: impl Into<String>,
new_path: impl Into<String>,
) -> Result<(), AfcError> {
let old_path_ptr: *const c_char = old_path.into().as_ptr() as *const c_char;
let new_path_ptr: *const c_char = new_path.into().as_ptr() as *const c_char;
let result =
unsafe { unsafe_bindings::afc_rename_path(self.pointer, old_path_ptr, new_path_ptr) }
.into();
if result != AfcError::Success {
return Err(result);
}
Ok(())
}
pub fn make_directory(&self, path: impl Into<String>) -> Result<(), AfcError> {
let path_ptr: *const c_char = path.into().as_ptr() as *const c_char;
let result = unsafe { unsafe_bindings::afc_make_directory(self.pointer, path_ptr) }.into();
if result != AfcError::Success {
return Err(result);
}
Ok(())
}
pub fn truncate(&self, path: impl Into<String>, length: u64) -> Result<(), AfcError> {
let path_ptr: *const c_char = path.into().as_ptr() as *const c_char;
let result =
unsafe { unsafe_bindings::afc_truncate(self.pointer, path_ptr, length) }.into();
if result != AfcError::Success {
return Err(result);
}
Ok(())
}
pub fn make_link(
&self,
target: impl Into<String>,
link_type: LinkType,
link_path: impl Into<String>,
) -> Result<(), AfcError> {
let target_ptr: *const c_char = target.into().as_ptr() as *const c_char;
let link_name_ptr: *const c_char = link_path.into().as_ptr() as *const c_char;
let result = unsafe {
unsafe_bindings::afc_make_link(
self.pointer,
link_type.into(),
target_ptr,
link_name_ptr,
)
}
.into();
if result != AfcError::Success {
return Err(result);
}
Ok(())
}
pub fn set_file_time(&self, path: impl Into<String>, mtime: u64) -> Result<(), AfcError> {
let path_ptr: *const c_char = path.into().as_ptr() as *const c_char;
let result =
unsafe { unsafe_bindings::afc_set_file_time(self.pointer, path_ptr, mtime) }.into();
if result != AfcError::Success {
return Err(result);
}
Ok(())
}
pub fn remove_path_and_contents(&self, path: impl Into<String>) -> Result<(), AfcError> {
let path_ptr: *const c_char = path.into().as_ptr() as *const c_char;
let result =
unsafe { unsafe_bindings::afc_remove_path_and_contents(self.pointer, path_ptr) }.into();
if result != AfcError::Success {
return Err(result);
}
Ok(())
}
pub fn get_device_info_key(&self, key: impl Into<String>) -> Result<String, AfcError> {
let key_ptr: *const c_char = key.into().as_ptr() as *const c_char;
let mut value_ptr = unsafe { std::mem::zeroed() };
let result = unsafe {
unsafe_bindings::afc_get_device_info_key(self.pointer, key_ptr, &mut value_ptr)
}
.into();
if result != AfcError::Success {
return Err(result);
}
Ok(unsafe { CStr::from_ptr(value_ptr) }
.to_string_lossy()
.into_owned())
}
}
impl TryFrom<HouseArrest<'_>> for AfcClient<'_> {
type Error = AfcError;
fn try_from(house_arrest: HouseArrest<'_>) -> Result<Self, Self::Error> {
let mut to_fill = unsafe { std::mem::zeroed() };
let result = unsafe {
unsafe_bindings::afc_client_new_from_house_arrest_client(
house_arrest.pointer,
&mut to_fill,
)
}
.into();
if result != AfcError::Success {
return Err(result);
}
Ok(Self {
pointer: to_fill,
phantom: std::marker::PhantomData,
})
}
}
pub enum AfcFileMode {
ReadOnly,
ReadWrite,
WriteOnly,
WriteRead,
Append,
ReadAppend,
}
impl From<i8> for AfcFileMode {
fn from(mode: i8) -> Self {
match mode {
1 => AfcFileMode::ReadOnly,
2 => AfcFileMode::ReadWrite,
3 => AfcFileMode::WriteOnly,
4 => AfcFileMode::WriteRead,
5 => AfcFileMode::Append,
6 => AfcFileMode::ReadAppend,
_ => panic!("Invalid file mode"),
}
}
}
impl From<AfcFileMode> for u32 {
fn from(mode: AfcFileMode) -> Self {
match mode {
AfcFileMode::ReadOnly => 1,
AfcFileMode::ReadWrite => 2,
AfcFileMode::WriteOnly => 3,
AfcFileMode::WriteRead => 4,
AfcFileMode::Append => 5,
AfcFileMode::ReadAppend => 6,
}
}
}
pub enum AfcLockOp {
Sh,
Ex,
Un,
}
impl From<AfcLockOp> for u32 {
fn from(op: AfcLockOp) -> Self {
match op {
AfcLockOp::Sh => 5,
AfcLockOp::Ex => 6,
AfcLockOp::Un => 12,
}
}
}
pub enum LinkType {
HardLink,
SymbolicLink,
}
impl From<LinkType> for u32 {
fn from(link_type: LinkType) -> Self {
match link_type {
LinkType::HardLink => 1,
LinkType::SymbolicLink => 2,
}
}
}
impl Drop for AfcClient<'_> {
fn drop(&mut self) {
unsafe {
unsafe_bindings::afc_client_free(self.pointer);
}
}
}