Finish installation proxy bindings

This commit is contained in:
Jackson Coxson
2025-04-07 09:44:11 -06:00
parent d39966dac9
commit 2859b691df
2 changed files with 432 additions and 6 deletions

View File

@@ -11,8 +11,6 @@ use crate::{
};
pub struct InstallationProxyClientHandle(pub InstallationProxyClient);
#[allow(non_camel_case_types)]
pub struct plist_t;
/// Automatically creates and connects to Installation Proxy, returning a client handle
///
@@ -143,10 +141,10 @@ pub unsafe extern "C" fn installation_proxy_new(
/// Gets installed apps on the device
///
/// # Arguments
/// * `client` - A valid InstallationProxyClient handle
/// * `application_type` - The application type to filter by (optional, NULL for "Any")
/// * `bundle_identifiers` - The identifiers to filter by (optional, NULL for all apps)
/// * `out_result` - On success, will be set to point to a newly allocated array of PlistRef
/// * [`client`] - A valid InstallationProxyClient handle
/// * [`application_type`] - The application type to filter by (optional, NULL for "Any")
/// * [`bundle_identifiers`] - The identifiers to filter by (optional, NULL for all apps)
/// * [`out_result`] - On success, will be set to point to a newly allocated array of PlistRef
///
/// # Returns
/// An error code indicating success or failure
@@ -236,3 +234,421 @@ pub unsafe extern "C" fn installation_proxy_client_free(
let _ = unsafe { Box::from_raw(handle) };
}
}
/// Installs an application package on the device
///
/// # Arguments
/// * [`client`] - A valid InstallationProxyClient handle
/// * [`package_path`] - Path to the .ipa package in the AFC jail
/// * [`options`] - Optional installation options as a plist dictionary (can be NULL)
///
/// # Returns
/// An error code indicating success or failure
///
/// # Safety
/// `client` must be a valid pointer to a handle allocated by this library
/// `package_path` must be a valid C string
/// `options` must be a valid plist dictionary or NULL
#[unsafe(no_mangle)]
pub unsafe extern "C" fn installation_proxy_install(
client: *mut InstallationProxyClientHandle,
package_path: *const libc::c_char,
options: *mut c_void,
) -> IdeviceErrorCode {
if client.is_null() || package_path.is_null() {
return IdeviceErrorCode::InvalidArg;
}
let package_path = unsafe { std::ffi::CStr::from_ptr(package_path) }
.to_string_lossy()
.into_owned();
let options = if options.is_null() {
None
} else {
Some(util::libplist_to_plist(options))
};
let res = RUNTIME.block_on(async {
unsafe { &mut *client }
.0
.install(package_path, options)
.await
});
match res {
Ok(_) => IdeviceErrorCode::IdeviceSuccess,
Err(e) => e.into(),
}
}
/// Installs an application package on the device
///
/// # Arguments
/// * [`client`] - A valid InstallationProxyClient handle
/// * [`package_path`] - Path to the .ipa package in the AFC jail
/// * [`options`] - Optional installation options as a plist dictionary (can be NULL)
/// * [`callback`] - Progress callback function
/// * [`context`] - User context to pass to callback
///
/// # Returns
/// An error code indicating success or failure
///
/// # Safety
/// `client` must be a valid pointer to a handle allocated by this library
/// `package_path` must be a valid C string
/// `options` must be a valid plist dictionary or NULL
#[unsafe(no_mangle)]
pub unsafe extern "C" fn installation_proxy_install_with_callback(
client: *mut InstallationProxyClientHandle,
package_path: *const libc::c_char,
options: *mut c_void,
callback: extern "C" fn(progress: u64, context: *mut c_void),
context: *mut c_void,
) -> IdeviceErrorCode {
if client.is_null() || package_path.is_null() {
return IdeviceErrorCode::InvalidArg;
}
let package_path = unsafe { std::ffi::CStr::from_ptr(package_path) }
.to_string_lossy()
.into_owned();
let options = if options.is_null() {
None
} else {
Some(util::libplist_to_plist(options))
};
let res = RUNTIME.block_on(async {
let callback_wrapper = |(progress, context)| async move {
callback(progress, context);
};
unsafe { &mut *client }
.0
.install_with_callback(package_path, options, callback_wrapper, context)
.await
});
match res {
Ok(_) => IdeviceErrorCode::IdeviceSuccess,
Err(e) => e.into(),
}
}
/// Upgrades an existing application on the device
///
/// # Arguments
/// * [`client`] - A valid InstallationProxyClient handle
/// * [`package_path`] - Path to the .ipa package in the AFC jail
/// * [`options`] - Optional upgrade options as a plist dictionary (can be NULL)
///
/// # Returns
/// An error code indicating success or failure
///
/// # Safety
/// `client` must be a valid pointer to a handle allocated by this library
/// `package_path` must be a valid C string
/// `options` must be a valid plist dictionary or NULL
#[unsafe(no_mangle)]
pub unsafe extern "C" fn installation_proxy_upgrade(
client: *mut InstallationProxyClientHandle,
package_path: *const libc::c_char,
options: *mut c_void,
) -> IdeviceErrorCode {
if client.is_null() || package_path.is_null() {
return IdeviceErrorCode::InvalidArg;
}
let package_path = unsafe { std::ffi::CStr::from_ptr(package_path) }
.to_string_lossy()
.into_owned();
let options = if options.is_null() {
None
} else {
Some(util::libplist_to_plist(options))
};
let res = RUNTIME.block_on(async {
unsafe { &mut *client }
.0
.upgrade(package_path, options)
.await
});
match res {
Ok(_) => IdeviceErrorCode::IdeviceSuccess,
Err(e) => e.into(),
}
}
/// Upgrades an existing application on the device
///
/// # Arguments
/// * [`client`] - A valid InstallationProxyClient handle
/// * [`package_path`] - Path to the .ipa package in the AFC jail
/// * [`options`] - Optional upgrade options as a plist dictionary (can be NULL)
/// * [`callback`] - Progress callback function
/// * [`context`] - User context to pass to callback
///
/// # Returns
/// An error code indicating success or failure
///
/// # Safety
/// `client` must be a valid pointer to a handle allocated by this library
/// `package_path` must be a valid C string
/// `options` must be a valid plist dictionary or NULL
#[unsafe(no_mangle)]
pub unsafe extern "C" fn installation_proxy_upgrade_with_callback(
client: *mut InstallationProxyClientHandle,
package_path: *const libc::c_char,
options: *mut c_void,
callback: extern "C" fn(progress: u64, context: *mut c_void),
context: *mut c_void,
) -> IdeviceErrorCode {
if client.is_null() || package_path.is_null() {
return IdeviceErrorCode::InvalidArg;
}
let package_path = unsafe { std::ffi::CStr::from_ptr(package_path) }
.to_string_lossy()
.into_owned();
let options = if options.is_null() {
None
} else {
Some(util::libplist_to_plist(options))
};
let res = RUNTIME.block_on(async {
let callback_wrapper = |(progress, context)| async move {
callback(progress, context);
};
unsafe { &mut *client }
.0
.upgrade_with_callback(package_path, options, callback_wrapper, context)
.await
});
match res {
Ok(_) => IdeviceErrorCode::IdeviceSuccess,
Err(e) => e.into(),
}
}
/// Uninstalls an application from the device
///
/// # Arguments
/// * [`client`] - A valid InstallationProxyClient handle
/// * [`bundle_id`] - Bundle identifier of the application to uninstall
/// * [`options`] - Optional uninstall options as a plist dictionary (can be NULL)
///
/// # Returns
/// An error code indicating success or failure
///
/// # Safety
/// `client` must be a valid pointer to a handle allocated by this library
/// `bundle_id` must be a valid C string
/// `options` must be a valid plist dictionary or NULL
#[unsafe(no_mangle)]
pub unsafe extern "C" fn installation_proxy_uninstall(
client: *mut InstallationProxyClientHandle,
bundle_id: *const libc::c_char,
options: *mut c_void,
) -> IdeviceErrorCode {
if client.is_null() || bundle_id.is_null() {
return IdeviceErrorCode::InvalidArg;
}
let bundle_id = unsafe { std::ffi::CStr::from_ptr(bundle_id) }
.to_string_lossy()
.into_owned();
let options = if options.is_null() {
None
} else {
Some(util::libplist_to_plist(options))
};
let res = RUNTIME.block_on(async {
unsafe { &mut *client }
.0
.uninstall(bundle_id, options)
.await
});
match res {
Ok(_) => IdeviceErrorCode::IdeviceSuccess,
Err(e) => e.into(),
}
}
/// Uninstalls an application from the device
///
/// # Arguments
/// * [`client`] - A valid InstallationProxyClient handle
/// * [`bundle_id`] - Bundle identifier of the application to uninstall
/// * [`options`] - Optional uninstall options as a plist dictionary (can be NULL)
/// * [`callback`] - Progress callback function
/// * [`context`] - User context to pass to callback
///
/// # Returns
/// An error code indicating success or failure
///
/// # Safety
/// `client` must be a valid pointer to a handle allocated by this library
/// `bundle_id` must be a valid C string
/// `options` must be a valid plist dictionary or NULL
#[unsafe(no_mangle)]
pub unsafe extern "C" fn installation_proxy_uninstall_with_callback(
client: *mut InstallationProxyClientHandle,
bundle_id: *const libc::c_char,
options: *mut c_void,
callback: extern "C" fn(progress: u64, context: *mut c_void),
context: *mut c_void,
) -> IdeviceErrorCode {
if client.is_null() || bundle_id.is_null() {
return IdeviceErrorCode::InvalidArg;
}
let bundle_id = unsafe { std::ffi::CStr::from_ptr(bundle_id) }
.to_string_lossy()
.into_owned();
let options = if options.is_null() {
None
} else {
Some(util::libplist_to_plist(options))
};
let res = RUNTIME.block_on(async {
let callback_wrapper = |(progress, context)| async move {
callback(progress, context);
};
unsafe { &mut *client }
.0
.uninstall_with_callback(bundle_id, options, callback_wrapper, context)
.await
});
match res {
Ok(_) => IdeviceErrorCode::IdeviceSuccess,
Err(e) => e.into(),
}
}
/// Checks if the device capabilities match the required capabilities
///
/// # Arguments
/// * [`client`] - A valid InstallationProxyClient handle
/// * [`capabilities`] - Array of plist values representing required capabilities
/// * [`capabilities_len`] - Length of the capabilities array
/// * [`options`] - Optional check options as a plist dictionary (can be NULL)
/// * [`out_result`] - Will be set to true if all capabilities are supported, false otherwise
///
/// # Returns
/// An error code indicating success or failure
///
/// # Safety
/// `client` must be a valid pointer to a handle allocated by this library
/// `capabilities` must be a valid array of plist values or NULL
/// `options` must be a valid plist dictionary or NULL
/// `out_result` must be a valid pointer to a bool
#[unsafe(no_mangle)]
pub unsafe extern "C" fn installation_proxy_check_capabilities_match(
client: *mut InstallationProxyClientHandle,
capabilities: *const *mut c_void,
capabilities_len: libc::size_t,
options: *mut c_void,
out_result: *mut bool,
) -> IdeviceErrorCode {
if client.is_null() || out_result.is_null() {
return IdeviceErrorCode::InvalidArg;
}
let capabilities = if capabilities.is_null() {
Vec::new()
} else {
unsafe { std::slice::from_raw_parts(capabilities, capabilities_len) }
.iter()
.map(|&ptr| util::libplist_to_plist(ptr))
.collect()
};
let options = if options.is_null() {
None
} else {
Some(util::libplist_to_plist(options))
};
let res = RUNTIME.block_on(async {
unsafe { &mut *client }
.0
.check_capabilities_match(capabilities, options)
.await
});
match res {
Ok(result) => {
unsafe { *out_result = result };
IdeviceErrorCode::IdeviceSuccess
}
Err(e) => e.into(),
}
}
/// Browses installed applications on the device
///
/// # Arguments
/// * [`client`] - A valid InstallationProxyClient handle
/// * [`options`] - Optional browse options as a plist dictionary (can be NULL)
/// * [`out_result`] - On success, will be set to point to a newly allocated array of PlistRef
/// * [`out_result_len`] - Will be set to the length of the result array
///
/// # Returns
/// An error code indicating success or failure
///
/// # Safety
/// `client` must be a valid pointer to a handle allocated by this library
/// `options` must be a valid plist dictionary or NULL
/// `out_result` must be a valid, non-null pointer to a location where the result will be stored
/// `out_result_len` must be a valid, non-null pointer to a location where the length will be stored
#[unsafe(no_mangle)]
pub unsafe extern "C" fn installation_proxy_browse(
client: *mut InstallationProxyClientHandle,
options: *mut c_void,
out_result: *mut *mut c_void,
out_result_len: *mut libc::size_t,
) -> IdeviceErrorCode {
if client.is_null() || out_result.is_null() || out_result_len.is_null() {
return IdeviceErrorCode::InvalidArg;
}
let options = if options.is_null() {
None
} else {
Some(util::libplist_to_plist(options))
};
let res: Result<Vec<*mut c_void>, IdeviceError> = RUNTIME.block_on(async {
unsafe { &mut *client }.0.browse(options).await.map(|apps| {
apps.into_iter()
.map(|v| util::plist_to_libplist(&v))
.collect()
})
});
match res {
Ok(r) => {
let len = r.len();
let boxed_slice = r.into_boxed_slice();
let ptr = boxed_slice.as_ptr();
std::mem::forget(boxed_slice);
unsafe {
*out_result = ptr as *mut c_void;
*out_result_len = len;
}
IdeviceErrorCode::IdeviceSuccess
}
Err(e) => e.into(),
}
}

View File

@@ -3,6 +3,7 @@
use std::{
ffi::c_int,
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
os::raw::c_void,
};
use libc::{sockaddr_in, sockaddr_in6};
@@ -87,3 +88,12 @@ pub(crate) fn plist_to_libplist(v: &Value) -> *mut libc::c_void {
p.false_drop();
ptr
}
pub(crate) fn libplist_to_plist(v: *mut c_void) -> Value {
let v: plist_plus::Plist = v.into();
let v_string = v.to_string();
v.false_drop();
let reader = std::io::Cursor::new(v_string.as_bytes());
plist::from_reader(reader).unwrap()
}