mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 06:26:15 +01:00
Separate headers into cpp source files
This commit is contained in:
@@ -1,29 +0,0 @@
|
||||
// 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
|
||||
@@ -1,88 +0,0 @@
|
||||
// Jackson Coxson
|
||||
|
||||
#ifndef IDEVICE_CPP
|
||||
#define IDEVICE_CPP
|
||||
|
||||
#include "ffi.hpp"
|
||||
#include "pairing_file.hpp"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
// MSVC doesn't have BSD u_int* types
|
||||
using u_int8_t = std::uint8_t;
|
||||
using u_int16_t = std::uint16_t;
|
||||
using u_int32_t = std::uint32_t;
|
||||
using u_int64_t = std::uint64_t;
|
||||
#endif
|
||||
|
||||
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
|
||||
8
cpp/include/idevice++/bindings.hpp
Normal file
8
cpp/include/idevice++/bindings.hpp
Normal file
@@ -0,0 +1,8 @@
|
||||
// Jackson Coxson
|
||||
|
||||
#ifndef IDEVICE_BINDINGS_H
|
||||
#define IDEVICE_BINDINGS_H
|
||||
extern "C" {
|
||||
#include <idevice.h> // this file is generated by bindgen
|
||||
}
|
||||
#endif
|
||||
21
cpp/include/idevice++/ffi.hpp
Normal file
21
cpp/include/idevice++/ffi.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
// Jackson Coxson
|
||||
|
||||
#ifndef IDEVICE_FFI
|
||||
#define IDEVICE_FFI
|
||||
|
||||
#include <idevice++/bindings.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace IdeviceFFI {
|
||||
class FfiError {
|
||||
public:
|
||||
int32_t code = 0;
|
||||
std::string message;
|
||||
|
||||
FfiError(const IdeviceFfiError* err);
|
||||
FfiError();
|
||||
|
||||
explicit operator bool() const { return code != 0; }
|
||||
};
|
||||
} // namespace IdeviceFFI
|
||||
#endif
|
||||
63
cpp/include/idevice++/idevice.hpp
Normal file
63
cpp/include/idevice++/idevice.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
// Jackson Coxson
|
||||
|
||||
#ifndef IDEVICE_CPP
|
||||
#define IDEVICE_CPP
|
||||
|
||||
#include <idevice++/ffi.hpp>
|
||||
#include <idevice++/pairing_file.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
// MSVC doesn't have BSD u_int* types
|
||||
using u_int8_t = std::uint8_t;
|
||||
using u_int16_t = std::uint16_t;
|
||||
using u_int32_t = std::uint32_t;
|
||||
using u_int64_t = std::uint64_t;
|
||||
#endif
|
||||
|
||||
namespace IdeviceFFI {
|
||||
|
||||
// Generic “bind a free function” deleter
|
||||
template <class T, void (*FreeFn)(T*)> struct FnDeleter {
|
||||
void operator()(T* p) const noexcept {
|
||||
if (p)
|
||||
FreeFn(p);
|
||||
}
|
||||
};
|
||||
|
||||
using IdevicePtr = std::unique_ptr<IdeviceHandle, FnDeleter<IdeviceHandle, idevice_free>>;
|
||||
|
||||
class Idevice {
|
||||
public:
|
||||
static std::optional<Idevice>
|
||||
create(IdeviceSocketHandle* socket, const std::string& label, FfiError& err);
|
||||
|
||||
static std::optional<Idevice>
|
||||
create_tcp(const sockaddr* addr, socklen_t addr_len, const std::string& label, FfiError& err);
|
||||
|
||||
// Methods
|
||||
std::optional<std::string> get_type(FfiError& err) const;
|
||||
bool rsd_checkin(FfiError& err);
|
||||
bool start_session(const PairingFile& pairing_file, FfiError& err);
|
||||
|
||||
// Ownership/RAII
|
||||
~Idevice() noexcept = default;
|
||||
Idevice(Idevice&&) noexcept = default;
|
||||
Idevice& operator=(Idevice&&) noexcept = default;
|
||||
Idevice(const Idevice&) = delete;
|
||||
Idevice& operator=(const Idevice&) = delete;
|
||||
|
||||
static Idevice adopt(IdeviceHandle* h) noexcept { return Idevice(h); }
|
||||
|
||||
// Accessor
|
||||
IdeviceHandle* raw() const noexcept { return handle_.get(); }
|
||||
IdeviceHandle* release() noexcept { return handle_.release(); }
|
||||
|
||||
private:
|
||||
explicit Idevice(IdeviceHandle* h) noexcept : handle_(h) {}
|
||||
IdevicePtr handle_{};
|
||||
};
|
||||
|
||||
} // namespace IdeviceFFI
|
||||
#endif
|
||||
45
cpp/include/idevice++/lockdown.hpp
Normal file
45
cpp/include/idevice++/lockdown.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <idevice++/bindings.hpp>
|
||||
#include <idevice++/ffi.hpp>
|
||||
#include <idevice++/provider.hpp>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace IdeviceFFI {
|
||||
|
||||
using LockdownPtr =
|
||||
std::unique_ptr<LockdowndClientHandle, FnDeleter<LockdowndClientHandle, lockdownd_client_free>>;
|
||||
|
||||
class Lockdown {
|
||||
public:
|
||||
// Factory: connect via Provider
|
||||
static std::optional<Lockdown> connect(Provider& provider, FfiError& err);
|
||||
|
||||
// Factory: wrap an existing Idevice socket (consumes it on success)
|
||||
static std::optional<Lockdown> from_socket(Idevice&& socket, FfiError& err);
|
||||
|
||||
// Ops
|
||||
bool start_session(const PairingFile& pf, FfiError& err);
|
||||
std::optional<std::pair<uint16_t, bool>> start_service(const std::string& identifier,
|
||||
FfiError& err);
|
||||
std::optional<plist_t> get_value(const char* key, const char* domain, FfiError& err);
|
||||
|
||||
// RAII / moves
|
||||
~Lockdown() noexcept = default;
|
||||
Lockdown(Lockdown&&) noexcept = default;
|
||||
Lockdown& operator=(Lockdown&&) noexcept = default;
|
||||
Lockdown(const Lockdown&) = delete;
|
||||
Lockdown& operator=(const Lockdown&) = delete;
|
||||
|
||||
LockdowndClientHandle* raw() const noexcept { return handle_.get(); }
|
||||
static Lockdown adopt(LockdowndClientHandle* h) noexcept { return Lockdown(h); }
|
||||
|
||||
private:
|
||||
explicit Lockdown(LockdowndClientHandle* h) noexcept : handle_(h) {}
|
||||
LockdownPtr handle_{};
|
||||
};
|
||||
|
||||
} // namespace IdeviceFFI
|
||||
44
cpp/include/idevice++/pairing_file.hpp
Normal file
44
cpp/include/idevice++/pairing_file.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// Jackson Coxson
|
||||
|
||||
#ifndef IDEVICE_PAIRING_FILE
|
||||
#define IDEVICE_PAIRING_FILE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <idevice++/ffi.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace IdeviceFFI {
|
||||
struct PairingFileDeleter {
|
||||
void operator()(IdevicePairingFile* p) const noexcept;
|
||||
};
|
||||
|
||||
using PairingFilePtr = std::unique_ptr<IdevicePairingFile, PairingFileDeleter>;
|
||||
|
||||
class PairingFile {
|
||||
public:
|
||||
static std::optional<PairingFile> read(const std::string& path, FfiError& err);
|
||||
static std::optional<PairingFile> from_bytes(const uint8_t* data, size_t size, FfiError& err);
|
||||
|
||||
~PairingFile() noexcept = default; // unique_ptr handles destruction
|
||||
|
||||
PairingFile(const PairingFile&) = delete;
|
||||
PairingFile& operator=(const PairingFile&) = delete;
|
||||
|
||||
PairingFile(PairingFile&&) noexcept = default; // move is correct by default
|
||||
PairingFile& operator=(PairingFile&&) noexcept = default;
|
||||
|
||||
std::optional<std::vector<uint8_t>> serialize(FfiError& err) const;
|
||||
|
||||
explicit PairingFile(IdevicePairingFile* ptr) noexcept : ptr_(ptr) {}
|
||||
IdevicePairingFile* raw() const noexcept { return ptr_.get(); }
|
||||
IdevicePairingFile* release() noexcept { return ptr_.release(); }
|
||||
|
||||
private:
|
||||
PairingFilePtr ptr_{}; // owns the handle
|
||||
};
|
||||
|
||||
} // namespace IdeviceFFI
|
||||
#endif
|
||||
49
cpp/include/idevice++/provider.hpp
Normal file
49
cpp/include/idevice++/provider.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// Jackson Coxson
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <idevice++/bindings.hpp>
|
||||
#include <idevice++/ffi.hpp>
|
||||
#include <idevice++/usbmuxd.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace IdeviceFFI {
|
||||
|
||||
class FfiError;
|
||||
class PairingFile; // has: IdevicePairingFile* raw() const; void release_on_success();
|
||||
class UsbmuxdAddr; // has: UsbmuxdAddrHandle* raw() const; void release_on_success();
|
||||
|
||||
using ProviderPtr =
|
||||
std::unique_ptr<IdeviceProviderHandle, FnDeleter<IdeviceProviderHandle, idevice_provider_free>>;
|
||||
|
||||
class Provider {
|
||||
public:
|
||||
static std::optional<Provider> tcp_new(const idevice_sockaddr* ip,
|
||||
PairingFile&& pairing,
|
||||
const std::string& label,
|
||||
FfiError& err);
|
||||
|
||||
static std::optional<Provider> usbmuxd_new(UsbmuxdAddr&& addr,
|
||||
uint32_t tag,
|
||||
const std::string& udid,
|
||||
uint32_t device_id,
|
||||
const std::string& label,
|
||||
FfiError& err);
|
||||
|
||||
~Provider() noexcept = default;
|
||||
Provider(Provider&&) noexcept = default;
|
||||
Provider& operator=(Provider&&) noexcept = default;
|
||||
Provider(const Provider&) = delete;
|
||||
Provider& operator=(const Provider&) = delete;
|
||||
|
||||
IdeviceProviderHandle* raw() const noexcept { return handle_.get(); }
|
||||
static Provider adopt(IdeviceProviderHandle* h) noexcept { return Provider(h); }
|
||||
IdeviceProviderHandle* release() noexcept { return handle_.release(); }
|
||||
|
||||
private:
|
||||
explicit Provider(IdeviceProviderHandle* h) noexcept : handle_(h) {}
|
||||
ProviderPtr handle_{};
|
||||
};
|
||||
|
||||
} // namespace IdeviceFFI
|
||||
127
cpp/include/idevice++/usbmuxd.hpp
Normal file
127
cpp/include/idevice++/usbmuxd.hpp
Normal file
@@ -0,0 +1,127 @@
|
||||
// Jackson Coxson
|
||||
|
||||
#ifndef IDEVICE_USBMUXD_HPP
|
||||
#define IDEVICE_USBMUXD_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <idevice++/idevice.hpp>
|
||||
#include <idevice++/pairing_file.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
namespace IdeviceFFI {
|
||||
|
||||
using AddrPtr =
|
||||
std::unique_ptr<UsbmuxdAddrHandle, FnDeleter<UsbmuxdAddrHandle, idevice_usbmuxd_addr_free>>;
|
||||
using DevicePtr = std::unique_ptr<UsbmuxdDeviceHandle,
|
||||
FnDeleter<UsbmuxdDeviceHandle, idevice_usbmuxd_device_free>>;
|
||||
using ConnectionPtr =
|
||||
std::unique_ptr<UsbmuxdConnectionHandle,
|
||||
FnDeleter<UsbmuxdConnectionHandle, idevice_usbmuxd_connection_free>>;
|
||||
|
||||
class UsbmuxdAddr {
|
||||
public:
|
||||
static std::optional<UsbmuxdAddr>
|
||||
tcp_new(const sockaddr* addr, socklen_t addr_len, FfiError& err);
|
||||
#if defined(__unix__) || defined(__APPLE__)
|
||||
static std::optional<UsbmuxdAddr> unix_new(const std::string& path, FfiError& err);
|
||||
#endif
|
||||
static UsbmuxdAddr default_new();
|
||||
|
||||
~UsbmuxdAddr() noexcept = default;
|
||||
UsbmuxdAddr(UsbmuxdAddr&&) noexcept = default;
|
||||
UsbmuxdAddr& operator=(UsbmuxdAddr&&) noexcept = default;
|
||||
UsbmuxdAddr(const UsbmuxdAddr&) = delete;
|
||||
UsbmuxdAddr& operator=(const UsbmuxdAddr&) = delete;
|
||||
|
||||
UsbmuxdAddrHandle* raw() const noexcept { return handle_.get(); }
|
||||
UsbmuxdAddrHandle* release() noexcept { return handle_.release(); }
|
||||
static UsbmuxdAddr adopt(UsbmuxdAddrHandle* h) noexcept { return UsbmuxdAddr(h); }
|
||||
|
||||
private:
|
||||
explicit UsbmuxdAddr(UsbmuxdAddrHandle* h) noexcept : handle_(h) {}
|
||||
AddrPtr handle_{};
|
||||
};
|
||||
|
||||
class UsbmuxdConnectionType {
|
||||
public:
|
||||
enum class Value : uint8_t { Usb = 1, Network = 2, Unknown = 3 };
|
||||
explicit UsbmuxdConnectionType(uint8_t v) : _value(static_cast<Value>(v)) {}
|
||||
|
||||
std::string to_string() const; // body in .cpp
|
||||
Value value() const noexcept { return _value; }
|
||||
bool operator==(Value other) const noexcept { return _value == other; }
|
||||
|
||||
private:
|
||||
Value _value{Value::Unknown};
|
||||
};
|
||||
|
||||
class UsbmuxdDevice {
|
||||
public:
|
||||
~UsbmuxdDevice() noexcept = default;
|
||||
UsbmuxdDevice(UsbmuxdDevice&&) noexcept = default;
|
||||
UsbmuxdDevice& operator=(UsbmuxdDevice&&) noexcept = default;
|
||||
UsbmuxdDevice(const UsbmuxdDevice&) = delete;
|
||||
UsbmuxdDevice& operator=(const UsbmuxdDevice&) = delete;
|
||||
|
||||
static UsbmuxdDevice adopt(UsbmuxdDeviceHandle* h) noexcept { return UsbmuxdDevice(h); }
|
||||
|
||||
UsbmuxdDeviceHandle* raw() const noexcept { return handle_.get(); }
|
||||
|
||||
std::optional<std::string> get_udid() const;
|
||||
std::optional<uint32_t> get_id() const;
|
||||
std::optional<UsbmuxdConnectionType> get_connection_type() const;
|
||||
|
||||
private:
|
||||
explicit UsbmuxdDevice(UsbmuxdDeviceHandle* h) noexcept : handle_(h) {}
|
||||
DevicePtr handle_{};
|
||||
|
||||
friend class UsbmuxdConnection;
|
||||
};
|
||||
|
||||
class PairingFile;
|
||||
|
||||
class UsbmuxdConnection {
|
||||
public:
|
||||
static std::optional<UsbmuxdConnection>
|
||||
tcp_new(const idevice_sockaddr* addr, idevice_socklen_t addr_len, uint32_t tag, FfiError& err);
|
||||
#if defined(__unix__) || defined(__APPLE__)
|
||||
static std::optional<UsbmuxdConnection>
|
||||
unix_new(const std::string& path, uint32_t tag, FfiError& err);
|
||||
#endif
|
||||
static std::optional<UsbmuxdConnection> default_new(uint32_t tag, FfiError& err);
|
||||
|
||||
~UsbmuxdConnection() noexcept = default;
|
||||
UsbmuxdConnection(UsbmuxdConnection&&) noexcept = default;
|
||||
UsbmuxdConnection& operator=(UsbmuxdConnection&&) noexcept = default;
|
||||
UsbmuxdConnection(const UsbmuxdConnection&) = delete;
|
||||
UsbmuxdConnection& operator=(const UsbmuxdConnection&) = delete;
|
||||
|
||||
std::optional<std::vector<UsbmuxdDevice>> get_devices(FfiError& err) const;
|
||||
std::optional<std::string> get_buid(FfiError& err) const;
|
||||
std::optional<PairingFile> get_pair_record(const std::string& udid, FfiError& err);
|
||||
|
||||
std::optional<Idevice>
|
||||
connect_to_device(uint32_t device_id, uint16_t port, const std::string& path, FfiError& err) &&;
|
||||
std::optional<Idevice>
|
||||
connect_to_device(uint32_t, uint16_t, const std::string&, FfiError&) & = delete;
|
||||
std::optional<Idevice>
|
||||
connect_to_device(uint32_t, uint16_t, const std::string&, FfiError&) const& = delete;
|
||||
|
||||
UsbmuxdConnectionHandle* raw() const noexcept { return handle_.get(); }
|
||||
|
||||
private:
|
||||
explicit UsbmuxdConnection(UsbmuxdConnectionHandle* h) noexcept : handle_(h) {}
|
||||
ConnectionPtr handle_{};
|
||||
};
|
||||
|
||||
} // namespace IdeviceFFI
|
||||
#endif
|
||||
@@ -1,82 +0,0 @@
|
||||
// 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
|
||||
@@ -1,351 +0,0 @@
|
||||
// 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 <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 idevice_sockaddr* addr, idevice_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