diff --git a/StikJIT/StikJIT-Bridging-Header.h b/StikJIT/StikJIT-Bridging-Header.h index 1f665e2..32630d5 100644 --- a/StikJIT/StikJIT-Bridging-Header.h +++ b/StikJIT/StikJIT-Bridging-Header.h @@ -8,3 +8,4 @@ #include "JSSupport/JSSupport.h" #include "idevice/ideviceinfo.h" #include "idevice/ls.h" +#include "idevice/profiles.h" diff --git a/StikJIT/Utilities/mountDDI.swift b/StikJIT/Utilities/mountDDI.swift index deb0d0d..17a76d5 100644 --- a/StikJIT/Utilities/mountDDI.swift +++ b/StikJIT/Utilities/mountDDI.swift @@ -89,11 +89,10 @@ func isMounted() -> Bool { } idevice_provider_free(provider) - var devices: UnsafeMutableRawPointer? + var deviceList: UnsafeMutablePointer? var devicesLen: size_t = 0 - let listError = image_mounter_copy_devices(client, &devices, &devicesLen) + let listError = image_mounter_copy_devices(client, &deviceList, &devicesLen) if listError == nil { - let deviceList = devices?.assumingMemoryBound(to: plist_t.self) var devices: [String] = [] for i in 0.. FileWrapper { + FileWrapper(regularFileWithContents: data ?? Data()) + } +} + +// the following 2 extensions are from AltStore +extension Color { + static let refreshRed = Color(red: 1.0, green: 0.231, blue: 0.188) + static let refreshOrange = Color(red: 1.0, green: 0.584, blue: 0.0) + static let refreshYellow = Color(red: 1.0, green: 0.8, blue: 0.0) + static let refreshGreen = Color(red: 0.204, green: 0.780, blue: 0.348) + +} + +extension Date { + func numberOfCalendarDays(since date: Date) -> Int + { + let today = Calendar.current.startOfDay(for: self) + let previousDay = Calendar.current.startOfDay(for: date) + + let components = Calendar.current.dateComponents([.day], from: previousDay, to: today) + return components.day! + } +} + +struct ProfileView: View { + @State private var working = true + @State private var isExporterPresented = false + @State private var exportFileName = "" + @State private var exportDoc = ProfileDocument() + @State private var isImporterPresented = false + + @State private var searchText = "" + @State private var alert = false + @State private var alertTitle = "" + @State private var alertMsg = "" + @State private var alertSuccess = false + + @State private var profiles: [Profile] = [] + @State private var confirmRemove = false + @State private var removeTargetName: String = "" + @State private var removeTargetUUID: String = "" + @State private var expandedAppIds: Set = [] + + // Computed property to group profiles by appId and sort by expiration date + private var groupedProfiles: [String: [Profile]] { + Dictionary(grouping: profiles, by: { $0.appId }) + .mapValues { profiles in + profiles.sorted { profile1, profile2 in + // Sort by expiration date descending (latest first) + guard let date1 = profile1.expirationDate else { return false } + guard let date2 = profile2.expirationDate else { return true } + return date1 > date2 + } + } + } + + private var sortedAppIds: [String] { + groupedProfiles.keys.sorted() + } + + + @AppStorage("appTheme") private var appThemeRaw: String = AppTheme.system.rawValue + @Environment(\.themeExpansionManager) private var themeExpansion + private var backgroundStyle: BackgroundStyle { themeExpansion?.backgroundStyle(for: appThemeRaw) ?? AppTheme.system.backgroundStyle } + private var preferredScheme: ColorScheme? { themeExpansion?.preferredColorScheme(for: appThemeRaw) } + + var body: some View { + NavigationStack { + ZStack { + ThemedBackground(style: backgroundStyle) + .ignoresSafeArea() + + ScrollView { + VStack(spacing: 20) { + infoCard + } + .padding(.horizontal, 20) + .padding(.vertical, 30) + } + + if alert { + CustomErrorView(title: alertTitle, + message: alertMsg, + onDismiss: { alert = false }, + messageType: alertSuccess ? .success : .error) + } + + if confirmRemove { + CustomErrorView( + title: "Confirm Removal", + message: "Remove profile for \(removeTargetName) (UUID: \(removeTargetUUID))?\n**Apps associated this profile may become unavailable.**", + onDismiss: { confirmRemove = false }, + showButton: true, + primaryButtonText: "Remove", + secondaryButtonText: "Cancel", + onPrimaryButtonTap: { + Task { await removeProfile(uuid: removeTargetUUID) } + }, + onSecondaryButtonTap: { + // Just dismiss + }, + showSecondaryButton: true, + messageType: .info + ) + } + + } + .navigationTitle("Profiles") + .toolbar { + ToolbarItemGroup(placement: .navigationBarTrailing) { + Button { + isImporterPresented = true + } label: { + Label("Add", systemImage: "plus") + } + + Button { + Task {await loadProfiles()} + } label: { + Label("Reload", systemImage: "arrow.clockwise") + } + + } + } + + .onAppear { Task { await loadProfiles() } } + .fileImporter( + isPresented: $isImporterPresented, + allowedContentTypes: [UTType(filenameExtension: "mobileprovision")!], + allowsMultipleSelection: false + ) { result in + Task { await handleImport(result: result) } + } + .fileExporter( + isPresented: $isExporterPresented, + document: exportDoc, + contentType: .data, + defaultFilename: exportFileName + ) { result in + switch result { + case .success(let url): + print("Saved at \(url)") + case .failure(let error): + print("Error: \(error.localizedDescription)") + } + } + } + .preferredColorScheme(preferredScheme) + } + + // MARK: - UI Sections + + private var infoCard: some View { + VStack(alignment: .leading, spacing: 14) { + + if profiles.isEmpty { + Text(working ? "Loading Profiles" : "No profile available") + } else { + ForEach(sortedAppIds, id: \.self) { appId in + if let appProfiles = groupedProfiles[appId] { + VStack(alignment: .leading, spacing: 8) { + // App ID header with expand/collapse control + HStack { + Text(appId) + .font(.headline) + .foregroundColor(.primary) + Spacer() + if appProfiles.count > 1 { + Button { + withAnimation(.easeInOut(duration: 0.3)) { + if expandedAppIds.contains(appId) { + expandedAppIds.remove(appId) + } else { + expandedAppIds.insert(appId) + } + } + } label: { + HStack(spacing: 4) { + Image(systemName: expandedAppIds.contains(appId) ? "chevron.up" : "chevron.down") + } + } + } + } + .padding(.top, appId != sortedAppIds.first ? 8 : 0) + + // Profiles for this app (only first if collapsed) + let displayProfiles: [Profile] = expandedAppIds.contains(appId) ? appProfiles : Array(appProfiles.prefix(1)) + ForEach(Array(displayProfiles.enumerated()), id: \.element.uuid) { pair in + let index = pair.offset + let entry = pair.element + HStack { + VStack(alignment: .leading, spacing: 6) { + Text(entry.appName).bold() + Text(entry.uuid).lineLimit(1) + Text("Expires: \(entry.formattedDate)") + .foregroundStyle(index == 0 ? entry.dateColor : Color.secondary) + } + .font(.caption.monospaced()) + .foregroundColor(.secondary) + .textSelection(.enabled) + Spacer() + HStack(spacing: 12) { + Button { saveProfile(profile: entry) } label: { Image(systemName: "square.and.arrow.down") } + Button(role: .destructive) { + removeTargetName = entry.appName + removeTargetUUID = entry.uuid + confirmRemove = true + } label: { Image(systemName: "trash") } + } + } + .padding(.vertical, 4) + .transition(.opacity.combined(with: .move(edge: .top))) + if entry.uuid != displayProfiles.last?.uuid { + Divider().background(Color.white.opacity(0.08)) + } + } + .animation(.easeInOut(duration: 0.3), value: expandedAppIds) + } + + // Divider between app groups + if appId != sortedAppIds.last { + Divider() + .background(Color.white.opacity(0.12)) + .padding(.vertical, 4) + } + } + } + } + } + .padding(20) + .background( + RoundedRectangle(cornerRadius: 20, style: .continuous) + .fill(.ultraThinMaterial) + .overlay( + RoundedRectangle(cornerRadius: 20, style: .continuous) + .strokeBorder(Color.white.opacity(0.15), lineWidth: 1) + ) + ) + .clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous)) + .shadow(color: .black.opacity(0.15), radius: 12, x: 0, y: 4) + } + + func loadProfiles() async { + working = true + do { + let profileDatas = try JITEnableContext.shared.fetchAllProfiles() + self.profiles = profileDatas.map { Profile(data: $0) } + } catch { + alertMsg = error.localizedDescription + alertTitle = "Failed to Fetch Profiles" + alertSuccess = false + alert = true + } + working = false + } + + func saveProfile(profile: Profile) { + exportFileName = "\(profile.appName).mobileprovision" + exportDoc = ProfileDocument(data: profile.data) + isExporterPresented = true + } + + func removeProfile(uuid: String) async { + working = true + do { + try JITEnableContext.shared.removeProfile(withUUID: uuid) + alertMsg = "Profile removed successfully" + alertTitle = "Success" + alertSuccess = true + alert = true + // Reload profiles after removal + await loadProfiles() + } catch { + alertMsg = error.localizedDescription + alertTitle = "Failed to Remove Profile" + alertSuccess = false + alert = true + } + working = false + } + + func handleImport(result: Result<[URL], Error>) async { + working = true + do { + let fileURL = try result.get().first + guard let fileURL = fileURL else { + throw NSError(domain: "ProfileView", code: -1, userInfo: [NSLocalizedDescriptionKey: "No file selected"]) + } + + // Start accessing security-scoped resource + let accessing = fileURL.startAccessingSecurityScopedResource() + defer { + if accessing { + fileURL.stopAccessingSecurityScopedResource() + } + } + + let profileData = try Data(contentsOf: fileURL) + try JITEnableContext.shared.addProfile(profileData) + + alertMsg = "Profile added successfully" + alertTitle = "Success" + alertSuccess = true + alert = true + + // Reload profiles after adding + await loadProfiles() + } catch { + alertMsg = error.localizedDescription + alertTitle = "Failed to Add Profile" + alertSuccess = false + alert = true + } + working = false + } + +} diff --git a/StikJIT/Views/SettingsView.swift b/StikJIT/Views/SettingsView.swift index 05b44e0..5a5cf26 100644 --- a/StikJIT/Views/SettingsView.swift +++ b/StikJIT/Views/SettingsView.swift @@ -367,6 +367,23 @@ struct SettingsView: View { Text("Advanced") .font(.headline) .foregroundColor(.primary) + + NavigationLink { + DeviceInfoView() + } label: { + HStack { + Image(systemName: "info.circle") + .font(.system(size: 18)) + .foregroundColor(.primary.opacity(0.8)) + Text("Device Info") + .foregroundColor(.primary.opacity(0.8)) + Spacer() + Image(systemName: "chevron.right") + .font(.system(size: 14)) + .foregroundColor(.white) + } + .padding(.vertical, 8) + } Button(action: { openAppFolder() }) { HStack { diff --git a/StikJIT/idevice/JITEnableContext.h b/StikJIT/idevice/JITEnableContext.h index 5d18bd3..0371c00 100644 --- a/StikJIT/idevice/JITEnableContext.h +++ b/StikJIT/idevice/JITEnableContext.h @@ -29,4 +29,7 @@ typedef void (^SyslogErrorHandler)(NSError *error); - (void)startSyslogRelayWithHandler:(SyslogLineHandler)lineHandler onError:(SyslogErrorHandler)errorHandler NS_SWIFT_NAME(startSyslogRelay(handler:onError:)); - (void)stopSyslogRelay; +- (NSArray*)fetchAllProfiles:(NSError **)error; +- (BOOL)removeProfileWithUUID:(NSString*)uuid error:(NSError **)error; +- (BOOL)addProfile:(NSData*)profile error:(NSError **)error; @end diff --git a/StikJIT/idevice/JITEnableContext.m b/StikJIT/idevice/JITEnableContext.m index 6525209..eb816f9 100644 --- a/StikJIT/idevice/JITEnableContext.m +++ b/StikJIT/idevice/JITEnableContext.m @@ -11,6 +11,7 @@ #include "heartbeat.h" #include "jit.h" #include "applist.h" +#include "profiles.h" #include "JITEnableContext.h" #import "StikDebug-Swift.h" @@ -364,6 +365,33 @@ JITEnableContext* sharedJITContext = nil; } } +- (NSArray*)fetchAllProfiles:(NSError **)error { + if (!provider) { + NSLog(@"Provider not initialized!"); + *error = [self errorWithStr:@"Provider not initialized!" code:-1]; + return nil; + } + return fetchAppProfiles(provider, error); +} + +- (BOOL)removeProfileWithUUID:(NSString*)uuid error:(NSError **)error { + if (!provider) { + NSLog(@"Provider not initialized!"); + *error = [self errorWithStr:@"Provider not initialized!" code:-1]; + return nil; + } + return removeProfile(provider, uuid, error); +} + +- (BOOL)addProfile:(NSData*)profile error:(NSError **)error { + if (!provider) { + NSLog(@"Provider not initialized!"); + *error = [self errorWithStr:@"Provider not initialized!" code:-1]; + return nil; + } + return addProfile(provider, profile, error); +} + - (void)dealloc { [self stopSyslogRelay]; if (provider) { diff --git a/StikJIT/idevice/applist.m b/StikJIT/idevice/applist.m index 03e808f..e17f949 100644 --- a/StikJIT/idevice/applist.m +++ b/StikJIT/idevice/applist.m @@ -152,7 +152,7 @@ static NSDictionary *performAppQuery(IdeviceProviderHandle BOOL (^filter)(plist_t app)) { InstallationProxyClientHandle *client = NULL; - if (installation_proxy_connect_tcp(provider, &client)) { + if (installation_proxy_connect(provider, &client)) { *error = @"Failed to connect to installation proxy"; return nil; } diff --git a/StikJIT/idevice/idevice.h b/StikJIT/idevice/idevice.h index b0bdec1..d452c71 100644 --- a/StikJIT/idevice/idevice.h +++ b/StikJIT/idevice/idevice.h @@ -1,6 +1,21 @@ // Jackson Coxson // Bindings to idevice - https://github.com/jkcoxson/idevice +#ifdef _WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include + #include + typedef int idevice_socklen_t; + typedef struct sockaddr idevice_sockaddr; +#else + #include + #include + typedef socklen_t idevice_socklen_t; + typedef struct sockaddr idevice_sockaddr; +#endif + #ifndef IDEVICE_H #define IDEVICE_H @@ -9,8 +24,6 @@ #include #include #include -#include -#include "plist.h" #define LOCKDOWN_PORT 62078 @@ -31,6 +44,13 @@ typedef enum AfcLinkType { Symbolic = 2, } AfcLinkType; +typedef enum IdeviceLoggerError { + Success = 0, + FileError = -1, + AlreadyInitialized = -2, + InvalidPathString = -3, +} IdeviceLoggerError; + typedef enum IdeviceLogLevel { Disabled = 0, ErrorLevel = 1, @@ -40,13 +60,6 @@ typedef enum IdeviceLogLevel { Trace = 5, } IdeviceLogLevel; -typedef enum IdeviceLoggerError { - Success = 0, - FileError = -1, - AlreadyInitialized = -2, - InvalidPathString = -3, -} IdeviceLoggerError; - typedef struct AdapterHandle AdapterHandle; typedef struct AdapterStreamHandle AdapterStreamHandle; @@ -60,6 +73,11 @@ typedef struct AfcFileHandle AfcFileHandle; typedef struct AmfiClientHandle AmfiClientHandle; +/** + * Opaque handle to an AppServiceClient + */ +typedef struct AppServiceHandle AppServiceHandle; + typedef struct CoreDeviceProxyHandle CoreDeviceProxyHandle; /** @@ -67,6 +85,13 @@ typedef struct CoreDeviceProxyHandle CoreDeviceProxyHandle; */ typedef struct DebugProxyHandle DebugProxyHandle; +typedef struct DiagnosticsRelayClientHandle DiagnosticsRelayClientHandle; + +/** + * Opaque handle to an AppServiceClient + */ +typedef struct DiagnosticsServiceHandle DiagnosticsServiceHandle; + typedef struct HeartbeatClientHandle HeartbeatClientHandle; /** @@ -117,17 +142,33 @@ typedef struct RemoteServerHandle RemoteServerHandle; */ typedef struct RsdHandshakeHandle RsdHandshakeHandle; +/** + * An opaque FFI handle for a [`ScreenshotClient`]. + * + * This type wraps a [`ScreenshotClient`] that communicates with + * a connected device to capture screenshots through the DVT (Device Virtualization Toolkit) service. + */ +typedef struct ScreenshotClientHandle ScreenshotClientHandle; + typedef struct SpringBoardServicesClientHandle SpringBoardServicesClientHandle; +typedef struct SysdiagnoseStreamHandle SysdiagnoseStreamHandle; + typedef struct SyslogRelayClientHandle SyslogRelayClientHandle; +typedef struct TcpEatObject TcpEatObject; + +typedef struct TcpFeedObject TcpFeedObject; + typedef struct UsbmuxdAddrHandle UsbmuxdAddrHandle; typedef struct UsbmuxdConnectionHandle UsbmuxdConnectionHandle; -typedef struct Vec_u64 Vec_u64; +typedef struct UsbmuxdDeviceHandle UsbmuxdDeviceHandle; -typedef struct sockaddr sockaddr; +typedef struct UsbmuxdListenerHandle UsbmuxdListenerHandle; + +typedef struct Vec_u64 Vec_u64; typedef struct IdeviceFfiError { int32_t code; @@ -157,6 +198,64 @@ typedef struct AfcDeviceInfo { size_t block_size; } AfcDeviceInfo; +/** + * C-compatible app list entry + */ +typedef struct AppListEntryC { + int is_removable; + char *name; + int is_first_party; + char *path; + char *bundle_identifier; + int is_developer_app; + char *bundle_version; + int is_internal; + int is_hidden; + int is_app_clip; + char *version; +} AppListEntryC; + +/** + * C-compatible launch response + */ +typedef struct LaunchResponseC { + uint32_t process_identifier_version; + uint32_t pid; + char *executable_url; + uint32_t *audit_token; + uintptr_t audit_token_len; +} LaunchResponseC; + +/** + * C-compatible process token + */ +typedef struct ProcessTokenC { + uint32_t pid; + char *executable_url; +} ProcessTokenC; + +/** + * C-compatible signal response + */ +typedef struct SignalResponseC { + uint32_t pid; + char *executable_url; + uint64_t device_timestamp; + uint32_t signal; +} SignalResponseC; + +/** + * C-compatible icon data + */ +typedef struct IconDataC { + uint8_t *data; + uintptr_t data_len; + double icon_width; + double icon_height; + double minimum_width; + double minimum_height; +} IconDataC; + /** * Represents a debugserver command */ @@ -166,6 +265,11 @@ typedef struct DebugserverCommandHandle { uintptr_t argv_count; } DebugserverCommandHandle; +/** + * Stub to avoid header problems + */ +typedef void *plist_t; + typedef struct SyslogLabel { const char *subsystem; const char *category; @@ -248,6 +352,17 @@ struct IdeviceFfiError *idevice_new(struct IdeviceSocketHandle *socket, const char *label, struct IdeviceHandle **idevice); +/** + * Creates an Idevice object from a socket file descriptor + * + * # Safety + * The socket FD must be valid. + * The pointers must be valid and non-null. + */ +struct IdeviceFfiError *idevice_from_fd(int32_t fd, + const char *label, + struct IdeviceHandle **idevice); + /** * Creates a new Idevice connection * @@ -265,8 +380,8 @@ struct IdeviceFfiError *idevice_new(struct IdeviceSocketHandle *socket, * `label` must be a valid null-terminated C string * `idevice` must be a valid, non-null pointer to a location where the handle will be stored */ -struct IdeviceFfiError *idevice_new_tcp_socket(const struct sockaddr *addr, - socklen_t addr_len, +struct IdeviceFfiError *idevice_new_tcp_socket(const idevice_sockaddr *addr, + idevice_socklen_t addr_len, const char *label, struct IdeviceHandle **idevice); @@ -316,7 +431,8 @@ struct IdeviceFfiError *idevice_rsd_checkin(struct IdeviceHandle *idevice); * `pairing_file` must be a valid, non-null pointer to a pairing file handle */ struct IdeviceFfiError *idevice_start_session(struct IdeviceHandle *idevice, - const struct IdevicePairingFile *pairing_file); + const struct IdevicePairingFile *pairing_file, + bool legacy); /** * Frees an Idevice handle @@ -342,6 +458,18 @@ void idevice_free(struct IdeviceHandle *idevice); */ void idevice_string_free(char *string); +/** + * Frees data allocated by this library + * + * # Arguments + * * [`data`] - The data to free + * + * # Safety + * `data` must be a valid pointer to data that was allocated by this library, + * or NULL (in which case this function does nothing) + */ +void idevice_data_free(uint8_t *data, uintptr_t len); + /** * Connects the adapter to a specific port * @@ -379,7 +507,7 @@ struct IdeviceFfiError *adapter_connect(struct AdapterHandle *adapter_handle, struct IdeviceFfiError *adapter_pcap(struct AdapterHandle *handle, const char *path); /** - * Closes the adapter connection + * Closes the adapter stream connection * * # Arguments * * [`handle`] - The adapter stream handle @@ -390,13 +518,27 @@ struct IdeviceFfiError *adapter_pcap(struct AdapterHandle *handle, const char *p * # Safety * `handle` must be a valid pointer to a handle allocated by this library */ -struct IdeviceFfiError *adapter_close(struct AdapterStreamHandle *handle); +struct IdeviceFfiError *adapter_stream_close(struct AdapterStreamHandle *handle); /** - * Sends data through the adapter + * Stops the entire adapter TCP stack * * # Arguments * * [`handle`] - The adapter handle + * + * # Returns + * Null on success, an IdeviceFfiError otherwise + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *adapter_close(struct AdapterHandle *handle); + +/** + * Sends data through the adapter stream + * + * # Arguments + * * [`handle`] - The adapter stream handle * * [`data`] - The data to send * * [`length`] - The length of the data * @@ -412,10 +554,10 @@ struct IdeviceFfiError *adapter_send(struct AdapterStreamHandle *handle, uintptr_t length); /** - * Receives data from the adapter + * Receives data from the adapter stream * * # Arguments - * * [`handle`] - The adapter handle + * * [`handle`] - The adapter stream handle * * [`data`] - Pointer to a buffer where the received data will be stored * * [`length`] - Pointer to store the actual length of received data * * [`max_length`] - Maximum number of bytes that can be stored in `data` @@ -818,6 +960,325 @@ struct IdeviceFfiError *amfi_accept_developer_mode(struct AmfiClientHandle *clie */ void amfi_client_free(struct AmfiClientHandle *handle); +/** + * Creates a new AppServiceClient using RSD connection + * + * # Arguments + * * [`provider`] - An adapter created by this library + * * [`handshake`] - An RSD handshake from the same provider + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `provider` and `handshake` must be valid pointers to handles allocated by this library + * `handle` must be a valid pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *app_service_connect_rsd(struct AdapterHandle *provider, + struct RsdHandshakeHandle *handshake, + struct AppServiceHandle **handle); + +/** + * Creates a new AppServiceClient from a socket + * + * # Arguments + * * [`socket`] - The socket to use for communication + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `socket` must be a valid pointer to a handle allocated by this library + * `handle` must be a valid pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *app_service_new(struct ReadWriteOpaque *socket, + struct AppServiceHandle **handle); + +/** + * Frees an AppServiceClient handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void app_service_free(struct AppServiceHandle *handle); + +/** + * Lists applications on the device + * + * # Arguments + * * [`handle`] - The AppServiceClient handle + * * [`app_clips`] - Include app clips + * * [`removable_apps`] - Include removable apps + * * [`hidden_apps`] - Include hidden apps + * * [`internal_apps`] - Include internal apps + * * [`default_apps`] - Include default apps + * * [`apps`] - Pointer to store the array of apps (caller must free) + * * [`count`] - Pointer to store the number of apps + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `handle`, `apps`, and `count` must be valid pointers + */ +struct IdeviceFfiError *app_service_list_apps(struct AppServiceHandle *handle, + int app_clips, + int removable_apps, + int hidden_apps, + int internal_apps, + int default_apps, + struct AppListEntryC **apps, + uintptr_t *count); + +/** + * Frees an array of AppListEntryC structures + * + * # Safety + * `apps` must be a valid pointer to an array allocated by app_service_list_apps + * `count` must match the count returned by app_service_list_apps + */ +void app_service_free_app_list(struct AppListEntryC *apps, uintptr_t count); + +/** + * Launches an application + * + * # Arguments + * * [`handle`] - The AppServiceClient handle + * * [`bundle_id`] - Bundle identifier of the app to launch + * * [`argv`] - NULL-terminated array of arguments + * * [`argc`] - Number of arguments + * * [`kill_existing`] - Whether to kill existing instances + * * [`start_suspended`] - Whether to start suspended + * * [`stdio_uuid`] - The UUID received from openstdiosocket, null for none + * * [`response`] - Pointer to store the launch response (caller must free) + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *app_service_launch_app(struct AppServiceHandle *handle, + const char *bundle_id, + const char *const *argv, + uintptr_t argc, + int kill_existing, + int start_suspended, + const uint8_t *stdio_uuid, + struct LaunchResponseC **response); + +/** + * Frees a LaunchResponseC structure + * + * # Safety + * `response` must be a valid pointer allocated by app_service_launch_app + */ +void app_service_free_launch_response(struct LaunchResponseC *response); + +/** + * Lists running processes + * + * # Arguments + * * [`handle`] - The AppServiceClient handle + * * [`processes`] - Pointer to store the array of processes (caller must free) + * * [`count`] - Pointer to store the number of processes + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *app_service_list_processes(struct AppServiceHandle *handle, + struct ProcessTokenC **processes, + uintptr_t *count); + +/** + * Frees an array of ProcessTokenC structures + * + * # Safety + * `processes` must be a valid pointer allocated by app_service_list_processes + * `count` must match the count returned by app_service_list_processes + */ +void app_service_free_process_list(struct ProcessTokenC *processes, uintptr_t count); + +/** + * Uninstalls an application + * + * # Arguments + * * [`handle`] - The AppServiceClient handle + * * [`bundle_id`] - Bundle identifier of the app to uninstall + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *app_service_uninstall_app(struct AppServiceHandle *handle, + const char *bundle_id); + +/** + * Sends a signal to a process + * + * # Arguments + * * [`handle`] - The AppServiceClient handle + * * [`pid`] - Process ID + * * [`signal`] - Signal number + * * [`response`] - Pointer to store the signal response (caller must free) + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *app_service_send_signal(struct AppServiceHandle *handle, + uint32_t pid, + uint32_t signal, + struct SignalResponseC **response); + +/** + * Frees a SignalResponseC structure + * + * # Safety + * `response` must be a valid pointer allocated by app_service_send_signal + */ +void app_service_free_signal_response(struct SignalResponseC *response); + +/** + * Fetches an app icon + * + * # Arguments + * * [`handle`] - The AppServiceClient handle + * * [`bundle_id`] - Bundle identifier of the app + * * [`width`] - Icon width + * * [`height`] - Icon height + * * [`scale`] - Icon scale + * * [`allow_placeholder`] - Whether to allow placeholder icons + * * [`icon_data`] - Pointer to store the icon data (caller must free) + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *app_service_fetch_app_icon(struct AppServiceHandle *handle, + const char *bundle_id, + float width, + float height, + float scale, + int allow_placeholder, + struct IconDataC **icon_data); + +/** + * Frees an IconDataC structure + * + * # Safety + * `icon_data` must be a valid pointer allocated by app_service_fetch_app_icon + */ +void app_service_free_icon_data(struct IconDataC *icon_data); + +/** + * Creates a new DiagnosticsServiceClient using RSD connection + * + * # Arguments + * * [`provider`] - An adapter created by this library + * * [`handshake`] - An RSD handshake from the same provider + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `provider` and `handshake` must be valid pointers to handles allocated by this library + * `handle` must be a valid pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *diagnostics_service_connect_rsd(struct AdapterHandle *provider, + struct RsdHandshakeHandle *handshake, + struct DiagnosticsServiceHandle **handle); + +/** + * Creates a new DiagnostisServiceClient from a socket + * + * # Arguments + * * [`socket`] - The socket to use for communication + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `socket` must be a valid pointer to a handle allocated by this library + * `handle` must be a valid pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *diagnostics_service_new(struct ReadWriteOpaque *socket, + struct DiagnosticsServiceHandle **handle); + +/** + * Captures a sysdiagnose from the device. + * Note that this will take a LONG time to return while the device collects enough information to + * return to the service. This function returns a stream that can be called on to get the next + * chunk of data. A typical sysdiagnose is roughly 1-2 GB. + * + * # Arguments + * * [`handle`] - The handle to the client + * * [`dry_run`] - Whether or not to do a dry run with a simple .txt file from the device + * * [`preferred_filename`] - The name the device wants to save the sysdaignose as + * * [`expected_length`] - The size in bytes of the sysdiagnose + * * [`stream_handle`] - The handle that will be set to capture bytes for + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * Pointers must be all valid. Handle must be allocated by this library. Preferred filename must + * be freed `idevice_string_free`. + */ +struct IdeviceFfiError *diagnostics_service_capture_sysdiagnose(struct DiagnosticsServiceHandle *handle, + bool dry_run, + char **preferred_filename, + uintptr_t *expected_length, + struct SysdiagnoseStreamHandle **stream_handle); + +/** + * Gets the next packet from the stream. + * Data will be set to 0 when there is no more data to get from the stream. + * + * # Arguments + * * [`handle`] - The handle to the stream + * * [`data`] - A pointer to the bytes + * * [`len`] - The length of the bytes written + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * Pass valid pointers. The handle must be allocated by this library. + */ +struct IdeviceFfiError *sysdiagnose_stream_next(struct SysdiagnoseStreamHandle *handle, + uint8_t **data, + uintptr_t *len); + +/** + * Frees a DiagnostisServiceClient handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void diagnostics_service_free(struct DiagnosticsServiceHandle *handle); + +/** + * Frees a SysdiagnoseStreamHandle handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void sysdiagnose_stream_free(struct SysdiagnoseStreamHandle *handle); + /** * Automatically creates and connects to Core Device Proxy, returning a client handle * @@ -1027,6 +1488,23 @@ struct IdeviceFfiError *debug_proxy_connect_rsd(struct AdapterHandle *provider, struct RsdHandshakeHandle *handshake, struct DebugProxyHandle **handle); +/** + * Creates a new DebugProxyClient + * + * # Arguments + * * [`socket`] - The socket to use for communication. Any object that supports ReadWrite. + * * [`handle`] - Pointer to store the newly created DebugProxyClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `socket` must be a valid pointer to a handle allocated by this library + * `handle` must be a valid pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *debug_proxy_new(struct ReadWriteOpaque *socket, + struct DebugProxyHandle **handle); + /** * Frees a DebugProxyClient handle * @@ -1173,6 +1651,473 @@ struct IdeviceFfiError *debug_proxy_send_nack(struct DebugProxyHandle *handle); */ void debug_proxy_set_ack_mode(struct DebugProxyHandle *handle, int enabled); +/** + * Automatically creates and connects to Diagnostics Relay, returning a client handle + * + * # Arguments + * * [`provider`] - An IdeviceProvider + * * [`client`] - On success, will be set to point to a newly allocated DiagnosticsRelayClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `provider` must be a valid pointer to a handle allocated by this library + * `client` must be a valid, non-null pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *diagnostics_relay_client_connect(struct IdeviceProviderHandle *provider, + struct DiagnosticsRelayClientHandle **client); + +/** + * Automatically creates and connects to Diagnostics Relay, returning a client handle + * + * # Arguments + * * [`socket`] - An IdeviceSocket handle + * * [`client`] - On success, will be set to point to a newly allocated DiagnosticsRelayClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `socket` must be a valid pointer to a handle allocated by this library. The socket is consumed, + * and may not be used again. + * `client` must be a valid, non-null pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *diagnostics_relay_client_new(struct IdeviceHandle *socket, + struct DiagnosticsRelayClientHandle **client); + +/** + * Queries the device IO registry + * + * # Arguments + * * `client` - A valid DiagnosticsRelayClient handle + * * `current_plane` - A string to search by or null + * * `entry_name` - A string to search by or null + * * `entry_class` - A string to search by or null + * * `res` - Will be set to a pointer of a plist dictionary node on search success + * + * # Returns + * An IdeviceFfiError on error, null on success. Note that res can be null on success + * if the search resulted in no values. + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *diagnostics_relay_client_ioregistry(struct DiagnosticsRelayClientHandle *client, + const char *current_plane, + const char *entry_name, + const char *entry_class, + plist_t *res); + +/** + * Requests MobileGestalt information from the device + * + * # Arguments + * * `client` - A valid DiagnosticsRelayClient handle + * * `keys` - Optional list of specific keys to request. If None, requests all available keys + * * `res` - Will be set to a pointer of a plist dictionary node on search success + * + * # Returns + * An IdeviceFfiError on error, null on success. Note that res can be null on success + * if the search resulted in no values. + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *diagnostics_relay_client_mobilegestalt(struct DiagnosticsRelayClientHandle *client, + const char *const *keys, + uintptr_t keys_len, + plist_t *res); + +/** + * Requests gas gauge information from the device + * + * # Arguments + * * `client` - A valid DiagnosticsRelayClient handle + * * `res` - Will be set to a pointer of a plist dictionary node on search success + * + * # Returns + * An IdeviceFfiError on error, null on success. Note that res can be null on success + * if the search resulted in no values. + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *diagnostics_relay_client_gasguage(struct DiagnosticsRelayClientHandle *client, + plist_t *res); + +/** + * Requests nand information from the device + * + * # Arguments + * * `client` - A valid DiagnosticsRelayClient handle + * * `res` - Will be set to a pointer of a plist dictionary node on search success + * + * # Returns + * An IdeviceFfiError on error, null on success. Note that res can be null on success + * if the search resulted in no values. + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *diagnostics_relay_client_nand(struct DiagnosticsRelayClientHandle *client, + plist_t *res); + +/** + * Requests all available information from the device + * + * # Arguments + * * `client` - A valid DiagnosticsRelayClient handle + * * `res` - Will be set to a pointer of a plist dictionary node on search success + * + * # Returns + * An IdeviceFfiError on error, null on success. Note that res can be null on success + * if the search resulted in no values. + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *diagnostics_relay_client_all(struct DiagnosticsRelayClientHandle *client, + plist_t *res); + +/** + * Restarts the device + * + * # Arguments + * * `client` - A valid DiagnosticsRelayClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success. + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *diagnostics_relay_client_restart(struct DiagnosticsRelayClientHandle *client); + +/** + * Shuts down the device + * + * # Arguments + * * `client` - A valid DiagnosticsRelayClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success. + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *diagnostics_relay_client_shutdown(struct DiagnosticsRelayClientHandle *client); + +/** + * Puts the device to sleep + * + * # Arguments + * * `client` - A valid DiagnosticsRelayClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success. + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *diagnostics_relay_client_sleep(struct DiagnosticsRelayClientHandle *client); + +/** + * Requests WiFi diagnostics from the device + * + * # Arguments + * * `client` - A valid DiagnosticsRelayClient handle + * * `res` - Will be set to a pointer of a plist dictionary node on search success + * + * # Returns + * An IdeviceFfiError on error, null on success. Note that res can be null on success + * if the search resulted in no values. + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *diagnostics_relay_client_wifi(struct DiagnosticsRelayClientHandle *client, + plist_t *res); + +/** + * Puts the device to sleep + * + * # Arguments + * * `client` - A valid DiagnosticsRelayClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success. + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *diagnostics_relay_client_goodbye(struct DiagnosticsRelayClientHandle *client); + +/** + * Frees a handle + * + * # Arguments + * * [`handle`] - The handle to free + * + * # Safety + * `handle` must be a valid pointer to the handle that was allocated by this library, + * or NULL (in which case this function does nothing) + */ +void diagnostics_relay_client_free(struct DiagnosticsRelayClientHandle *handle); + +/** + * Creates a new ProcessControlClient from a RemoteServerClient + * + * # Arguments + * * [`server`] - The RemoteServerClient to use + * * [`handle`] - Pointer to store the newly created ProcessControlClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `server` must be a valid pointer to a handle allocated by this library + * `handle` must be a valid pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *location_simulation_new(struct RemoteServerHandle *server, + struct LocationSimulationHandle **handle); + +/** + * Frees a ProcessControlClient handle + * + * # Arguments + * * [`handle`] - The handle to free + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void location_simulation_free(struct LocationSimulationHandle *handle); + +/** + * Clears the location set + * + * # Arguments + * * [`handle`] - The LocationSimulation handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid or NULL where appropriate + */ +struct IdeviceFfiError *location_simulation_clear(struct LocationSimulationHandle *handle); + +/** + * Sets the location + * + * # Arguments + * * [`handle`] - The LocationSimulation handle + * * [`latitude`] - The latitude to set + * * [`longitude`] - The longitude to set + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid or NULL where appropriate + */ +struct IdeviceFfiError *location_simulation_set(struct LocationSimulationHandle *handle, + double latitude, + double longitude); + +/** + * Creates a new ProcessControlClient from a RemoteServerClient + * + * # Arguments + * * [`server`] - The RemoteServerClient to use + * * [`handle`] - Pointer to store the newly created ProcessControlClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `server` must be a valid pointer to a handle allocated by this library + * `handle` must be a valid pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *process_control_new(struct RemoteServerHandle *server, + struct ProcessControlHandle **handle); + +/** + * Frees a ProcessControlClient handle + * + * # Arguments + * * [`handle`] - The handle to free + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void process_control_free(struct ProcessControlHandle *handle); + +/** + * Launches an application on the device + * + * # Arguments + * * [`handle`] - The ProcessControlClient handle + * * [`bundle_id`] - The bundle identifier of the app to launch + * * [`env_vars`] - NULL-terminated array of environment variables (format "KEY=VALUE") + * * [`arguments`] - NULL-terminated array of arguments + * * [`start_suspended`] - Whether to start the app suspended + * * [`kill_existing`] - Whether to kill existing instances of the app + * * [`pid`] - Pointer to store the process ID of the launched app + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid or NULL where appropriate + */ +struct IdeviceFfiError *process_control_launch_app(struct ProcessControlHandle *handle, + const char *bundle_id, + const char *const *env_vars, + uintptr_t env_vars_count, + const char *const *arguments, + uintptr_t arguments_count, + bool start_suspended, + bool kill_existing, + uint64_t *pid); + +/** + * Kills a running process + * + * # Arguments + * * [`handle`] - The ProcessControlClient handle + * * [`pid`] - The process ID to kill + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *process_control_kill_app(struct ProcessControlHandle *handle, uint64_t pid); + +/** + * Disables memory limits for a process + * + * # Arguments + * * [`handle`] - The ProcessControlClient handle + * * [`pid`] - The process ID to modify + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *process_control_disable_memory_limit(struct ProcessControlHandle *handle, + uint64_t pid); + +/** + * Creates a new RemoteServerClient from a ReadWrite connection + * + * # Arguments + * * [`socket`] - The connection to use for communication, an object that implements ReadWrite + * * [`handle`] - Pointer to store the newly created RemoteServerClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `socket` must be a valid pointer to a handle allocated by this library. It is consumed and may + * not be used again. + * `handle` must be a valid pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *remote_server_new(struct ReadWriteOpaque *socket, + struct RemoteServerHandle **handle); + +/** + * Creates a new RemoteServerClient from a handshake and adapter + * + * # Arguments + * * [`provider`] - An adapter created by this library + * * [`handshake`] - An RSD handshake from the same provider + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `provider` must be a valid pointer to a handle allocated by this library + * `handshake` must be a valid pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *remote_server_connect_rsd(struct AdapterHandle *provider, + struct RsdHandshakeHandle *handshake, + struct RemoteServerHandle **handle); + +/** + * Frees a RemoteServerClient handle + * + * # Arguments + * * [`handle`] - The handle to free + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void remote_server_free(struct RemoteServerHandle *handle); + +/** + * Creates a new [`ScreenshotClient`] associated with a given [`RemoteServerHandle`]. + * + * # Arguments + * * `server` - A pointer to a valid [`RemoteServerHandle`], previously created by this library. + * * `handle` - A pointer to a location where the newly created [`ScreenshotClientHandle`] will be stored. + * + * # Returns + * * `null_mut()` on success. + * * A pointer to an [`IdeviceFfiError`] on failure. + * + * # Safety + * - `server` must be a non-null pointer to a valid remote server handle allocated by this library. + * - `handle` must be a non-null pointer to a writable memory location where the handle will be stored. + * - The returned handle must later be freed using [`screenshot_client_free`]. + */ +struct IdeviceFfiError *screenshot_client_new(struct RemoteServerHandle *server, + struct ScreenshotClientHandle **handle); + +/** + * Frees a [`ScreenshotClientHandle`]. + * + * This releases all memory associated with the handle. + * After calling this function, the handle pointer must not be used again. + * + * # Arguments + * * `handle` - Pointer to a [`ScreenshotClientHandle`] previously returned by [`screenshot_client_new`]. + * + * # Safety + * - `handle` must either be `NULL` or a valid pointer created by this library. + * - Double-freeing or using the handle after freeing causes undefined behavior. + */ +void screenshot_client_free(struct ScreenshotClientHandle *handle); + +/** + * Captures a screenshot from the connected device. + * + * On success, this function writes a pointer to the PNG-encoded screenshot data and its length + * into the provided output arguments. The caller is responsible for freeing this data using + * `idevice_data_free`. + * + * # Arguments + * * `handle` - A pointer to a valid [`ScreenshotClientHandle`]. + * * `data` - Output pointer where the screenshot buffer pointer will be written. + * * `len` - Output pointer where the buffer length (in bytes) will be written. + * + * # Returns + * * `null_mut()` on success. + * * A pointer to an [`IdeviceFfiError`] on failure. + * + * # Safety + * - `handle` must be a valid pointer to a [`ScreenshotClientHandle`]. + * - `data` and `len` must be valid writable pointers. + * - The data returned through `*data` must be freed by the caller with `idevice_data_free`. + */ +struct IdeviceFfiError *screenshot_client_take_screenshot(struct ScreenshotClientHandle *handle, + uint8_t **data, + uintptr_t *len); + /** * Frees the IdeviceFfiError * @@ -1274,8 +2219,8 @@ void heartbeat_client_free(struct HeartbeatClientHandle *handle); * `provider` must be a valid pointer to a handle allocated by this library * `client` must be a valid, non-null pointer to a location where the handle will be stored */ -struct IdeviceFfiError *installation_proxy_connect_tcp(struct IdeviceProviderHandle *provider, - struct InstallationProxyClientHandle **client); +struct IdeviceFfiError *installation_proxy_connect(struct IdeviceProviderHandle *provider, + struct InstallationProxyClientHandle **client); /** * Automatically creates and connects to Installation Proxy, returning a client handle @@ -1348,7 +2293,7 @@ void installation_proxy_client_free(struct InstallationProxyClientHandle *handle */ struct IdeviceFfiError *installation_proxy_install(struct InstallationProxyClientHandle *client, const char *package_path, - void *options); + plist_t options); /** * Installs an application package on the device @@ -1370,7 +2315,7 @@ struct IdeviceFfiError *installation_proxy_install(struct InstallationProxyClien */ struct IdeviceFfiError *installation_proxy_install_with_callback(struct InstallationProxyClientHandle *client, const char *package_path, - void *options, + plist_t options, void (*callback)(uint64_t progress, void *context), void *context); @@ -1393,7 +2338,7 @@ struct IdeviceFfiError *installation_proxy_install_with_callback(struct Installa */ struct IdeviceFfiError *installation_proxy_upgrade(struct InstallationProxyClientHandle *client, const char *package_path, - void *options); + plist_t options); /** * Upgrades an existing application on the device @@ -1415,7 +2360,7 @@ struct IdeviceFfiError *installation_proxy_upgrade(struct InstallationProxyClien */ struct IdeviceFfiError *installation_proxy_upgrade_with_callback(struct InstallationProxyClientHandle *client, const char *package_path, - void *options, + plist_t options, void (*callback)(uint64_t progress, void *context), void *context); @@ -1438,7 +2383,7 @@ struct IdeviceFfiError *installation_proxy_upgrade_with_callback(struct Installa */ struct IdeviceFfiError *installation_proxy_uninstall(struct InstallationProxyClientHandle *client, const char *bundle_id, - void *options); + plist_t options); /** * Uninstalls an application from the device @@ -1460,7 +2405,7 @@ struct IdeviceFfiError *installation_proxy_uninstall(struct InstallationProxyCli */ struct IdeviceFfiError *installation_proxy_uninstall_with_callback(struct InstallationProxyClientHandle *client, const char *bundle_id, - void *options, + plist_t options, void (*callback)(uint64_t progress, void *context), void *context); @@ -1485,9 +2430,9 @@ struct IdeviceFfiError *installation_proxy_uninstall_with_callback(struct Instal * `out_result` must be a valid pointer to a bool */ struct IdeviceFfiError *installation_proxy_check_capabilities_match(struct InstallationProxyClientHandle *client, - void *const *capabilities, + const plist_t *capabilities, size_t capabilities_len, - void *options, + plist_t options, bool *out_result); /** @@ -1509,72 +2454,12 @@ struct IdeviceFfiError *installation_proxy_check_capabilities_match(struct Insta * `out_result_len` must be a valid, non-null pointer to a location where the length will be stored */ struct IdeviceFfiError *installation_proxy_browse(struct InstallationProxyClientHandle *client, - void *options, - void **out_result, + plist_t options, + plist_t **out_result, size_t *out_result_len); /** - * Creates a new ProcessControlClient from a RemoteServerClient - * - * # Arguments - * * [`server`] - The RemoteServerClient to use - * * [`handle`] - Pointer to store the newly created ProcessControlClient handle - * - * # Returns - * An IdeviceFfiError on error, null on success - * - * # Safety - * `server` must be a valid pointer to a handle allocated by this library - * `handle` must be a valid pointer to a location where the handle will be stored - */ -struct IdeviceFfiError *location_simulation_new(struct RemoteServerHandle *server, - struct LocationSimulationHandle **handle); - -/** - * Frees a ProcessControlClient handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL - */ -void location_simulation_free(struct LocationSimulationHandle *handle); - -/** - * Clears the location set - * - * # Arguments - * * [`handle`] - The LocationSimulation handle - * - * # Returns - * An IdeviceFfiError on error, null on success - * - * # Safety - * All pointers must be valid or NULL where appropriate - */ -struct IdeviceFfiError *location_simulation_clear(struct LocationSimulationHandle *handle); - -/** - * Sets the location - * - * # Arguments - * * [`handle`] - The LocationSimulation handle - * * [`latitude`] - The latitude to set - * * [`longitude`] - The longitude to set - * - * # Returns - * An IdeviceFfiError on error, null on success - * - * # Safety - * All pointers must be valid or NULL where appropriate - */ -struct IdeviceFfiError *location_simulation_set(struct LocationSimulationHandle *handle, - double latitude, - double longitude); - -/** - * Connects to lockdownd service using TCP provider + * Connects to lockdownd service using provider * * # Arguments * * [`provider`] - An IdeviceProvider @@ -1667,25 +2552,7 @@ struct IdeviceFfiError *lockdownd_start_service(struct LockdowndClientHandle *cl struct IdeviceFfiError *lockdownd_get_value(struct LockdowndClientHandle *client, const char *key, const char *domain, - void **out_plist); - -/** - * Gets all values from lockdownd - * - * # Arguments - * * `client` - A valid LockdowndClient handle - * * `out_plist` - Pointer to store the returned plist dictionary - * - * # Returns - * An IdeviceFfiError on error, null on success - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `out_plist` must be a valid pointer to store the plist - */ -struct IdeviceFfiError *lockdownd_get_all_values(struct LockdowndClientHandle *client, - const char *domain, - void **out_plist); + plist_t *out_plist); /** * Frees a LockdowndClient handle @@ -1700,26 +2567,10 @@ struct IdeviceFfiError *lockdownd_get_all_values(struct LockdowndClientHandle *c void lockdownd_client_free(struct LockdowndClientHandle *handle); /** - * Initializes the logger - * - * # Arguments - * * [`console_level`] - The level to log to the file - * * [`file_level`] - The level to log to the file - * * [`file_path`] - If not null, the file to write logs to - * - * ## Log Level - * 0. Disabled - * 1. Error - * 2. Warn - * 3. Info - * 4. Debug - * 5. Trace - * - * # Returns - * 0 for success, -1 if the file couldn't be created, -2 if a logger has been initialized, -3 for invalid path string + * Initializes the global logger * * # Safety - * Pass a valid CString for file_path. Pass valid log levels according to the enum + * Pass a valid file path string */ enum IdeviceLoggerError idevice_init_logger(enum IdeviceLogLevel console_level, enum IdeviceLogLevel file_level, @@ -1886,7 +2737,7 @@ void image_mounter_free(struct ImageMounterHandle *handle); * `devices` must be a valid, non-null pointer to a location where the plist will be stored */ struct IdeviceFfiError *image_mounter_copy_devices(struct ImageMounterHandle *client, - void **devices, + plist_t **devices, size_t *devices_len); /** @@ -2082,7 +2933,7 @@ struct IdeviceFfiError *image_mounter_query_nonce(struct ImageMounterHandle *cli */ struct IdeviceFfiError *image_mounter_query_personalization_identifiers(struct ImageMounterHandle *client, const char *image_type, - void **identifiers); + plist_t *identifiers); /** * Rolls the personalization nonce @@ -2352,93 +3203,6 @@ struct IdeviceFfiError *idevice_pairing_file_serialize(const struct IdevicePairi */ void idevice_pairing_file_free(struct IdevicePairingFile *pairing_file); -/** - * Creates a new ProcessControlClient from a RemoteServerClient - * - * # Arguments - * * [`server`] - The RemoteServerClient to use - * * [`handle`] - Pointer to store the newly created ProcessControlClient handle - * - * # Returns - * An IdeviceFfiError on error, null on success - * - * # Safety - * `server` must be a valid pointer to a handle allocated by this library - * `handle` must be a valid pointer to a location where the handle will be stored - */ -struct IdeviceFfiError *process_control_new(struct RemoteServerHandle *server, - struct ProcessControlHandle **handle); - -/** - * Frees a ProcessControlClient handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL - */ -void process_control_free(struct ProcessControlHandle *handle); - -/** - * Launches an application on the device - * - * # Arguments - * * [`handle`] - The ProcessControlClient handle - * * [`bundle_id`] - The bundle identifier of the app to launch - * * [`env_vars`] - NULL-terminated array of environment variables (format "KEY=VALUE") - * * [`arguments`] - NULL-terminated array of arguments - * * [`start_suspended`] - Whether to start the app suspended - * * [`kill_existing`] - Whether to kill existing instances of the app - * * [`pid`] - Pointer to store the process ID of the launched app - * - * # Returns - * An IdeviceFfiError on error, null on success - * - * # Safety - * All pointers must be valid or NULL where appropriate - */ -struct IdeviceFfiError *process_control_launch_app(struct ProcessControlHandle *handle, - const char *bundle_id, - const char *const *env_vars, - uintptr_t env_vars_count, - const char *const *arguments, - uintptr_t arguments_count, - bool start_suspended, - bool kill_existing, - uint64_t *pid); - -/** - * Kills a running process - * - * # Arguments - * * [`handle`] - The ProcessControlClient handle - * * [`pid`] - The process ID to kill - * - * # Returns - * An IdeviceFfiError on error, null on success - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - */ -struct IdeviceFfiError *process_control_kill_app(struct ProcessControlHandle *handle, uint64_t pid); - -/** - * Disables memory limits for a process - * - * # Arguments - * * [`handle`] - The ProcessControlClient handle - * * [`pid`] - The process ID to modify - * - * # Returns - * An IdeviceFfiError on error, null on success - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - */ -struct IdeviceFfiError *process_control_disable_memory_limit(struct ProcessControlHandle *handle, - uint64_t pid); - /** * Creates a TCP provider for idevice * @@ -2457,7 +3221,7 @@ struct IdeviceFfiError *process_control_disable_memory_limit(struct ProcessContr * `label` must be a valid Cstr * `provider` must be a valid, non-null pointer to a location where the handle will be stored */ -struct IdeviceFfiError *idevice_tcp_provider_new(const struct sockaddr *ip, +struct IdeviceFfiError *idevice_tcp_provider_new(const idevice_sockaddr *ip, struct IdevicePairingFile *pairing_file, const char *label, struct IdeviceProviderHandle **provider); @@ -2502,51 +3266,20 @@ struct IdeviceFfiError *usbmuxd_provider_new(struct UsbmuxdAddrHandle *addr, struct IdeviceProviderHandle **provider); /** - * Creates a new RemoteServerClient from a ReadWrite connection + * Gets the pairing file for the device * * # Arguments - * * [`socket`] - The connection to use for communication, an object that implements ReadWrite - * * [`handle`] - Pointer to store the newly created RemoteServerClient handle + * * [`provider`] - A pointer to the provider + * * [`pairing_file`] - A pointer to the newly allocated pairing file * * # Returns * An IdeviceFfiError on error, null on success * * # Safety - * `socket` must be a valid pointer to a handle allocated by this library. It is consumed and may - * not be used again. - * `handle` must be a valid pointer to a location where the handle will be stored + * `provider` must be a valid, non-null pointer to the provider */ -struct IdeviceFfiError *remote_server_new(struct ReadWriteOpaque *socket, - struct RemoteServerHandle **handle); - -/** - * Creates a new RemoteServerClient from a handshake and adapter - * - * # Arguments - * * [`provider`] - An adapter created by this library - * * [`handshake`] - An RSD handshake from the same provider - * - * # Returns - * An IdeviceFfiError on error, null on success - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `handshake` must be a valid pointer to a location where the handle will be stored - */ -struct IdeviceFfiError *remote_server_connect_rsd(struct AdapterHandle *provider, - struct RsdHandshakeHandle *handshake, - struct RemoteServerHandle **handle); - -/** - * Frees a RemoteServerClient handle - * - * # Arguments - * * [`handle`] - The handle to free - * - * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL - */ -void remote_server_free(struct RemoteServerHandle *handle); +struct IdeviceFfiError *idevice_provider_get_pairing_file(struct IdeviceProviderHandle *provider, + struct IdevicePairingFile **pairing_file); /** * Creates a new RSD handshake from a ReadWrite connection @@ -2658,6 +3391,14 @@ struct IdeviceFfiError *rsd_get_service_info(struct RsdHandshakeHandle *handle, const char *service_name, struct CRsdService **service_info); +/** + * Clones an RSD handshake + * + * # Safety + * Pass a valid pointer allocated by this library + */ +struct RsdHandshakeHandle *rsd_handshake_clone(struct RsdHandshakeHandle *handshake); + /** * Frees a string returned by RSD functions * @@ -2810,6 +3551,48 @@ void syslog_relay_client_free(struct SyslogRelayClientHandle *handle); struct IdeviceFfiError *syslog_relay_next(struct SyslogRelayClientHandle *client, char **log_message); +/** + * # Safety + * Pass valid pointers. + */ +struct IdeviceFfiError *idevice_tcp_stack_into_sync_objects(const char *our_ip, + const char *their_ip, + struct TcpFeedObject **feeder, + struct TcpEatObject **tcp_receiver, + struct AdapterHandle **adapter_handle); + +/** + * Feed the TCP stack with data + * # Safety + * Pass valid pointers. Data is cloned out of slice. + */ +struct IdeviceFfiError *idevice_tcp_feed_object_write(struct TcpFeedObject *object, + const uint8_t *data, + uintptr_t len); + +/** + * Block on getting a block of data to write to the underlying stream. + * Write this to the stream as is, and free the data with idevice_data_free + * + * # Safety + * Pass valid pointers + */ +struct IdeviceFfiError *idevice_tcp_eat_object_read(struct TcpEatObject *object, + uint8_t **data, + uintptr_t *len); + +/** + * # Safety + * Pass a valid pointer allocated by this library + */ +void idevice_free_tcp_feed_object(struct TcpFeedObject *object); + +/** + * # Safety + * Pass a valid pointer allocated by this library + */ +void idevice_free_tcp_eat_object(struct TcpEatObject *object); + /** * Connects to a usbmuxd instance over TCP * @@ -2826,10 +3609,10 @@ struct IdeviceFfiError *syslog_relay_next(struct SyslogRelayClientHandle *client * `addr` must be a valid sockaddr * `usbmuxd_connection` must be a valid, non-null pointer to a location where the handle will be stored */ -struct IdeviceFfiError *idevice_usbmuxd_new_tcp_connection(const struct sockaddr *addr, - socklen_t addr_len, +struct IdeviceFfiError *idevice_usbmuxd_new_tcp_connection(const idevice_sockaddr *addr, + idevice_socklen_t addr_len, uint32_t tag, - struct UsbmuxdConnectionHandle **usbmuxd_connection); + struct UsbmuxdConnectionHandle **out); /** * Connects to a usbmuxd instance over unix socket @@ -2850,6 +3633,175 @@ struct IdeviceFfiError *idevice_usbmuxd_new_unix_socket_connection(const char *a uint32_t tag, struct UsbmuxdConnectionHandle **usbmuxd_connection); +/** + * Connects to a usbmuxd instance over the default connection for the platform + * + * # Arguments + * * [`addr`] - The socket path to connect to + * * [`tag`] - A tag that will be returned by usbmuxd responses + * * [`usbmuxd_connection`] - On success, will be set to point to a newly allocated UsbmuxdConnection handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `addr` must be a valid CStr + * `usbmuxd_connection` must be a valid, non-null pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *idevice_usbmuxd_new_default_connection(uint32_t tag, + struct UsbmuxdConnectionHandle **usbmuxd_connection); + +/** + * Gets a list of connected devices from usbmuxd. + * + * The returned list must be freed with `idevice_usbmuxd_device_list_free`. + * + * # Arguments + * * `usbmuxd_conn` - A valid connection to usbmuxd. + * * `devices` - A pointer to a C-style array of `UsbmuxdDeviceHandle` pointers. On success, this will be filled. + * * `count` - A pointer to an integer. On success, this will be filled with the number of devices found. + * + * # Returns + * An `IdeviceFfiError` on error, `null` on success. + * + * # Safety + * * `usbmuxd_conn` must be a valid pointer. + * * `devices` and `count` must be valid, non-null pointers. + */ +struct IdeviceFfiError *idevice_usbmuxd_get_devices(struct UsbmuxdConnectionHandle *usbmuxd_conn, + struct UsbmuxdDeviceHandle ***devices, + int *count); + +/** + * Connects to a service on a given device. + * + * This function consumes the `UsbmuxdConnectionHandle`. The handle will be invalid after this call + * and must not be used again. The caller is NOT responsible for freeing it. + * A new `IdeviceHandle` is returned on success, which must be freed by the caller. + * + * # Arguments + * * `usbmuxd_connection` - The connection to use. It will be consumed. + * * `device_id` - The ID of the device to connect to. + * * `port` - The TCP port on the device to connect to. + * * `idevice` - On success, points to the new device connection handle. + * + * # Returns + * An `IdeviceFfiError` on error, `null` on success. + * + * # Safety + * * `usbmuxd_connection` must be a valid pointer allocated by this library and never used again. + * The value is consumed. + * * `idevice` must be a valid, non-null pointer. + */ +struct IdeviceFfiError *idevice_usbmuxd_connect_to_device(struct UsbmuxdConnectionHandle *usbmuxd_connection, + uint32_t device_id, + uint16_t port, + const char *label, + struct IdeviceHandle **idevice); + +/** + * Reads the pairing record for a given device UDID. + * + * The returned `PairingFileHandle` must be freed with `idevice_pair_record_free`. + * + * # Arguments + * * `usbmuxd_conn` - A valid connection to usbmuxd. + * * `udid` - The UDID of the device. + * * `pair_record` - On success, points to the new pairing file handle. + * + * # Returns + * An `IdeviceFfiError` on error, `null` on success. + * + * # Safety + * * `usbmuxd_conn` must be a valid pointer. + * * `udid` must be a valid, null-terminated C string. + * * `pair_record` must be a valid, non-null pointer. + */ +struct IdeviceFfiError *idevice_usbmuxd_get_pair_record(struct UsbmuxdConnectionHandle *usbmuxd_conn, + const char *udid, + struct IdevicePairingFile **pair_record); + +/** + * Saves the pairing record for a given device UDID. + * + * # Arguments + * * `usbmuxd_conn` - A valid connection to usbmuxd. + * * `device_id` - The muxer ID for the device + * * `udid` - The UDID of the device. + * * `pair_record` - The bytes of the pairing record plist to save + * * `pair_record_len` - the length of the pairing record bytes + * + * # Returns + * An `IdeviceFfiError` on error, `null` on success. + * + * # Safety + * * `usbmuxd_conn` must be a valid pointer. + * * `udid` must be a valid, null-terminated C string. + * * `pair_record` must be a valid, non-null pointer. + */ +struct IdeviceFfiError *idevice_usbmuxd_save_pair_record(struct UsbmuxdConnectionHandle *usbmuxd_conn, + const char *udid, + uint8_t *pair_record, + uintptr_t pair_record_len); + +/** + * Listens on the socket for connections and disconnections + * + * # Safety + * Pass valid pointers. Free the stream with ``idevice_usbmuxd_listener_handle_free``. + * The stream must outlive the usbmuxd connection, and the usbmuxd connection cannot + * be used for other requests. + */ +struct IdeviceFfiError *idevice_usbmuxd_listen(struct UsbmuxdConnectionHandle *usbmuxd_conn, + struct UsbmuxdListenerHandle **stream_handle); + +/** + * Frees a stream created by ``listen`` or does nothing on null + * + * # Safety + * Pass a valid pointer. + */ +void idevice_usbmuxd_listener_handle_free(struct UsbmuxdListenerHandle *stream_handle); + +/** + * Gets the next event from the stream. + * Connect will be set to true if the event is a connection event, + * and the connection_device will be filled with the device information. + * If connection is false, the mux ID of the device will be filled. + * + * # Arguments + * * `stream_handle` - The handle to the stream returned by listen + * * `connect` - The bool that will be set + * * `connection_device` - The pointer that will be filled on a connect event + * * `disconnection_id` - The mux ID that will be set on a disconnect event + * + * # Safety + * Pass valid pointers + */ +struct IdeviceFfiError *idevice_usbmuxd_listener_next(struct UsbmuxdListenerHandle *stream_handle, + bool *connect, + struct UsbmuxdDeviceHandle **connection_device, + uint32_t *disconnection_id); + +/** + * Reads the BUID (Boot-Unique ID) from usbmuxd. + * + * The returned string must be freed with `idevice_string_free`. + * + * # Arguments + * * `usbmuxd_conn` - A valid connection to usbmuxd. + * * `buid` - On success, points to a newly allocated, null-terminated C string. + * + * # Returns + * An `IdeviceFfiError` on error, `null` on success. + * + * # Safety + * * `usbmuxd_conn` must be a valid pointer. + * * `buid` must be a valid, non-null pointer. + */ +struct IdeviceFfiError *idevice_usbmuxd_get_buid(struct UsbmuxdConnectionHandle *usbmuxd_conn, + char **buid); + /** * Frees a UsbmuxdConnection handle * @@ -2877,8 +3829,8 @@ void idevice_usbmuxd_connection_free(struct UsbmuxdConnectionHandle *usbmuxd_con * `addr` must be a valid sockaddr * `usbmuxd_Addr` must be a valid, non-null pointer to a location where the handle will be stored */ -struct IdeviceFfiError *idevice_usbmuxd_tcp_addr_new(const struct sockaddr *addr, - socklen_t addr_len, +struct IdeviceFfiError *idevice_usbmuxd_tcp_addr_new(const idevice_sockaddr *addr, + idevice_socklen_t addr_len, struct UsbmuxdAddrHandle **usbmuxd_addr); /** @@ -2898,6 +3850,20 @@ struct IdeviceFfiError *idevice_usbmuxd_tcp_addr_new(const struct sockaddr *addr struct IdeviceFfiError *idevice_usbmuxd_unix_addr_new(const char *addr, struct UsbmuxdAddrHandle **usbmuxd_addr); +/** + * Creates a default UsbmuxdAddr struct for the platform + * + * # Arguments + * * [`usbmuxd_addr`] - On success, will be set to point to a newly allocated UsbmuxdAddr handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `usbmuxd_addr` must be a valid, non-null pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *idevice_usbmuxd_default_addr_new(struct UsbmuxdAddrHandle **usbmuxd_addr); + /** * Frees a UsbmuxdAddr handle * @@ -2910,4 +3876,1508 @@ struct IdeviceFfiError *idevice_usbmuxd_unix_addr_new(const char *addr, */ void idevice_usbmuxd_addr_free(struct UsbmuxdAddrHandle *usbmuxd_addr); +/** + * Frees a list of devices returned by `idevice_usbmuxd_get_devices`. + * + * # Arguments + * * `devices` - The array of device handles to free. + * * `count` - The number of elements in the array. + * + * # Safety + * `devices` must be a valid pointer to an array of `count` device handles + * allocated by this library, or NULL. + */ +void idevice_usbmuxd_device_list_free(struct UsbmuxdDeviceHandle **devices, int count); + +/** + * Frees a usbmuxd device + * + * # Arguments + * * `device` - The device handle to free. + * + * # Safety + * `device` must be a valid pointer to the device handle + * allocated by this library, or NULL. + */ +void idevice_usbmuxd_device_free(struct UsbmuxdDeviceHandle *device); + +/** + * Gets the UDID from a device handle. + * The returned string must be freed by the caller using `idevice_string_free`. + * + * # Safety + * `device` must be a valid pointer to a `UsbmuxdDeviceHandle`. + */ +char *idevice_usbmuxd_device_get_udid(const struct UsbmuxdDeviceHandle *device); + +/** + * Gets the device ID from a device handle. + * + * # Safety + * `device` must be a valid pointer to a `UsbmuxdDeviceHandle`. + */ +uint32_t idevice_usbmuxd_device_get_device_id(const struct UsbmuxdDeviceHandle *device); + +/** + * Gets the connection type (UsbmuxdConnectionType) from a device handle. + * + * # Returns + * The enum value of the connection type, or 0 for null device handles + * + * # Safety + * `device` must be a valid pointer to a `UsbmuxdDeviceHandle`. + */ +uint8_t idevice_usbmuxd_device_get_connection_type(const struct UsbmuxdDeviceHandle *device); + +#endif /* IDEVICE_H */ + + + +/** + * @file plist/plist.h + * @brief Main include of libplist + * \internal + * + * Copyright (c) 2012-2023 Nikias Bassen, All Rights Reserved. + * Copyright (c) 2008-2009 Jonathan Beck, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBPLIST_H +#define LIBPLIST_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if _MSC_VER && _MSC_VER < 1700 + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; + + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + +#else +#include +#endif + +/*{{{ deprecation macros */ +#ifdef __llvm__ + #if defined(__has_extension) + #if (__has_extension(attribute_deprecated_with_message)) + #ifndef PLIST_WARN_DEPRECATED + #define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated(x))) + #endif + #else + #ifndef PLIST_WARN_DEPRECATED + #define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated)) + #endif + #endif + #else + #ifndef PLIST_WARN_DEPRECATED + #define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated)) + #endif + #endif +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 5))) + #ifndef PLIST_WARN_DEPRECATED + #define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated(x))) + #endif +#elif defined(_MSC_VER) + #ifndef PLIST_WARN_DEPRECATED + #define PLIST_WARN_DEPRECATED(x) __declspec(deprecated(x)) + #endif +#else + #define PLIST_WARN_DEPRECATED(x) + #pragma message("WARNING: You need to implement DEPRECATED for this compiler") +#endif +/*}}}*/ + +#ifndef PLIST_API + #ifdef LIBPLIST_STATIC + #define PLIST_API + #elif defined(_WIN32) + #define PLIST_API __declspec(dllimport) + #else + #define PLIST_API + #endif +#endif + +#include +#include +#include + + /** + * libplist : A library to handle Apple Property Lists + * \defgroup PublicAPI Public libplist API + */ + /*@{*/ + + + /** + * The basic plist abstract data type. + */ + typedef void *plist_t; + + /** + * The plist dictionary iterator. + */ + typedef void* plist_dict_iter; + + /** + * The plist array iterator. + */ + typedef void* plist_array_iter; + + /** + * The enumeration of plist node types. + */ + typedef enum + { + PLIST_NONE =-1, /**< No type */ + PLIST_BOOLEAN, /**< Boolean, scalar type */ + PLIST_INT, /**< Integer, scalar type */ + PLIST_REAL, /**< Real, scalar type */ + PLIST_STRING, /**< ASCII string, scalar type */ + PLIST_ARRAY, /**< Ordered array, structured type */ + PLIST_DICT, /**< Unordered dictionary (key/value pair), structured type */ + PLIST_DATE, /**< Date, scalar type */ + PLIST_DATA, /**< Binary data, scalar type */ + PLIST_KEY, /**< Key in dictionaries (ASCII String), scalar type */ + PLIST_UID, /**< Special type used for 'keyed encoding' */ + PLIST_NULL, /**< NULL type */ + } plist_type; + + /* for backwards compatibility */ + #define PLIST_UINT PLIST_INT + + /** + * libplist error values + */ + typedef enum + { + PLIST_ERR_SUCCESS = 0, /**< operation successful */ + PLIST_ERR_INVALID_ARG = -1, /**< one or more of the parameters are invalid */ + PLIST_ERR_FORMAT = -2, /**< the plist contains nodes not compatible with the output format */ + PLIST_ERR_PARSE = -3, /**< parsing of the input format failed */ + PLIST_ERR_NO_MEM = -4, /**< not enough memory to handle the operation */ + PLIST_ERR_IO = -5, /**< I/O error */ + PLIST_ERR_UNKNOWN = -255 /**< an unspecified error occurred */ + } plist_err_t; + + /** + * libplist format types + */ + typedef enum + { + PLIST_FORMAT_NONE = 0, /**< No format */ + PLIST_FORMAT_XML = 1, /**< XML format */ + PLIST_FORMAT_BINARY = 2, /**< bplist00 format */ + PLIST_FORMAT_JSON = 3, /**< JSON format */ + PLIST_FORMAT_OSTEP = 4, /**< OpenStep "old-style" plist format */ + /* 5-9 are reserved for possible future use */ + PLIST_FORMAT_PRINT = 10, /**< human-readable output-only format */ + PLIST_FORMAT_LIMD = 11, /**< "libimobiledevice" output-only format (ideviceinfo) */ + PLIST_FORMAT_PLUTIL = 12, /**< plutil-style output-only format */ + } plist_format_t; + + /** + * libplist write options + */ + typedef enum + { + PLIST_OPT_NONE = 0, /**< Default value to use when none of the options is needed. */ + PLIST_OPT_COMPACT = 1 << 0, /**< Use a compact representation (non-prettified). Only valid for #PLIST_FORMAT_JSON and #PLIST_FORMAT_OSTEP. */ + PLIST_OPT_PARTIAL_DATA = 1 << 1, /**< Print 24 bytes maximum of #PLIST_DATA values. If the data is longer than 24 bytes, the first 16 and last 8 bytes will be written. Only valid for #PLIST_FORMAT_PRINT. */ + PLIST_OPT_NO_NEWLINE = 1 << 2, /**< Do not print a final newline character. Only valid for #PLIST_FORMAT_PRINT, #PLIST_FORMAT_LIMD, and #PLIST_FORMAT_PLUTIL. */ + PLIST_OPT_INDENT = 1 << 3, /**< Indent each line of output. Currently only #PLIST_FORMAT_PRINT and #PLIST_FORMAT_LIMD are supported. Use #PLIST_OPT_INDENT_BY() macro to specify the level of indentation. */ + } plist_write_options_t; + + /** To be used with #PLIST_OPT_INDENT - encodes the level of indentation for OR'ing it into the #plist_write_options_t bitfield. */ + #define PLIST_OPT_INDENT_BY(x) ((x & 0xFF) << 24) + + + /******************************************** + * * + * Creation & Destruction * + * * + ********************************************/ + + /** + * Create a new root plist_t type #PLIST_DICT + * + * @return the created plist + * @sa #plist_type + */ + PLIST_API plist_t plist_new_dict(void); + + /** + * Create a new root plist_t type #PLIST_ARRAY + * + * @return the created plist + * @sa #plist_type + */ + PLIST_API plist_t plist_new_array(void); + + /** + * Create a new plist_t type #PLIST_STRING + * + * @param val the sting value, encoded in UTF8. + * @return the created item + * @sa #plist_type + */ + PLIST_API plist_t plist_new_string(const char *val); + + /** + * Create a new plist_t type #PLIST_BOOLEAN + * + * @param val the boolean value, 0 is false, other values are true. + * @return the created item + * @sa #plist_type + */ + PLIST_API plist_t plist_new_bool(uint8_t val); + + /** + * Create a new plist_t type #PLIST_INT with an unsigned integer value + * + * @param val the unsigned integer value + * @return the created item + * @sa #plist_type + * @note The value is always stored as uint64_t internally. + * Use #plist_get_uint_val or #plist_get_int_val to get the unsigned or signed value. + */ + PLIST_API plist_t plist_new_uint(uint64_t val); + + /** + * Create a new plist_t type #PLIST_INT with a signed integer value + * + * @param val the signed integer value + * @return the created item + * @sa #plist_type + * @note The value is always stored as uint64_t internally. + * Use #plist_get_uint_val or #plist_get_int_val to get the unsigned or signed value. + */ + PLIST_API plist_t plist_new_int(int64_t val); + + /** + * Create a new plist_t type #PLIST_REAL + * + * @param val the real value + * @return the created item + * @sa #plist_type + */ + PLIST_API plist_t plist_new_real(double val); + + /** + * Create a new plist_t type #PLIST_DATA + * + * @param val the binary buffer + * @param length the length of the buffer + * @return the created item + * @sa #plist_type + */ + PLIST_API plist_t plist_new_data(const char *val, uint64_t length); + + /** + * Create a new plist_t type #PLIST_DATE + * + * @param sec The number of seconds since 01/01/1970 (UNIX timestamp) + * @return the created item + * @sa #plist_type + */ + PLIST_API plist_t plist_new_unix_date(int64_t sec); + + /** + * Create a new plist_t type #PLIST_UID + * + * @param val the unsigned integer value + * @return the created item + * @sa #plist_type + */ + PLIST_API plist_t plist_new_uid(uint64_t val); + + /** + * Create a new plist_t type #PLIST_NULL + * @return the created item + * @sa #plist_type + * @note This type is not valid for all formats, e.g. the XML format + * does not support it. + */ + PLIST_API plist_t plist_new_null(void); + + /** + * Destruct a plist_t node and all its children recursively + * + * @param plist the plist to free + */ + PLIST_API void plist_free(plist_t plist); + + /** + * Return a copy of passed node and it's children + * + * @param node the plist to copy + * @return copied plist + */ + PLIST_API plist_t plist_copy(plist_t node); + + + /******************************************** + * * + * Array functions * + * * + ********************************************/ + + /** + * Get size of a #PLIST_ARRAY node. + * + * @param node the node of type #PLIST_ARRAY + * @return size of the #PLIST_ARRAY node + */ + PLIST_API uint32_t plist_array_get_size(plist_t node); + + /** + * Get the nth item in a #PLIST_ARRAY node. + * + * @param node the node of type #PLIST_ARRAY + * @param n the index of the item to get. Range is [0, array_size[ + * @return the nth item or NULL if node is not of type #PLIST_ARRAY + */ + PLIST_API plist_t plist_array_get_item(plist_t node, uint32_t n); + + /** + * Get the index of an item. item must be a member of a #PLIST_ARRAY node. + * + * @param node the node + * @return the node index or UINT_MAX if node index can't be determined + */ + PLIST_API uint32_t plist_array_get_item_index(plist_t node); + + /** + * Set the nth item in a #PLIST_ARRAY node. + * The previous item at index n will be freed using #plist_free + * + * @param node the node of type #PLIST_ARRAY + * @param item the new item at index n. The array is responsible for freeing item when it is no longer needed. + * @param n the index of the item to get. Range is [0, array_size[. Assert if n is not in range. + */ + PLIST_API void plist_array_set_item(plist_t node, plist_t item, uint32_t n); + + /** + * Append a new item at the end of a #PLIST_ARRAY node. + * + * @param node the node of type #PLIST_ARRAY + * @param item the new item. The array is responsible for freeing item when it is no longer needed. + */ + PLIST_API void plist_array_append_item(plist_t node, plist_t item); + + /** + * Insert a new item at position n in a #PLIST_ARRAY node. + * + * @param node the node of type #PLIST_ARRAY + * @param item the new item to insert. The array is responsible for freeing item when it is no longer needed. + * @param n The position at which the node will be stored. Range is [0, array_size[. Assert if n is not in range. + */ + PLIST_API void plist_array_insert_item(plist_t node, plist_t item, uint32_t n); + + /** + * Remove an existing position in a #PLIST_ARRAY node. + * Removed position will be freed using #plist_free. + * + * @param node the node of type #PLIST_ARRAY + * @param n The position to remove. Range is [0, array_size[. Assert if n is not in range. + */ + PLIST_API void plist_array_remove_item(plist_t node, uint32_t n); + + /** + * Remove a node that is a child node of a #PLIST_ARRAY node. + * node will be freed using #plist_free. + * + * @param node The node to be removed from its #PLIST_ARRAY parent. + */ + PLIST_API void plist_array_item_remove(plist_t node); + + /** + * Create an iterator of a #PLIST_ARRAY node. + * The allocated iterator should be freed with the standard free function. + * + * @param node The node of type #PLIST_ARRAY + * @param iter Location to store the iterator for the array. + */ + PLIST_API void plist_array_new_iter(plist_t node, plist_array_iter *iter); + + /** + * Increment iterator of a #PLIST_ARRAY node. + * + * @param node The node of type #PLIST_ARRAY. + * @param iter Iterator of the array + * @param item Location to store the item. The caller must *not* free the + * returned item. Will be set to NULL when no more items are left + * to iterate. + */ + PLIST_API void plist_array_next_item(plist_t node, plist_array_iter iter, plist_t *item); + + + /******************************************** + * * + * Dictionary functions * + * * + ********************************************/ + + /** + * Get size of a #PLIST_DICT node. + * + * @param node the node of type #PLIST_DICT + * @return size of the #PLIST_DICT node + */ + PLIST_API uint32_t plist_dict_get_size(plist_t node); + + /** + * Create an iterator of a #PLIST_DICT node. + * The allocated iterator should be freed with the standard free function. + * + * @param node The node of type #PLIST_DICT. + * @param iter Location to store the iterator for the dictionary. + */ + PLIST_API void plist_dict_new_iter(plist_t node, plist_dict_iter *iter); + + /** + * Increment iterator of a #PLIST_DICT node. + * + * @param node The node of type #PLIST_DICT + * @param iter Iterator of the dictionary + * @param key Location to store the key, or NULL. The caller is responsible + * for freeing the the returned string. + * @param val Location to store the value, or NULL. The caller must *not* + * free the returned value. Will be set to NULL when no more + * key/value pairs are left to iterate. + */ + PLIST_API void plist_dict_next_item(plist_t node, plist_dict_iter iter, char **key, plist_t *val); + + /** + * Get key associated key to an item. Item must be member of a dictionary. + * + * @param node the item + * @param key a location to store the key. The caller is responsible for freeing the returned string. + */ + PLIST_API void plist_dict_get_item_key(plist_t node, char **key); + + /** + * Get the nth item in a #PLIST_DICT node. + * + * @param node the node of type #PLIST_DICT + * @param key the identifier of the item to get. + * @return the item or NULL if node is not of type #PLIST_DICT. The caller should not free + * the returned node. + */ + PLIST_API plist_t plist_dict_get_item(plist_t node, const char* key); + + /** + * Get key node associated to an item. Item must be member of a dictionary. + * + * @param node the item + * @return the key node of the given item, or NULL. + */ + PLIST_API plist_t plist_dict_item_get_key(plist_t node); + + /** + * Set item identified by key in a #PLIST_DICT node. + * The previous item identified by key will be freed using #plist_free. + * If there is no item for the given key a new item will be inserted. + * + * @param node the node of type #PLIST_DICT + * @param item the new item associated to key + * @param key the identifier of the item to set. + */ + PLIST_API void plist_dict_set_item(plist_t node, const char* key, plist_t item); + + /** + * Remove an existing position in a #PLIST_DICT node. + * Removed position will be freed using #plist_free + * + * @param node the node of type #PLIST_DICT + * @param key The identifier of the item to remove. Assert if identifier is not present. + */ + PLIST_API void plist_dict_remove_item(plist_t node, const char* key); + + /** + * Merge a dictionary into another. This will add all key/value pairs + * from the source dictionary to the target dictionary, overwriting + * any existing key/value pairs that are already present in target. + * + * @param target pointer to an existing node of type #PLIST_DICT + * @param source node of type #PLIST_DICT that should be merged into target + */ + PLIST_API void plist_dict_merge(plist_t *target, plist_t source); + + /** + * Get a boolean value from a given #PLIST_DICT entry. + * + * The value node can be of type #PLIST_BOOLEAN, but also + * #PLIST_STRING (either 'true' or 'false'), + * #PLIST_INT with a numerical value of 0 or >= 1, + * or #PLIST_DATA with a single byte with a value of 0 or >= 1. + * + * @note This function returns 0 if the dictionary does not contain an + * entry for the given key, if the value node is of any other than + * the above mentioned type, or has any mismatching value. + * + * @param dict A node of type #PLIST_DICT + * @param key The key to look for in dict + * @return 0 or 1 depending on the value of the node. + */ + PLIST_API uint8_t plist_dict_get_bool(plist_t dict, const char *key); + + /** + * Get a signed integer value from a given #PLIST_DICT entry. + * The value node can be of type #PLIST_INT, but also + * #PLIST_STRING with a numerical value as string (decimal or hexadecimal), + * or #PLIST_DATA with a size of 1, 2, 4, or 8 bytes in little endian byte order. + * + * @note This function returns 0 if the dictionary does not contain an + * entry for the given key, if the value node is of any other than + * the above mentioned type, or has any mismatching value. + * + * @param dict A node of type #PLIST_DICT + * @param key The key to look for in dict + * @return Signed integer value depending on the value of the node. + */ + PLIST_API int64_t plist_dict_get_int(plist_t dict, const char *key); + + /** + * Get an unsigned integer value from a given #PLIST_DICT entry. + * The value node can be of type #PLIST_INT, but also + * #PLIST_STRING with a numerical value as string (decimal or hexadecimal), + * or #PLIST_DATA with a size of 1, 2, 4, or 8 bytes in little endian byte order. + * + * @note This function returns 0 if the dictionary does not contain an + * entry for the given key, if the value node is of any other than + * the above mentioned type, or has any mismatching value. + * + * @param dict A node of type #PLIST_DICT + * @param key The key to look for in dict + * @return Signed integer value depending on the value of the node. + */ + PLIST_API uint64_t plist_dict_get_uint(plist_t dict, const char *key); + + /** + * Copy a node from *source_dict* to *target_dict*. + * The node is looked up in *source_dict* with given *key*, unless *alt_source_key* + * is non-NULL, in which case it is looked up with *alt_source_key*. + * The entry in *target_dict* is **always** created with *key*. + * + * @param target_dict The target dictionary to copy to. + * @param source_dict The source dictionary to copy from. + * @param key The key for the node to copy. + * @param alt_source_key The alternative source key for lookup in *source_dict* or NULL. + * + * @result PLIST_ERR_SUCCESS on success or PLIST_ERR_INVALID_ARG if the source dictionary does not contain + * any entry with given key or alt_source_key. + */ + PLIST_API plist_err_t plist_dict_copy_item(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); + + /** + * Copy a boolean value from *source_dict* to *target_dict*. + * The node is looked up in *source_dict* with given *key*, unless *alt_source_key* + * is non-NULL, in which case it is looked up with *alt_source_key*. + * The entry in *target_dict* is **always** created with *key*. + * + * @note The boolean value from *source_dict* is retrieved with #plist_dict_get_bool, + * but is **always** created as #PLIST_BOOLEAN in *target_dict*. + * + * @param target_dict The target dictionary to copy to. + * @param source_dict The source dictionary to copy from. + * @param key The key for the node to copy. + * @param alt_source_key The alternative source key for lookup in *source_dict* or NULL. + * + * @result PLIST_ERR_SUCCESS on success or PLIST_ERR_INVALID_ARG if the source dictionary does not contain + * any entry with given key or alt_source_key. + */ + PLIST_API plist_err_t plist_dict_copy_bool(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); + + /** + * Copy a signed integer value from *source_dict* to *target_dict*. + * The node is looked up in *source_dict* with given *key*, unless *alt_source_key* + * is non-NULL, in which case it is looked up with *alt_source_key*. + * The entry in *target_dict* is **always** created with *key*. + * + * @note The signed integer value from *source_dict* is retrieved with #plist_dict_get_int, + * but is **always** created as #PLIST_INT. + * + * @param target_dict The target dictionary to copy to. + * @param source_dict The source dictionary to copy from. + * @param key The key for the node value to copy. + * @param alt_source_key The alternative source key for lookup in *source_dict* or NULL. + * + * @result PLIST_ERR_SUCCESS on success or PLIST_ERR_INVALID_ARG if the source dictionary does not contain + * any entry with given key or alt_source_key. + */ + PLIST_API plist_err_t plist_dict_copy_int(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); + + /** + * Copy an unsigned integer value from *source_dict* to *target_dict*. + * The node is looked up in *source_dict* with given *key*, unless *alt_source_key* + * is non-NULL, in which case it is looked up with *alt_source_key*. + * The entry in *target_dict* is **always** created with *key*. + * + * @note The unsigned integer value from *source_dict* is retrieved with #plist_dict_get_uint, + * but is **always** created as #PLIST_INT. + * + * @param target_dict The target dictionary to copy to. + * @param source_dict The source dictionary to copy from. + * @param key The key for the node value to copy. + * @param alt_source_key The alternative source key for lookup in *source_dict* or NULL. + * + * @result PLIST_ERR_SUCCESS on success or PLIST_ERR_INVALID_ARG if the source dictionary does not contain + * any entry with given key or alt_source_key. + */ + PLIST_API plist_err_t plist_dict_copy_uint(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); + + /** + * Copy a #PLIST_DATA node from *source_dict* to *target_dict*. + * The node is looked up in *source_dict* with given *key*, unless *alt_source_key* + * is non-NULL, in which case it is looked up with *alt_source_key*. + * The entry in *target_dict* is **always** created with *key*. + * + * @note This function is like #plist_dict_copy_item, except that it fails + * if the source node is not of type #PLIST_DATA. + * + * @param target_dict The target dictionary to copy to. + * @param source_dict The source dictionary to copy from. + * @param key The key for the node value to copy. + * @param alt_source_key The alternative source key for lookup in *source_dict* or NULL. + * + * @result PLIST_ERR_SUCCESS on success or PLIST_ERR_INVALID_ARG if the source dictionary does not contain + * any entry with given key or alt_source_key, or if it is not of type #PLIST_DATA. + */ + PLIST_API plist_err_t plist_dict_copy_data(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); + + /** + * Copy a #PLIST_STRING node from *source_dict* to *target_dict*. + * The node is looked up in *source_dict* with given *key*, unless *alt_source_key* + * is non-NULL, in which case it is looked up with *alt_source_key*. + * The entry in *target_dict* is **always** created with *key*. + * + * @note This function is like #plist_dict_copy_item, except that it fails + * if the source node is not of type #PLIST_STRING. + * + * @param target_dict The target dictionary to copy to. + * @param source_dict The source dictionary to copy from. + * @param key The key for the node value to copy. + * @param alt_source_key The alternative source key for lookup in *source_dict* or NULL. + * + * @result PLIST_ERR_SUCCESS on success or PLIST_ERR_INVALID_ARG if the source dictionary does not contain + * any entry with given key or alt_source_key, or if it is not of type #PLIST_STRING. + */ + PLIST_API plist_err_t plist_dict_copy_string(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); + + /******************************************** + * * + * Getters * + * * + ********************************************/ + + /** + * Get the parent of a node + * + * @param node the parent (NULL if node is root) + */ + PLIST_API plist_t plist_get_parent(plist_t node); + + /** + * Get the #plist_type of a node. + * + * @param node the node + * @return the type of the node + */ + PLIST_API plist_type plist_get_node_type(plist_t node); + + /** + * Get the value of a #PLIST_KEY node. + * This function does nothing if node is not of type #PLIST_KEY + * + * @param node the node + * @param val a pointer to a C-string. This function allocates the memory, + * caller is responsible for freeing it. + * @note Use plist_mem_free() to free the allocated memory. + */ + PLIST_API void plist_get_key_val(plist_t node, char **val); + + /** + * Get the value of a #PLIST_STRING node. + * This function does nothing if node is not of type #PLIST_STRING + * + * @param node the node + * @param val a pointer to a C-string. This function allocates the memory, + * caller is responsible for freeing it. Data is UTF-8 encoded. + * @note Use plist_mem_free() to free the allocated memory. + */ + PLIST_API void plist_get_string_val(plist_t node, char **val); + + /** + * Get a pointer to the buffer of a #PLIST_STRING node. + * + * @note DO NOT MODIFY the buffer. Mind that the buffer is only available + * until the plist node gets freed. Make a copy if needed. + * + * @param node The node + * @param length If non-NULL, will be set to the length of the string + * + * @return Pointer to the NULL-terminated buffer. + */ + PLIST_API const char* plist_get_string_ptr(plist_t node, uint64_t* length); + + /** + * Get the value of a #PLIST_BOOLEAN node. + * This function does nothing if node is not of type #PLIST_BOOLEAN + * + * @param node the node + * @param val a pointer to a uint8_t variable. + */ + PLIST_API void plist_get_bool_val(plist_t node, uint8_t * val); + + /** + * Get the unsigned integer value of a #PLIST_INT node. + * This function does nothing if node is not of type #PLIST_INT + * + * @param node the node + * @param val a pointer to a uint64_t variable. + */ + PLIST_API void plist_get_uint_val(plist_t node, uint64_t * val); + + /** + * Get the signed integer value of a #PLIST_INT node. + * This function does nothing if node is not of type #PLIST_INT + * + * @param node the node + * @param val a pointer to a int64_t variable. + */ + PLIST_API void plist_get_int_val(plist_t node, int64_t * val); + + /** + * Get the value of a #PLIST_REAL node. + * This function does nothing if node is not of type #PLIST_REAL + * + * @param node the node + * @param val a pointer to a double variable. + */ + PLIST_API void plist_get_real_val(plist_t node, double *val); + + /** + * Get the value of a #PLIST_DATA node. + * This function does nothing if node is not of type #PLIST_DATA + * + * @param node the node + * @param val a pointer to an unallocated char buffer. This function allocates the memory, + * caller is responsible for freeing it. + * @param length the length of the buffer + * @note Use plist_mem_free() to free the allocated memory. + */ + PLIST_API void plist_get_data_val(plist_t node, char **val, uint64_t * length); + + /** + * Get a pointer to the data buffer of a #PLIST_DATA node. + * + * @note DO NOT MODIFY the buffer. Mind that the buffer is only available + * until the plist node gets freed. Make a copy if needed. + * + * @param node The node + * @param length Pointer to a uint64_t that will be set to the length of the buffer + * + * @return Pointer to the buffer + */ + PLIST_API const char* plist_get_data_ptr(plist_t node, uint64_t* length); + + /** + * Get the value of a #PLIST_DATE node. + * This function does nothing if node is not of type #PLIST_DATE + * + * @param node the node + * @param sec a pointer to an int64_t variable. Represents the number of seconds since 01/01/1970 (UNIX timestamp). + */ + PLIST_API void plist_get_unix_date_val(plist_t node, int64_t *sec); + + /** + * Get the value of a #PLIST_UID node. + * This function does nothing if node is not of type #PLIST_UID + * + * @param node the node + * @param val a pointer to a uint64_t variable. + */ + PLIST_API void plist_get_uid_val(plist_t node, uint64_t * val); + + + /******************************************** + * * + * Setters * + * * + ********************************************/ + + /** + * Set the value of a node. + * Forces type of node to #PLIST_KEY + * + * @param node the node + * @param val the key value + */ + PLIST_API void plist_set_key_val(plist_t node, const char *val); + + /** + * Set the value of a node. + * Forces type of node to #PLIST_STRING + * + * @param node the node + * @param val the string value. The string is copied when set and will be + * freed by the node. + */ + PLIST_API void plist_set_string_val(plist_t node, const char *val); + + /** + * Set the value of a node. + * Forces type of node to #PLIST_BOOLEAN + * + * @param node the node + * @param val the boolean value + */ + PLIST_API void plist_set_bool_val(plist_t node, uint8_t val); + + /** + * Set the value of a node. + * Forces type of node to #PLIST_INT + * + * @param node the node + * @param val the unsigned integer value + */ + PLIST_API void plist_set_uint_val(plist_t node, uint64_t val); + + /** + * Set the value of a node. + * Forces type of node to #PLIST_INT + * + * @param node the node + * @param val the signed integer value + */ + PLIST_API void plist_set_int_val(plist_t node, int64_t val); + + /** + * Set the value of a node. + * Forces type of node to #PLIST_REAL + * + * @param node the node + * @param val the real value + */ + PLIST_API void plist_set_real_val(plist_t node, double val); + + /** + * Set the value of a node. + * Forces type of node to #PLIST_DATA + * + * @param node the node + * @param val the binary buffer. The buffer is copied when set and will + * be freed by the node. + * @param length the length of the buffer + */ + PLIST_API void plist_set_data_val(plist_t node, const char *val, uint64_t length); + + /** + * Set the value of a node. + * Forces type of node to #PLIST_DATE + * + * @param node the node + * @param sec the number of seconds since 01/01/1970 (UNIX timestamp) + */ + PLIST_API void plist_set_unix_date_val(plist_t node, int64_t sec); + + /** + * Set the value of a node. + * Forces type of node to #PLIST_UID + * + * @param node the node + * @param val the unsigned integer value + */ + PLIST_API void plist_set_uid_val(plist_t node, uint64_t val); + + + /******************************************** + * * + * Import & Export * + * * + ********************************************/ + + /** + * Export the #plist_t structure to XML format. + * + * @param plist the root node to export + * @param plist_xml a pointer to a C-string. This function allocates the memory, + * caller is responsible for freeing it. Data is UTF-8 encoded. + * @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer. + * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure + * @note Use plist_mem_free() to free the allocated memory. + */ + PLIST_API plist_err_t plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length); + + /** + * Export the #plist_t structure to binary format. + * + * @param plist the root node to export + * @param plist_bin a pointer to a char* buffer. This function allocates the memory, + * caller is responsible for freeing it. + * @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer. + * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure + * @note Use plist_mem_free() to free the allocated memory. + */ + PLIST_API plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length); + + /** + * Export the #plist_t structure to JSON format. + * + * @param plist the root node to export + * @param plist_json a pointer to a char* buffer. This function allocates the memory, + * caller is responsible for freeing it. + * @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer. + * @param prettify pretty print the output if != 0 + * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure + * @note Use plist_mem_free() to free the allocated memory. + */ + PLIST_API plist_err_t plist_to_json(plist_t plist, char **plist_json, uint32_t* length, int prettify); + + /** + * Export the #plist_t structure to OpenStep format. + * + * @param plist the root node to export + * @param plist_openstep a pointer to a char* buffer. This function allocates the memory, + * caller is responsible for freeing it. + * @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer. + * @param prettify pretty print the output if != 0 + * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure + * @note Use plist_mem_free() to free the allocated memory. + */ + PLIST_API plist_err_t plist_to_openstep(plist_t plist, char **plist_openstep, uint32_t* length, int prettify); + + + /** + * Import the #plist_t structure from XML format. + * + * @param plist_xml a pointer to the xml buffer. + * @param length length of the buffer to read. + * @param plist a pointer to the imported plist. + * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure + */ + PLIST_API plist_err_t plist_from_xml(const char *plist_xml, uint32_t length, plist_t * plist); + + /** + * Import the #plist_t structure from binary format. + * + * @param plist_bin a pointer to the xml buffer. + * @param length length of the buffer to read. + * @param plist a pointer to the imported plist. + * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure + */ + PLIST_API plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist); + + /** + * Import the #plist_t structure from JSON format. + * + * @param json a pointer to the JSON buffer. + * @param length length of the buffer to read. + * @param plist a pointer to the imported plist. + * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure + */ + PLIST_API plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist); + + /** + * Import the #plist_t structure from OpenStep plist format. + * + * @param openstep a pointer to the OpenStep plist buffer. + * @param length length of the buffer to read. + * @param plist a pointer to the imported plist. + * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure + */ + PLIST_API plist_err_t plist_from_openstep(const char *openstep, uint32_t length, plist_t * plist); + + /** + * Import the #plist_t structure from memory data. + * + * This function will look at the first bytes of plist_data + * to determine if plist_data contains a binary, JSON, OpenStep, or XML plist + * and tries to parse the data in the appropriate format. + * @note This is just a convenience function and the format detection is + * very basic. It checks with plist_is_binary() if the data supposedly + * contains binary plist data, if not it checks if the first bytes have + * either '{' or '[' and assumes JSON format, and XML tags will result + * in parsing as XML, otherwise it will try to parse as OpenStep. + * + * @param plist_data A pointer to the memory buffer containing plist data. + * @param length Length of the buffer to read. + * @param plist A pointer to the imported plist. + * @param format If non-NULL, the #plist_format_t value pointed to will be set to the parsed format. + * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure + */ + PLIST_API plist_err_t plist_from_memory(const char *plist_data, uint32_t length, plist_t *plist, plist_format_t *format); + + /** + * Import the #plist_t structure directly from file. + * + * This function will look at the first bytes of the file data + * to determine if it contains a binary, JSON, OpenStep, or XML plist + * and tries to parse the data in the appropriate format. + * Uses plist_from_memory() internally. + * + * @param filename The name of the file to parse. + * @param plist A pointer to the imported plist. + * @param format If non-NULL, the #plist_format_t value pointed to will be set to the parsed format. + * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure + */ + PLIST_API plist_err_t plist_read_from_file(const char *filename, plist_t *plist, plist_format_t *format); + + /** + * Write the #plist_t structure to a NULL-terminated string using the given format and options. + * + * @param plist The input plist structure + * @param output Pointer to a char* buffer. This function allocates the memory, + * caller is responsible for freeing it. + * @param length A pointer to a uint32_t value that will receive the lenght of the allocated buffer. + * @param format A #plist_format_t value that specifies the output format to use. + * @param options One or more bitwise ORed values of #plist_write_options_t. + * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure. + * @note Use plist_mem_free() to free the allocated memory. + * @note #PLIST_FORMAT_BINARY is not supported by this function. + */ + PLIST_API plist_err_t plist_write_to_string(plist_t plist, char **output, uint32_t* length, plist_format_t format, plist_write_options_t options); + + /** + * Write the #plist_t structure to a FILE* stream using the given format and options. + * + * @param plist The input plist structure + * @param stream A writeable FILE* stream that the data will be written to. + * @param format A #plist_format_t value that specifies the output format to use. + * @param options One or more bitwise ORed values of #plist_write_options_t. + * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure. + * @note While this function allows all formats to be written to the given stream, + * only the formats #PLIST_FORMAT_PRINT, #PLIST_FORMAT_LIMD, and #PLIST_FORMAT_PLUTIL + * (basically all output-only formats) are directly and efficiently written to the stream; + * the other formats are written to a memory buffer first. + */ + PLIST_API plist_err_t plist_write_to_stream(plist_t plist, FILE* stream, plist_format_t format, plist_write_options_t options); + + /** + * Write the #plist_t structure to a file at given path using the given format and options. + * + * @param plist The input plist structure + * @param filename The file name of the file to write to. Existing files will be overwritten. + * @param format A #plist_format_t value that specifies the output format to use. + * @param options One or more bitwise ORed values of #plist_write_options_t. + * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure. + * @note Use plist_mem_free() to free the allocated memory. + */ + PLIST_API plist_err_t plist_write_to_file(plist_t plist, const char *filename, plist_format_t format, plist_write_options_t options); + + /** + * Print the given plist in human-readable format to standard output. + * This is equivalent to + * plist_write_to_stream(plist, stdout, PLIST_FORMAT_PRINT, PLIST_OPT_PARTIAL_DATA); + * @param plist The #plist_t structure to print + * @note For #PLIST_DATA nodes, only a maximum of 24 bytes (first 16 and last 8) are written. + */ + PLIST_API void plist_print(plist_t plist); + + /** + * Test if in-memory plist data is in binary format. + * This function will look at the first bytes of plist_data to determine + * if it supposedly contains a binary plist. + * @note The function is not validating the whole memory buffer to check + * if the content is truly a plist, it is only using some heuristic on + * the first few bytes of plist_data. + * + * @param plist_data a pointer to the memory buffer containing plist data. + * @param length length of the buffer to read. + * @return 1 if the buffer is a binary plist, 0 otherwise. + */ + PLIST_API int plist_is_binary(const char *plist_data, uint32_t length); + + /******************************************** + * * + * Utils * + * * + ********************************************/ + + /** + * Get a node from its path. Each path element depends on the associated father node type. + * For Dictionaries, var args are casted to const char*, for arrays, var args are caster to uint32_t + * Search is breath first order. + * + * @param plist the node to access result from. + * @param length length of the path to access + * @return the value to access. + */ + PLIST_API plist_t plist_access_path(plist_t plist, uint32_t length, ...); + + /** + * Variadic version of #plist_access_path. + * + * @param plist the node to access result from. + * @param length length of the path to access + * @param v list of array's index and dic'st key + * @return the value to access. + */ + PLIST_API plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v); + + /** + * Compare two node values + * + * @param node_l left node to compare + * @param node_r rigth node to compare + * @return TRUE is type and value match, FALSE otherwise. + */ + PLIST_API char plist_compare_node_value(plist_t node_l, plist_t node_r); + + /** Helper macro used by PLIST_IS_* macros that will evaluate the type of a plist node. */ + #define _PLIST_IS_TYPE(__plist, __plist_type) (__plist && (plist_get_node_type(__plist) == PLIST_##__plist_type)) + + /* Helper macros for the different plist types */ + /** Evaluates to true if the given plist node is of type PLIST_BOOLEAN */ + #define PLIST_IS_BOOLEAN(__plist) _PLIST_IS_TYPE(__plist, BOOLEAN) + /** Evaluates to true if the given plist node is of type PLIST_INT */ + #define PLIST_IS_INT(__plist) _PLIST_IS_TYPE(__plist, INT) + /** Evaluates to true if the given plist node is of type PLIST_REAL */ + #define PLIST_IS_REAL(__plist) _PLIST_IS_TYPE(__plist, REAL) + /** Evaluates to true if the given plist node is of type PLIST_STRING */ + #define PLIST_IS_STRING(__plist) _PLIST_IS_TYPE(__plist, STRING) + /** Evaluates to true if the given plist node is of type PLIST_ARRAY */ + #define PLIST_IS_ARRAY(__plist) _PLIST_IS_TYPE(__plist, ARRAY) + /** Evaluates to true if the given plist node is of type PLIST_DICT */ + #define PLIST_IS_DICT(__plist) _PLIST_IS_TYPE(__plist, DICT) + /** Evaluates to true if the given plist node is of type PLIST_DATE */ + #define PLIST_IS_DATE(__plist) _PLIST_IS_TYPE(__plist, DATE) + /** Evaluates to true if the given plist node is of type PLIST_DATA */ + #define PLIST_IS_DATA(__plist) _PLIST_IS_TYPE(__plist, DATA) + /** Evaluates to true if the given plist node is of type PLIST_KEY */ + #define PLIST_IS_KEY(__plist) _PLIST_IS_TYPE(__plist, KEY) + /** Evaluates to true if the given plist node is of type PLIST_UID */ + #define PLIST_IS_UID(__plist) _PLIST_IS_TYPE(__plist, UID) + /* for backwards compatibility */ + #define PLIST_IS_UINT PLIST_IS_INT + + /** + * Helper function to check the value of a PLIST_BOOL node. + * + * @param boolnode node of type PLIST_BOOL + * @return 1 if the boolean node has a value of TRUE or 0 if FALSE. + */ + PLIST_API int plist_bool_val_is_true(plist_t boolnode); + + /** + * Helper function to test if a given #PLIST_INT node's value is negative + * + * @param intnode node of type PLIST_INT + * @return 1 if the node's value is negative, or 0 if positive. + */ + PLIST_API int plist_int_val_is_negative(plist_t intnode); + + /** + * Helper function to compare the value of a PLIST_INT node against + * a given signed integer value. + * + * @param uintnode node of type PLIST_INT + * @param cmpval value to compare against + * @return 0 if the node's value and cmpval are equal, + * 1 if the node's value is greater than cmpval, + * or -1 if the node's value is less than cmpval. + */ + PLIST_API int plist_int_val_compare(plist_t uintnode, int64_t cmpval); + + /** + * Helper function to compare the value of a PLIST_INT node against + * a given unsigned integer value. + * + * @param uintnode node of type PLIST_INT + * @param cmpval value to compare against + * @return 0 if the node's value and cmpval are equal, + * 1 if the node's value is greater than cmpval, + * or -1 if the node's value is less than cmpval. + */ + PLIST_API int plist_uint_val_compare(plist_t uintnode, uint64_t cmpval); + + /** + * Helper function to compare the value of a PLIST_UID node against + * a given value. + * + * @param uidnode node of type PLIST_UID + * @param cmpval value to compare against + * @return 0 if the node's value and cmpval are equal, + * 1 if the node's value is greater than cmpval, + * or -1 if the node's value is less than cmpval. + */ + PLIST_API int plist_uid_val_compare(plist_t uidnode, uint64_t cmpval); + + /** + * Helper function to compare the value of a PLIST_REAL node against + * a given value. + * + * @note WARNING: Comparing floating point values can give inaccurate + * results because of the nature of floating point values on computer + * systems. While this function is designed to be as accurate as + * possible, please don't rely on it too much. + * + * @param realnode node of type PLIST_REAL + * @param cmpval value to compare against + * @return 0 if the node's value and cmpval are (almost) equal, + * 1 if the node's value is greater than cmpval, + * or -1 if the node's value is less than cmpval. + */ + PLIST_API int plist_real_val_compare(plist_t realnode, double cmpval); + + /** + * Helper function to compare the value of a PLIST_DATE node against + * a given number of seconds since epoch (UNIX timestamp). + * + * @param datenode node of type PLIST_DATE + * @param cmpval Number of seconds to compare against (UNIX timestamp) + * @return 0 if the node's date is equal to the supplied values, + * 1 if the node's date is greater than the supplied values, + * or -1 if the node's date is less than the supplied values. + */ + PLIST_API int plist_unix_date_val_compare(plist_t datenode, int64_t cmpval); + + /** + * Helper function to compare the value of a PLIST_STRING node against + * a given value. + * This function basically behaves like strcmp. + * + * @param strnode node of type PLIST_STRING + * @param cmpval value to compare against + * @return 0 if the node's value and cmpval are equal, + * > 0 if the node's value is lexicographically greater than cmpval, + * or < 0 if the node's value is lexicographically less than cmpval. + */ + PLIST_API int plist_string_val_compare(plist_t strnode, const char* cmpval); + + /** + * Helper function to compare the value of a PLIST_STRING node against + * a given value, while not comparing more than n characters. + * This function basically behaves like strncmp. + * + * @param strnode node of type PLIST_STRING + * @param cmpval value to compare against + * @param n maximum number of characters to compare + * @return 0 if the node's value and cmpval are equal, + * > 0 if the node's value is lexicographically greater than cmpval, + * or < 0 if the node's value is lexicographically less than cmpval. + */ + PLIST_API int plist_string_val_compare_with_size(plist_t strnode, const char* cmpval, size_t n); + + /** + * Helper function to match a given substring in the value of a + * PLIST_STRING node. + * + * @param strnode node of type PLIST_STRING + * @param substr value to match + * @return 1 if the node's value contains the given substring, + * or 0 if not. + */ + PLIST_API int plist_string_val_contains(plist_t strnode, const char* substr); + + /** + * Helper function to compare the value of a PLIST_KEY node against + * a given value. + * This function basically behaves like strcmp. + * + * @param keynode node of type PLIST_KEY + * @param cmpval value to compare against + * @return 0 if the node's value and cmpval are equal, + * > 0 if the node's value is lexicographically greater than cmpval, + * or < 0 if the node's value is lexicographically less than cmpval. + */ + PLIST_API int plist_key_val_compare(plist_t keynode, const char* cmpval); + + /** + * Helper function to compare the value of a PLIST_KEY node against + * a given value, while not comparing more than n characters. + * This function basically behaves like strncmp. + * + * @param keynode node of type PLIST_KEY + * @param cmpval value to compare against + * @param n maximum number of characters to compare + * @return 0 if the node's value and cmpval are equal, + * > 0 if the node's value is lexicographically greater than cmpval, + * or < 0 if the node's value is lexicographically less than cmpval. + */ + PLIST_API int plist_key_val_compare_with_size(plist_t keynode, const char* cmpval, size_t n); + + /** + * Helper function to match a given substring in the value of a + * PLIST_KEY node. + * + * @param keynode node of type PLIST_KEY + * @param substr value to match + * @return 1 if the node's value contains the given substring, + * or 0 if not. + */ + PLIST_API int plist_key_val_contains(plist_t keynode, const char* substr); + + /** + * Helper function to compare the data of a PLIST_DATA node against + * a given blob and size. + * This function basically behaves like memcmp after making sure the + * size of the node's data value is equal to the size of cmpval (n), + * making this a "full match" comparison. + * + * @param datanode node of type PLIST_DATA + * @param cmpval data blob to compare against + * @param n size of data blob passed in cmpval + * @return 0 if the node's data blob and cmpval are equal, + * > 0 if the node's value is lexicographically greater than cmpval, + * or < 0 if the node's value is lexicographically less than cmpval. + */ + PLIST_API int plist_data_val_compare(plist_t datanode, const uint8_t* cmpval, size_t n); + + /** + * Helper function to compare the data of a PLIST_DATA node against + * a given blob and size, while no more than n bytes are compared. + * This function basically behaves like memcmp after making sure the + * size of the node's data value is at least n, making this a + * "starts with" comparison. + * + * @param datanode node of type PLIST_DATA + * @param cmpval data blob to compare against + * @param n size of data blob passed in cmpval + * @return 0 if the node's value and cmpval are equal, + * > 0 if the node's value is lexicographically greater than cmpval, + * or < 0 if the node's value is lexicographically less than cmpval. + */ + PLIST_API int plist_data_val_compare_with_size(plist_t datanode, const uint8_t* cmpval, size_t n); + + /** + * Helper function to match a given data blob within the value of a + * PLIST_DATA node. + * + * @param datanode node of type PLIST_KEY + * @param cmpval data blob to match + * @param n size of data blob passed in cmpval + * @return 1 if the node's value contains the given data blob + * or 0 if not. + */ + PLIST_API int plist_data_val_contains(plist_t datanode, const uint8_t* cmpval, size_t n); + + /** + * Sort all PLIST_DICT key/value pairs in a property list lexicographically + * by key. Recurses into the child nodes if necessary. + * + * @param plist The property list to perform the sorting operation on. + */ + PLIST_API void plist_sort(plist_t plist); + + /** + * Free memory allocated by relevant libplist API calls: + * - plist_to_xml() + * - plist_to_bin() + * - plist_get_key_val() + * - plist_get_string_val() + * - plist_get_data_val() + * + * @param ptr pointer to the memory to free + * + * @note Do not use this function to free plist_t nodes, use plist_free() + * instead. + */ + PLIST_API void plist_mem_free(void* ptr); + + /** + * Set debug level for the format parsers. + * @note This function does nothing if libplist was not configured with --enable-debug . + * + * @param debug Debug level. Currently, only 0 (off) and 1 (enabled) are supported. + */ + PLIST_API void plist_set_debug(int debug); + + /** + * Returns a static string of the libplist version. + * + * @return The libplist version as static ascii string + */ + PLIST_API const char* libplist_version(); + + + /******************************************** + * * + * Deprecated API * + * * + ********************************************/ + + /** + * Create a new plist_t type #PLIST_DATE + * + * @deprecated Deprecated. Use plist_new_unix_date instead. + * + * @param sec the number of seconds since 01/01/2001 + * @param usec the number of microseconds + * @return the created item + * @sa #plist_type + */ + PLIST_WARN_DEPRECATED("use plist_new_unix_date instead") + PLIST_API plist_t plist_new_date(int32_t sec, int32_t usec); + + /** + * Get the value of a #PLIST_DATE node. + * This function does nothing if node is not of type #PLIST_DATE + * + * @deprecated Deprecated. Use plist_get_unix_date_val instead. + * + * @param node the node + * @param sec a pointer to an int32_t variable. Represents the number of seconds since 01/01/2001. + * @param usec a pointer to an int32_t variable. Represents the number of microseconds + */ + PLIST_WARN_DEPRECATED("use plist_get_unix_date_val instead") + PLIST_API void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec); + + /** + * Set the value of a node. + * Forces type of node to #PLIST_DATE + * + * @deprecated Deprecated. Use plist_set_unix_date_val instead. + * + * @param node the node + * @param sec the number of seconds since 01/01/2001 + * @param usec the number of microseconds + */ + PLIST_WARN_DEPRECATED("use plist_set_unix_date_val instead") + PLIST_API void plist_set_date_val(plist_t node, int32_t sec, int32_t usec); + + /** + * Helper function to compare the value of a PLIST_DATE node against + * a given set of seconds and fraction of a second since epoch. + * + * @deprecated Deprecated. Use plist_unix_date_val_compare instead. + * + * @param datenode node of type PLIST_DATE + * @param cmpsec number of seconds since epoch to compare against + * @param cmpusec fraction of a second in microseconds to compare against + * @return 0 if the node's date is equal to the supplied values, + * 1 if the node's date is greater than the supplied values, + * or -1 if the node's date is less than the supplied values. + */ + PLIST_WARN_DEPRECATED("use plist_unix_date_val_compare instead") + PLIST_API int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t cmpusec); + + /*@}*/ + +#ifdef __cplusplus +} +#endif #endif diff --git a/StikJIT/idevice/ideviceinfo.c b/StikJIT/idevice/ideviceinfo.c index a3d7f19..ac8b305 100644 --- a/StikJIT/idevice/ideviceinfo.c +++ b/StikJIT/idevice/ideviceinfo.c @@ -9,7 +9,6 @@ #include #include "ideviceinfo.h" #include "idevice.h" -#include "plist.h" static struct IdeviceProviderHandle * g_provider = NULL; static struct HeartbeatClientHandle * g_hb = NULL; @@ -80,7 +79,7 @@ char *ideviceinfo_c_get_xml(void) { } void *plist_obj = NULL; - struct IdeviceFfiError *err = lockdownd_get_all_values(g_client, NULL, &plist_obj); + struct IdeviceFfiError *err = lockdownd_get_value(g_client, NULL, NULL, &plist_obj); if (err) { idevice_error_free(err); return NULL; diff --git a/StikJIT/idevice/libidevice_ffi.a b/StikJIT/idevice/libidevice_ffi.a index 1658132..e42fb8a 100644 Binary files a/StikJIT/idevice/libidevice_ffi.a and b/StikJIT/idevice/libidevice_ffi.a differ diff --git a/StikJIT/idevice/libplist-2.0.a b/StikJIT/idevice/libplist-2.0.a deleted file mode 100644 index 41adc1d..0000000 Binary files a/StikJIT/idevice/libplist-2.0.a and /dev/null differ diff --git a/StikJIT/idevice/plist.h b/StikJIT/idevice/plist.h deleted file mode 100644 index 2bb947f..0000000 --- a/StikJIT/idevice/plist.h +++ /dev/null @@ -1,1094 +0,0 @@ -/** - * @file plist/plist.h - * @brief Main include of libplist - * \internal - * - * Copyright (c) 2012-2023 Nikias Bassen, All Rights Reserved. - * Copyright (c) 2008-2009 Jonathan Beck, All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LIBPLIST_H -#define LIBPLIST_H - -#ifdef __cplusplus -extern "C" -{ -#endif - -#if _MSC_VER && _MSC_VER < 1700 - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - -#else -#include -#endif - -#ifdef __llvm__ - #if defined(__has_extension) - #if (__has_extension(attribute_deprecated_with_message)) - #ifndef PLIST_WARN_DEPRECATED - #define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated(x))) - #endif - #else - #ifndef PLIST_WARN_DEPRECATED - #define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated)) - #endif - #endif - #else - #ifndef PLIST_WARN_DEPRECATED - #define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated)) - #endif - #endif -#elif (__GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 5))) - #ifndef PLIST_WARN_DEPRECATED - #define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated(x))) - #endif -#elif defined(_MSC_VER) - #ifndef PLIST_WARN_DEPRECATED - #define PLIST_WARN_DEPRECATED(x) __declspec(deprecated(x)) - #endif -#else - #define PLIST_WARN_DEPRECATED(x) - #pragma message("WARNING: You need to implement DEPRECATED for this compiler") -#endif - -#include -#include - - /** - * \mainpage libplist : A library to handle Apple Property Lists - * \defgroup PublicAPI Public libplist API - */ - /*@{*/ - - - /** - * The basic plist abstract data type. - */ - typedef void *plist_t; - - /** - * The plist dictionary iterator. - */ - typedef void* plist_dict_iter; - - /** - * The plist array iterator. - */ - typedef void* plist_array_iter; - - /** - * The enumeration of plist node types. - */ - typedef enum - { - PLIST_BOOLEAN, /**< Boolean, scalar type */ - PLIST_INT, /**< Integer, scalar type */ - PLIST_REAL, /**< Real, scalar type */ - PLIST_STRING, /**< ASCII string, scalar type */ - PLIST_ARRAY, /**< Ordered array, structured type */ - PLIST_DICT, /**< Unordered dictionary (key/value pair), structured type */ - PLIST_DATE, /**< Date, scalar type */ - PLIST_DATA, /**< Binary data, scalar type */ - PLIST_KEY, /**< Key in dictionaries (ASCII String), scalar type */ - PLIST_UID, /**< Special type used for 'keyed encoding' */ - PLIST_NULL, /**< NULL type */ - PLIST_NONE /**< No type */ - } plist_type; - - /* for backwards compatibility */ - #define PLIST_UINT PLIST_INT - - /** - * libplist error values - */ - typedef enum - { - PLIST_ERR_SUCCESS = 0, /**< operation successful */ - PLIST_ERR_INVALID_ARG = -1, /**< one or more of the parameters are invalid */ - PLIST_ERR_FORMAT = -2, /**< the plist contains nodes not compatible with the output format */ - PLIST_ERR_PARSE = -3, /**< parsing of the input format failed */ - PLIST_ERR_NO_MEM = -4, /**< not enough memory to handle the operation */ - PLIST_ERR_UNKNOWN = -255 /**< an unspecified error occurred */ - } plist_err_t; - - /******************************************** - * * - * Creation & Destruction * - * * - ********************************************/ - - /** - * Create a new root plist_t type #PLIST_DICT - * - * @return the created plist - * @sa #plist_type - */ - plist_t plist_new_dict(void); - - /** - * Create a new root plist_t type #PLIST_ARRAY - * - * @return the created plist - * @sa #plist_type - */ - plist_t plist_new_array(void); - - /** - * Create a new plist_t type #PLIST_STRING - * - * @param val the sting value, encoded in UTF8. - * @return the created item - * @sa #plist_type - */ - plist_t plist_new_string(const char *val); - - /** - * Create a new plist_t type #PLIST_BOOLEAN - * - * @param val the boolean value, 0 is false, other values are true. - * @return the created item - * @sa #plist_type - */ - plist_t plist_new_bool(uint8_t val); - - /** - * Create a new plist_t type #PLIST_INT with an unsigned integer value - * - * @param val the unsigned integer value - * @return the created item - * @sa #plist_type - * @note The value is always stored as uint64_t internally. - * Use #plist_get_uint_val or #plist_get_int_val to get the unsigned or signed value. - */ - plist_t plist_new_uint(uint64_t val); - - /** - * Create a new plist_t type #PLIST_INT with a signed integer value - * - * @param val the signed integer value - * @return the created item - * @sa #plist_type - * @note The value is always stored as uint64_t internally. - * Use #plist_get_uint_val or #plist_get_int_val to get the unsigned or signed value. - */ - plist_t plist_new_int(int64_t val); - - /** - * Create a new plist_t type #PLIST_REAL - * - * @param val the real value - * @return the created item - * @sa #plist_type - */ - plist_t plist_new_real(double val); - - /** - * Create a new plist_t type #PLIST_DATA - * - * @param val the binary buffer - * @param length the length of the buffer - * @return the created item - * @sa #plist_type - */ - plist_t plist_new_data(const char *val, uint64_t length); - - /** - * Create a new plist_t type #PLIST_DATE - * - * @param sec the number of seconds since 01/01/2001 - * @param usec the number of microseconds - * @return the created item - * @sa #plist_type - */ - plist_t plist_new_date(int32_t sec, int32_t usec); - - /** - * Create a new plist_t type #PLIST_UID - * - * @param val the unsigned integer value - * @return the created item - * @sa #plist_type - */ - plist_t plist_new_uid(uint64_t val); - - /** - * Create a new plist_t type #PLIST_NULL - * @return the created item - * @sa #plist_type - * @note This type is not valid for all formats, e.g. the XML format - * does not support it. - */ - plist_t plist_new_null(void); - - /** - * Destruct a plist_t node and all its children recursively - * - * @param plist the plist to free - */ - void plist_free(plist_t plist); - - /** - * Return a copy of passed node and it's children - * - * @param node the plist to copy - * @return copied plist - */ - plist_t plist_copy(plist_t node); - - - /******************************************** - * * - * Array functions * - * * - ********************************************/ - - /** - * Get size of a #PLIST_ARRAY node. - * - * @param node the node of type #PLIST_ARRAY - * @return size of the #PLIST_ARRAY node - */ - uint32_t plist_array_get_size(plist_t node); - - /** - * Get the nth item in a #PLIST_ARRAY node. - * - * @param node the node of type #PLIST_ARRAY - * @param n the index of the item to get. Range is [0, array_size[ - * @return the nth item or NULL if node is not of type #PLIST_ARRAY - */ - plist_t plist_array_get_item(plist_t node, uint32_t n); - - /** - * Get the index of an item. item must be a member of a #PLIST_ARRAY node. - * - * @param node the node - * @return the node index or UINT_MAX if node index can't be determined - */ - uint32_t plist_array_get_item_index(plist_t node); - - /** - * Set the nth item in a #PLIST_ARRAY node. - * The previous item at index n will be freed using #plist_free - * - * @param node the node of type #PLIST_ARRAY - * @param item the new item at index n. The array is responsible for freeing item when it is no longer needed. - * @param n the index of the item to get. Range is [0, array_size[. Assert if n is not in range. - */ - void plist_array_set_item(plist_t node, plist_t item, uint32_t n); - - /** - * Append a new item at the end of a #PLIST_ARRAY node. - * - * @param node the node of type #PLIST_ARRAY - * @param item the new item. The array is responsible for freeing item when it is no longer needed. - */ - void plist_array_append_item(plist_t node, plist_t item); - - /** - * Insert a new item at position n in a #PLIST_ARRAY node. - * - * @param node the node of type #PLIST_ARRAY - * @param item the new item to insert. The array is responsible for freeing item when it is no longer needed. - * @param n The position at which the node will be stored. Range is [0, array_size[. Assert if n is not in range. - */ - void plist_array_insert_item(plist_t node, plist_t item, uint32_t n); - - /** - * Remove an existing position in a #PLIST_ARRAY node. - * Removed position will be freed using #plist_free. - * - * @param node the node of type #PLIST_ARRAY - * @param n The position to remove. Range is [0, array_size[. Assert if n is not in range. - */ - void plist_array_remove_item(plist_t node, uint32_t n); - - /** - * Remove a node that is a child node of a #PLIST_ARRAY node. - * node will be freed using #plist_free. - * - * @param node The node to be removed from its #PLIST_ARRAY parent. - */ - void plist_array_item_remove(plist_t node); - - /** - * Create an iterator of a #PLIST_ARRAY node. - * The allocated iterator should be freed with the standard free function. - * - * @param node The node of type #PLIST_ARRAY - * @param iter Location to store the iterator for the array. - */ - void plist_array_new_iter(plist_t node, plist_array_iter *iter); - - /** - * Increment iterator of a #PLIST_ARRAY node. - * - * @param node The node of type #PLIST_ARRAY. - * @param iter Iterator of the array - * @param item Location to store the item. The caller must *not* free the - * returned item. Will be set to NULL when no more items are left - * to iterate. - */ - void plist_array_next_item(plist_t node, plist_array_iter iter, plist_t *item); - - - /******************************************** - * * - * Dictionary functions * - * * - ********************************************/ - - /** - * Get size of a #PLIST_DICT node. - * - * @param node the node of type #PLIST_DICT - * @return size of the #PLIST_DICT node - */ - uint32_t plist_dict_get_size(plist_t node); - - /** - * Create an iterator of a #PLIST_DICT node. - * The allocated iterator should be freed with the standard free function. - * - * @param node The node of type #PLIST_DICT. - * @param iter Location to store the iterator for the dictionary. - */ - void plist_dict_new_iter(plist_t node, plist_dict_iter *iter); - - /** - * Increment iterator of a #PLIST_DICT node. - * - * @param node The node of type #PLIST_DICT - * @param iter Iterator of the dictionary - * @param key Location to store the key, or NULL. The caller is responsible - * for freeing the the returned string. - * @param val Location to store the value, or NULL. The caller must *not* - * free the returned value. Will be set to NULL when no more - * key/value pairs are left to iterate. - */ - void plist_dict_next_item(plist_t node, plist_dict_iter iter, char **key, plist_t *val); - - /** - * Get key associated key to an item. Item must be member of a dictionary. - * - * @param node the item - * @param key a location to store the key. The caller is responsible for freeing the returned string. - */ - void plist_dict_get_item_key(plist_t node, char **key); - - /** - * Get the nth item in a #PLIST_DICT node. - * - * @param node the node of type #PLIST_DICT - * @param key the identifier of the item to get. - * @return the item or NULL if node is not of type #PLIST_DICT. The caller should not free - * the returned node. - */ - plist_t plist_dict_get_item(plist_t node, const char* key); - - /** - * Get key node associated to an item. Item must be member of a dictionary. - * - * @param node the item - * @return the key node of the given item, or NULL. - */ - plist_t plist_dict_item_get_key(plist_t node); - - /** - * Set item identified by key in a #PLIST_DICT node. - * The previous item identified by key will be freed using #plist_free. - * If there is no item for the given key a new item will be inserted. - * - * @param node the node of type #PLIST_DICT - * @param item the new item associated to key - * @param key the identifier of the item to set. - */ - void plist_dict_set_item(plist_t node, const char* key, plist_t item); - - /** - * Insert a new item into a #PLIST_DICT node. - * - * @deprecated Deprecated. Use plist_dict_set_item instead. - * - * @param node the node of type #PLIST_DICT - * @param item the new item to insert - * @param key The identifier of the item to insert. - */ - PLIST_WARN_DEPRECATED("use plist_dict_set_item instead") - void plist_dict_insert_item(plist_t node, const char* key, plist_t item); - - /** - * Remove an existing position in a #PLIST_DICT node. - * Removed position will be freed using #plist_free - * - * @param node the node of type #PLIST_DICT - * @param key The identifier of the item to remove. Assert if identifier is not present. - */ - void plist_dict_remove_item(plist_t node, const char* key); - - /** - * Merge a dictionary into another. This will add all key/value pairs - * from the source dictionary to the target dictionary, overwriting - * any existing key/value pairs that are already present in target. - * - * @param target pointer to an existing node of type #PLIST_DICT - * @param source node of type #PLIST_DICT that should be merged into target - */ - void plist_dict_merge(plist_t *target, plist_t source); - - - /******************************************** - * * - * Getters * - * * - ********************************************/ - - /** - * Get the parent of a node - * - * @param node the parent (NULL if node is root) - */ - plist_t plist_get_parent(plist_t node); - - /** - * Get the #plist_type of a node. - * - * @param node the node - * @return the type of the node - */ - plist_type plist_get_node_type(plist_t node); - - /** - * Get the value of a #PLIST_KEY node. - * This function does nothing if node is not of type #PLIST_KEY - * - * @param node the node - * @param val a pointer to a C-string. This function allocates the memory, - * caller is responsible for freeing it. - * @note Use plist_mem_free() to free the allocated memory. - */ - void plist_get_key_val(plist_t node, char **val); - - /** - * Get the value of a #PLIST_STRING node. - * This function does nothing if node is not of type #PLIST_STRING - * - * @param node the node - * @param val a pointer to a C-string. This function allocates the memory, - * caller is responsible for freeing it. Data is UTF-8 encoded. - * @note Use plist_mem_free() to free the allocated memory. - */ - void plist_get_string_val(plist_t node, char **val); - - /** - * Get a pointer to the buffer of a #PLIST_STRING node. - * - * @note DO NOT MODIFY the buffer. Mind that the buffer is only available - * until the plist node gets freed. Make a copy if needed. - * - * @param node The node - * @param length If non-NULL, will be set to the length of the string - * - * @return Pointer to the NULL-terminated buffer. - */ - const char* plist_get_string_ptr(plist_t node, uint64_t* length); - - /** - * Get the value of a #PLIST_BOOLEAN node. - * This function does nothing if node is not of type #PLIST_BOOLEAN - * - * @param node the node - * @param val a pointer to a uint8_t variable. - */ - void plist_get_bool_val(plist_t node, uint8_t * val); - - /** - * Get the unsigned integer value of a #PLIST_INT node. - * This function does nothing if node is not of type #PLIST_INT - * - * @param node the node - * @param val a pointer to a uint64_t variable. - */ - void plist_get_uint_val(plist_t node, uint64_t * val); - - /** - * Get the signed integer value of a #PLIST_INT node. - * This function does nothing if node is not of type #PLIST_INT - * - * @param node the node - * @param val a pointer to a int64_t variable. - */ - void plist_get_int_val(plist_t node, int64_t * val); - - /** - * Get the value of a #PLIST_REAL node. - * This function does nothing if node is not of type #PLIST_REAL - * - * @param node the node - * @param val a pointer to a double variable. - */ - void plist_get_real_val(plist_t node, double *val); - - /** - * Get the value of a #PLIST_DATA node. - * This function does nothing if node is not of type #PLIST_DATA - * - * @param node the node - * @param val a pointer to an unallocated char buffer. This function allocates the memory, - * caller is responsible for freeing it. - * @param length the length of the buffer - * @note Use plist_mem_free() to free the allocated memory. - */ - void plist_get_data_val(plist_t node, char **val, uint64_t * length); - - /** - * Get a pointer to the data buffer of a #PLIST_DATA node. - * - * @note DO NOT MODIFY the buffer. Mind that the buffer is only available - * until the plist node gets freed. Make a copy if needed. - * - * @param node The node - * @param length Pointer to a uint64_t that will be set to the length of the buffer - * - * @return Pointer to the buffer - */ - const char* plist_get_data_ptr(plist_t node, uint64_t* length); - - /** - * Get the value of a #PLIST_DATE node. - * This function does nothing if node is not of type #PLIST_DATE - * - * @param node the node - * @param sec a pointer to an int32_t variable. Represents the number of seconds since 01/01/2001. - * @param usec a pointer to an int32_t variable. Represents the number of microseconds - */ - void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec); - - /** - * Get the value of a #PLIST_UID node. - * This function does nothing if node is not of type #PLIST_UID - * - * @param node the node - * @param val a pointer to a uint64_t variable. - */ - void plist_get_uid_val(plist_t node, uint64_t * val); - - - /******************************************** - * * - * Setters * - * * - ********************************************/ - - /** - * Set the value of a node. - * Forces type of node to #PLIST_KEY - * - * @param node the node - * @param val the key value - */ - void plist_set_key_val(plist_t node, const char *val); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_STRING - * - * @param node the node - * @param val the string value. The string is copied when set and will be - * freed by the node. - */ - void plist_set_string_val(plist_t node, const char *val); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_BOOLEAN - * - * @param node the node - * @param val the boolean value - */ - void plist_set_bool_val(plist_t node, uint8_t val); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_INT - * - * @param node the node - * @param val the unsigned integer value - */ - void plist_set_uint_val(plist_t node, uint64_t val); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_INT - * - * @param node the node - * @param val the signed integer value - */ - void plist_set_int_val(plist_t node, int64_t val); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_REAL - * - * @param node the node - * @param val the real value - */ - void plist_set_real_val(plist_t node, double val); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_DATA - * - * @param node the node - * @param val the binary buffer. The buffer is copied when set and will - * be freed by the node. - * @param length the length of the buffer - */ - void plist_set_data_val(plist_t node, const char *val, uint64_t length); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_DATE - * - * @param node the node - * @param sec the number of seconds since 01/01/2001 - * @param usec the number of microseconds - */ - void plist_set_date_val(plist_t node, int32_t sec, int32_t usec); - - /** - * Set the value of a node. - * Forces type of node to #PLIST_UID - * - * @param node the node - * @param val the unsigned integer value - */ - void plist_set_uid_val(plist_t node, uint64_t val); - - - /******************************************** - * * - * Import & Export * - * * - ********************************************/ - - /** - * Export the #plist_t structure to XML format. - * - * @param plist the root node to export - * @param plist_xml a pointer to a C-string. This function allocates the memory, - * caller is responsible for freeing it. Data is UTF-8 encoded. - * @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer. - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - * @note Use plist_mem_free() to free the allocated memory. - */ - plist_err_t plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length); - - /** - * Export the #plist_t structure to binary format. - * - * @param plist the root node to export - * @param plist_bin a pointer to a char* buffer. This function allocates the memory, - * caller is responsible for freeing it. - * @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer. - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - * @note Use plist_mem_free() to free the allocated memory. - */ - plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length); - - /** - * Export the #plist_t structure to JSON format. - * - * @param plist the root node to export - * @param plist_json a pointer to a char* buffer. This function allocates the memory, - * caller is responsible for freeing it. - * @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer. - * @param prettify pretty print the output if != 0 - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - * @note Use plist_mem_free() to free the allocated memory. - */ - plist_err_t plist_to_json(plist_t plist, char **plist_json, uint32_t* length, int prettify); - - /** - * Export the #plist_t structure to OpenStep format. - * - * @param plist the root node to export - * @param plist_openstep a pointer to a char* buffer. This function allocates the memory, - * caller is responsible for freeing it. - * @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer. - * @param prettify pretty print the output if != 0 - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - * @note Use plist_mem_free() to free the allocated memory. - */ - plist_err_t plist_to_openstep(plist_t plist, char **plist_openstep, uint32_t* length, int prettify); - - - /** - * Import the #plist_t structure from XML format. - * - * @param plist_xml a pointer to the xml buffer. - * @param length length of the buffer to read. - * @param plist a pointer to the imported plist. - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - */ - plist_err_t plist_from_xml(const char *plist_xml, uint32_t length, plist_t * plist); - - /** - * Import the #plist_t structure from binary format. - * - * @param plist_bin a pointer to the xml buffer. - * @param length length of the buffer to read. - * @param plist a pointer to the imported plist. - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - */ - plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist); - - /** - * Import the #plist_t structure from JSON format. - * - * @param json a pointer to the JSON buffer. - * @param length length of the buffer to read. - * @param plist a pointer to the imported plist. - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - */ - plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist); - - /** - * Import the #plist_t structure from OpenStep plist format. - * - * @param openstep a pointer to the OpenStep plist buffer. - * @param length length of the buffer to read. - * @param plist a pointer to the imported plist. - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - */ - plist_err_t plist_from_openstep(const char *openstep, uint32_t length, plist_t * plist); - - /** - * Import the #plist_t structure from memory data. - * This method will look at the first bytes of plist_data - * to determine if plist_data contains a binary, JSON, or XML plist - * and tries to parse the data in the appropriate format. - * @note This is just a convenience function and the format detection is - * very basic. It checks with plist_is_binary() if the data supposedly - * contains binary plist data, if not it checks if the first byte is - * either '{' or '[' and assumes JSON format, otherwise it will try - * to parse the data as XML. - * - * @param plist_data a pointer to the memory buffer containing plist data. - * @param length length of the buffer to read. - * @param plist a pointer to the imported plist. - * @return PLIST_ERR_SUCCESS on success or a #plist_err_t on failure - */ - plist_err_t plist_from_memory(const char *plist_data, uint32_t length, plist_t * plist); - - /** - * Test if in-memory plist data is in binary format. - * This function will look at the first bytes of plist_data to determine - * if it supposedly contains a binary plist. - * @note The function is not validating the whole memory buffer to check - * if the content is truly a plist, it is only using some heuristic on - * the first few bytes of plist_data. - * - * @param plist_data a pointer to the memory buffer containing plist data. - * @param length length of the buffer to read. - * @return 1 if the buffer is a binary plist, 0 otherwise. - */ - int plist_is_binary(const char *plist_data, uint32_t length); - - /******************************************** - * * - * Utils * - * * - ********************************************/ - - /** - * Get a node from its path. Each path element depends on the associated father node type. - * For Dictionaries, var args are casted to const char*, for arrays, var args are caster to uint32_t - * Search is breath first order. - * - * @param plist the node to access result from. - * @param length length of the path to access - * @return the value to access. - */ - plist_t plist_access_path(plist_t plist, uint32_t length, ...); - - /** - * Variadic version of #plist_access_path. - * - * @param plist the node to access result from. - * @param length length of the path to access - * @param v list of array's index and dic'st key - * @return the value to access. - */ - plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v); - - /** - * Compare two node values - * - * @param node_l left node to compare - * @param node_r rigth node to compare - * @return TRUE is type and value match, FALSE otherwise. - */ - char plist_compare_node_value(plist_t node_l, plist_t node_r); - - #define _PLIST_IS_TYPE(__plist, __plist_type) (__plist && (plist_get_node_type(__plist) == PLIST_##__plist_type)) - - /* Helper macros for the different plist types */ - #define PLIST_IS_BOOLEAN(__plist) _PLIST_IS_TYPE(__plist, BOOLEAN) - #define PLIST_IS_INT(__plist) _PLIST_IS_TYPE(__plist, INT) - #define PLIST_IS_REAL(__plist) _PLIST_IS_TYPE(__plist, REAL) - #define PLIST_IS_STRING(__plist) _PLIST_IS_TYPE(__plist, STRING) - #define PLIST_IS_ARRAY(__plist) _PLIST_IS_TYPE(__plist, ARRAY) - #define PLIST_IS_DICT(__plist) _PLIST_IS_TYPE(__plist, DICT) - #define PLIST_IS_DATE(__plist) _PLIST_IS_TYPE(__plist, DATE) - #define PLIST_IS_DATA(__plist) _PLIST_IS_TYPE(__plist, DATA) - #define PLIST_IS_KEY(__plist) _PLIST_IS_TYPE(__plist, KEY) - #define PLIST_IS_UID(__plist) _PLIST_IS_TYPE(__plist, UID) - /* for backwards compatibility */ - #define PLIST_IS_UINT PLIST_IS_INT - - /** - * Helper function to check the value of a PLIST_BOOL node. - * - * @param boolnode node of type PLIST_BOOL - * @return 1 if the boolean node has a value of TRUE or 0 if FALSE. - */ - int plist_bool_val_is_true(plist_t boolnode); - - /** - * Helper function to test if a given #PLIST_INT node's value is negative - * - * @param intnode node of type PLIST_INT - * @return 1 if the node's value is negative, or 0 if positive. - */ - int plist_int_val_is_negative(plist_t intnode); - - /** - * Helper function to compare the value of a PLIST_INT node against - * a given signed integer value. - * - * @param uintnode node of type PLIST_INT - * @param cmpval value to compare against - * @return 0 if the node's value and cmpval are equal, - * 1 if the node's value is greater than cmpval, - * or -1 if the node's value is less than cmpval. - */ - int plist_int_val_compare(plist_t uintnode, int64_t cmpval); - - /** - * Helper function to compare the value of a PLIST_INT node against - * a given unsigned integer value. - * - * @param uintnode node of type PLIST_INT - * @param cmpval value to compare against - * @return 0 if the node's value and cmpval are equal, - * 1 if the node's value is greater than cmpval, - * or -1 if the node's value is less than cmpval. - */ - int plist_uint_val_compare(plist_t uintnode, uint64_t cmpval); - - /** - * Helper function to compare the value of a PLIST_UID node against - * a given value. - * - * @param uidnode node of type PLIST_UID - * @param cmpval value to compare against - * @return 0 if the node's value and cmpval are equal, - * 1 if the node's value is greater than cmpval, - * or -1 if the node's value is less than cmpval. - */ - int plist_uid_val_compare(plist_t uidnode, uint64_t cmpval); - - /** - * Helper function to compare the value of a PLIST_REAL node against - * a given value. - * - * @note WARNING: Comparing floating point values can give inaccurate - * results because of the nature of floating point values on computer - * systems. While this function is designed to be as accurate as - * possible, please don't rely on it too much. - * - * @param realnode node of type PLIST_REAL - * @param cmpval value to compare against - * @return 0 if the node's value and cmpval are (almost) equal, - * 1 if the node's value is greater than cmpval, - * or -1 if the node's value is less than cmpval. - */ - int plist_real_val_compare(plist_t realnode, double cmpval); - - /** - * Helper function to compare the value of a PLIST_DATE node against - * a given set of seconds and fraction of a second since epoch. - * - * @param datenode node of type PLIST_DATE - * @param cmpsec number of seconds since epoch to compare against - * @param cmpusec fraction of a second in microseconds to compare against - * @return 0 if the node's date is equal to the supplied values, - * 1 if the node's date is greater than the supplied values, - * or -1 if the node's date is less than the supplied values. - */ - int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t cmpusec); - - /** - * Helper function to compare the value of a PLIST_STRING node against - * a given value. - * This function basically behaves like strcmp. - * - * @param strnode node of type PLIST_STRING - * @param cmpval value to compare against - * @return 0 if the node's value and cmpval are equal, - * > 0 if the node's value is lexicographically greater than cmpval, - * or < 0 if the node's value is lexicographically less than cmpval. - */ - int plist_string_val_compare(plist_t strnode, const char* cmpval); - - /** - * Helper function to compare the value of a PLIST_STRING node against - * a given value, while not comparing more than n characters. - * This function basically behaves like strncmp. - * - * @param strnode node of type PLIST_STRING - * @param cmpval value to compare against - * @param n maximum number of characters to compare - * @return 0 if the node's value and cmpval are equal, - * > 0 if the node's value is lexicographically greater than cmpval, - * or < 0 if the node's value is lexicographically less than cmpval. - */ - int plist_string_val_compare_with_size(plist_t strnode, const char* cmpval, size_t n); - - /** - * Helper function to match a given substring in the value of a - * PLIST_STRING node. - * - * @param strnode node of type PLIST_STRING - * @param substr value to match - * @return 1 if the node's value contains the given substring, - * or 0 if not. - */ - int plist_string_val_contains(plist_t strnode, const char* substr); - - /** - * Helper function to compare the value of a PLIST_KEY node against - * a given value. - * This function basically behaves like strcmp. - * - * @param keynode node of type PLIST_KEY - * @param cmpval value to compare against - * @return 0 if the node's value and cmpval are equal, - * > 0 if the node's value is lexicographically greater than cmpval, - * or < 0 if the node's value is lexicographically less than cmpval. - */ - int plist_key_val_compare(plist_t keynode, const char* cmpval); - - /** - * Helper function to compare the value of a PLIST_KEY node against - * a given value, while not comparing more than n characters. - * This function basically behaves like strncmp. - * - * @param keynode node of type PLIST_KEY - * @param cmpval value to compare against - * @param n maximum number of characters to compare - * @return 0 if the node's value and cmpval are equal, - * > 0 if the node's value is lexicographically greater than cmpval, - * or < 0 if the node's value is lexicographically less than cmpval. - */ - int plist_key_val_compare_with_size(plist_t keynode, const char* cmpval, size_t n); - - /** - * Helper function to match a given substring in the value of a - * PLIST_KEY node. - * - * @param keynode node of type PLIST_KEY - * @param substr value to match - * @return 1 if the node's value contains the given substring, - * or 0 if not. - */ - int plist_key_val_contains(plist_t keynode, const char* substr); - - /** - * Helper function to compare the data of a PLIST_DATA node against - * a given blob and size. - * This function basically behaves like memcmp after making sure the - * size of the node's data value is equal to the size of cmpval (n), - * making this a "full match" comparison. - * - * @param datanode node of type PLIST_DATA - * @param cmpval data blob to compare against - * @param n size of data blob passed in cmpval - * @return 0 if the node's data blob and cmpval are equal, - * > 0 if the node's value is lexicographically greater than cmpval, - * or < 0 if the node's value is lexicographically less than cmpval. - */ - int plist_data_val_compare(plist_t datanode, const uint8_t* cmpval, size_t n); - - /** - * Helper function to compare the data of a PLIST_DATA node against - * a given blob and size, while no more than n bytes are compared. - * This function basically behaves like memcmp after making sure the - * size of the node's data value is at least n, making this a - * "starts with" comparison. - * - * @param datanode node of type PLIST_DATA - * @param cmpval data blob to compare against - * @param n size of data blob passed in cmpval - * @return 0 if the node's value and cmpval are equal, - * > 0 if the node's value is lexicographically greater than cmpval, - * or < 0 if the node's value is lexicographically less than cmpval. - */ - int plist_data_val_compare_with_size(plist_t datanode, const uint8_t* cmpval, size_t n); - - /** - * Helper function to match a given data blob within the value of a - * PLIST_DATA node. - * - * @param datanode node of type PLIST_KEY - * @param cmpval data blob to match - * @param n size of data blob passed in cmpval - * @return 1 if the node's value contains the given data blob - * or 0 if not. - */ - int plist_data_val_contains(plist_t datanode, const uint8_t* cmpval, size_t n); - - /** - * Free memory allocated by relevant libplist API calls: - * - plist_to_xml() - * - plist_to_bin() - * - plist_get_key_val() - * - plist_get_string_val() - * - plist_get_data_val() - * - * @param ptr pointer to the memory to free - * - * @note Do not use this function to free plist_t nodes, use plist_free() - * instead. - */ - void plist_mem_free(void* ptr); - - /*@}*/ - -#ifdef __cplusplus -} -#endif -#endif diff --git a/StikJIT/idevice/profiles.h b/StikJIT/idevice/profiles.h new file mode 100644 index 0000000..3bd5dd0 --- /dev/null +++ b/StikJIT/idevice/profiles.h @@ -0,0 +1,22 @@ +// +// profiles.h +// StikDebug +// +// Created by s s on 2025/11/29. +// + +#ifndef PROFILES_H +#define PROFILES_H +#include "idevice.h" +#include +NSArray* fetchAppProfiles(IdeviceProviderHandle* provider, NSError** error); +bool removeProfile(IdeviceProviderHandle* provider, NSString* uuid, NSError** error); +bool addProfile(IdeviceProviderHandle* provider, NSData* profile, NSError** error); + +@interface CMSDecoderHelper : NSObject +// Decode CMS/PKCS7 data and return decoded payload and any embedded certs ++ (NSData*)decodeCMSData:(NSData *)cmsData +// outCerts:(NSArray * _Nullable * _Nullable)outCerts + error:(NSError * _Nullable * _Nullable)error; +@end +#endif diff --git a/StikJIT/idevice/profiles.m b/StikJIT/idevice/profiles.m new file mode 100644 index 0000000..53b298e --- /dev/null +++ b/StikJIT/idevice/profiles.m @@ -0,0 +1,177 @@ +// +// profiles.m +// StikDebug +// +// Created by s s on 2025/11/29. +// +#include "profiles.h" +@import Foundation; +@import Security; + +NSError* makeError(int code, NSString* msg) { + return [NSError errorWithDomain:@"profiles" code:code userInfo:@{NSLocalizedDescriptionKey: msg}]; +} + + +NSArray* fetchAppProfiles(IdeviceProviderHandle* provider, NSError** error) { + MisagentClientHandle* misagentHandle = 0; + IdeviceFfiError * err = misagent_connect(provider, &misagentHandle); + if (err) { + *error = makeError(err->code, @(err->message)); + idevice_error_free(err); + return nil; + } + + uint8_t** profileArr = 0; + size_t profileCount = 0; + size_t* profileLengthArr = 0; + err = misagent_copy_all(misagentHandle, &profileArr, &profileLengthArr, &profileCount); + + if (err) { + *error = makeError((err)->code, @((err)->message)); + misagent_client_free(misagentHandle); + idevice_error_free(err); + return nil; + } + + NSMutableArray* ans = [NSMutableArray array]; + for(int i = 0; i < profileCount; ++i) { + size_t len = profileLengthArr[i]; + uint8_t* profile = profileArr[i]; + NSData* profileData = [NSData dataWithBytes:profile length:len]; + + [ans addObject:profileData]; + } + + misagent_free_profiles(profileArr, profileLengthArr, profileCount); + misagent_client_free(misagentHandle); + + return ans; +} + +bool removeProfile(IdeviceProviderHandle* provider, NSString* uuid, NSError** error) { + MisagentClientHandle* misagentHandle = 0; + IdeviceFfiError * err = misagent_connect(provider, &misagentHandle); + if (err) { + *error = makeError(err->code, @(err->message)); + idevice_error_free(err); + return false; + } + + err = misagent_remove(misagentHandle, [uuid UTF8String]); + if (err) { + *error = makeError((err)->code, @((err)->message)); + misagent_client_free(misagentHandle); + idevice_error_free(err); + return false; + } + + misagent_client_free(misagentHandle); + return true; +} + +bool addProfile(IdeviceProviderHandle* provider, NSData* profile, NSError** error) { + MisagentClientHandle* misagentHandle = 0; + IdeviceFfiError * err = misagent_connect(provider, &misagentHandle); + if (err) { + *error = makeError(err->code, @(err->message)); + idevice_error_free(err); + return false; + } + + err = misagent_install(misagentHandle, [profile bytes], [profile length]); + if (err) { + *error = makeError((err)->code, @((err)->message)); + misagent_client_free(misagentHandle); + idevice_error_free(err); + return false; + } + + misagent_client_free(misagentHandle); + return true; +} + +typedef CFTypeRef CMSDecoderRef; +OSStatus CMSDecoderCreate(CMSDecoderRef * cmsDecoderOut); +OSStatus CMSDecoderUpdateMessage(CMSDecoderRef cmsDecoder, const void * msgBytes, size_t msgBytesLen); +OSStatus CMSDecoderFinalizeMessage(CMSDecoderRef cmsDecoder); +OSStatus CMSDecoderCopyContent(CMSDecoderRef cmsDecoder, CFDataRef * contentOut); +OSStatus CMSDecoderCopyAllCerts(CMSDecoderRef cmsDecoder, CFArrayRef * certsOut); + + +// Helper to convert OSStatus -> NSError +static NSError *NSErrorFromOSStatus(OSStatus status, NSString *message) { + if (status == errSecSuccess) return nil; + NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: message ?: @"Security error", + @"OSStatus" : @(status) }; + return [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:userInfo]; +} + + +@implementation CMSDecoderHelper + ++ (NSData*)decodeCMSData:(NSData *)cmsData +// outCerts:(NSArray * _Nullable * _Nullable)outCerts + error:(NSError * _Nullable * _Nullable)error +{ + if (!cmsData) { + if (error) *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSURLErrorBadURL userInfo:@{NSLocalizedDescriptionKey: @"cmsData is nil"}]; + return nil; + } + + CMSDecoderRef decoder = NULL; + OSStatus status = CMSDecoderCreate(&decoder); + if (status != errSecSuccess || decoder == NULL) { + if (error) *error = NSErrorFromOSStatus(status, @"Failed to create CMS decoder"); + return nil; + } + + // Feed data to decoder + status = CMSDecoderUpdateMessage(decoder, cmsData.bytes, cmsData.length); + if (status != errSecSuccess) { + if (error) *error = NSErrorFromOSStatus(status, @"Failed to update CMS decoder with message bytes"); + CFRelease(decoder); + return nil; + } + + // Finalize (parse) the message + status = CMSDecoderFinalizeMessage(decoder); + if (status != errSecSuccess) { + if (error) *error = NSErrorFromOSStatus(status, @"Failed to finalize CMS message"); + CFRelease(decoder); + return nil; + } + + // Extract the content (the inner payload). This may be NULL if content is detached. + CFDataRef contentData = NULL; + status = CMSDecoderCopyContent(decoder, &contentData); + if (status != errSecSuccess && status != errSecItemNotFound) { + // errSecItemNotFound could mean no content (detached signature) + if (error) *error = NSErrorFromOSStatus(status, @"Failed to copy CMS content"); + if (contentData) CFRelease(contentData); + CFRelease(decoder); + return nil; + } + +// // Extract embedded certificates (if any) +// CFArrayRef certsArray = NULL; +// status = CMSDecoderCopyAllCerts(decoder, &certsArray); +// if (status == errSecSuccess && certsArray) { +// // certsArray contains SecCertificateRef items +// NSArray *certs = (__bridge_transfer NSArray *)certsArray; +// if (outCerts) *outCerts = certs; +// } else { +// // no certs or error +// if (status != errSecSuccess && status != errSecItemNotFound) { +// if (error) *error = NSErrorFromOSStatus(status, @"Failed to copy embedded certificates (if any)"); +// CFRelease(decoder); +// return NO; +// } +// if (outCerts) *outCerts = @[]; // empty +// } + + CFRelease(decoder); + return (__bridge NSData *)(contentData); +} + +@end diff --git a/StikJIT/idevice/something.c b/StikJIT/idevice/something.c index c453763..3ee5da4 100644 --- a/StikJIT/idevice/something.c +++ b/StikJIT/idevice/something.c @@ -138,7 +138,7 @@ int install_ipa(const char *ip, } InstallationProxyClientHandle *ipc = NULL; - err = installation_proxy_connect_tcp(provider, &ipc); + err = installation_proxy_connect(provider, &ipc); if (err) { fprintf(stderr, "installation_proxy connect failed: [%d] %s\n", err->code, err->message);