mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 06:26:15 +01:00
usbmuxd class implementation for usbmuxd
This commit is contained in:
28
cpp/.clang-format
Normal file
28
cpp/.clang-format
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# .clang-format
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
IndentWidth: 4
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: Never
|
||||||
|
ColumnLimit: 100
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlignConsecutiveAssignments: AcrossEmptyLinesAndComments
|
||||||
|
AlignConsecutiveDeclarations: AcrossEmptyLinesAndComments
|
||||||
|
AlignTrailingComments: true
|
||||||
|
SortIncludes: CaseInsensitive
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
SpacesInAngles: Never
|
||||||
|
SpaceAfterCStyleCast: true
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
BreakBeforeBinaryOperators: NonAssignment
|
||||||
|
ReflowComments: true
|
||||||
|
PointerAlignment: Left
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
3
cpp/.clangd
Normal file
3
cpp/.clangd
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
CompileFlags:
|
||||||
|
Add: ["-I/usr/local/include/"]
|
||||||
|
CompilationDatabase: "examples/build"
|
||||||
77
cpp/examples/CMakeLists.txt
Normal file
77
cpp/examples/CMakeLists.txt
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# Jackson Coxson
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
project(IdeviceFFI CXX)
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
# Set the paths
|
||||||
|
set(HEADER_FILE ${CMAKE_SOURCE_DIR}/../../ffi/idevice.h)
|
||||||
|
set(STATIC_LIB ${CMAKE_SOURCE_DIR}/../../target/release/libidevice_ffi.a)
|
||||||
|
set(EXAMPLES_DIR ${CMAKE_SOURCE_DIR}/../examples)
|
||||||
|
|
||||||
|
set(IDEVICE_CPP_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/../include) # cpp/include
|
||||||
|
set(IDEVICE_FFI_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/../../ffi) # ffi/
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
|
||||||
|
|
||||||
|
# Find all C++ example files
|
||||||
|
file(GLOB EXAMPLE_SOURCES ${EXAMPLES_DIR}/*.cpp)
|
||||||
|
|
||||||
|
# Create an executable for each example file
|
||||||
|
foreach(EXAMPLE_FILE ${EXAMPLE_SOURCES})
|
||||||
|
get_filename_component(EXAMPLE_NAME ${EXAMPLE_FILE} NAME_WE)
|
||||||
|
add_executable(${EXAMPLE_NAME} ${EXAMPLE_FILE})
|
||||||
|
|
||||||
|
target_include_directories(${EXAMPLE_NAME} PRIVATE
|
||||||
|
${IDEVICE_CPP_INCLUDE_DIR}
|
||||||
|
${IDEVICE_FFI_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Include the generated header
|
||||||
|
target_include_directories(${EXAMPLE_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/..)
|
||||||
|
|
||||||
|
# Link the static Rust library
|
||||||
|
target_link_libraries(${EXAMPLE_NAME} PRIVATE ${STATIC_LIB})
|
||||||
|
|
||||||
|
# libplist
|
||||||
|
|
||||||
|
if( APPLE )
|
||||||
|
# use static linking
|
||||||
|
find_library( LIBPLIST libplist-2.0.a REQUIRED )
|
||||||
|
message( STATUS "(Static linking) LIBPLIST " ${LIBPLIST} )
|
||||||
|
target_link_libraries ( ${EXAMPLE_NAME} PRIVATE ${LIBPLIST} )
|
||||||
|
elseif( WIN32)
|
||||||
|
pkg_search_module(PLIST REQUIRED libplist-2.0)
|
||||||
|
find_library( LIBPLIST ${PLIST_LIBRARIES} PATH ${PLIST_LIBDIR} )
|
||||||
|
target_link_libraries ( ${EXAMPLE_NAME} PRIVATE ${LIBPLIST} )
|
||||||
|
else ()
|
||||||
|
pkg_search_module(PLIST libplist>=2.0)
|
||||||
|
if(NOT PLIST_FOUND)
|
||||||
|
pkg_search_module(PLIST REQUIRED libplist-2.0)
|
||||||
|
endif()
|
||||||
|
find_library( LIBPLIST ${PLIST_LIBRARIES} PATH ${PLIST_LIBDIR} )
|
||||||
|
target_link_libraries ( ${EXAMPLE_NAME} PUBLIC ${LIBPLIST} )
|
||||||
|
endif()
|
||||||
|
if ( PLIST_FOUND )
|
||||||
|
message( STATUS "found libplist-${PLIST_VERSION}" )
|
||||||
|
endif()
|
||||||
|
target_include_directories( ${EXAMPLE_NAME} PRIVATE ${PLIST_INCLUDE_DIRS} )
|
||||||
|
|
||||||
|
# Bulk-link common macOS system frameworks
|
||||||
|
if(APPLE)
|
||||||
|
target_link_libraries(${EXAMPLE_NAME} PRIVATE
|
||||||
|
"-framework CoreFoundation"
|
||||||
|
"-framework Security"
|
||||||
|
"-framework SystemConfiguration"
|
||||||
|
"-framework CoreServices"
|
||||||
|
"-framework IOKit"
|
||||||
|
"-framework CFNetwork"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
|
||||||
38
cpp/examples/idevice_id.cpp
Normal file
38
cpp/examples/idevice_id.cpp
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#include "ffi.hpp"
|
||||||
|
#include "usbmuxd.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "Getting devices from usbmuxd\n";
|
||||||
|
|
||||||
|
IdeviceFFI::FfiError e;
|
||||||
|
std::optional<IdeviceFFI::UsbmuxdConnection> u =
|
||||||
|
IdeviceFFI::UsbmuxdConnection::default_new(0, e);
|
||||||
|
if (u == std::nullopt) {
|
||||||
|
std::cerr << "failed to connect to usbmuxd";
|
||||||
|
std::cerr << e.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto devices = u->get_devices(e);
|
||||||
|
if (u == std::nullopt) {
|
||||||
|
std::cerr << "failed to get devices from usbmuxd";
|
||||||
|
std::cerr << e.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IdeviceFFI::UsbmuxdDevice& d : *devices) {
|
||||||
|
auto udid = d.get_udid();
|
||||||
|
if (!udid) {
|
||||||
|
std::cerr << "failed to get udid";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto connection_type = d.get_connection_type();
|
||||||
|
if (!connection_type) {
|
||||||
|
std::cerr << "failed to get connection type";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::cout << *udid << " (" << connection_type->to_string() << ")" << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
29
cpp/include/ffi.hpp
Normal file
29
cpp/include/ffi.hpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#ifndef IDEVICE_FFI
|
||||||
|
#define IDEVICE_FFI
|
||||||
|
|
||||||
|
#include "idevice.hpp"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace IdeviceFFI {
|
||||||
|
struct FfiError {
|
||||||
|
int32_t code = 0;
|
||||||
|
std::string message;
|
||||||
|
|
||||||
|
static FfiError from(const IdeviceFfiError* err) {
|
||||||
|
FfiError out;
|
||||||
|
if (err) {
|
||||||
|
out.code = err->code;
|
||||||
|
out.message = err->message ? err->message : "";
|
||||||
|
idevice_error_free(const_cast<IdeviceFfiError*>(err));
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FfiError success() { return {0, ""}; }
|
||||||
|
|
||||||
|
explicit operator bool() const { return code != 0; }
|
||||||
|
};
|
||||||
|
} // namespace IdeviceFFI
|
||||||
|
#endif
|
||||||
80
cpp/include/idevice++.hpp
Normal file
80
cpp/include/idevice++.hpp
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#ifndef IDEVICE_CPP
|
||||||
|
#define IDEVICE_CPP
|
||||||
|
|
||||||
|
#include "ffi.hpp"
|
||||||
|
#include "pairing_file.hpp"
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
|
class Idevice {
|
||||||
|
public:
|
||||||
|
static std::optional<Idevice>
|
||||||
|
create(IdeviceSocketHandle* socket, const std::string& label, FfiError& err) {
|
||||||
|
IdeviceHandle* handle = nullptr;
|
||||||
|
IdeviceFfiError* e = idevice_new(socket, label.c_str(), &handle);
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return Idevice(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::optional<Idevice>
|
||||||
|
create_tcp(const sockaddr* addr, socklen_t addr_len, const std::string& label, FfiError& err) {
|
||||||
|
IdeviceHandle* handle = nullptr;
|
||||||
|
IdeviceFfiError* e = idevice_new_tcp_socket(addr, addr_len, label.c_str(), &handle);
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return Idevice(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> get_type(FfiError& err) {
|
||||||
|
char* type_cstr = nullptr;
|
||||||
|
IdeviceFfiError* e = idevice_get_type(handle_, &type_cstr);
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string type_str(type_cstr);
|
||||||
|
idevice_string_free(type_cstr);
|
||||||
|
return type_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rsd_checkin(FfiError& err) {
|
||||||
|
IdeviceFfiError* e = idevice_rsd_checkin(handle_);
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool start_session(PairingFile& pairing_file, FfiError& err) {
|
||||||
|
IdeviceFfiError* e = idevice_start_session(handle_, pairing_file.raw());
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Idevice() {
|
||||||
|
if (handle_)
|
||||||
|
idevice_free(handle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit Idevice(IdeviceHandle* h) : handle_(h) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
IdeviceHandle* handle_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace IdeviceFFI
|
||||||
|
#endif
|
||||||
82
cpp/include/pairing_file.hpp
Normal file
82
cpp/include/pairing_file.hpp
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#ifndef IDEVICE_PAIRING_FILE
|
||||||
|
#define IDEVICE_PAIRING_FILE
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ffi.hpp"
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace IdeviceFFI {
|
||||||
|
class PairingFile {
|
||||||
|
public:
|
||||||
|
static std::optional<PairingFile> read(const std::string& path, FfiError& err) {
|
||||||
|
IdevicePairingFile* ptr = nullptr;
|
||||||
|
IdeviceFfiError* e = idevice_pairing_file_read(path.c_str(), &ptr);
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return PairingFile(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::optional<PairingFile> from_bytes(const uint8_t* data, size_t size, FfiError& err) {
|
||||||
|
IdevicePairingFile* raw = nullptr;
|
||||||
|
IdeviceFfiError* e = idevice_pairing_file_from_bytes(data, size, &raw);
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return PairingFile(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
~PairingFile() {
|
||||||
|
if (ptr_) {
|
||||||
|
idevice_pairing_file_free(ptr_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deleted copy constructor and assignment — use unique ownersship
|
||||||
|
PairingFile(const PairingFile&) = delete;
|
||||||
|
PairingFile& operator=(const PairingFile&) = delete;
|
||||||
|
|
||||||
|
// Move constructor and assignment
|
||||||
|
PairingFile(PairingFile&& other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; }
|
||||||
|
|
||||||
|
PairingFile& operator=(PairingFile&& other) noexcept {
|
||||||
|
if (this != &other) {
|
||||||
|
if (ptr_) {
|
||||||
|
idevice_pairing_file_free(ptr_);
|
||||||
|
}
|
||||||
|
ptr_ = other.ptr_;
|
||||||
|
other.ptr_ = nullptr;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::vector<uint8_t>> serialize(FfiError& err) const {
|
||||||
|
uint8_t* data = nullptr;
|
||||||
|
size_t size = 0;
|
||||||
|
IdeviceFfiError* e = idevice_pairing_file_serialize(ptr_, &data, &size);
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> result(data, data + size);
|
||||||
|
delete[] data; // NOTE: adjust this if deallocation uses `free` or a custom function
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit PairingFile(IdevicePairingFile* ptr) : ptr_(ptr) {}
|
||||||
|
IdevicePairingFile* raw() const { return ptr_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
IdevicePairingFile* ptr_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace IdeviceFFI
|
||||||
|
#endif
|
||||||
352
cpp/include/usbmuxd.hpp
Normal file
352
cpp/include/usbmuxd.hpp
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#ifndef IDEVICE_USBMUXD_HPP
|
||||||
|
#define IDEVICE_USBMUXD_HPP
|
||||||
|
|
||||||
|
#include "ffi.hpp"
|
||||||
|
#include "idevice++.hpp"
|
||||||
|
#include "pairing_file.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/_types/_u_int8_t.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#else
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
|
/// @brief A C++ wrapper for a UsbmuxdAddrHandle.
|
||||||
|
/// @details This class manages the memory of a UsbmuxdAddrHandle pointer.
|
||||||
|
/// It is non-copyable but is movable.
|
||||||
|
class UsbmuxdAddr {
|
||||||
|
public:
|
||||||
|
/// @brief Creates a new TCP usbmuxd address.
|
||||||
|
/// @param addr The socket address to connect to.
|
||||||
|
/// @param addr_len The length of the socket address.
|
||||||
|
/// @param err An error that will be populated on failure.
|
||||||
|
/// @return A UsbmuxdAddr on success, std::nullopt on failure.
|
||||||
|
static std::optional<UsbmuxdAddr>
|
||||||
|
tcp_new(const sockaddr* addr, socklen_t addr_len, FfiError& err) {
|
||||||
|
UsbmuxdAddrHandle* handle = nullptr;
|
||||||
|
IdeviceFfiError* e = idevice_usbmuxd_tcp_addr_new(addr, addr_len, &handle);
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return UsbmuxdAddr(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__unix__) || defined(__APPLE__)
|
||||||
|
/// @brief Creates a new Unix socket usbmuxd address.
|
||||||
|
/// @param path The path to the unix socket.
|
||||||
|
/// @param err An error that will be populated on failure.
|
||||||
|
/// @return A UsbmuxdAddr on success, std::nullopt on failure.
|
||||||
|
static std::optional<UsbmuxdAddr> unix_new(const std::string& path, FfiError& err) {
|
||||||
|
UsbmuxdAddrHandle* handle = nullptr;
|
||||||
|
IdeviceFfiError* e = idevice_usbmuxd_unix_addr_new(path.c_str(), &handle);
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return UsbmuxdAddr(handle);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
~UsbmuxdAddr() {
|
||||||
|
if (handle_) {
|
||||||
|
idevice_usbmuxd_addr_free(handle_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete copy constructor and assignment operator
|
||||||
|
UsbmuxdAddr(const UsbmuxdAddr&) = delete;
|
||||||
|
UsbmuxdAddr& operator=(const UsbmuxdAddr&) = delete;
|
||||||
|
|
||||||
|
// Define move constructor and assignment operator
|
||||||
|
UsbmuxdAddr(UsbmuxdAddr&& other) noexcept : handle_(other.handle_) { other.handle_ = nullptr; }
|
||||||
|
UsbmuxdAddr& operator=(UsbmuxdAddr&& other) noexcept {
|
||||||
|
if (this != &other) {
|
||||||
|
if (handle_) {
|
||||||
|
idevice_usbmuxd_addr_free(handle_);
|
||||||
|
}
|
||||||
|
handle_ = other.handle_;
|
||||||
|
other.handle_ = nullptr;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Gets the raw handle.
|
||||||
|
/// @return The raw UsbmuxdAddrHandle pointer.
|
||||||
|
UsbmuxdAddrHandle* raw() const { return handle_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit UsbmuxdAddr(UsbmuxdAddrHandle* handle) : handle_(handle) {}
|
||||||
|
UsbmuxdAddrHandle* handle_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UsbmuxdConnectionType {
|
||||||
|
public:
|
||||||
|
enum Value : uint8_t { Usb = 1, Network = 2, Unknown = 3 };
|
||||||
|
explicit UsbmuxdConnectionType(uint8_t v) : _value(static_cast<Value>(v)) {}
|
||||||
|
|
||||||
|
std::string to_string() {
|
||||||
|
switch (_value) {
|
||||||
|
case UsbmuxdConnectionType::Usb:
|
||||||
|
return "USB";
|
||||||
|
case UsbmuxdConnectionType::Network:
|
||||||
|
return "Network";
|
||||||
|
case UsbmuxdConnectionType::Unknown:
|
||||||
|
return "Unknown";
|
||||||
|
default:
|
||||||
|
return "UnknownEnumValue";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Value value() const { return _value; }
|
||||||
|
|
||||||
|
bool operator==(Value other) const { return _value == other; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Value _value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UsbmuxdDevice {
|
||||||
|
public:
|
||||||
|
~UsbmuxdDevice() {
|
||||||
|
if (handle_) {
|
||||||
|
idevice_usbmuxd_device_free(handle_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Delete copy constructor and assignment operator
|
||||||
|
UsbmuxdDevice(const UsbmuxdDevice&) = delete;
|
||||||
|
UsbmuxdDevice& operator=(const UsbmuxdDevice&) = delete;
|
||||||
|
|
||||||
|
// Define move constructor and assignment operator
|
||||||
|
UsbmuxdDevice(UsbmuxdDevice&& other) noexcept : handle_(other.handle_) {
|
||||||
|
other.handle_ = nullptr;
|
||||||
|
}
|
||||||
|
UsbmuxdDevice& operator=(UsbmuxdDevice&& other) noexcept {
|
||||||
|
if (this != &other) {
|
||||||
|
if (handle_) {
|
||||||
|
idevice_usbmuxd_device_free(handle_);
|
||||||
|
}
|
||||||
|
handle_ = other.handle_;
|
||||||
|
other.handle_ = nullptr;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Gets the raw handle.
|
||||||
|
/// @return The raw UsbmuxdConnectionHandle pointer.
|
||||||
|
UsbmuxdDeviceHandle* raw() const { return handle_; }
|
||||||
|
|
||||||
|
std::optional<std::string> get_udid() {
|
||||||
|
char* udid = idevice_usbmuxd_device_get_udid(handle_);
|
||||||
|
if (udid) {
|
||||||
|
std::string cppUdid(udid);
|
||||||
|
idevice_string_free(udid);
|
||||||
|
return cppUdid;
|
||||||
|
} else {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<uint32_t> get_id() {
|
||||||
|
uint32_t id = idevice_usbmuxd_device_get_device_id(handle_);
|
||||||
|
if (id == 0) {
|
||||||
|
return std::nullopt;
|
||||||
|
} else {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<UsbmuxdConnectionType> get_connection_type() {
|
||||||
|
u_int8_t t = idevice_usbmuxd_device_get_connection_type(handle_);
|
||||||
|
if (t == 0) {
|
||||||
|
return std::nullopt;
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
return static_cast<UsbmuxdConnectionType>(t);
|
||||||
|
}
|
||||||
|
explicit UsbmuxdDevice(UsbmuxdDeviceHandle* handle) : handle_(handle) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
UsbmuxdDeviceHandle* handle_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief A C++ wrapper for a UsbmuxdConnectionHandle.
|
||||||
|
/// @details This class manages the memory of a UsbmuxdConnectionHandle pointer.
|
||||||
|
/// It is non-copyable but is movable.
|
||||||
|
class UsbmuxdConnection {
|
||||||
|
public:
|
||||||
|
/// @brief Creates a new TCP usbmuxd connection.
|
||||||
|
/// @param addr The socket address to connect to.
|
||||||
|
/// @param addr_len The length of the socket address.
|
||||||
|
/// @param tag A tag that will be returned by usbmuxd responses.
|
||||||
|
/// @param err An error that will be populated on failure.
|
||||||
|
/// @return A UsbmuxdConnection on success, std::nullopt on failure.
|
||||||
|
static std::optional<UsbmuxdConnection>
|
||||||
|
tcp_new(const sockaddr* addr, socklen_t addr_len, uint32_t tag, FfiError& err) {
|
||||||
|
UsbmuxdConnectionHandle* handle = nullptr;
|
||||||
|
IdeviceFfiError* e = idevice_usbmuxd_new_tcp_connection(addr, addr_len, tag, &handle);
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return UsbmuxdConnection(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__unix__) || defined(__APPLE__)
|
||||||
|
/// @brief Creates a new Unix socket usbmuxd connection.
|
||||||
|
/// @param path The path to the unix socket.
|
||||||
|
/// @param tag A tag that will be returned by usbmuxd responses.
|
||||||
|
/// @param err An error that will be populated on failure.
|
||||||
|
/// @return A UsbmuxdConnection on success, std::nullopt on failure.
|
||||||
|
static std::optional<UsbmuxdConnection>
|
||||||
|
unix_new(const std::string& path, uint32_t tag, FfiError& err) {
|
||||||
|
UsbmuxdConnectionHandle* handle = nullptr;
|
||||||
|
IdeviceFfiError* e = idevice_usbmuxd_new_unix_socket_connection(path.c_str(), tag, &handle);
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return UsbmuxdConnection(handle);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @brief Creates a new usbmuxd connection using the default path.
|
||||||
|
/// @param tag A tag that will be returned by usbmuxd responses.
|
||||||
|
/// @param err An error that will be populated on failure.
|
||||||
|
/// @return A UsbmuxdConnection on success, std::nullopt on failure.
|
||||||
|
static std::optional<UsbmuxdConnection> default_new(uint32_t tag, FfiError& err) {
|
||||||
|
UsbmuxdConnectionHandle* handle = nullptr;
|
||||||
|
IdeviceFfiError* e = idevice_usbmuxd_new_default_connection(tag, &handle);
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return UsbmuxdConnection(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Gets a list of all connected devices.
|
||||||
|
/// @param err An error that will be populated on failure.
|
||||||
|
/// @return A vector of UsbmuxdDevice objects on success, std::nullopt on failure.
|
||||||
|
std::optional<std::vector<UsbmuxdDevice>> get_devices(FfiError& err) {
|
||||||
|
UsbmuxdDeviceHandle** devices = nullptr;
|
||||||
|
int count = 0;
|
||||||
|
IdeviceFfiError* e = idevice_usbmuxd_get_devices(handle_, &devices, &count);
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<UsbmuxdDevice> result;
|
||||||
|
result.reserve(count);
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
result.emplace_back(devices[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Gets the BUID from the daemon.
|
||||||
|
/// @param err An error that will be populated on failure.
|
||||||
|
/// @return The BUID string on success, std::nullopt on failure.
|
||||||
|
std::optional<std::string> get_buid(FfiError& err) {
|
||||||
|
char* buid_c = nullptr;
|
||||||
|
IdeviceFfiError* e = idevice_usbmuxd_get_buid(handle_, &buid_c);
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
std::string buid(buid_c);
|
||||||
|
idevice_string_free(buid_c);
|
||||||
|
return buid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Gets the pairing record for a device.
|
||||||
|
/// @param udid The UDID of the target device.
|
||||||
|
/// @param err An error that will be populated on failure.
|
||||||
|
/// @return A PairingFile object on success, std::nullopt on failure.
|
||||||
|
std::optional<PairingFile> get_pair_record(const std::string& udid, FfiError& err) {
|
||||||
|
IdevicePairingFile* pf_handle = nullptr;
|
||||||
|
IdeviceFfiError* e = idevice_usbmuxd_get_pair_record(handle_, udid.c_str(), &pf_handle);
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return PairingFile(pf_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Connects to a port on a given device.
|
||||||
|
/// @details This operation consumes the UsbmuxdConnection object. After a successful call,
|
||||||
|
/// this object will be invalid and should not be used.
|
||||||
|
/// @param device_id The ID of the target device.
|
||||||
|
/// @param port The port number to connect to.
|
||||||
|
/// @param err An error that will be populated on failure.
|
||||||
|
/// @return An Idevice connection object on success, std::nullopt on failure.
|
||||||
|
std::optional<Idevice>
|
||||||
|
connect_to_device(uint32_t device_id, uint16_t port, const std::string& path, FfiError& err) {
|
||||||
|
if (!handle_) {
|
||||||
|
// Can't connect with an invalid handle
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdeviceHandle* idevice_handle = nullptr;
|
||||||
|
IdeviceFfiError* e = idevice_usbmuxd_connect_to_device(
|
||||||
|
handle_, device_id, port, path.c_str(), &idevice_handle);
|
||||||
|
|
||||||
|
// The handle is always consumed by the FFI call, so we must invalidate it.
|
||||||
|
handle_ = nullptr;
|
||||||
|
|
||||||
|
if (e) {
|
||||||
|
err = FfiError::from(e);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Idevice(idevice_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
~UsbmuxdConnection() {
|
||||||
|
if (handle_) {
|
||||||
|
idevice_usbmuxd_connection_free(handle_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete copy constructor and assignment operator
|
||||||
|
UsbmuxdConnection(const UsbmuxdConnection&) = delete;
|
||||||
|
UsbmuxdConnection& operator=(const UsbmuxdConnection&) = delete;
|
||||||
|
|
||||||
|
// Define move constructor and assignment operator
|
||||||
|
UsbmuxdConnection(UsbmuxdConnection&& other) noexcept : handle_(other.handle_) {
|
||||||
|
other.handle_ = nullptr;
|
||||||
|
}
|
||||||
|
UsbmuxdConnection& operator=(UsbmuxdConnection&& other) noexcept {
|
||||||
|
if (this != &other) {
|
||||||
|
if (handle_) {
|
||||||
|
idevice_usbmuxd_connection_free(handle_);
|
||||||
|
}
|
||||||
|
handle_ = other.handle_;
|
||||||
|
other.handle_ = nullptr;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Gets the raw handle.
|
||||||
|
/// @return The raw UsbmuxdConnectionHandle pointer.
|
||||||
|
UsbmuxdConnectionHandle* raw() const { return handle_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit UsbmuxdConnection(UsbmuxdConnectionHandle* handle) : handle_(handle) {}
|
||||||
|
UsbmuxdConnectionHandle* handle_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace IdeviceFFI
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user