From 868efa4525d95cb9d45dfc253c89d21611b6a5c2 Mon Sep 17 00:00:00 2001 From: uncor3 Date: Tue, 6 Jan 2026 16:42:00 +0000 Subject: [PATCH] WIP: migrate to idevice-rs (90% done) --- .gitmodules | 3 + CMakeLists.txt | 192 +++--- lib/ztoast | 1 + src/afcexplorerwidget.cpp | 131 ++-- src/afcexplorerwidget.h | 6 +- src/appcontext.cpp | 340 +++++----- src/appdownloadbasedialog.cpp | 15 +- src/appdownloadbasedialog.h | 4 +- src/appinstalldialog.cpp | 145 ++-- src/appinstalldialog.h | 8 +- src/cableinfowidget.cpp | 10 +- src/cableinfowidget.h | 1 - src/core/helpers/mounted_image_info_free.cpp | 11 + src/core/services/afc2_client_new.cpp | 54 -- src/core/services/detect-afc2.cpp | 2 +- .../detect_has_been_jailbroken_before.cpp | 37 +- src/core/services/detect_jailbroken.cpp | 13 +- src/core/services/get_battery_info.cpp | 25 +- src/core/services/get_cable_info.cpp | 55 +- src/core/services/get_file_tree.cpp | 21 +- src/core/services/get_mounted_image.cpp | 109 +-- src/core/services/init_device.cpp | 132 ++-- src/core/services/install_ipa.cpp | 583 ++-------------- src/core/services/mount_dev_image.cpp | 615 ++--------------- src/core/services/query_mobilegestalt.cpp | 70 -- src/core/services/restart.cpp | 90 --- src/core/services/set_location.cpp | 145 ++-- src/core/services/shutdown.cpp | 88 --- src/core/services/take_screenshot.cpp | 61 -- src/devdiskimagehelper.cpp | 107 +-- src/devdiskimageswidget.cpp | 217 +++--- src/devdiskimageswidget.h | 2 +- src/devdiskmanager.cpp | 168 ++--- src/devdiskmanager.h | 6 +- src/deviceimagewidget.cpp | 18 +- src/devicemenuwidget.cpp | 63 +- src/devicemenuwidget.h | 10 +- src/devicesidebarwidget.h | 12 + src/diskusagewidget.cpp | 152 +++-- src/exportmanager.cpp | 181 +++-- src/exportmanager.h | 6 +- src/gallerywidget.cpp | 40 +- src/iDescriptor.h | 191 +++++- src/installedappswidget.cpp | 626 +++++++++--------- src/installedappswidget.h | 6 +- src/livescreenwidget.cpp | 201 +++--- src/livescreenwidget.h | 3 - src/main.cpp | 3 +- src/mainwindow.cpp | 97 ++- src/mediapreviewdialog.cpp | 19 +- src/mediapreviewdialog.h | 5 +- src/mediastreamer.cpp | 117 ++-- src/mediastreamer.h | 9 +- src/mediastreamermanager.cpp | 2 +- src/mediastreamermanager.h | 2 +- src/photomodel.cpp | 116 ++-- src/qballoontip.cpp | 299 +++++++++ src/qballoontip.h | 44 ++ src/querymobilegestaltwidget.cpp | 50 +- src/recoverydeviceinfowidget.cpp | 229 +++---- src/recoverydeviceinfowidget.h | 2 +- src/servicemanager.cpp | 110 +-- src/servicemanager.h | 11 + src/toolboxwidget.cpp | 165 ++--- src/toolboxwidget.h | 1 - src/virtuallocationwidget.cpp | 111 ++-- 66 files changed, 2822 insertions(+), 3546 deletions(-) create mode 160000 lib/ztoast create mode 100644 src/core/helpers/mounted_image_info_free.cpp delete mode 100644 src/core/services/afc2_client_new.cpp delete mode 100644 src/core/services/query_mobilegestalt.cpp delete mode 100644 src/core/services/restart.cpp delete mode 100644 src/core/services/shutdown.cpp delete mode 100644 src/core/services/take_screenshot.cpp create mode 100644 src/qballoontip.cpp create mode 100644 src/qballoontip.h diff --git a/.gitmodules b/.gitmodules index 13c8d2e..ca79437 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "lib/idevice-rs"] path = lib/idevice-rs url = https://github.com/jkcoxson/idevice.git +[submodule "lib/ztoast"] + path = lib/ztoast + url = https://github.com/niklashenning/qt-toast.git diff --git a/CMakeLists.txt b/CMakeLists.txt index df8f2aa..573e0bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,7 +96,7 @@ add_custom_command( OUTPUT ${IDEVICE_RS_LIB_PATH} COMMAND ${CARGO_EXECUTABLE} build --manifest-path ${IDEVICE_RS_SOURCE_DIR}/Cargo.toml WORKING_DIRECTORY ${IDEVICE_RS_SOURCE_DIR} - COMMENT "Building idevice-rs FFI library" + COMMENT "Building idevice-rs FFI libraryy" VERBATIM ) @@ -204,84 +204,106 @@ pkg_check_modules(PUGIXML REQUIRED IMPORTED_TARGET pugixml) file(GLOB PROJECT_SOURCES -# src/*.cpp -# src/core/helpers/*.cpp -# src/core/services/*.cpp -# src/*.h -src/mainwindow.cpp -src/mainwindow.h -src/devicemonitor.h -src/main.cpp -src/core/services/init_device.cpp -src/core/services/get_battery_info.cpp -src/core/services/detect_jailbroken.cpp -src/appcontext.cpp -src/appcontext.h -src/devicedatabase.cpp -src/devicedatabase.h -src/core/helpers/compare_product_type.cpp -src/welcomewidget.cpp -src/welcomewidget.h -src/ztabwidget.cpp -src/ztabwidget.h -src/devicemanagerwidget.cpp -src/devicemanagerwidget.h -src/responsiveqlabel.cpp -src/responsiveqlabel.h -src/devicesidebarwidget.cpp -src/devicesidebarwidget.h -src/devicemenuwidget.cpp -src/devicemenuwidget.h -src/deviceinfowidget.cpp -src/deviceinfowidget.h -src/batterywidget.cpp -src/batterywidget.h -src/diskusagewidget.cpp -src/diskusagewidget.h +src/*.cpp +src/core/helpers/*.cpp +src/core/services/*.cpp +src/*.h +# src/mainwindow.cpp +# src/mainwindow.h +# src/devicemonitor.h +# src/main.cpp +# src/core/services/init_device.cpp +# src/core/services/get_battery_info.cpp +# src/core/services/detect_jailbroken.cpp +# src/appcontext.cpp +# src/appcontext.h +# src/devicedatabase.cpp +# src/devicedatabase.h +# src/core/helpers/compare_product_type.cpp +# src/welcomewidget.cpp +# src/welcomewidget.h +# src/ztabwidget.cpp +# src/ztabwidget.h +# src/devicemanagerwidget.cpp +# src/devicemanagerwidget.h +# src/responsiveqlabel.cpp +# src/responsiveqlabel.h +# src/devicesidebarwidget.cpp +# src/devicesidebarwidget.h +# src/devicemenuwidget.cpp +# src/devicemenuwidget.h +# src/deviceinfowidget.cpp +# src/deviceinfowidget.h +# src/batterywidget.cpp +# src/batterywidget.h +# src/diskusagewidget.cpp +# src/diskusagewidget.h +# # src/deviceimagewidget.cpp +# # src/deviceimagewidget.h +# src/iDescriptor-ui.h +# src/infolabel.cpp +# src/infolabel.h +# src/privateinfolabel.cpp +# src/privateinfolabel.h +# src/qprocessindicator.cpp +# src/qprocessindicator.h +# src/diagnosewidget.cpp +# src/diagnosewidget.h +# src/core/services/get-device-info.cpp # src/deviceimagewidget.cpp # src/deviceimagewidget.h -src/iDescriptor-ui.h -src/infolabel.cpp -src/infolabel.h -src/privateinfolabel.cpp -src/privateinfolabel.h -src/qprocessindicator.cpp -src/qprocessindicator.h -src/diagnosewidget.cpp -src/diagnosewidget.h -src/core/services/get-device-info.cpp -src/deviceimagewidget.cpp -src/deviceimagewidget.h -src/settingsmanager.cpp -src/settingsmanager.h -# src/fileexplorerwidget.cpp -# src/fileexplorerwidget.h -src/settingswidget.cpp -src/settingswidget.h -src/ifusemanager.cpp -src/ifusemanager.h -src/ifusediskunmountbutton.cpp -src/ifusediskunmountbutton.h -src/core/services/get_battery_info.cpp -src/networkdeviceswidget.cpp -src/core/services/avahi/avahi_service.h -src/core/services/avahi/avahi_service.cpp -src/networkdevicemanager.cpp -src/networkdevicemanager.h -src/*.ui +# src/settingsmanager.cpp +# src/settingsmanager.h +# # src/fileexplorerwidget.cpp +# # src/fileexplorerwidget.h +# src/settingswidget.cpp +# src/settingswidget.h +# src/ifusemanager.cpp +# src/ifusemanager.h +# src/ifusediskunmountbutton.cpp +# src/ifusediskunmountbutton.h +# src/core/services/get_battery_info.cpp +# src/networkdeviceswidget.cpp +# src/core/services/avahi/avahi_service.h +# src/core/services/avahi/avahi_service.cpp +# src/networkdevicemanager.cpp +# src/networkdevicemanager.h +# src/*.ui resources.qrc -src/gallerywidget.cpp -src/gallerywidget.h -src/photomodel.cpp -src/photomodel.h -src/core/services/load_heic.cpp -src/servicemanager.cpp -src/servicemanager.h -src/core/services/get_file_tree.cpp -src/core/helpers/read_afc_file_to_byte_array.cpp -src/heartbeat.h -src/zloadingwidget.h -src/zloadingwidget.cpp +# src/gallerywidget.cpp +# src/gallerywidget.h +# src/photomodel.cpp +# src/photomodel.h +# src/core/services/load_heic.cpp +# src/servicemanager.cpp +# src/servicemanager.h +# src/core/services/get_file_tree.cpp +# src/core/helpers/read_afc_file_to_byte_array.cpp +# src/heartbeat.h +# src/zloadingwidget.h +# src/zloadingwidget.cpp +# src/mediapreviewdialog.h +# src/mediapreviewdialog.cpp +# src/mediastreamer.h +# src/mediastreamer.cpp +# src/mediastreamermanager.h +# src/mediastreamermanager.cpp +# src/querymobilegestaltwidget.h +# src/querymobilegestaltwidget.cpp +# src/core/services/mount_dev_image.cpp +# src/core/services/get_mounted_image.cpp +# src/devdiskimageswidget.h +# src/devdiskimageswidget.cpp +# src/devdiskmanager.h +# src/devdiskmanager.cpp +# src/core/helpers/mounted_image_info_free.cpp +# src/core/services/get_cable_info.cpp +# src/cableinfowidget.h +# src/cableinfowidget.cpp +# src/livescreenwidget.h +# src/livescreenwidget.cpp +# src/virtuallocationwidget.h +# src/virtuallocationwidget.cpp ) if(APPLE) @@ -290,9 +312,7 @@ if(APPLE) src/core/services/dnssd/dnssd_service.cpp src/core/services/dnssd/dnssd_service.h ) -endif() - -if (WIN32) +elseif (WIN32) list(APPEND PROJECT_SOURCES src/core/services/dnssd/dnssd_service.cpp src/core/services/dnssd/dnssd_service.h @@ -300,15 +320,13 @@ if (WIN32) file(GLOB WINDOWS_PLATFORM_SOURCES src/platform/windows/*.cpp src/platform/windows/*.h) list(APPEND PROJECT_SOURCES ${WINDOWS_PLATFORM_SOURCES}) +else() + list(APPEND PROJECT_SOURCES + src/core/services/avahi/avahi_service.cpp + src/core/services/avahi/avahi_service.h + ) endif() -# if(LINUX) -# list(APPEND PROJECT_SOURCES -# src/core/services/avahi/avahi_service.cpp -# src/core/services/avahi/avahi_service.h -# ) -# endif() - if (NOT ENABLE_RECOVERY_DEVICE_SUPPORT) list(REMOVE_ITEM PROJECT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/recoverydeviceinfowidget.cpp @@ -323,6 +341,7 @@ endif() add_subdirectory(lib/airplay) add_subdirectory(lib/ipatool-go) add_subdirectory(lib/zupdater) +add_subdirectory(lib/ztoast) if (WIN32) set(NO_DEPLOY_WIN_IFUSE ON) @@ -392,6 +411,7 @@ target_link_libraries(iDescriptor PRIVATE airplay ipatool-go ZUpdater + ZToast ${IDEVICE_IMPLEMENTATION_LIBS} ) diff --git a/lib/ztoast b/lib/ztoast new file mode 160000 index 0000000..c7ebd75 --- /dev/null +++ b/lib/ztoast @@ -0,0 +1 @@ +Subproject commit c7ebd7514efe138e45d6d1a0ad6c33fd14ae579e diff --git a/src/afcexplorerwidget.cpp b/src/afcexplorerwidget.cpp index b979996..386d796 100644 --- a/src/afcexplorerwidget.cpp +++ b/src/afcexplorerwidget.cpp @@ -45,7 +45,7 @@ #include AfcExplorerWidget::AfcExplorerWidget(iDescriptorDevice *device, bool favEnabled, - afc_client_t afcClient, QString root, + AfcClientHandle *afcClient, QString root, QWidget *parent) : QWidget(parent), m_device(device), m_favEnabled(favEnabled), m_afc(afcClient), m_errorMessage("Failed to load directory"), m_root(root) @@ -373,6 +373,7 @@ void AfcExplorerWidget::exportSelectedFile(QListWidgetItem *item, // Save to selected directory QString savePath = directory + "/" + fileName; + // FIXME: this should be async int result = exportFileToPath(m_afc, devicePath.toStdString().c_str(), savePath.toStdString().c_str()); @@ -401,34 +402,81 @@ void AfcExplorerWidget::exportSelectedFile(QListWidgetItem *item, even though we are using safe wrappers, we better move this to services */ -int AfcExplorerWidget::exportFileToPath(afc_client_t afc, +// FIXME: this should be async +// use connect to signals/slots to notify progress +// create a progress dialog to show progress +// dont do this on the main thread +int AfcExplorerWidget::exportFileToPath(AfcClientHandle *afc, const char *device_path, const char *local_path) { - uint64_t handle = 0; - if (ServiceManager::safeAfcFileOpen(m_device, device_path, AFC_FOPEN_RDONLY, - &handle, m_afc) != AFC_E_SUCCESS) { - qDebug() << "Failed to open file on device:" << device_path; + AfcFileHandle *afcHandle = nullptr; + IdeviceFfiError *err_open = ServiceManager::safeAfcFileOpen( + m_device, device_path, AfcRdOnly, &afcHandle); + if (err_open != nullptr) { + qDebug() << "Failed to open file on device:" << device_path + << "Error Code:" << err_open->code + << "Message:" << err_open->message; + idevice_error_free(err_open); return -1; } + FILE *out = fopen(local_path, "wb"); if (!out) { qDebug() << "Failed to open local file:" << local_path; - afc_file_close(afc, handle); + IdeviceFfiError *err_close = + ServiceManager::safeAfcFileClose(m_device, afcHandle); + if (err_close != nullptr) { + idevice_error_free(err_close); + } return -1; } - char buffer[4096]; - uint32_t bytes_read = 0; - while (ServiceManager::safeAfcFileRead(m_device, handle, buffer, - sizeof(buffer), &bytes_read, - m_afc) == AFC_E_SUCCESS && - bytes_read > 0) { - fwrite(buffer, 1, bytes_read, out); + const size_t CHUNK_SIZE = 256 * 1024; // 256KB chunks + uint8_t *chunkData = nullptr; + size_t bytesRead = 0; + + // Read file in chunks + while (true) { + IdeviceFfiError *read_err = ServiceManager::safeAfcFileRead( + m_device, afcHandle, &chunkData, CHUNK_SIZE, &bytesRead); + + if (read_err != nullptr) { + qDebug() << "Error reading file:" << read_err->message; + idevice_error_free(read_err); + break; + } + + if (bytesRead == 0) { + // End of file reached + break; + } + + // Write chunk to local file + size_t written = fwrite(chunkData, 1, bytesRead, out); + + // Free the memory allocated by afc_file_read + afc_file_read_data_free(chunkData, bytesRead); + chunkData = nullptr; + + if (written != bytesRead) { + qDebug() << "Failed to write all bytes to local file"; + fclose(out); + ServiceManager::safeAfcFileClose(m_device, afcHandle); + return -1; + } } fclose(out); - ServiceManager::safeAfcFileClose(m_device, handle, m_afc); + + IdeviceFfiError *err_close = + ServiceManager::safeAfcFileClose(m_device, afcHandle); + if (err_close != nullptr) { + qDebug() << "Failed to close AFC file:" << err_close->message; + idevice_error_free(err_close); + return -1; + } + return 0; } @@ -464,39 +512,40 @@ void AfcExplorerWidget::onImportClicked() /* FIXME : move to services */ -int AfcExplorerWidget::importFileToDevice(afc_client_t afc, +int AfcExplorerWidget::importFileToDevice(AfcClientHandle *afc, const char *device_path, const char *local_path) { QFile in(local_path); - if (!in.open(QIODevice::ReadOnly)) { - qDebug() << "Failed to open local file for import:" << local_path; - return -1; - } + // if (!in.open(QIODevice::ReadOnly)) { + // qDebug() << "Failed to open local file for import:" << local_path; + // return -1; + // } - uint64_t handle = 0; - if (ServiceManager::safeAfcFileOpen(m_device, device_path, AFC_FOPEN_WRONLY, - &handle, m_afc) != AFC_E_SUCCESS) { - qDebug() << "Failed to open file on device for writing:" << device_path; - return -1; - } + // uint64_t handle = 0; + // if (ServiceManager::safeAfcFileOpen(m_device, device_path, + // AFC_FOPEN_WRONLY, + // &handle, m_afc) != AFC_E_SUCCESS) { + // qDebug() << "Failed to open file on device for writing:" << + // device_path; return -1; + // } - char buffer[4096]; - qint64 bytesRead; - while ((bytesRead = in.read(buffer, sizeof(buffer))) > 0) { - uint32_t bytesWritten = 0; - if (ServiceManager::safeAfcFileWrite( - m_device, handle, buffer, static_cast(bytesRead), - &bytesWritten, m_afc) != AFC_E_SUCCESS || - bytesWritten != bytesRead) { - qDebug() << "Failed to write to device file:" << device_path; - ServiceManager::safeAfcFileClose(m_device, handle, m_afc); - in.close(); - return -1; - } - } + // char buffer[4096]; + // qint64 bytesRead; + // while ((bytesRead = in.read(buffer, sizeof(buffer))) > 0) { + // uint32_t bytesWritten = 0; + // if (ServiceManager::safeAfcFileWrite( + // m_device, handle, buffer, static_cast(bytesRead), + // &bytesWritten, m_afc) != AFC_E_SUCCESS || + // bytesWritten != bytesRead) { + // qDebug() << "Failed to write to device file:" << device_path; + // ServiceManager::safeAfcFileClose(m_device, handle, m_afc); + // in.close(); + // return -1; + // } + // } - ServiceManager::safeAfcFileClose(m_device, handle, m_afc); + // ServiceManager::safeAfcFileClose(m_device, handle, m_afc); in.close(); return 0; } diff --git a/src/afcexplorerwidget.h b/src/afcexplorerwidget.h index 7c7362e..9b7250a 100644 --- a/src/afcexplorerwidget.h +++ b/src/afcexplorerwidget.h @@ -47,7 +47,7 @@ class AfcExplorerWidget : public QWidget public: explicit AfcExplorerWidget(iDescriptorDevice *device = nullptr, bool favEnabled = false, - afc_client_t afcClient = nullptr, + AfcClientHandle *afcClient = nullptr, QString root = "/", QWidget *parent = nullptr); void navigateToPath(const QString &path); void goHome(); @@ -109,9 +109,9 @@ private: void setupContextMenu(); void exportSelectedFile(QListWidgetItem *item); void exportSelectedFile(QListWidgetItem *item, const QString &directory); - int exportFileToPath(afc_client_t afc, const char *device_path, + int exportFileToPath(AfcClientHandle *afc, const char *device_path, const char *local_path); - int importFileToDevice(afc_client_t afc, const char *device_path, + int importFileToDevice(AfcClientHandle *afc, const char *device_path, const char *local_path); void updateNavStyles(); void updateButtonStates(); diff --git a/src/appcontext.cpp b/src/appcontext.cpp index 591d3e2..13b41eb 100644 --- a/src/appcontext.cpp +++ b/src/appcontext.cpp @@ -91,170 +91,194 @@ void AppContext::addDevice(QString udid, DeviceMonitorThread::IdeviceConnectionType conn_type, AddType addType, QString wifiMacAddress) { + try { - iDescriptorInitDeviceResult initResult; + // iDescriptorInitDeviceResult initResult; + auto initResult = std::make_shared(); + QFuture future = QtConcurrent::run([this, udid, conn_type, + addType, wifiMacAddress, + initResult]() { + if (addType == AddType::UpgradeToWireless) { + const IdevicePairingFile *pairingFile = + getCachedPairingFile(udid); + if (!pairingFile) { + qDebug() + << "Cannot upgrade to wireless, no pairing file for" + << udid; + return; + } - if (addType == AddType::UpgradeToWireless) { - const IdevicePairingFile *pairingFile = getCachedPairingFile(udid); - if (!pairingFile) { - qDebug() << "Cannot upgrade to wireless, no pairing file for" - << udid; - return; + QList networkDevices = + NetworkDeviceManager::sharedInstance() + ->m_networkProvider->getNetworkDevices(); + + auto it = std::find_if( + networkDevices.constBegin(), networkDevices.constEnd(), + [wifiMacAddress](const NetworkDevice &device) { + return device.macAddress.compare( + wifiMacAddress, Qt::CaseInsensitive) == 0; + }); + + if (it != networkDevices.constEnd()) { + + IdevicePairingFile *pairing_file = nullptr; + idevice_pairing_file_read( + QString("/var/lib/lockdown/%1.plist") + .arg(udid) + .toUtf8() + .constData(), + &pairing_file); + *initResult = init_idescriptor_device( + udid, {it->address, pairing_file}); + } else { + qDebug() << "No network device found with MAC address:" + << wifiMacAddress; + return; + } + } else if (addType == AddType::Wireless) { + // FIXME: its not udid here its macAddress + const IdevicePairingFile *pairingFile = + getCachedPairingFile(udid); + if (!pairingFile) { + qDebug() << "Cannot initialize wireless device, no pairing " + "file for" + << udid; + return; + } + + QList networkDevices = + NetworkDeviceManager::sharedInstance() + ->m_networkProvider->getNetworkDevices(); + + auto it = std::find_if( + networkDevices.constBegin(), networkDevices.constEnd(), + [wifiMacAddress](const NetworkDevice &device) { + return device.macAddress.compare( + wifiMacAddress, Qt::CaseInsensitive) == 0; + }); + + if (it != networkDevices.constEnd()) { + *initResult = init_idescriptor_device( + udid, {it->address, pairingFile}); + } else { + qDebug() << "No network device found with MAC address:" + << wifiMacAddress; + return; + } } - QList networkDevices = - NetworkDeviceManager::sharedInstance() - ->m_networkProvider->getNetworkDevices(); + else { - auto it = std::find_if( - networkDevices.constBegin(), networkDevices.constEnd(), - [wifiMacAddress](const NetworkDevice &device) { - return device.macAddress.compare(wifiMacAddress, - Qt::CaseInsensitive) == 0; + *initResult = init_idescriptor_device(udid, {nullptr, nullptr}); + } + }); + QFutureWatcher *watcher = new QFutureWatcher(); + watcher->setFuture(future); + connect(watcher, &QFutureWatcher::finished, this, + [this, udid, initResult, addType, conn_type, watcher]() { + watcher->deleteLater(); + qDebug() << "init_idescriptor_device success ?: " + << initResult->success; + // qDebug() << "init_idescriptor_device error code: " << + // initResult.error; + if (!initResult->success) { + qDebug() << "Failed to initialize device with UDID: " + << udid; + return; + } + // if (!initResult.success) { + // qDebug() << "Failed to initialize device with UDID: " + // << udid; if (initResult.error == + // LOCKDOWN_E_PASSWORD_PROTECTED) + // { + // if (addType == AddType::Regular) { + // m_pendingDevices.append(udid); + // emit devicePasswordProtected(udid); + // emit deviceChange(); + // QTimer::singleShot( + // SettingsManager::sharedInstance()->connectionTimeout() + // * + // 1000, + // this, [this, udid]() { + // if (m_pendingDevices.contains(udid)) + // { + // qDebug() << "Pairing expired for + // device UDID: + // " + // << udid; + // m_pendingDevices.removeAll(udid); + // emit devicePairingExpired(udid); + // emit deviceChange(); + // } + // }); + // } + // } else if (initResult.error == + // LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING + // || + // initResult.error == + // LOCKDOWN_E_INVALID_HOST_ID) { + // m_pendingDevices.append(udid); + // emit devicePairPending(udid); + // emit deviceChange(); + // QTimer::singleShot( + // SettingsManager::sharedInstance()->connectionTimeout() + // * + // 1000, + // this, [this, udid]() { + // qDebug() + // << "Pairing timer fired for device + // UDID: " << udid; + // if (m_pendingDevices.contains(udid)) { + // qDebug() + // << "Pairing expired for device + // UDID: " << udid; + // m_pendingDevices.removeAll(udid); + // emit devicePairingExpired(udid); + // emit deviceChange(); + // } + // }); + // } else { + // qDebug() << "Unhandled error for device UDID: " + // << udid + // << " Error code: " << initResult.error; + // } + // return; + // } + qDebug() << "Device initialized: " << udid; + + iDescriptorDevice *device = new iDescriptorDevice{ + .udid = udid.toStdString(), + .conn_type = conn_type, + .provider = initResult->provider, + .deviceInfo = initResult->deviceInfo, + .afcClient = initResult->afcClient, + .afc2Client = initResult->afc2Client, + .lockdown = initResult->lockdown, + .mutex = new std::recursive_mutex(), + .imageMounter = initResult->imageMounter, + .diagRelay = initResult->diagRelay, + .screenshotrClient = initResult->screenshotrClient, + .locationSimulation = initResult->locationSimulation}; + m_devices[device->udid] = device; + if (addType == AddType::Regular) { + qDebug() << "Regular device added: " << udid; + // SettingsManager::sharedInstance()->doIfEnabled( + // SettingsManager::Setting::AutoRaiseWindow, []() { + // if (MainWindow *mainWindow = + // MainWindow::sharedInstance()) { + // mainWindow->raise(); + // mainWindow->activateWindow(); + // } + // }); + + emit deviceAdded(device); + emit deviceChange(); + return; + } + emit devicePaired(device); + emit deviceChange(); + m_pendingDevices.removeAll(udid); }); - - if (it != networkDevices.constEnd()) { - - IdevicePairingFile *pairing_file = nullptr; - idevice_pairing_file_read(QString("/var/lib/lockdown/%1.plist") - .arg(udid) - .toUtf8() - .constData(), - &pairing_file); - initResult = - init_idescriptor_device(udid, {it->address, pairing_file}); - } else { - qDebug() << "No network device found with MAC address:" - << wifiMacAddress; - return; - } - } else if (addType == AddType::Wireless) { - // FIXME: its not udid here its macAddress - const IdevicePairingFile *pairingFile = getCachedPairingFile(udid); - if (!pairingFile) { - qDebug() - << "Cannot initialize wireless device, no pairing file for" - << udid; - return; - } - - QList networkDevices = - NetworkDeviceManager::sharedInstance() - ->m_networkProvider->getNetworkDevices(); - - auto it = std::find_if( - networkDevices.constBegin(), networkDevices.constEnd(), - [wifiMacAddress](const NetworkDevice &device) { - return device.macAddress.compare(wifiMacAddress, - Qt::CaseInsensitive) == 0; - }); - - if (it != networkDevices.constEnd()) { - initResult = - init_idescriptor_device(udid, {it->address, pairingFile}); - } else { - qDebug() << "No network device found with MAC address:" - << wifiMacAddress; - return; - } - } - - else { - - initResult = init_idescriptor_device(udid, {nullptr, nullptr}); - } - - qDebug() << "init_idescriptor_device success ?: " << initResult.success; - // qDebug() << "init_idescriptor_device error code: " << - // initResult.error; - if (!initResult.success) { - qDebug() << "Failed to initialize device with UDID: " << udid; - return; - } - // if (!initResult.success) { - // qDebug() << "Failed to initialize device with UDID: " << - // udid; if (initResult.error == LOCKDOWN_E_PASSWORD_PROTECTED) - // { - // if (addType == AddType::Regular) { - // m_pendingDevices.append(udid); - // emit devicePasswordProtected(udid); - // emit deviceChange(); - // QTimer::singleShot( - // SettingsManager::sharedInstance()->connectionTimeout() - // * - // 1000, - // this, [this, udid]() { - // if (m_pendingDevices.contains(udid)) { - // qDebug() << "Pairing expired for device - // UDID: - // " - // << udid; - // m_pendingDevices.removeAll(udid); - // emit devicePairingExpired(udid); - // emit deviceChange(); - // } - // }); - // } - // } else if (initResult.error == - // LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING || - // initResult.error == LOCKDOWN_E_INVALID_HOST_ID) { - // m_pendingDevices.append(udid); - // emit devicePairPending(udid); - // emit deviceChange(); - // QTimer::singleShot( - // SettingsManager::sharedInstance()->connectionTimeout() - // * - // 1000, - // this, [this, udid]() { - // qDebug() - // << "Pairing timer fired for device UDID: " << - // udid; - // if (m_pendingDevices.contains(udid)) { - // qDebug() - // << "Pairing expired for device UDID: " << - // udid; - // m_pendingDevices.removeAll(udid); - // emit devicePairingExpired(udid); - // emit deviceChange(); - // } - // }); - // } else { - // qDebug() << "Unhandled error for device UDID: " << udid - // << " Error code: " << initResult.error; - // } - // return; - // } - qDebug() << "Device initialized: " << udid; - - iDescriptorDevice *device = new iDescriptorDevice{ - .udid = udid.toStdString(), - .conn_type = conn_type, - .provider = initResult.provider, - .deviceInfo = initResult.deviceInfo, - .afcClient = initResult.afcClient, - .afc2Client = initResult.afc2Client, - .lockdown = initResult.lockdown, - .mutex = new std::recursive_mutex(), - }; - m_devices[device->udid] = device; - if (addType == AddType::Regular) { - qDebug() << "Regular device added: " << udid; - // SettingsManager::sharedInstance()->doIfEnabled( - // SettingsManager::Setting::AutoRaiseWindow, []() { - // if (MainWindow *mainWindow = - // MainWindow::sharedInstance()) { - // mainWindow->raise(); - // mainWindow->activateWindow(); - // } - // }); - - emit deviceAdded(device); - emit deviceChange(); - return; - } - emit devicePaired(device); - emit deviceChange(); - m_pendingDevices.removeAll(udid); } catch (const std::exception &e) { qDebug() << "Exception in onDeviceAdded: " << e.what(); } diff --git a/src/appdownloadbasedialog.cpp b/src/appdownloadbasedialog.cpp index 04d508b..2f60bdc 100644 --- a/src/appdownloadbasedialog.cpp +++ b/src/appdownloadbasedialog.cpp @@ -72,7 +72,8 @@ AppDownloadBaseDialog::AppDownloadBaseDialog(const QString &appName, void AppDownloadBaseDialog::startDownloadProcess(const QString &bundleId, const QString &outputDir, int index, - bool promptToOpenDir) + bool promptToOpenDir, + bool close) { if (bundleId.isEmpty()) { @@ -86,12 +87,12 @@ void AppDownloadBaseDialog::startDownloadProcess(const QString &bundleId, m_actionButton->setEnabled(false); m_operationInProgress = true; - tryToDownload(bundleId, outputDir, promptToOpenDir); + tryToDownload(bundleId, outputDir, promptToOpenDir, close); } void AppDownloadBaseDialog::tryToDownload(const QString &bundleId, const QString &outputDir, - bool promptToOpenDir) + bool promptToOpenDir, bool close) { AppStoreManager *manager = AppStoreManager::sharedInstance(); if (!manager) { @@ -117,7 +118,7 @@ void AppDownloadBaseDialog::tryToDownload(const QString &bundleId, manager->downloadApp( bundleId, outputDir, "", acquireLicense, - [safeThis, promptToOpenDir, outputDir](int result) { + [safeThis, promptToOpenDir, outputDir, close](int result) { if (!safeThis) { return; } @@ -146,7 +147,8 @@ void AppDownloadBaseDialog::tryToDownload(const QString &bundleId, } } } - safeThis->accept(); + if (close) + safeThis->accept(); } else { // Failure // 3 attempts if (safeThis->m_tries < 3) { @@ -166,7 +168,8 @@ void AppDownloadBaseDialog::tryToDownload(const QString &bundleId, "in. Error code: %2") .arg(safeThis->m_appName) .arg(result)); - safeThis->reject(); + if (close) + safeThis->reject(); } }, progressCallback); diff --git a/src/appdownloadbasedialog.h b/src/appdownloadbasedialog.h index 79bbe2e..2810f1b 100644 --- a/src/appdownloadbasedialog.h +++ b/src/appdownloadbasedialog.h @@ -44,13 +44,13 @@ protected: void reject() override; void startDownloadProcess(const QString &bundleId, const QString &workingDir, int index, - bool promptToOpenDir = true); + bool promptToOpenDir = true, bool close = false); void checkDownloadProgress(const QString &logFilePath, const QString &appName, const QString &outputDir); void addProgressBar(int index); void tryToDownload(const QString &bundleId, const QString &outputDir, - bool promptToOpenDir); + bool promptToOpenDir, bool close = false); QProgressBar *m_progressBar; QTimer *m_progressTimer; QProcess *m_downloadProcess; diff --git a/src/appinstalldialog.cpp b/src/appinstalldialog.cpp index 0493cf6..97afa78 100644 --- a/src/appinstalldialog.cpp +++ b/src/appinstalldialog.cpp @@ -21,6 +21,7 @@ #include "appcontext.h" #include "appdownloadbasedialog.h" #include "iDescriptor.h" +#include "servicemanager.h" #include #include #include @@ -151,46 +152,59 @@ void AppInstallDialog::updateDeviceList() } void AppInstallDialog::performInstallation(const QString &ipaPath, + const QString &ipaName, const QString &deviceUdid) { m_statusLabel->setText("Installing app..."); // Setup install watcher - m_installWatcher = new QFutureWatcher(this); - connect(m_installWatcher, &QFutureWatcher::finished, this, [this]() { - int result = m_installWatcher->result(); - m_installWatcher->deleteLater(); - m_installWatcher = nullptr; + m_installWatcher = new QFutureWatcher(this); + connect( + m_installWatcher, &QFutureWatcher::finished, this, + [this]() { + IdeviceFfiError *result = m_installWatcher->result(); + m_installWatcher->deleteLater(); + m_installWatcher = nullptr; - if (result == 0) { - m_statusLabel->setText("Installation completed successfully!"); - m_statusLabel->setStyleSheet( - "font-size: 14px; color: #34C759; padding: 5px;"); - QMessageBox::information(this, "Success", - "App installed successfully!"); - accept(); - } else { - m_statusLabel->setText("Installation failed"); - m_statusLabel->setStyleSheet( - "font-size: 14px; color: #FF3B30; padding: 5px;"); - QMessageBox::critical( - this, "Error", - QString("Installation failed with error code: %1").arg(result)); - } - }); + if (result == nullptr) { + m_statusLabel->setText("Installation completed successfully!"); + m_statusLabel->setStyleSheet( + "font-size: 14px; color: #34C759; padding: 5px;"); + QMessageBox::information(this, "Success", + "App installed successfully!"); + accept(); + } else { + m_statusLabel->setText("Installation failed"); + m_statusLabel->setStyleSheet( + "font-size: 14px; color: #FF3B30; padding: 5px;"); + QMessageBox::critical( + this, "Error", + QString( + "Installation failed with message %1 and error code %2") + .arg(QString::fromUtf8(result->message)) + .arg(result->code)); + idevice_error_free(result); + reject(); + } + }); // Run installation in background thread - QFuture future = QtConcurrent::run([ipaPath, deviceUdid]() -> int { - iDescriptorDevice *device = - AppContext::sharedInstance()->getDevice(deviceUdid.toStdString()); - if (!device) { - return -1; - } + QFuture future = QtConcurrent::run( + [ipaPath, ipaName, deviceUdid]() -> IdeviceFfiError * { + iDescriptorDevice *device = AppContext::sharedInstance()->getDevice( + deviceUdid.toStdString()); + if (!device) { + return nullptr; + } - instproxy_error_t ret = install_IPA(device->device, device->afcClient, - ipaPath.toStdString().c_str()); - return static_cast(ret); - }); + IdeviceFfiError *err = ServiceManager::install_IPA( + device, ipaPath.toStdString().c_str(), + ipaName.toStdString().c_str()); + if (err != nullptr) { + return err; + } + return nullptr; + }); m_installWatcher->setFuture(future); } @@ -229,43 +243,44 @@ void AppInstallDialog::onInstallClicked() return; } - startDownloadProcess(m_bundleId, m_tempDir->path(), buttonIndex, false); - connect(this, &AppDownloadBaseDialog::downloadFinished, this, - [this, selectedDevice](bool success) { - if (success) { - qDebug() << "Download finished, starting installation..."; - /* - FIXME: libipatool generates random id and appends that - to the downloaded IPA filename, so we need to search for - it. - */ - // Find the actual downloaded IPA file - QDir outDir(m_tempDir->path()); - QStringList filters; - filters << m_bundleId + "*.ipa"; - QStringList matches = - outDir.entryList(filters, QDir::Files, QDir::Time); - if (matches.isEmpty()) { - m_statusLabel->setText( - "Download failed - IPA not found"); - m_statusLabel->setStyleSheet( - "font-size: 14px; color: #FF3B30; padding: 5px;"); - QMessageBox::critical( - this, "Error", - QString("Downloaded IPA not found in %1") - .arg(outDir.absolutePath())); - return; - } - - QString ipaFile = outDir.filePath(matches.first()); - performInstallation(ipaFile, selectedDevice); - - } else { - m_statusLabel->setText("Download failed"); + startDownloadProcess(m_bundleId, m_tempDir->path(), buttonIndex, false, + false); + connect( + this, &AppDownloadBaseDialog::downloadFinished, this, + [this, selectedDevice](bool success) { + if (success) { + qDebug() << "Download finished, starting installation..."; + /* + FIXME: libipatool generates random id and appends that + to the downloaded IPA filename, so we need to search for + it. + */ + // Find the actual downloaded IPA file + QDir outDir(m_tempDir->path()); + QStringList filters; + filters << m_bundleId + "*.ipa"; + QStringList matches = + outDir.entryList(filters, QDir::Files, QDir::Time); + if (matches.isEmpty()) { + m_statusLabel->setText("Download failed - IPA not found"); m_statusLabel->setStyleSheet( "font-size: 14px; color: #FF3B30; padding: 5px;"); + QMessageBox::critical( + this, "Error", + QString("Downloaded IPA not found in %1") + .arg(outDir.absolutePath())); + return; } - }); + qDebug() << "Found downloaded IPA:" << matches.first(); + QString ipaFile = outDir.filePath(matches.first()); + performInstallation(ipaFile, matches.first(), selectedDevice); + + } else { + m_statusLabel->setText("Download failed"); + m_statusLabel->setStyleSheet( + "font-size: 14px; color: #FF3B30; padding: 5px;"); + } + }); } void AppInstallDialog::reject() diff --git a/src/appinstalldialog.h b/src/appinstalldialog.h index 5d38fd7..b91ab5d 100644 --- a/src/appinstalldialog.h +++ b/src/appinstalldialog.h @@ -25,8 +25,9 @@ #include #include #include -#include #include +#include +#include "iDescriptor.h" class AppInstallDialog : public AppDownloadBaseDialog { @@ -48,11 +49,12 @@ private: QComboBox *m_deviceCombo; QString m_bundleId; QLabel *m_statusLabel; - QFutureWatcher *m_installWatcher; + QFutureWatcher *m_installWatcher; QTemporaryDir *m_tempDir = nullptr; QNetworkAccessManager *m_manager = nullptr; void updateDeviceList(); - void performInstallation(const QString &ipaPath, const QString &deviceUdid); + void performInstallation(const QString &ipaPath, const QString &ipaName, + const QString &deviceUdid); }; #endif // APPINSTALLDIALOG_H diff --git a/src/cableinfowidget.cpp b/src/cableinfowidget.cpp index 7b93109..0c4a336 100644 --- a/src/cableinfowidget.cpp +++ b/src/cableinfowidget.cpp @@ -19,6 +19,7 @@ #include "cableinfowidget.h" #include "appcontext.h" +#include "servicemanager.h" #include #include #include @@ -74,7 +75,7 @@ void CableInfoWidget::setupUI() void CableInfoWidget::initCableInfo() { - if (!m_device || !m_device->device) { + if (!m_device) { m_statusLabel->setText("Something went wrong (no device ?)"); m_statusLabel->setStyleSheet( "QLabel { color: #dc3545; font-size: 18px; font-weight: bold; }"); @@ -82,7 +83,7 @@ void CableInfoWidget::initCableInfo() } m_statusLabel->setText("Analyzing cable..."); - get_cable_info(m_device->device, m_response); + ServiceManager::getCableInfo(m_device, m_response); analyzeCableInfo(); updateUI(); @@ -97,9 +98,8 @@ void CableInfoWidget::analyzeCableInfo() if (!m_response) { return; } - - PlistNavigator nav(m_response); - PlistNavigator ioreg = nav["IORegistry"]; + plist_print(m_response); + PlistNavigator ioreg(m_response); if (!ioreg.valid()) { return; diff --git a/src/cableinfowidget.h b/src/cableinfowidget.h index bd28801..f9aee59 100644 --- a/src/cableinfowidget.h +++ b/src/cableinfowidget.h @@ -29,7 +29,6 @@ #include #include #include -#include class CableInfoWidget : public QWidget { diff --git a/src/core/helpers/mounted_image_info_free.cpp b/src/core/helpers/mounted_image_info_free.cpp new file mode 100644 index 0000000..06cb0b6 --- /dev/null +++ b/src/core/helpers/mounted_image_info_free.cpp @@ -0,0 +1,11 @@ +#include "../../iDescriptor.h" + +void mounted_image_info_free(MountedImageInfo &info) +{ + if (info.err) { + idevice_error_free(info.err); + } + if (info.signature) { + idevice_data_free(info.signature, info.signature_len); + } +} \ No newline at end of file diff --git a/src/core/services/afc2_client_new.cpp b/src/core/services/afc2_client_new.cpp deleted file mode 100644 index 06ee21f..0000000 --- a/src/core/services/afc2_client_new.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * iDescriptor: A free and open-source idevice management tool. - * - * Copyright (C) 2025 Uncore - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include "../../iDescriptor.h" -#include -#include -#include -#include - -afc_error_t afc2_client_new(idevice_t device, afc_client_t *afc) -{ - - lockdownd_service_descriptor_t service = NULL; - // TODO: should free service ? - lockdownd_client_t client = NULL; - - if (lockdownd_client_new_with_handshake(device, &client, APP_LABEL) != - LOCKDOWN_E_SUCCESS) { - qDebug() << "Could not connect to lockdownd"; - return AFC_E_UNKNOWN_ERROR; - } - if (lockdownd_start_service(client, AFC2_SERVICE_NAME, &service) != - LOCKDOWN_E_SUCCESS) { - qDebug() << "Could not start AFC service"; - lockdownd_client_free(client); - return AFC_E_UNKNOWN_ERROR; - } - - return afc_client_new(device, service, afc); - - // char **dirs = NULL; - // if (afc_read_directory(afc, argv[1], &dirs) == AFC_E_SUCCESS) { - // for (int i = 0; dirs[i]; i++) { - // printf("Entry: %s\n", dirs[i]); - // } - // // free(dirs); - // } -} \ No newline at end of file diff --git a/src/core/services/detect-afc2.cpp b/src/core/services/detect-afc2.cpp index 79e832d..09c1b7b 100644 --- a/src/core/services/detect-afc2.cpp +++ b/src/core/services/detect-afc2.cpp @@ -17,4 +17,4 @@ * along with this program. If not, see . */ -// Detect AFC2 (Apple File Conduit 2) support \ No newline at end of file +// TODO:Detect AFC2 (Apple File Conduit 2) support \ No newline at end of file diff --git a/src/core/services/detect_has_been_jailbroken_before.cpp b/src/core/services/detect_has_been_jailbroken_before.cpp index 02d4314..9d7ff4a 100644 --- a/src/core/services/detect_has_been_jailbroken_before.cpp +++ b/src/core/services/detect_has_been_jailbroken_before.cpp @@ -18,7 +18,6 @@ */ #include "../../iDescriptor.h" -#include #include #include @@ -27,25 +26,29 @@ struct JailbreakDetectionResult { std::vector found_folders; }; -JailbreakDetectionResult detect_has_jailbroken_before(afc_client_t afc) +// char *possible_jailbreak_paths[] = { +// "/Applications/Cydia.app", +// "/Library/MobileSubstrate/MobileSubstrate.dylib", +// "/bin/bash", +// "/usr/sbin/sshd", +// "/etc/apt", +// NULL +// }; + +JailbreakDetectionResult detect_has_jailbroken_before(AfcClientHandle *afc) { - std::vector jailbreak_folders = {".installed_palera1n", - ".procursus_strapped"}; + // std::vector jailbreak_folders = {".installed_palera1n", + // ".procursus_strapped"}; JailbreakDetectionResult result = {false, {}}; - char **dirs = NULL; - if (afc_read_directory(afc, POSSIBLE_ROOT, &dirs) == AFC_E_SUCCESS) { - for (char **dir = dirs; *dir != nullptr; ++dir) { - std::string dirname = *dir; - for (const auto &jb_folder : jailbreak_folders) { - if (dirname == jb_folder) { - result.found_folders.push_back(jb_folder); - result.is_jailbroken = true; - } - } - } - } - afc_dictionary_free(dirs); + // char **dirs = NULL; + // size_t count = 0; + // if (!afc_list_directory(afc, (std::string(POSSIBLE_ROOT) + + // "bin").c_str(), + // &dirs, &count)) { + // free(dirs); + // } + // afc_dictionary_free(dirs); return result; } \ No newline at end of file diff --git a/src/core/services/detect_jailbroken.cpp b/src/core/services/detect_jailbroken.cpp index 10c9347..788e219 100644 --- a/src/core/services/detect_jailbroken.cpp +++ b/src/core/services/detect_jailbroken.cpp @@ -19,25 +19,16 @@ #include "../../iDescriptor.h" -// char *possible_jailbreak_paths[] = { -// "/Applications/Cydia.app", -// "/Library/MobileSubstrate/MobileSubstrate.dylib", -// "/bin/bash", -// "/usr/sbin/sshd", -// "/etc/apt", -// NULL -// }; #include bool detect_jailbroken(AfcClientHandle *afc) { char **dirs = NULL; size_t count = 0; - bool res = false; if (!afc_list_directory(afc, (std::string(POSSIBLE_ROOT) + "bin").c_str(), &dirs, &count)) { - free(dirs); + free_directory_listing(dirs, count); } - return res > 0; + return count > 0; } \ No newline at end of file diff --git a/src/core/services/get_battery_info.cpp b/src/core/services/get_battery_info.cpp index 18ab2e5..2afb964 100644 --- a/src/core/services/get_battery_info.cpp +++ b/src/core/services/get_battery_info.cpp @@ -23,27 +23,10 @@ #include // FIXME: return bool -void get_battery_info(IdeviceProviderHandle *provider, plist_t &diagnostics) +void get_battery_info(DiagnosticsRelay *diag_client, plist_t &diagnostics) { - // 1. Connect to the diagnostics_relay service using the raw C function. - DiagnosticsRelayClientHandle *client_handle = nullptr; - IdeviceFfiError *err = - ::diagnostics_relay_client_connect(provider, &client_handle); - - if (err) { - qDebug() << "Failed to create diagnostics relay client:" - << err->message; - idevice_error_free(err); - return; - } - - // 2. Adopt the raw handle into the C++ RAII wrapper. - // The client will now be automatically freed when it goes out of scope. - auto diagnostics_client = - IdeviceFFI::DiagnosticsRelay::adopt(client_handle); - - // 3. Query IORegistry for battery info. - auto ioreg_result = diagnostics_client.ioregistry( + qDebug() << "Fetching battery info via DiagnosticsRelay"; + auto ioreg_result = diag_client->ioregistry( IdeviceFFI::None, // current_plane IdeviceFFI::None, // entry_name IdeviceFFI::Some(std::string("IOPMPowerSource")) // entry_class @@ -55,10 +38,8 @@ void get_battery_info(IdeviceProviderHandle *provider, plist_t &diagnostics) return; } - // 4. Unwrap the result and handle the optional plist. auto plist_opt = std::move(ioreg_result).unwrap(); if (plist_opt.is_some()) { - // The caller of get_battery_info is responsible for freeing this plist. diagnostics = std::move(plist_opt).unwrap(); } } \ No newline at end of file diff --git a/src/core/services/get_cable_info.cpp b/src/core/services/get_cable_info.cpp index 68532ef..61dc781 100644 --- a/src/core/services/get_cable_info.cpp +++ b/src/core/services/get_cable_info.cpp @@ -18,51 +18,24 @@ */ #include "../../iDescriptor.h" -#include -#include -#include -void get_cable_info(idevice_t device, plist_t &response) +void _get_cable_info(const iDescriptorDevice *device, plist_t &response) { - lockdownd_client_t lockdown_client = NULL; - diagnostics_relay_client_t diagnostics_client = NULL; - lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; - lockdownd_service_descriptor_t service = NULL; - int use_network = 0; - if (LOCKDOWN_E_SUCCESS != (ret = lockdownd_client_new_with_handshake( - device, &lockdown_client, TOOL_NAME))) { - idevice_free(device); - printf("ERROR: Could not connect to lockdownd, error code %d\n", ret); + auto ioreg_result = device->diagRelay->ioregistry( + IdeviceFFI::None, // current_plane + IdeviceFFI::None, // entry_name + IdeviceFFI::Some(std::string("AppleTriStarBuiltIn")) // entry_class + ); + + if (!ioreg_result.is_ok()) { + qDebug() << "Failed to query IORegistry:" + << ioreg_result.unwrap_err().message.c_str(); + return; } - /* attempt to use newer diagnostics service available on iOS 5 and later */ - ret = lockdownd_start_service( - lockdown_client, "com.apple.mobile.diagnostics_relay", &service); - if (ret == LOCKDOWN_E_INVALID_SERVICE) { - /* attempt to use older diagnostics service */ - ret = lockdownd_start_service( - lockdown_client, "com.apple.iosdiagnostics.relay", &service); + auto plist_opt = std::move(ioreg_result).unwrap(); + if (plist_opt.is_some()) { + response = std::move(plist_opt).unwrap(); } - lockdownd_client_free(lockdown_client); - - if (ret != LOCKDOWN_E_SUCCESS) { - idevice_free(device); - printf("ERROR: Could not start diagnostics relay service: %s\n", - lockdownd_strerror(ret)); - } - - if ((ret == LOCKDOWN_E_SUCCESS) && service && (service->port > 0)) { - if (diagnostics_relay_client_new(device, service, - &diagnostics_client) != - DIAGNOSTICS_RELAY_E_SUCCESS) { - printf("ERROR: Could not connect to diagnostics_relay!\n"); - } - } - - diagnostics_relay_error_t err = diagnostics_relay_query_ioregistry_entry( - diagnostics_client, NULL, "AppleTriStarBuiltIn", &response); - - if (diagnostics_client) - diagnostics_relay_client_free(diagnostics_client); } \ No newline at end of file diff --git a/src/core/services/get_file_tree.cpp b/src/core/services/get_file_tree.cpp index e53e5bc..1bf3021 100644 --- a/src/core/services/get_file_tree.cpp +++ b/src/core/services/get_file_tree.cpp @@ -74,7 +74,8 @@ AFCFileTree get_file_tree(const iDescriptorDevice *device, bool checkDir, if (info_err) { qDebug() << "Failed to get file info for:" << fullPath.c_str() - << "Error:" << info_err->message; + << "Error:" << info_err->message + << "Code:" << info_err->code; } bool isDir = false; @@ -86,19 +87,15 @@ AFCFileTree get_file_tree(const iDescriptorDevice *device, bool checkDir, } else if (strcmp(info.st_ifmt, "S_IFLNK") == 0) { // Check if symlink points to a directory char **dir_contents = nullptr; + // FIXME: recursively call safeAfcGetFileInfo to figure out if + // it's a dir IdeviceFfiError *link_err = ServiceManager::safeAfcReadDirectory( device, fullPath.c_str(), &dir_contents); if (!link_err) { isDir = true; - // if (dir_contents) { - // // FIXME: is this ok ? - // for (int j = 0; dir_contents[j]; j++) { - // free(dir_contents[j]); - // } - // free(dir_contents); - // } + free_directory_listing(dir_contents, count); } if (link_err) { @@ -106,8 +103,7 @@ AFCFileTree get_file_tree(const iDescriptorDevice *device, bool checkDir, } } - // Free file info - // afc_file_info_free(&info); + afc_file_info_free(&info); } if (info_err) { @@ -119,10 +115,7 @@ AFCFileTree get_file_tree(const iDescriptorDevice *device, bool checkDir, // Free the directory list if (dirs) { - // for (int i = 0; dirs[i]; i++) { - // free(dirs[i]); - // } - // free(dirs); + free_directory_listing(dirs, count); } result.success = true; diff --git a/src/core/services/get_mounted_image.cpp b/src/core/services/get_mounted_image.cpp index a37221f..ff52f42 100644 --- a/src/core/services/get_mounted_image.cpp +++ b/src/core/services/get_mounted_image.cpp @@ -18,101 +18,30 @@ */ #include "../../iDescriptor.h" -#include -#define _GNU_SOURCE 1 -#define __USE_GNU 1 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef _WIN32 -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef _WIN32 -#include -#endif - -plist_t _get_mounted_image(const char *udid) +MountedImageInfo _get_mounted_image(const iDescriptorDevice *device) { - mobile_image_mounter_client_t mim = NULL; - lockdownd_client_t lckd = NULL; - lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR; - afc_client_t afc = NULL; - lockdownd_service_descriptor_t service = NULL; - idevice_t device = NULL; - - mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; - plist_t result = NULL; - size_t sig_length = 0; - const char *imagetype = "Developer"; - - if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, - - IDEVICE_LOOKUP_USBMUX)) { - qDebug() << "ERROR: Could not create idevice!"; + uint8_t *signature = NULL; + size_t signature_len = 0; + IdeviceFfiError *err = nullptr; + qDebug() << "_get_mounted_image"; + if (err) { + qDebug() << "Failed to connect to image mounter:" << err->message; goto leave; } - if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake( - device, &lckd, TOOL_NAME))) { - qDebug() << "ERROR: Could not connect to lockdownd service!"; - goto leave; + err = image_mounter_lookup_image(device->imageMounter, + DISK_IMAGE_TYPE_DEVELOPER, &signature, + &signature_len); + if (err) { + qDebug() << "Failed to lookup image:" << err->message + << "Code:" << err->code; } - lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", - &service); - - if (!service || service->port == 0) { - printf("ERROR: Could not start mobile_image_mounter service!\n"); - goto leave; - } - - if (mobile_image_mounter_new(device, service, &mim) != - MOBILE_IMAGE_MOUNTER_E_SUCCESS) { - printf("ERROR: Could not connect to mobile_image_mounter!\n"); - goto leave; - } - - if (!service || service->port == 0) { - qDebug() << "ERROR: Could not start mobile_image_mounter service!"; - goto leave; - } - - // will sometimes return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the device - // is locked - mostly on older devices - err = mobile_image_mounter_lookup_image(mim, imagetype, &result); - leave: - if (mim) { - mobile_image_mounter_free(mim); - } - if (afc) { - afc_client_free(afc); - } - if (lckd) { - lockdownd_client_free(lckd); - } - if (device) { - idevice_free(device); - } - - return result; -} - -// int main(){return 0;} \ No newline at end of file + return { + .err = err, + .signature = signature, + .signature_len = signature_len, + }; +} \ No newline at end of file diff --git a/src/core/services/init_device.cpp b/src/core/services/init_device.cpp index 49fdd65..bfc94d7 100644 --- a/src/core/services/init_device.cpp +++ b/src/core/services/init_device.cpp @@ -156,7 +156,7 @@ void parseDeviceBattery(PlistNavigator &ioreg, DeviceInfo &d) DeviceInfo fullDeviceInfo(const pugi::xml_document &doc, AfcClientHandle *afcClient, - IdeviceProviderHandle *provider, + DiagnosticsRelay *diagRelay, iDescriptorInitDeviceResult &result) { pugi::xml_node dict = doc.child("plist").child("dict"); @@ -205,11 +205,12 @@ DeviceInfo fullDeviceInfo(const pugi::xml_document &doc, QString q_version = QString::fromStdString(d.productVersion); QStringList parts = q_version.split('.'); - int major = (parts.length() > 0) ? parts[0].toInt() : 0; - int minor = (parts.length() > 1) ? parts[1].toInt() : 0; - int patch = (parts.length() > 2) ? parts[2].toInt() : 0; + unsigned int major = (parts.length() > 0) ? parts[0].toInt() : 0; + unsigned int minor = (parts.length() > 1) ? parts[1].toInt() : 0; + unsigned int patch = (parts.length() > 2) ? parts[2].toInt() : 0; - d.parsedDeviceVersion = IDEVICE_DEVICE_VERSION(major, minor, patch); + d.parsedDeviceVersion = + DeviceVersion{.major = major, .minor = minor, .patch = patch}; /*DiskInfo*/ try { @@ -241,7 +242,9 @@ DeviceInfo fullDeviceInfo(const pugi::xml_document &doc, // "FSTotalBytes: 63966400512" // "FSFreeBytes: 2867101696" // "FSBlockSize: 4096" + // FIXME: it's too slow on older devices? AfcDeviceInfo *info = new AfcDeviceInfo(); + qDebug() << "afc_get_device_info..."; IdeviceFfiError *err = afc_get_device_info(afcClient, info); if (err) { qDebug() << "AFC get device info error code: " << err->message; @@ -302,7 +305,7 @@ DeviceInfo fullDeviceInfo(const pugi::xml_document &doc, /*BatteryInfo*/ plist_t diagnostics = nullptr; - get_battery_info(provider, diagnostics); + get_battery_info(diagRelay, diagnostics); if (!diagnostics) { qDebug() << "Failed to get diagnostics plist."; @@ -365,6 +368,7 @@ DeviceInfo fullDeviceInfo(const pugi::xml_document &doc, // FIXME:spawn on a new thread? // wireless connections sometimes take more than 10sec to connect // and ofc it freezes the ui +// TODO:idevice_start_session ? iDescriptorInitDeviceResult init_idescriptor_device(const QString &udid, WirelessInitArgs wirelessArgs) { @@ -388,9 +392,13 @@ init_idescriptor_device(const QString &udid, WirelessInitArgs wirelessArgs) IdeviceHandle *deviceHandle = nullptr; HeartbeatClientHandle *heartbeat = nullptr; HeartBeatThread *heartbeatThread = nullptr; - + ImageMounterHandle *image_mounter = nullptr; + DiagnosticsRelayClientHandle *diagnostics_relay = nullptr; + ScreenshotrClientHandle *screenshotr_client = nullptr; + LocationSimulationHandle *location_simulation = nullptr; // FIXME: remove debug std::stringstream ss; + plist_t val = nullptr; // 1. Connect to usbmuxd IdeviceFfiError *err = @@ -428,8 +436,18 @@ init_idescriptor_device(const QString &udid, WirelessInitArgs wirelessArgs) qDebug() << "Failed to create wireless provider"; goto cleanup; } - // err = heartbeat_new(); - // heartbeat_connect + err = heartbeat_connect(provider, &heartbeat); + if (err) { + qDebug() << "Failed to start Heartbeat service"; + goto cleanup; + } + heartbeatThread = new HeartBeatThread(heartbeat); + heartbeatThread->start(); + + while (!heartbeatThread->initialCompleted()) { + sleep(1); + } + } else { UsbmuxdDeviceHandle **devices; @@ -458,17 +476,11 @@ init_idescriptor_device(const QString &udid, WirelessInitArgs wirelessArgs) goto cleanup; } - // 4. Connect to lockdown (actual function name) err = lockdownd_connect(provider, &lockdown); if (err) { qDebug() << "Failed to connect to lockdown"; goto cleanup; } - // err = idevice_new(socket, "iDescriptor", &deviceHandle); - // if (err) { - // qDebug() << "Failed to create idevice handle"; - // goto cleanup; - // } err = idevice_provider_get_pairing_file(provider, &pairing_file); if (err) { @@ -482,71 +494,79 @@ init_idescriptor_device(const QString &udid, WirelessInitArgs wirelessArgs) goto cleanup; } - uint16_t heartbeat_port; - bool heartbeat_ssl; - // if (isWireless) { - // err = lockdownd_start_service(lockdown, "com.apple.heartbeat", - // &heartbeat_port, &heartbeat_ssl); - - // Start heartbeat client to keep connection alive - err = heartbeat_connect(provider, &heartbeat); - if (err) { - qDebug() << "Failed to start Heartbeat service"; - goto cleanup; - } - heartbeatThread = new HeartBeatThread(heartbeat); - heartbeatThread->start(); - - // while (!heartbeatThread->initialCompleted()) { - // sleep(1); - // } - if (err) { qDebug() << "Failed to connect to Heartbeat client"; goto cleanup; } - qDebug() << "Heartbeat client created successfully"; - // } - - // 5. Start AFC service - uint16_t afc_port; - bool afc_ssl; - err = - lockdownd_start_service(lockdown, "com.apple.afc", &afc_port, &afc_ssl); - if (err) { - qDebug() << "Failed to start AFC service"; - goto cleanup; - } - - // 6. Create AFC client from provider err = afc_client_connect(provider, &afc_client); if (err) { qDebug() << "Failed to create AFC client"; goto cleanup; } - // // 7. AFC2 is optional - // uint16_t afc2_port; - // bool afc2_ssl; - // err = lockdownd_start_service(lockdown, "com.apple.afc2", &afc2_port, - // &afc2_ssl); - // if (!err) { - // err = afc_client_connect(provider, &afc2_client); + err = image_mounter_connect(provider, &image_mounter); + if (err) { + qDebug() << "Failed to create Image Mounter client"; + goto cleanup; + } + + err = diagnostics_relay_client_connect(provider, &diagnostics_relay); + + if (err) { + qDebug() << "Failed to create Diagnostics Relay client"; + goto cleanup; + } + + // err = screenshotr_connect(provider, &screenshotr_client); + + // if (err) { + // qDebug() << "Failed to create Screenshotr client"; + // goto cleanup; // } + err = afc2_client_connect(provider, &afc2_client); + if (err) { + qDebug() << "Failed to create AFC2 client"; + // dont cleanup here, afc2 is optional + } + + // FIXME: will probably not work on iOS 17 and above + // requires dev image disk + // err = location_simulation_connect(provider, &location_simulation); + // if (err) { + // qDebug() << "Failed to create Location Simulation client"; + // goto cleanup; + // } get_device_info_xml(udid.toUtf8().constData(), lockdown, infoXml); // infoXml.print(ss, " "); // " " for indentation // qDebug().noquote() << "--- Full Device Info XML ---" // << QString::fromStdString(ss.str()); + // Received plist: { + // Domain: "com.apple.mobile.wireless_lockdown", + // Key: "EnableWifiConnections", + // Request: "GetValue", + // Value: true + // } + lockdownd_get_value(lockdown, "EnableWifiConnections", + "com.apple.mobile.wireless_lockdown", &val); + if (val) + plist_print(val); + result.provider = provider; result.success = true; result.afcClient = afc_client; result.afc2Client = afc2_client; result.lockdown = lockdown; + result.imageMounter = image_mounter; + result.screenshotrClient = screenshotr_client; + result.diagRelay = std::make_shared( + DiagnosticsRelay::adopt(diagnostics_relay)); + result.locationSimulation = location_simulation; AppContext::sharedInstance()->cachePairingFile(udid, pairing_file); - fullDeviceInfo(infoXml, afc_client, provider, result); + result.deviceInfo.isWireless = isWireless; + fullDeviceInfo(infoXml, afc_client, result.diagRelay.get(), result); cleanup: // Cleanup on error diff --git a/src/core/services/install_ipa.cpp b/src/core/services/install_ipa.cpp index 01a2765..761b416 100644 --- a/src/core/services/install_ipa.cpp +++ b/src/core/services/install_ipa.cpp @@ -17,538 +17,95 @@ * along with this program. If not, see . */ -#ifdef HAVE_CONFIG_H -#include -#endif -#include -#define _GNU_SOURCE 1 -#define __USE_GNU 1 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "../../iDescriptor.h" -#ifdef HAVE_UNISTD_H -#include -#endif -#ifndef WIN32 -#include -#endif - -#include -#include -#include -#include -#include - - -#include - -#include - -#ifdef WIN32 -#include -#define wait_ms(x) Sleep(x) -#else -#define wait_ms(x) \ - { \ - struct timespec ts; \ - ts.tv_sec = 0; \ - ts.tv_nsec = x * 1000000; \ - nanosleep(&ts, NULL); \ - } -#endif - -#define ITUNES_METADATA_PLIST_FILENAME "iTunesMetadata.plist" - -const char PKG_PATH[] = "PublicStaging"; - -struct install_status_data { - int command_completed; - int err_occurred; - char *last_status; -}; - -static void status_cb(plist_t command, plist_t status, void *user_data) +IdeviceFfiError *_install_IPA(const iDescriptorDevice *device, + const char *filePath, const char *filename) { - struct install_status_data *isd = (struct install_status_data *)user_data; - if (command && status) { - char *command_name = NULL; - instproxy_command_get_name(command, &command_name); + IdeviceFfiError *err = nullptr; + InstallationProxyClientHandle *instproxy_client = NULL; + AfcFileHandle *file = NULL; + std::string dest_path_str = "/PublicStaging/" + std::string(filename); + AfcFileInfo info = {}; + uint8_t *data = NULL; + size_t length = 0; - /* get status */ - char *status_name = NULL; - instproxy_status_get_name(status, &status_name); + qDebug() << "Uploading " << filePath << " to " << dest_path_str.c_str() + << "..."; - if (status_name) { - if (!strcmp(status_name, "Complete")) { - isd->command_completed = 1; - } + err = afc_get_file_info(device->afcClient, "/PublicStaging", &info); + // -33 /* No such file or directory */ + if (err != NULL && err->code == -33) { + qDebug() << "/PublicStaging does not exist, creating..."; + err = afc_make_directory(device->afcClient, "/PublicStaging"); + if (err != NULL) { + qDebug() << "Failed to create /PublicStaging: [" << err->code + << "] " << err->message; + goto cleanup; } - - /* get error if any */ - char *error_name = NULL; - char *error_description = NULL; - uint64_t error_code = 0; - instproxy_status_get_error(status, &error_name, &error_description, - &error_code); - - /* output/handling */ - if (!error_name) { - if (status_name) { - /* get progress if any */ - int percent = -1; - instproxy_status_get_percent_complete(status, &percent); - - if (isd->last_status && - (strcmp(isd->last_status, status_name))) { - printf("\n"); - } - - if (percent >= 0) { - printf("\r%s: %s (%d%%)", command_name, status_name, - percent); - } else { - printf("\r%s: %s", command_name, status_name); - } - if (isd->command_completed) { - printf("\n"); - } - } - } else { - /* report error to the user */ - if (error_description) - fprintf(stderr, - "ERROR: %s failed. Got error \"%s\" with code " - "0x%08" PRIx64 ": %s\n", - command_name, error_name, error_code, - error_description ? error_description : "N/A"); - else - fprintf(stderr, "ERROR: %s failed. Got error \"%s\".\n", - command_name, error_name); - isd->err_occurred = 1; - } - - /* clean up */ - free(error_name); - free(error_description); - - free(isd->last_status); - isd->last_status = status_name; - - free(command_name); - command_name = NULL; - } else { - fprintf(stderr, "ERROR: %s was called with invalid arguments!\n", - __func__); - } -} - -static int zip_get_contents(struct zip *zf, const char *filename, - int locate_flags, char **buffer, uint32_t *len) -{ - struct zip_stat zs; - struct zip_file *zfile; - int zindex = zip_name_locate(zf, filename, locate_flags); - - *buffer = NULL; - *len = 0; - - if (zindex < 0) { - return -1; + qDebug() << "/PublicStaging created successfully"; + } else if (err != NULL) { + qDebug() << "Failed to get info for /PublicStaging: [" << err->code + << "] " << err->message; + goto cleanup; } - zip_stat_init(&zs); - - if (zip_stat_index(zf, zindex, 0, &zs) != 0) { - fprintf(stderr, "ERROR: zip_stat_index '%s' failed!\n", filename); - return -2; + if (!read_file(filePath, &data, &length)) { + err = new IdeviceFfiError{-1, "Failed to read IPA file"}; + goto cleanup; } - if (zs.size > 10485760) { - fprintf(stderr, "ERROR: file '%s' is too large!\n", filename); - return -3; + // todo should we use from service manager safe afc functions ? + err = afc_file_open(device->afcClient, dest_path_str.c_str(), AfcWrOnly, + &file); + if (err != NULL) { + qDebug() << "Failed to open destination file: [" << err->code << "] " + << err->message; + goto cleanup; } - zfile = zip_fopen_index(zf, zindex, 0); - if (!zfile) { - fprintf(stderr, "ERROR: zip_fopen '%s' failed!\n", filename); - return -4; + err = afc_file_write(file, data, length); + + if (err != NULL) { + qDebug() << "Failed to write file: [" << err->code << "] " + << err->message; + goto cleanup; } - *buffer = (char *)malloc(zs.size); - if (zs.size > LLONG_MAX || - zip_fread(zfile, *buffer, zs.size) != (zip_int64_t)zs.size) { - fprintf(stderr, "ERROR: zip_fread %" PRIu64 " bytes from '%s'\n", - (uint64_t)zs.size, filename); - free(*buffer); - *buffer = NULL; - zip_fclose(zfile); - return -5; - } - *len = zs.size; - zip_fclose(zfile); - return 0; -} + qDebug() << "Upload completed successfully"; -static int zip_get_app_directory(struct zip *zf, char **path) -{ - zip_int64_t i = 0; - zip_int64_t c = (zip_int64_t)zip_get_num_entries(zf, 0); - int len = 0; - const char *name = NULL; - - /* look through all filenames in the archive */ - do { - /* get filename at current index */ - name = zip_get_name(zf, i++, 0); - if (name != NULL) { - /* check if we have a "Payload/.../" name */ - len = strlen(name); - if (!strncmp(name, "Payload/", 8) && (len > 8)) { - /* skip hidden files */ - if (name[8] == '.') - continue; - - /* locate the second directory delimiter */ - const char *p = name + 8; - do { - if (*p == '/') { - break; - } - } while (p++ != NULL); - - /* try next entry if not found */ - if (p == NULL) - continue; - - len = p - name + 1; - - /* make sure app directory endwith .app */ - if (len < 12 || strncmp(p - 4, ".app", 4)) { - continue; - } - - if (path != NULL) { - free(*path); - *path = NULL; - } - - /* allocate and copy filename */ - *path = (char *)malloc(len + 1); - strncpy(*path, name, len); - - /* add terminating null character */ - char *t = *path + len; - *t = '\0'; - break; - } - } - } while (i < c); - - if (*path == NULL) { - return -1; + err = installation_proxy_connect(device->provider, &instproxy_client); + if (err != NULL) { + qDebug() << "Failed to connect to installation proxy:" << err->message + << "Code:" << err->code; + goto cleanup; } - return 0; -} - -static int afc_upload_file(afc_client_t afc, const char *filename, - const char *dstfn) -{ - FILE *f = NULL; - uint64_t af = 0; - char buf[1048576]; - - f = fopen(filename, "rb"); - if (!f) { - fprintf(stderr, "fopen: %s: %s\n", filename, strerror(errno)); - return -1; - } - - if ((afc_file_open(afc, dstfn, AFC_FOPEN_WRONLY, &af) != AFC_E_SUCCESS) || - !af) { - fclose(f); - fprintf(stderr, "afc_file_open on '%s' failed!\n", dstfn); - return -1; - } - - size_t amount = 0; - do { - amount = fread(buf, 1, sizeof(buf), f); - if (amount > 0) { - uint32_t written, total = 0; - while (total < amount) { - written = 0; - afc_error_t aerr = - afc_file_write(afc, af, buf, amount, &written); - if (aerr != AFC_E_SUCCESS) { - fprintf(stderr, "AFC Write error: %d\n", aerr); - break; - } - total += written; - } - if (total != amount) { - fprintf(stderr, "Error: wrote only %u of %u\n", total, - (uint32_t)amount); - afc_file_close(afc, af); - fclose(f); - return -1; - } - } - } while (amount > 0); - - afc_file_close(afc, af); - fclose(f); - - return 0; -} - -instproxy_error_t install_IPA(idevice_t device, afc_client_t afc, - const char *filePath) -{ - lockdownd_client_t client = NULL; - instproxy_client_t ipc = NULL; - lockdownd_service_descriptor_t service = NULL; - instproxy_error_t err = INSTPROXY_E_UNKNOWN_ERROR; - char *bundleidentifier = NULL; - struct install_status_data status_data = {0, 0, NULL}; - plist_t sinf = NULL; - plist_t meta = NULL; - char *pkgname = NULL; - struct stat fst; - char **strs = NULL; - plist_t client_opts = instproxy_client_options_new(); - char *zbuf = NULL; - uint32_t len = 0; - plist_t meta_dict = NULL; - int errp = 0; - struct zip *zf = zip_open(filePath, 0, &errp); - plist_t info = NULL; - char *filename = NULL; - char *app_directory_name = NULL; - char *bundleexecutable = NULL; - plist_t bname = NULL; - char *sinfname = NULL; - - if (!device || !filePath || !afc) { - fprintf(stderr, "ERROR: Invalid arguments passed to install_IPA.\n"); - return INSTPROXY_E_INVALID_ARG; - } - - lockdownd_error_t lerr = lockdownd_client_new_with_handshake( - device, &client, "ideviceinstaller"); - if (lerr != LOCKDOWN_E_SUCCESS) { - fprintf(stderr, "Could not connect to lockdownd: %s. Exiting.\n", - lockdownd_strerror(lerr)); - return INSTPROXY_E_OP_FAILED; - } - - lerr = lockdownd_start_service( - client, "com.apple.mobile.installation_proxy", &service); - if (lerr != LOCKDOWN_E_SUCCESS) { - fprintf(stderr, - "Could not start com.apple.mobile.installation_proxy: %s\n", - lockdownd_strerror(lerr)); - lockdownd_client_free(client); - return INSTPROXY_E_OP_FAILED; - } - - err = instproxy_client_new(device, service, &ipc); - if (service) { - lockdownd_service_descriptor_free(service); - service = NULL; - } - - if (err != INSTPROXY_E_SUCCESS) { - fprintf(stderr, "Could not connect to installation_proxy!\n"); - lockdownd_client_free(client); - return err; - } - - setbuf(stdout, NULL); - - if (stat(filePath, &fst) != 0) { - fprintf(stderr, "ERROR: stat: %s: %s\n", filePath, strerror(errno)); - err = INSTPROXY_E_INVALID_ARG; - goto leave_cleanup; - } - - if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) { - if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) { - fprintf(stderr, - "WARNING: Could not create directory '%s' on device!\n", - PKG_PATH); - } - } - if (strs) { - int i = 0; - while (strs[i]) { - free(strs[i]); - i++; - } - free(strs); - } - - if (!zf) { - fprintf(stderr, "ERROR: zip_open: %s: %d\n", filePath, errp); - err = INSTPROXY_E_INVALID_ARG; - goto leave_cleanup; - } - - /* extract iTunesMetadata.plist from package */ - if (zip_get_contents(zf, ITUNES_METADATA_PLIST_FILENAME, 0, &zbuf, &len) == - 0) { - meta = plist_new_data(zbuf, len); - plist_from_memory(zbuf, len, &meta_dict, NULL); - } - if (!meta_dict) { - plist_free(meta); - meta = NULL; - fprintf(stderr, "WARNING: could not locate %s in archive!\n", - ITUNES_METADATA_PLIST_FILENAME); - } - free(zbuf); - - /* determine .app directory in archive */ - zbuf = NULL; - len = 0; - - if (zip_get_app_directory(zf, &app_directory_name)) { - fprintf(stderr, "ERROR: Unable to locate .app directory in archive. " - "Make sure it is inside a 'Payload' directory.\n"); - err = INSTPROXY_E_INVALID_ARG; - goto zip_cleanup; - } - - /* construct full filename to Info.plist */ - filename = (char *)malloc(strlen(app_directory_name) + 10 + 1); - strcpy(filename, app_directory_name); - free(app_directory_name); - app_directory_name = NULL; - strcat(filename, "Info.plist"); - - if (zip_get_contents(zf, filename, 0, &zbuf, &len) < 0) { - fprintf(stderr, "WARNING: could not locate %s in archive!\n", filename); - free(filename); - err = INSTPROXY_E_INVALID_ARG; - goto zip_cleanup; - } - free(filename); - plist_from_memory(zbuf, len, &info, NULL); - free(zbuf); - - if (!info) { - fprintf(stderr, "Could not parse Info.plist!\n"); - err = INSTPROXY_E_INVALID_ARG; - goto zip_cleanup; - } - - bname = plist_dict_get_item(info, "CFBundleExecutable"); - if (bname) { - plist_get_string_val(bname, &bundleexecutable); - } - - bname = plist_dict_get_item(info, "CFBundleIdentifier"); - if (bname) { - plist_get_string_val(bname, &bundleidentifier); - } - plist_free(info); - info = NULL; - - if (!bundleexecutable) { - fprintf(stderr, "Could not determine value for CFBundleExecutable!\n"); - err = INSTPROXY_E_INVALID_ARG; - goto zip_cleanup; - } - - if (asprintf(&sinfname, "Payload/%s.app/SC_Info/%s.sinf", bundleexecutable, - bundleexecutable) < 0) { - fprintf(stderr, "Out of memory!?\n"); - err = INSTPROXY_E_UNKNOWN_ERROR; - goto zip_cleanup; - } - free(bundleexecutable); - - /* extract .sinf from package */ - zbuf = NULL; - len = 0; - if (zip_get_contents(zf, sinfname, 0, &zbuf, &len) == 0) { - sinf = plist_new_data(zbuf, len); - } else { - fprintf(stderr, "WARNING: could not locate %s in archive!\n", sinfname); - } - free(sinfname); - free(zbuf); - - /* copy archive to device */ - pkgname = NULL; - if (asprintf(&pkgname, "%s/%s", PKG_PATH, bundleidentifier) < 0) { - fprintf(stderr, "Out of memory!?\n"); - err = INSTPROXY_E_UNKNOWN_ERROR; - goto zip_cleanup; - } - - printf("Copying '%s' to device... ", filePath); - - if (afc_upload_file(afc, filePath, pkgname) < 0) { - printf("FAILED\n"); - free(pkgname); - err = INSTPROXY_E_OP_FAILED; - goto zip_cleanup; - } - - printf("DONE.\n"); - - if (bundleidentifier) { - instproxy_client_options_add(client_opts, "CFBundleIdentifier", - bundleidentifier, NULL); - } - if (sinf) { - instproxy_client_options_add(client_opts, "ApplicationSINF", sinf, + qDebug() << "Installing the ipa on idevice, path on device is " + << dest_path_str.c_str(); + err = installation_proxy_install(instproxy_client, dest_path_str.c_str(), NULL); - } - if (meta) { - instproxy_client_options_add(client_opts, "iTunesMetadata", meta, NULL); - } - -zip_cleanup: - if (zf) { - zip_unchange_all(zf); - zip_close(zf); - } - if (err != INSTPROXY_E_SUCCESS) { - goto leave_cleanup; - } - - /* perform installation */ - printf("Installing '%s'\n", bundleidentifier); - instproxy_install(ipc, pkgname, client_opts, status_cb, &status_data); - - instproxy_client_options_free(client_opts); - free(pkgname); - - while (!status_data.command_completed && !status_data.err_occurred) { - wait_ms(50); - } - - if (status_data.err_occurred) { - err = INSTPROXY_E_OP_FAILED; + if (err != NULL) { + qDebug() << "Installation failed: [" << err->code << "] " + << err->message; + goto cleanup; } else { - err = INSTPROXY_E_SUCCESS; + qDebug() << "Installation completed successfully"; } -leave_cleanup: - instproxy_client_free(ipc); - lockdownd_client_free(client); - free(bundleidentifier); - free(status_data.last_status); +cleanup: + + if (data) { + free(data); + } + + if (file) { + afc_file_close(file); + } + + if (instproxy_client) { + installation_proxy_client_free(instproxy_client); + } return err; -} \ No newline at end of file +} diff --git a/src/core/services/mount_dev_image.cpp b/src/core/services/mount_dev_image.cpp index 5b9a9b3..542d994 100644 --- a/src/core/services/mount_dev_image.cpp +++ b/src/core/services/mount_dev_image.cpp @@ -19,596 +19,61 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#define _GNU_SOURCE 1 #include "../../iDescriptor.h" -#include -#define __USE_GNU 1 -#include -#include -#include -#include #include -#include -#include -#include -#include -#ifndef _WIN32 -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef _WIN32 -#include -#endif +#include -typedef enum { - DISK_IMAGE_UPLOAD_TYPE_AFC, - DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE -} disk_image_upload_type_t; - -static const char *imagetype = NULL; - -static const char PKG_PATH[] = "PublicStaging"; -static const char PATH_PREFIX[] = "/private/var/mobile/Media"; - -static ssize_t mim_upload_cb(void *buf, size_t size, void *userdata) +// Failed to mount developer image: [-21] DeviceLockedMount image result: false +IdeviceFfiError *mount_dev_image(const iDescriptorDevice *device, + const char *image_file, + const char *signature_file) { - return fread(buf, 1, size, (FILE *)userdata); -} -// extend the mobile_image_mounter_error_t type and return sucess if there is -// already a disk image -mobile_image_mounter_error_t mount_dev_image(idevice_t device, - unsigned int device_version, - const char *image_dir_path) -{ - mobile_image_mounter_client_t mim = NULL; - int res = -1; - size_t image_size = 0; - lockdownd_client_t lckd = NULL; - lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR; - afc_client_t afc = NULL; - lockdownd_service_descriptor_t service = NULL; - char *image_path = NULL; - char *image_sig_path = NULL; - FILE *f = NULL; - unsigned char *sig = NULL; - plist_t mount_options = NULL; - char *targetname = NULL; - char *mountname = NULL; - disk_image_upload_type_t disk_image_upload_type = - DISK_IMAGE_UPLOAD_TYPE_AFC; - mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; - plist_t result = NULL; - size_t sig_length = 0; - - if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake( - device, &lckd, TOOL_NAME))) { - qDebug() << "ERROR: Could not connect to lockdownd service!"; - res = -1; - goto leave; + if (!device || !device->provider || !device->imageMounter) { + qDebug() + << "Error: Invalid device or provider passed to mount_dev_image"; + return new IdeviceFfiError{// FIXME: whats the code ? + .code = -1, + .message = "Invalid device or provider"}; } - if (device_version >= IDEVICE_DEVICE_VERSION(7, 0, 0)) { - disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE; + size_t image_len = 0; + size_t signature_len = 0; + uint8_t *image_data = nullptr; + uint8_t *signature_data = nullptr; + IdeviceFfiError *err = nullptr; + + if (err) { + goto cleanup; } - if (disk_image_upload_type == DISK_IMAGE_UPLOAD_TYPE_AFC) { - lockdownd_error_t lerr = - lockdownd_start_service(lckd, "com.apple.afc", &service); - if (lerr != LOCKDOWN_E_SUCCESS) { - qDebug() << "ERROR: Could not start AFC service!" - << lockdownd_strerror(lerr) << "(" << lerr << ")"; - res = -1; - goto leave; - } - - afc_error_t rafc = afc_client_new(device, service, &afc); - if (rafc != AFC_E_SUCCESS) { - qDebug() << "ERROR: Could not connect to AFC!" << afc_strerror(rafc) - << "(" << rafc << ")"; - res = -1; - goto leave; - } - lockdownd_service_descriptor_free(service); - service = NULL; - } - - if (asprintf(&image_path, "%s/DeveloperDiskImage.dmg", image_dir_path) < - 0) { - qDebug() << "Out of memory constructing image path!"; - res = -1; - goto leave; - } - - if (asprintf(&image_sig_path, "%s/DeveloperDiskImage.dmg.signature", - image_dir_path) < 0) { - qDebug() << "Out of memory constructing signature path!"; - res = -1; - goto leave; - } - - qDebug() << "Using image:" << image_path; - qDebug() << "Using signature:" << image_sig_path; - - if (device_version >= IDEVICE_DEVICE_VERSION(16, 0, 0)) { - uint8_t dev_mode_status = 0; - plist_t val = NULL; - ldret = lockdownd_get_value(lckd, "com.apple.security.mac.amfi", - "DeveloperModeStatus", &val); - if (ldret == LOCKDOWN_E_SUCCESS) { - plist_get_bool_val(val, &dev_mode_status); - plist_free(val); - } - if (!dev_mode_status) { - qDebug() << "ERROR: You have to enable Developer Mode on the given " - "device in order to allowing mounting a developer disk " - "image."; - res = -1; - goto leave; - } - } - - lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", - &service); - - if (!service || service->port == 0) { - qDebug() << "ERROR: Could not start mobile_image_mounter service!"; - res = -1; - goto leave; - } - - if (mobile_image_mounter_new(device, service, &mim) != - MOBILE_IMAGE_MOUNTER_E_SUCCESS) { - qDebug() << "ERROR: Could not connect to mobile_image_mounter!"; - res = -1; - goto leave; - } - lockdownd_service_descriptor_free(service); - service = NULL; - - struct stat fst; - if (stat(image_path, &fst) != 0) { - qDebug() << "ERROR: stat:" << image_path << ":" << strerror(errno); - res = -1; - goto leave; - } - image_size = fst.st_size; - if (device_version < IDEVICE_DEVICE_VERSION(17, 0, 0) && - stat(image_sig_path, &fst) != 0) { - qDebug() << "ERROR: stat:" << image_sig_path << ":" << strerror(errno); - res = -1; - goto leave; - } - - if (device_version < IDEVICE_DEVICE_VERSION(17, 0, 0)) { - f = fopen(image_sig_path, "rb"); - if (!f) { - qDebug() << "Error opening signature file" << image_sig_path << ":" - << strerror(errno); - res = -1; - goto leave; - } - if (fstat(fileno(f), &fst) != 0) { - qDebug() << "Error: fstat:" << strerror(errno); - res = -1; - goto leave; - } - sig = (unsigned char *)malloc(fst.st_size); - sig_length = fread(sig, 1, fst.st_size, f); - fclose(f); - f = NULL; - if (sig_length == 0) { - qDebug() << "Could not read signature from file" << image_sig_path; - res = -1; - goto leave; - } - - f = fopen(image_path, "rb"); - if (!f) { - qDebug() << "Error opening image file" << image_path << ":" - << strerror(errno); - res = -1; - goto leave; - } + if (!read_file(image_file, &image_data, &image_len)) { + err = new IdeviceFfiError{.code = -1, + .message = "Failed to read image file"}; + goto cleanup; + } else if (!read_file(signature_file, &signature_data, &signature_len)) { + err = new IdeviceFfiError{.code = -1, + .message = "Failed to read signature file"}; + goto cleanup; } else { - char *build_manifest_path = - string_build_path(image_path, "BuildManifest.plist", NULL); - plist_t build_manifest = NULL; - if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) != - 0) { - free(build_manifest_path); - build_manifest_path = string_build_path( - image_path, "Restore", "BuildManifest.plist", NULL); - if (plist_read_from_file(build_manifest_path, &build_manifest, - NULL) == 0) { - char *image_path_new = - string_build_path(image_path, "Restore", NULL); - free(image_path); - image_path = image_path_new; - } - } - if (!build_manifest) { - qDebug() << "Error: Could not locate BuildManifest.plist inside " - "given disk image path!"; - res = -1; - goto leave; - } - - plist_t identifiers = NULL; - mobile_image_mounter_error_t merr = - mobile_image_mounter_query_personalization_identifiers( - mim, NULL, &identifiers); - if (merr != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { - qDebug() << "Failed to query personalization identifiers:" << merr; - res = -1; - goto leave; - } - - unsigned int board_id = plist_dict_get_uint(identifiers, "BoardId"); - unsigned int chip_id = plist_dict_get_uint(identifiers, "ChipID"); - - plist_t build_identities = - plist_dict_get_item(build_manifest, "BuildIdentities"); - plist_array_iter iter; - plist_array_new_iter(build_identities, &iter); - plist_t item = NULL; - plist_t build_identity = NULL; - do { - plist_array_next_item(build_identities, iter, &item); - if (!item) { - break; - } - unsigned int bi_board_id = - (unsigned int)plist_dict_get_uint(item, "ApBoardID"); - unsigned int bi_chip_id = - (unsigned int)plist_dict_get_uint(item, "ApChipID"); - if (bi_chip_id == chip_id && bi_board_id == board_id) { - build_identity = item; - break; - } - } while (item); - plist_mem_free(iter); - if (!build_identity) { - qDebug() << "Error: The given disk image is not compatible with " - "the current device."; - res = -1; - goto leave; - } - plist_t p_tc_path = - plist_access_path(build_identity, 4, "Manifest", - "LoadableTrustCache", "Info", "Path"); - if (!p_tc_path) { - qDebug() << "Error: Could not determine path for trust cache!"; - res = -1; - goto leave; - } - plist_t p_dmg_path = plist_access_path( - build_identity, 4, "Manifest", "PersonalizedDMG", "Info", "Path"); - if (!p_dmg_path) { - qDebug() << "Error: Could not determine path for disk image!"; - res = -1; - goto leave; - } - char *tc_path = string_build_path( - image_path, plist_get_string_ptr(p_tc_path, NULL), NULL); - unsigned char *trust_cache = NULL; - uint64_t trust_cache_size = 0; - if (!buffer_read_from_filename(tc_path, (char **)&trust_cache, - &trust_cache_size)) { - qDebug() << "Error: Trust cache does not exist at" << tc_path - << "!"; - res = -1; - goto leave; - } - mount_options = plist_new_dict(); - plist_dict_set_item( - mount_options, "ImageTrustCache", - plist_new_data((char *)trust_cache, trust_cache_size)); - free(trust_cache); - char *dmg_path = string_build_path( - image_path, plist_get_string_ptr(p_dmg_path, NULL), NULL); - free(image_path); - image_path = dmg_path; - f = fopen(image_path, "rb"); - if (!f) { - qDebug() << "Error opening image file" << image_path << ":" - << strerror(errno); - res = -1; - goto leave; - } - - unsigned char buf[8192]; - unsigned char sha384_digest[48]; - sha384_context ctx; - sha384_init(&ctx); - fstat(fileno(f), &fst); - image_size = fst.st_size; - while (!feof(f)) { - ssize_t fr = fread(buf, 1, sizeof(buf), f); - if (fr <= 0) { - break; - } - sha384_update(&ctx, buf, fr); - } - rewind(f); - sha384_final(&ctx, sha384_digest); - unsigned char *manifest = NULL; - unsigned int manifest_size = 0; - /* check if the device already has a personalization manifest for this - * image */ - if (mobile_image_mounter_query_personalization_manifest( - mim, "DeveloperDiskImage", sha384_digest, sizeof(sha384_digest), - &manifest, &manifest_size) == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { - qDebug() << "Using existing personalization manifest from device."; + err = image_mounter_mount_developer(device->imageMounter, image_data, + image_len, signature_data, + signature_len); + if (err == NULL) { + printf("Developer image mounted successfully\n"); } else { - /* we need to re-connect in this case */ - mobile_image_mounter_free(mim); - mim = NULL; - if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != - MOBILE_IMAGE_MOUNTER_E_SUCCESS) { - res = -1; - goto leave; - } - qDebug() << "No personalization manifest, requesting from TSS..."; - unsigned char *nonce = NULL; - unsigned int nonce_size = 0; - - /* create new TSS request and fill parameters */ - plist_t request = tss_request_new(NULL); - plist_t params = plist_new_dict(); - tss_parameters_add_from_manifest(params, build_identity, 1); - - /* copy all `Ap,*` items from identifiers */ - plist_dict_iter di = NULL; - plist_dict_new_iter(identifiers, &di); - plist_t node = NULL; - do { - char *key = NULL; - plist_dict_next_item(identifiers, di, &key, &node); - if (node) { - if (!strncmp(key, "Ap,", 3)) { - plist_dict_set_item(request, key, plist_copy(node)); - } - } - free(key); - } while (node); - plist_mem_free(di); - - plist_dict_copy_uint(params, identifiers, "ApECID", "UniqueChipID"); - plist_dict_set_item(params, "ApProductionMode", plist_new_bool(1)); - plist_dict_set_item(params, "ApSecurityMode", plist_new_bool(1)); - plist_dict_set_item(params, "ApSupportsImg4", plist_new_bool(1)); - - /* query nonce from image mounter service */ - merr = mobile_image_mounter_query_nonce(mim, "DeveloperDiskImage", - &nonce, &nonce_size); - if (merr == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { - plist_dict_set_item(params, "ApNonce", - plist_new_data((char *)nonce, nonce_size)); - } else { - qDebug() - << "ERROR: Failed to query nonce for developer disk image:" - << merr; - res = -1; - goto leave; - } - mobile_image_mounter_free(mim); - mim = NULL; - - plist_dict_set_item( - params, "ApSepNonce", - plist_new_data("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00\x00", - 20)); - plist_dict_set_item(params, "UID_MODE", plist_new_bool(0)); - tss_request_add_ap_tags(request, params, NULL); - tss_request_add_common_tags(request, params, NULL); - tss_request_add_ap_img4_tags(request, params); - plist_free(params); - - /* request IM4M from TSS */ - plist_t response = tss_request_send(request, NULL); - plist_free(request); - - plist_t p_manifest = plist_dict_get_item(response, "ApImg4Ticket"); - if (!PLIST_IS_DATA(p_manifest)) { - qDebug() << "Failed to get Img4Ticket"; - res = -1; - goto leave; - } - - uint64_t m4m_len = 0; - plist_get_data_val(p_manifest, (char **)&manifest, &m4m_len); - manifest_size = m4m_len; - plist_free(response); - qDebug() << "Done."; + fprintf(stderr, "Failed to mount developer image: [%d] %s", + err->code, err->message); + goto cleanup; } - sig = manifest; - sig_length = manifest_size; - - imagetype = "Personalized"; } - if (asprintf(&targetname, "%s/%s", PKG_PATH, "staging.dimage") < 0) { - qDebug() << "Out of memory!?"; - res = -1; - goto leave; - } - if (asprintf(&mountname, "%s/%s", PATH_PREFIX, targetname) < 0) { - qDebug() << "Out of memory!?"; - res = -1; - goto leave; - } +cleanup: - if (!imagetype) { - imagetype = "Developer"; - } - - switch (disk_image_upload_type) { - case DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE: - qDebug() << "Uploading" << image_path; - err = mobile_image_mounter_upload_image(mim, imagetype, image_size, sig, - sig_length, mim_upload_cb, f); - break; - case DISK_IMAGE_UPLOAD_TYPE_AFC: - default: - qDebug() << "Uploading" << image_path << "--> afc:///" << targetname; - plist_t fileinfo = NULL; - if (afc_get_file_info_plist(afc, PKG_PATH, &fileinfo) != - AFC_E_SUCCESS) { - if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) { - qDebug() << "WARNING: Could not create directory" << PKG_PATH - << "on device!"; - } - } - plist_free(fileinfo); - - uint64_t af = 0; - if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) != - AFC_E_SUCCESS) || - !af) { - qDebug() << "afc_file_open on" << targetname << "failed!"; - res = -1; - goto leave; - } - - char buf[8192]; - size_t amount = 0; - do { - amount = fread(buf, 1, sizeof(buf), f); - if (amount > 0) { - uint32_t written, total = 0; - while (total < amount) { - written = 0; - if (afc_file_write(afc, af, buf + total, amount - total, - &written) != AFC_E_SUCCESS) { - qDebug() << "AFC Write error!"; - break; - } - total += written; - } - if (total != amount) { - qDebug() << "Error: wrote only" << total << "of" - << (unsigned int)amount; - afc_file_close(afc, af); - res = -1; - goto leave; - } - } - } while (amount > 0); - - afc_file_close(afc, af); - break; - } - - if (err != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { - if (err == MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED) { - qDebug() << "ERROR: Device is locked, can't mount. Unlock device " - "and try again."; - } else { - qDebug() << "ERROR: Unknown error occurred, can't mount."; - } - res = -1; - goto leave; - } - - qDebug() << "Mounting..."; - err = mobile_image_mounter_mount_image_with_options( - mim, mountname, sig, sig_length, imagetype, mount_options, &result); - if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { - if (result) { - plist_t node = plist_dict_get_item(result, "Status"); - if (node) { - char *status = NULL; - plist_get_string_val(node, &status); - if (status) { - if (!strcmp(status, "Complete")) { - qDebug() << "Done."; - res = 0; - } else { - err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; - } - free(status); - } else { - err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; - } - } else { - err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; - } - if (res != 0) { // If not complete, log the error - err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; - node = plist_dict_get_item(result, "Error"); - if (node) { - char *error = NULL; - plist_get_string_val(node, &error); - if (error) { - qDebug() << "Error:" << error; - free(error); - } - node = plist_dict_get_item(result, "DetailedError"); - if (node) { - const char *str = plist_get_string_ptr(node, NULL); - auto sd_str = std::string(str); - if (sd_str.find("already mounted at /Developer") != - std::string::npos) { - // FIXME: need an error code for this - qDebug() << "Image is already mounted"; - // res = 0; - // err = MOBILE_IMAGE_MOUNTER_E_SUCCESS; - } else { - qDebug() << "DetailedError:" << str; - } - } - } - } - } - } else { - qDebug() << "Error: mount_image returned" << err; - } - -leave: - if (f) { - fclose(f); - } - if (result) { - plist_free(result); - } - if (mim) { - mobile_image_mounter_free(mim); - } - if (afc) { - afc_client_free(afc); - } - if (lckd) { - lockdownd_client_free(lckd); - } - if (image_path) { - free(image_path); - } - if (image_sig_path) { - free(image_sig_path); - } - if (sig) { - free(sig); - } - if (mount_options) { - plist_free(mount_options); - } - if (targetname) { - free(targetname); - } - if (mountname) { - free(mountname); - } + if (image_data) + free(image_data); + if (signature_data) + free(signature_data); return err; } \ No newline at end of file diff --git a/src/core/services/query_mobilegestalt.cpp b/src/core/services/query_mobilegestalt.cpp deleted file mode 100644 index 82e2a4e..0000000 --- a/src/core/services/query_mobilegestalt.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * iDescriptor: A free and open-source idevice management tool. - * - * Copyright (C) 2025 Uncore - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include "../../iDescriptor.h" -#include "libimobiledevice/diagnostics_relay.h" -#include -#include - -bool query_mobile_gestalt(iDescriptorDevice *id_device, const QStringList &keys, - uint32_t &xml_size, char *&xml_data) -{ - if (!id_device) { - qDebug() << "Invalid device"; - return false; - } - - diagnostics_relay_client_t diagnostics_client = nullptr; - if (diagnostics_relay_client_start_service(id_device->device, - &diagnostics_client, nullptr) != - DIAGNOSTICS_RELAY_E_SUCCESS) { - qDebug() << "Failed to start diagnostics service"; - return false; - } - - plist_t result = nullptr; - plist_t keys_array = plist_new_array(); - for (const QString &key : keys) { - plist_t key_node = plist_new_string(key.toStdString().c_str()); - plist_array_append_item(keys_array, key_node); - } - - diagnostics_relay_error_t err = diagnostics_relay_query_mobilegestalt( - diagnostics_client, keys_array, &result); - - plist_free(keys_array); // Free the keys array - - if (err != DIAGNOSTICS_RELAY_E_SUCCESS) { - qDebug() << "Failed to query mobile gestalt"; - diagnostics_relay_client_free(diagnostics_client); - return false; - } - - if (!result) { - qDebug() << "No result from mobile gestalt query"; - diagnostics_relay_client_free(diagnostics_client); - return false; - } - - plist_to_xml(result, &xml_data, &xml_size); - plist_free(result); // Free the result plist - diagnostics_relay_client_free(diagnostics_client); - - return true; -} diff --git a/src/core/services/restart.cpp b/src/core/services/restart.cpp deleted file mode 100644 index a829009..0000000 --- a/src/core/services/restart.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * idevicediagnostics.c - * Retrieves diagnostics information from device - * - * Copyright (c) 2012 Martin Szulecki 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 - */ - -#include "../../iDescriptor.h" -#include -#include -#include - -bool restart(std::string _udid) -{ - idevice_t device = NULL; - lockdownd_client_t lockdown_client = NULL; - diagnostics_relay_client_t diagnostics_client = NULL; - lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; - lockdownd_service_descriptor_t service = NULL; - const char *udid = _udid.c_str(); - int use_network = 0; - - if (idevice_new_with_options(&device, udid, IDEVICE_LOOKUP_USBMUX) != - IDEVICE_E_SUCCESS) { - printf("ERROR: No device found, is it plugged in?\n"); - return false; - } - - if (LOCKDOWN_E_SUCCESS != (ret = lockdownd_client_new_with_handshake( - device, &lockdown_client, TOOL_NAME))) { - idevice_free(device); - printf("ERROR: Could not connect to lockdownd, error code %d\n", ret); - return false; - } - - /* attempt to use newer diagnostics service available on iOS 5 and later */ - ret = lockdownd_start_service( - lockdown_client, "com.apple.mobile.diagnostics_relay", &service); - if (ret == LOCKDOWN_E_INVALID_SERVICE) { - /* attempt to use older diagnostics service */ - ret = lockdownd_start_service( - lockdown_client, "com.apple.iosdiagnostics.relay", &service); - } - lockdownd_client_free(lockdown_client); - - if (ret != LOCKDOWN_E_SUCCESS) { - idevice_free(device); - printf("ERROR: Could not start diagnostics relay service: %s\n", - lockdownd_strerror(ret)); - return false; - } - - if ((ret == LOCKDOWN_E_SUCCESS) && service && (service->port > 0)) { - if (diagnostics_relay_client_new(device, service, - &diagnostics_client) != - DIAGNOSTICS_RELAY_E_SUCCESS) { - printf("ERROR: Could not connect to diagnostics_relay!\n"); - } else { - - if (diagnostics_relay_restart( - diagnostics_client, - DIAGNOSTICS_RELAY_ACTION_FLAG_WAIT_FOR_DISCONNECT) == - DIAGNOSTICS_RELAY_E_SUCCESS) { - printf("Restarting device.\n"); - return true; - } else { - printf("ERROR: Failed to restart device.\n"); - } - } - - diagnostics_relay_goodbye(diagnostics_client); - diagnostics_relay_client_free(diagnostics_client); - } - - return false; -} \ No newline at end of file diff --git a/src/core/services/set_location.cpp b/src/core/services/set_location.cpp index cfe8b0d..952ef8e 100644 --- a/src/core/services/set_location.cpp +++ b/src/core/services/set_location.cpp @@ -20,101 +20,80 @@ */ #include "../../iDescriptor.h" -#define DT_SIMULATELOCATION_SERVICE "com.apple.dt.simulatelocation" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(__APPLE__) -#include -#include -#define htobe32(x) OSSwapHostToBigInt32(x) -#elif defined(_WIN32) -#include -#define htobe32(x) htonl(x) -#else -#include -#endif -#include enum { SET_LOCATION = 0, RESET_LOCATION = 1 }; -bool set_location(idevice_t device, char *lat, char *lon) +bool set_location(void *device, char *lat, char *lon) { - uint32_t mode = 0; - lockdownd_client_t lockdown = NULL; - lockdownd_error_t lerr = - lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME); - try { - /* code */ + return false; + // uint32_t mode = 0; + // lockdownd_client_t lockdown = NULL; + // lockdownd_error_t lerr = + // lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME); + // try { + // /* code */ - if (lerr != LOCKDOWN_E_SUCCESS) { - idevice_free(device); - printf("ERROR: Could not connect to lockdownd: %s (%d)\n", - lockdownd_strerror(lerr), lerr); - return false; - } + // if (lerr != LOCKDOWN_E_SUCCESS) { + // idevice_free(device); + // printf("ERROR: Could not connect to lockdownd: %s (%d)\n", + // lockdownd_strerror(lerr), lerr); + // return false; + // } - lockdownd_service_descriptor_t svc = NULL; - lerr = lockdownd_start_service(lockdown, DT_SIMULATELOCATION_SERVICE, - &svc); - if (lerr != LOCKDOWN_E_SUCCESS) { - unsigned int device_version = idevice_get_device_version(device); - lockdownd_client_free(lockdown); - idevice_free(device); + // lockdownd_service_descriptor_t svc = NULL; + // lerr = lockdownd_start_service(lockdown, DT_SIMULATELOCATION_SERVICE, + // &svc); + // if (lerr != LOCKDOWN_E_SUCCESS) { + // unsigned int device_version = idevice_get_device_version(device); + // lockdownd_client_free(lockdown); + // idevice_free(device); - return false; - } - lockdownd_client_free(lockdown); + // return false; + // } + // lockdownd_client_free(lockdown); - service_client_t service = NULL; + // service_client_t service = NULL; - service_error_t serr = service_client_new(device, svc, &service); + // service_error_t serr = service_client_new(device, svc, &service); - lockdownd_service_descriptor_free(svc); + // lockdownd_service_descriptor_free(svc); - if (serr != SERVICE_E_SUCCESS) { - idevice_free(device); - return false; - } + // if (serr != SERVICE_E_SUCCESS) { + // idevice_free(device); + // return false; + // } - uint32_t l; - uint32_t s = 0; + // uint32_t l; + // uint32_t s = 0; - l = htobe32(mode); - service_send(service, (const char *)&l, 4, &s); - if (mode == SET_LOCATION) { - int len = 4 + strlen(lat) + 4 + strlen(lon); - char *buf = (char *)malloc(len); - uint32_t latlen; - latlen = strlen(lat); - l = htobe32(latlen); - memcpy(buf, &l, 4); - memcpy(buf + 4, lat, latlen); - uint32_t longlen = strlen(lon); - l = htobe32(longlen); - memcpy(buf + 4 + latlen, &l, 4); - memcpy(buf + 4 + latlen + 4, lon, longlen); + // l = htobe32(mode); + // service_send(service, (const char *)&l, 4, &s); + // if (mode == SET_LOCATION) { + // int len = 4 + strlen(lat) + 4 + strlen(lon); + // char *buf = (char *)malloc(len); + // uint32_t latlen; + // latlen = strlen(lat); + // l = htobe32(latlen); + // memcpy(buf, &l, 4); + // memcpy(buf + 4, lat, latlen); + // uint32_t longlen = strlen(lon); + // l = htobe32(longlen); + // memcpy(buf + 4 + latlen, &l, 4); + // memcpy(buf + 4 + latlen + 4, lon, longlen); - s = 0; - service_send(service, buf, len, &s); - free(buf); // <-- free the buffer after use - } + // s = 0; + // service_send(service, buf, len, &s); + // free(buf); // <-- free the buffer after use + // } - return true; - } catch (...) { - if (lockdown) { - lockdownd_client_free(lockdown); - } - if (device) { - idevice_free(device); - } - qDebug() << "Exception occurred while setting location."; - return false; - } + // return true; + // } catch (...) { + // if (lockdown) { + // lockdownd_client_free(lockdown); + // } + // if (device) { + // idevice_free(device); + // } + // qDebug() << "Exception occurred while setting location."; + // return false; + // } } \ No newline at end of file diff --git a/src/core/services/shutdown.cpp b/src/core/services/shutdown.cpp deleted file mode 100644 index c0d3478..0000000 --- a/src/core/services/shutdown.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * idevicediagnostics.c - * Retrieves diagnostics information from device - * - * Copyright (c) 2012 Martin Szulecki 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 - */ - -#include "../../iDescriptor.h" -#include -#include -#include - -bool shutdown(idevice_t device) -{ - lockdownd_client_t lockdown_client = NULL; - diagnostics_relay_client_t diagnostics_client = NULL; - lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; - lockdownd_service_descriptor_t service = NULL; - const char *udid = NULL; - int use_network = 0; - - if (LOCKDOWN_E_SUCCESS != (ret = lockdownd_client_new_with_handshake( - device, &lockdown_client, TOOL_NAME))) { - idevice_free(device); - printf("ERROR: Could not connect to lockdownd, error code %d\n", ret); - return false; - } - - /* attempt to use newer diagnostics service available on iOS 5 and later */ - ret = lockdownd_start_service( - lockdown_client, "com.apple.mobile.diagnostics_relay", &service); - if (ret == LOCKDOWN_E_INVALID_SERVICE) { - /* attempt to use older diagnostics service */ - ret = lockdownd_start_service( - lockdown_client, "com.apple.iosdiagnostics.relay", &service); - } - lockdownd_client_free(lockdown_client); - - if (ret != LOCKDOWN_E_SUCCESS) { - idevice_free(device); - printf("ERROR: Could not start diagnostics relay service: %s\n", - lockdownd_strerror(ret)); - return false; - } - - if ((ret == LOCKDOWN_E_SUCCESS) && service && (service->port > 0)) { - if (diagnostics_relay_client_new(device, service, - &diagnostics_client) != - DIAGNOSTICS_RELAY_E_SUCCESS) { - printf("ERROR: Could not connect to diagnostics_relay!\n"); - } else { - - if (diagnostics_relay_shutdown( - diagnostics_client, - DIAGNOSTICS_RELAY_ACTION_FLAG_WAIT_FOR_DISCONNECT) == - DIAGNOSTICS_RELAY_E_SUCCESS) { - printf("Shutting down device.\n"); - return true; - } else { - printf("ERROR: Failed to shut down device.\n"); - } - } - - diagnostics_relay_goodbye(diagnostics_client); - diagnostics_relay_client_free(diagnostics_client); - } - - if (service) { - lockdownd_service_descriptor_free(service); - service = NULL; - } - - return false; -} \ No newline at end of file diff --git a/src/core/services/take_screenshot.cpp b/src/core/services/take_screenshot.cpp deleted file mode 100644 index 3e41254..0000000 --- a/src/core/services/take_screenshot.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * idevicescreenshot.c - * Gets a screenshot from a device - * - * Copyright (C) 2010 Nikias Bassen - * - * 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 - */ - -#include "../../iDescriptor.h" -#include -#include -#include -#include -#include -#include - -QImage get_image(const char *imgdata, uint64_t imgsize) -{ - QByteArray byteArray(imgdata, static_cast(imgsize)); - QImage image; - image.loadFromData(byteArray); - return image; -} - -TakeScreenshotResult take_screenshot(screenshotr_client_t shotr) -{ - TakeScreenshotResult result; - try { - char *imgdata = NULL; - uint64_t imgsize = 0; - if (screenshotr_take_screenshot(shotr, &imgdata, &imgsize) == - SCREENSHOTR_E_SUCCESS) { - QImage image = get_image(imgdata, imgsize); - if (image.isNull()) { - printf("Could not decode screenshot image!\n"); - } else { - result.img = image; - result.success = true; - } - // Always free imgdata after use! - free(imgdata); - } - } catch (const std::exception &e) { - qDebug() << "Exception occurred while taking screenshot:" << e.what(); - } - // Always return result! - return result; -} diff --git a/src/devdiskimagehelper.cpp b/src/devdiskimagehelper.cpp index 56bb589..62e165b 100644 --- a/src/devdiskimagehelper.cpp +++ b/src/devdiskimagehelper.cpp @@ -97,9 +97,8 @@ void DevDiskImageHelper::start() m_loadingIndicator->start(); showStatus("Please wait..."); - unsigned int device_version = idevice_get_device_version(m_device->device); - unsigned int deviceMajorVersion = (device_version >> 16) & 0xFF; - unsigned int deviceMinorVersion = (device_version >> 8) & 0xFF; + unsigned int deviceMajorVersion = + m_device->deviceInfo.parsedDeviceVersion.major; // FIXME:we dont have developer disk images for ios 6 and below if (deviceMajorVersion > 5) { @@ -124,23 +123,22 @@ void DevDiskImageHelper::start() void DevDiskImageHelper::checkAndMount() { - GetMountedImageResult result = - DevDiskManager::sharedInstance()->getMountedImage( - m_device->udid.c_str()); - qDebug() << "checkAndMount result:" << result.success - << result.message.c_str() << QString::fromStdString(result.sig); - if (!result.success) { - showRetryUI(QString::fromStdString(result.message)); - return; - } + // GetMountedImageResult result = + // DevDiskManager::sharedInstance()->getMountedImage(m_device); + // qDebug() << "checkAndMount result:" << result.success + // << result.message.c_str() << QString::fromStdString(result.sig); + // if (!result.success) { + // showRetryUI(QString::fromStdString(result.message)); + // return; + // } - // If image is already mounted - if (!result.sig.empty()) { - finishWithSuccess(); - return; - } + // // If image is already mounted + // if (!result.sig.empty()) { + // finishWithSuccess(); + // return; + // } - onMountButtonClicked(); + // onMountButtonClicked(); } void DevDiskImageHelper::onMountButtonClicked() @@ -151,9 +149,10 @@ void DevDiskImageHelper::onMountButtonClicked() m_isMounting = true; // Check if we need to download first - unsigned int device_version = idevice_get_device_version(m_device->device); - unsigned int deviceMajorVersion = (device_version >> 16) & 0xFF; - unsigned int deviceMinorVersion = (device_version >> 8) & 0xFF; + unsigned int deviceMajorVersion = + m_device->deviceInfo.parsedDeviceVersion.major; + unsigned int deviceMinorVersion = + m_device->deviceInfo.parsedDeviceVersion.minor; QList images = DevDiskManager::sharedInstance()->parseImageList( path, deviceMajorVersion, deviceMinorVersion, "", 0); @@ -174,27 +173,28 @@ void DevDiskImageHelper::onMountButtonClicked() } if (hasDownloadedImage) { - // Mount directly - showStatus("Mounting developer disk image..."); + // // Mount directly + // showStatus("Mounting developer disk image..."); - mobile_image_mounter_error_t err = - DevDiskManager::sharedInstance()->mountImage(versionToMount, - m_device); + // mobile_image_mounter_error_t err = + // DevDiskManager::sharedInstance()->mountImage(versionToMount, + // m_device); - m_isMounting = false; - if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { - showStatus("Developer disk image mounted successfully"); - finishWithSuccess(); - } else if (err == MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED) { - showRetryUI( - "Device is locked. Please unlock your device and try again."); - } else { - showRetryUI("Failed to mount developer disk image.\n" - "Please ensure:\n" - "• Device is unlocked\n" - "• Using a genuine cable\n" - "• Developer mode is enabled (iOS 16+)"); - } + // m_isMounting = false; + // if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { + // showStatus("Developer disk image mounted successfully"); + // finishWithSuccess(); + // } else if (err == MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED) { + // showRetryUI( + // "Device is locked. Please unlock your device and try + // again."); + // } else { + // showRetryUI("Failed to mount developer disk image.\n" + // "Please ensure:\n" + // "• Device is unlocked\n" + // "• Using a genuine cable\n" + // "• Developer mode is enabled (iOS 16+)"); + // } } else { // Need to download first showStatus( @@ -237,20 +237,21 @@ void DevDiskImageHelper::onImageDownloadFinished(const QString &version, // Download successful, now mount showStatus("Download complete. Mounting..."); - mobile_image_mounter_error_t err = - DevDiskManager::sharedInstance()->mountImage(version, m_device); + // mobile_image_mounter_error_t err = + // DevDiskManager::sharedInstance()->mountImage(version, m_device); - if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { - showStatus("Developer disk image mounted successfully"); - finishWithSuccess(); - } else if (err == MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED) { - showRetryUI( - "Device is locked. Please unlock your device and try again."); - } else { - showRetryUI( - "Failed to mount developer disk image.\n" - "Please ensure the device is unlocked and using a genuine cable."); - } + // if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { + // showStatus("Developer disk image mounted successfully"); + // finishWithSuccess(); + // } else if (err == MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED) { + // showRetryUI( + // "Device is locked. Please unlock your device and try again."); + // } else { + // showRetryUI( + // "Failed to mount developer disk image.\n" + // "Please ensure the device is unlocked and using a genuine + // cable."); + // } } void DevDiskImageHelper::showRetryUI(const QString &errorMessage) diff --git a/src/devdiskimageswidget.cpp b/src/devdiskimageswidget.cpp index 3606463..5404f58 100644 --- a/src/devdiskimageswidget.cpp +++ b/src/devdiskimageswidget.cpp @@ -22,6 +22,7 @@ #include "devdiskmanager.h" #include "iDescriptor.h" #include "qprocessindicator.h" +#include "servicemanager.h" #include "settingsmanager.h" #include #include @@ -46,12 +47,13 @@ #include #include #include -#include #include DevDiskImagesWidget::DevDiskImagesWidget(iDescriptorDevice *device, QWidget *parent) - : QWidget{parent}, m_currentDevice(device) + : QWidget{parent}, + m_currentDeviceUdid( + device != nullptr ? QString::fromStdString(device->udid) : QString()) { setupUi(); connect(DevDiskManager::sharedInstance(), &DevDiskManager::imageListFetched, @@ -171,34 +173,34 @@ void DevDiskImagesWidget::onDeviceSelectionChanged(int index) if (device == nullptr) return; - m_currentDevice = device; + m_currentDeviceUdid = QString::fromStdString(device->udid); displayImages(); } void DevDiskImagesWidget::displayImages() { + qDebug() << "Displaying images for device"; m_imageListWidget->clear(); - int deviceMajorVersion = 0; - int deviceMinorVersion = 0; - bool hasConnectedDevice = false; - - if (m_currentDevice && m_currentDevice->device) { - unsigned int device_version = - idevice_get_device_version(m_currentDevice->device); - deviceMajorVersion = (device_version >> 16) & 0xFF; - deviceMinorVersion = (device_version >> 8) & 0xFF; - hasConnectedDevice = true; + // Look up device by UDID + iDescriptorDevice *currentDevice = nullptr; + if (!m_currentDeviceUdid.isEmpty()) { + currentDevice = AppContext::sharedInstance()->getDevice( + m_currentDeviceUdid.toStdString()); } + bool hasConnectedDevice = (currentDevice != nullptr); + + int major = hasConnectedDevice + ? currentDevice->deviceInfo.parsedDeviceVersion.major + : 0; + int minor = hasConnectedDevice + ? currentDevice->deviceInfo.parsedDeviceVersion.minor + : 0; - qDebug() << "Device version:" << deviceMajorVersion << "." - << deviceMinorVersion << "displayImages"; - // Parse images using manager QString path = SettingsManager::sharedInstance()->mkDevDiskImgPath(); QList allImages = DevDiskManager::sharedInstance()->parseImageList( - path, deviceMajorVersion, deviceMinorVersion, m_mounted_sig.c_str(), - m_mounted_sig_len); + path, major, minor, m_mounted_sig.c_str(), m_mounted_sig_len); qDebug() << "Total images:" << allImages.size(); @@ -307,8 +309,7 @@ void DevDiskImagesWidget::displayImages() // Show device info if available if (hasConnectedDevice) { - QString deviceVersion = - QString("%1.%2").arg(deviceMajorVersion).arg(deviceMinorVersion); + QString deviceVersion = QString("%1.%2").arg(major).arg(minor); m_statusLabel->setText( QString("Connected device: iOS %1 - Compatible images shown at top") .arg(deviceVersion)); @@ -499,7 +500,7 @@ void DevDiskImagesWidget::updateDeviceList() auto devices = AppContext::sharedInstance()->getAllDevices(); if (devices.isEmpty()) { - m_currentDevice = nullptr; + m_currentDeviceUdid.clear(); m_check_mountedButton->setEnabled(false); m_deviceComboBox->setEnabled(false); } else { @@ -511,6 +512,8 @@ void DevDiskImagesWidget::updateDeviceList() if (m_deviceComboBox->count() > 0 && m_deviceComboBox->currentIndex() >= 0) { currentUdid = m_deviceComboBox->currentData().toString(); + } else if (!m_currentDeviceUdid.isEmpty()) { + currentUdid = m_currentDeviceUdid; } m_deviceComboBox->clear(); @@ -582,61 +585,87 @@ void DevDiskImagesWidget::mountImage(const QString &version) m_mountButton->setEnabled(false); m_mountButton->setText("Mounting..."); - mobile_image_mounter_error_t err = - DevDiskManager::sharedInstance()->mountImage(version, m_currentDevice); - auto updateUI = [&]() { m_mountButton->setEnabled(true); m_mountButton->setText("Mount"); m_deviceComboBox->setEnabled(true); }; - switch (err) { - case MOBILE_IMAGE_MOUNTER_E_INVALID_ARG: - QMessageBox::critical(this, "Mount Failed", - "Invalid argument provided for mounting."); - updateUI(); - return; - case MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED: - QMessageBox::critical(this, "Mount Failed", - "The device is locked. Please unlock it and try " - "again."); - updateUI(); - return; - case MOBILE_IMAGE_MOUNTER_E_SUCCESS: + auto paths = DevDiskManager::sharedInstance()->getPathsForVersion(version); + + MountedImageInfo info = ServiceManager::getMountedImage( + AppContext::sharedInstance()->getDevice(udid.toStdString())); + + if (info.err == nullptr && info.signature && info.signature_len) { + qDebug() << "Mount image: already mounted sig found" + << QString::fromStdString(std::string((char *)info.signature, + info.signature_len)); + QMessageBox::information(this, "Already Mounted", + QString("A developer disk image is already " + "mounted on %1.") + .arg(m_deviceComboBox->currentText())); + return updateUI(); + } else if (info.err->code == DeviceLockedMountErrorCode) { + QMessageBox::critical(this, "Device Locked", + "The device is locked. Please unlock it and try" + " again."); + mounted_image_info_free(info); + return updateUI(); + } else if (info.err->code == NotFoundErrorCode) { + QMessageBox::critical( + this, "No Mounted Image", + "No developer disk image is mounted on the device."); + mounted_image_info_free(info); + return updateUI(); + } else { + QMessageBox::critical( + this, "Mount Check Failed", + QString("Failed to check mounted image on %1. Try with a " + "genuine cable.") + .arg(m_deviceComboBox->currentText())); + mounted_image_info_free(info); + return updateUI(); + } + + mounted_image_info_free(info); + + iDescriptorDevice *currentDevice = + m_currentDeviceUdid.isEmpty() ? nullptr + : AppContext::sharedInstance()->getDevice( + m_currentDeviceUdid.toStdString()); + + if (!currentDevice) { + QMessageBox::warning(this, "No Device", + "Device is no longer connected."); + return updateUI(); + } + + IdeviceFfiError *err = ServiceManager::mountImage( + currentDevice, paths.first.toStdString().c_str(), + paths.second.toStdString().c_str()); + + if (err == nullptr) { QMessageBox::information(this, "Success", QString("Image mounted successfully on %1.") .arg(m_deviceComboBox->currentText())); - displayImages(); // Refresh to show mounted status - updateUI(); - break; - default: - GetMountedImageResult result = - DevDiskManager::sharedInstance()->getMountedImage( - udid.toStdString().c_str()); - /* - * FIXME: there is no error enum like - * MOBILE_IMAGE_MOUNTER_E_ALREADY_MOUNTED so we work around here - */ - qDebug() << "Mount result:" << result.success << result.message.c_str() - << QString::fromStdString(result.sig); - if (result.success && !result.sig.empty()) { - m_mounted_sig = result.sig; - m_mounted_sig_len = result.sig.size(); - updateUI(); - displayImages(); - QMessageBox::information(this, "Already Mounted", - "There is already a developer disk image " - "mounted on the device."); - return; - } + return updateUI(); + } + qDebug() << "Mount image result:" << err->code + << QString::fromStdString(err->message); + + if (err->code == DeviceLockedMountErrorCode) { + QMessageBox::critical(this, "Mount Failed", + "The device is locked. Please unlock it and try" + " again."); + } else { QMessageBox::critical( this, "Mount Failed", QString("Failed to mount image on %1. Try with a genuine cable.") .arg(m_deviceComboBox->currentText())); - updateUI(); } + idevice_error_free(err); + updateUI(); } void DevDiskImagesWidget::closeEvent(QCloseEvent *event) @@ -674,35 +703,63 @@ void DevDiskImagesWidget::closeEvent(QCloseEvent *event) void DevDiskImagesWidget::checkMountedImage() { - // just in case - if (!m_currentDevice || !m_currentDevice->device) { + iDescriptorDevice *currentDevice = + m_currentDeviceUdid.isEmpty() ? nullptr + : AppContext::sharedInstance()->getDevice( + m_currentDeviceUdid.toStdString()); + + if (!currentDevice) { + qDebug() << "No device selected"; + auto devices = AppContext::sharedInstance()->getAllDevices(); + for (const auto &dev : devices) { + qDebug() << "Device:" + << QString::fromStdString(dev->deviceInfo.deviceName) + << "UDID:" << QString::fromStdString(dev->udid); + } return; } if (m_deviceComboBox->currentIndex() < 0) { + qDebug() << "No device selected in combo box"; return; } - GetMountedImageResult result = - DevDiskManager::sharedInstance()->getMountedImage( - m_currentDevice->udid.c_str()); + /* + older devices return something like this: + { + "ImagePresent": true, + "ImageSignature": <7b16200b 2ead1830 a59809d1 51e9060b ... 8a 9844eb07 + e0b8e0>, "Status": "Complete" + } + */ + MountedImageInfo info = ServiceManager::getMountedImage(currentDevice); - qDebug() << "checkMountedImage result:" << result.success - << result.message.c_str() << QString::fromStdString(result.sig); - - if (result.success && !result.sig.empty()) { - m_mounted_sig = result.sig; - m_mounted_sig_len = result.sig.size(); + if (info.err == nullptr && info.signature != nullptr && + info.signature_len > 0) { + m_mounted_sig = std::string( + reinterpret_cast(info.signature), info.signature_len); + m_mounted_sig_len = info.signature_len; displayImages(); // Refresh to show mounted status QMessageBox::information( this, "Check Mounted Image", "There is already a developer disk image mounted on the device."); - return; + mounted_image_info_free(info); + } else if (info.err->code == DeviceLockedMountErrorCode) { + QMessageBox::critical(this, "Device Locked", + "The device is locked. Please unlock it and try" + " again."); + mounted_image_info_free(info); + } else if (info.err->code == NotFoundErrorCode) { + QMessageBox::critical( + this, "No Mounted Image", + "No developer disk image is mounted on the device."); + mounted_image_info_free(info); + } else { + QMessageBox::critical( + this, "Check Mounted Image Failed", + QString("Failed to check mounted image on %1. Try with a " + "genuine cable. Error message: %2") + .arg(m_deviceComboBox->currentText()) + .arg(QString::fromStdString(info.err->message))); + mounted_image_info_free(info); } - - QString errorMsg = QString::fromStdString(result.message); - if (errorMsg.isEmpty()) { - errorMsg = "Unknown error occurred while checking mounted image"; - } - - QMessageBox::warning(this, "Check Mounted Image Failed", errorMsg); } diff --git a/src/devdiskimageswidget.h b/src/devdiskimageswidget.h index d1feb60..c6db239 100644 --- a/src/devdiskimageswidget.h +++ b/src/devdiskimageswidget.h @@ -86,7 +86,7 @@ private: QPushButton *m_check_mountedButton; QProcessIndicator *m_processIndicator; - iDescriptorDevice *m_currentDevice; + QString m_currentDeviceUdid; QStringList m_compatibleVersions; QStringList m_otherVersions; diff --git a/src/devdiskmanager.cpp b/src/devdiskmanager.cpp index d2d110d..3bdf20a 100644 --- a/src/devdiskmanager.cpp +++ b/src/devdiskmanager.cpp @@ -19,6 +19,7 @@ #include "devdiskmanager.h" #include "iDescriptor.h" +#include "servicemanager.h" #include "settingsmanager.h" #include #include @@ -31,7 +32,6 @@ #include #include #include -#include DevDiskManager *DevDiskManager::sharedInstance() { @@ -326,9 +326,10 @@ bool DevDiskManager::downloadCompatibleImage(iDescriptorDevice *device, std::function callback) { QString path = SettingsManager::sharedInstance()->mkDevDiskImgPath(); - unsigned int device_version = idevice_get_device_version(device->device); - unsigned int deviceMajorVersion = (device_version >> 16) & 0xFF; - unsigned int deviceMinorVersion = (device_version >> 8) & 0xFF; + unsigned int deviceMajorVersion = + device->deviceInfo.parsedDeviceVersion.major; + unsigned int deviceMinorVersion = + device->deviceInfo.parsedDeviceVersion.minor; qDebug() << "Device version:" << deviceMajorVersion << "." << deviceMinorVersion; QList images = @@ -405,37 +406,36 @@ bool DevDiskManager::downloadCompatibleImage(iDescriptorDevice *device, bool DevDiskManager::mountCompatibleImage(iDescriptorDevice *device) { QString path = SettingsManager::sharedInstance()->mkDevDiskImgPath(); - unsigned int device_version = idevice_get_device_version(device->device); - unsigned int deviceMajorVersion = (device_version >> 16) & 0xFF; - unsigned int deviceMinorVersion = (device_version >> 8) & 0xFF; QList images = - parseImageList(path, deviceMajorVersion, deviceMinorVersion, "", 0); + parseImageList(path, device->deviceInfo.parsedDeviceVersion.major, + device->deviceInfo.parsedDeviceVersion.minor, "", 0); + return false; // 1. Try to mount an already downloaded compatible image - for (const ImageInfo &info : images) { - if (info.compatibility != ImageCompatibility::Compatible && - info.compatibility != ImageCompatibility::MaybeCompatible) { - continue; - } - if (info.isDownloaded) { - qDebug() << "There is a compatible image already downloaded:" - << info.version; - qDebug() << "Attempting to mount image version" << info.version - << "on device:" << device->udid.c_str(); - if (MOBILE_IMAGE_MOUNTER_E_SUCCESS == - mountImage(info.version, device)) { - qDebug() << "Mounted existing image version" << info.version - << "on device:" << device->udid.c_str(); - return true; - } else { - qDebug() << "Failed to mount existing image version" - << info.version - << "on device:" << device->udid.c_str(); - return false; - } - } - } + // for (const ImageInfo &info : images) { + // if (info.compatibility != ImageCompatibility::Compatible && + // info.compatibility != ImageCompatibility::MaybeCompatible) { + // continue; + // } + // if (info.isDownloaded) { + // qDebug() << "There is a compatible image already downloaded:" + // << info.version; + // qDebug() << "Attempting to mount image version" << info.version + // << "on device:" << device->udid.c_str(); + // if (MOBILE_IMAGE_MOUNTER_E_SUCCESS == + // mountImage(info.version, device)) { + // qDebug() << "Mounted existing image version" << info.version + // << "on device:" << device->udid.c_str(); + // return true; + // } else { + // qDebug() << "Failed to mount existing image version" + // << info.version + // << "on device:" << device->udid.c_str(); + // return false; + // } + // } + // } // 2. If none are downloaded, download the newest compatible one for (const ImageInfo &info : images) { @@ -492,19 +492,38 @@ bool DevDiskManager::mountCompatibleImage(iDescriptorDevice *device) return false; } -mobile_image_mounter_error_t -DevDiskManager::mountImage(const QString &version, iDescriptorDevice *device) +bool DevDiskManager::mountImage(const QString &version, + iDescriptorDevice *device) { const QString downloadPath = SettingsManager::sharedInstance()->devdiskimgpath(); if (!isImageDownloaded(version, downloadPath)) { - return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; + return false; } QString versionPath = QDir(downloadPath).filePath(version); - return mount_dev_image(device->device, - device->deviceInfo.parsedDeviceVersion, - versionPath.toUtf8().constData()); + return mount_dev_image(device, + QDir(versionPath) + .filePath("DeveloperDiskImage.dmg") + .toUtf8() + .constData(), + QDir(versionPath) + .filePath("DeveloperDiskImage.dmg.signature") + .toUtf8() + .constData()); +} + +std::pair +DevDiskManager::getPathsForVersion(const QString &version) +{ + const QString downloadPath = + SettingsManager::sharedInstance()->devdiskimgpath(); + QString versionPath = QDir(downloadPath).filePath(version); + QString dmgPath = QDir(versionPath).filePath("DeveloperDiskImage.dmg"); + QString sigPath = + QDir(versionPath).filePath("DeveloperDiskImage.dmg.signature"); + + return {dmgPath, sigPath}; } void DevDiskManager::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) @@ -624,79 +643,4 @@ bool DevDiskManager::compareSignatures(const char *signature_file_path, free(local_sig); return matches; -} - -/* - older devices return something like this: - { - "ImagePresent": true, - "ImageSignature": <7b16200b 2ead1830 a59809d1 51e9060b ... 8a 9844eb07 - e0b8e0>, "Status": "Complete" - } -*/ -GetMountedImageResult DevDiskManager::getMountedImage(const char *udid) -{ - /* - FIXME: _get_mounted_image can return MOBILE_IMAGE_MOUNTER_E_SUCCESS even - if the device is locked so we are going to go off of the result - dictionary - */ - plist_t result = _get_mounted_image(udid); - plist_print(result); - const char *lockedErr = "DeviceLocked"; - - PlistNavigator r = PlistNavigator(result); - - std::string error = r["Error"].getString(); - - if (!error.empty()) { - plist_free(result); - if (error == lockedErr) { - return GetMountedImageResult{ - false, "", "Device is locked, please unlock it and try again."}; - } else - return GetMountedImageResult{false, "", "Unknown error"}; - } - - // for older devices - bool image_present = r["ImagePresent"].getBool(); - - /* FIXME: returning a madeup sig because iDescriptorTool::MountDevImage - * depends on it in toolboxwidget.cpp we can read the actual signature from - * the device but it’s not really necessary since we - * just need to know if there is an image mounted or - * not also we would need to check the ios version - * to know whether to treat ImageSignature as plist array or data - */ - if (image_present) { - plist_free(result); - return GetMountedImageResult{true, "FIXME", - "There is already an image mounted."}; - } - - plist_t sig_array_node = r["ImageSignature"].getNode(); - - if (sig_array_node == NULL) { - plist_free(result); - return GetMountedImageResult{true, "", "No disk image mounted"}; - } - - char *mounted_sig = nullptr; - uint64_t mounted_sig_len = 0; - // get the signature - if (sig_array_node && plist_get_node_type(sig_array_node) == PLIST_ARRAY && - plist_array_get_size(sig_array_node) > 0) { - plist_t sig_data_node = plist_array_get_item(sig_array_node, 0); - if (sig_data_node && plist_get_node_type(sig_data_node) == PLIST_DATA) { - plist_get_data_val(sig_data_node, &mounted_sig, &mounted_sig_len); - } - } - std::string mounted_sig_str(mounted_sig ? mounted_sig : ""); - free(mounted_sig); - plist_free(result); - if (mounted_sig_str.empty()) { - return GetMountedImageResult{ - true, "", "No disk image mounted (No signature found)"}; - } - return GetMountedImageResult{true, mounted_sig_str, "Success"}; } \ No newline at end of file diff --git a/src/devdiskmanager.h b/src/devdiskmanager.h index ba995d3..6d49071 100644 --- a/src/devdiskmanager.h +++ b/src/devdiskmanager.h @@ -27,7 +27,6 @@ #include #include #include -#include class DevDiskManager : public QObject { @@ -50,16 +49,15 @@ public: // Mount operations - mobile_image_mounter_error_t mountImage(const QString &version, - iDescriptorDevice *device); + bool mountImage(const QString &version, iDescriptorDevice *device); bool unmountImage(); + std::pair getPathsForVersion(const QString &version); // Signature comparison bool compareSignatures(const char *signature_file_path, const char *mounted_sig, uint64_t mounted_sig_len); QByteArray getImageListData() const { return m_imageListJsonData; } - GetMountedImageResult getMountedImage(const char *udid); bool mountCompatibleImage(iDescriptorDevice *device); bool downloadCompatibleImage(iDescriptorDevice *device, std::function callback); diff --git a/src/deviceimagewidget.cpp b/src/deviceimagewidget.cpp index 8b361ef..fab01cb 100644 --- a/src/deviceimagewidget.cpp +++ b/src/deviceimagewidget.cpp @@ -161,26 +161,12 @@ QString DeviceImageWidget::getMockupNameFromDisplayName( int DeviceImageWidget::getIosVersionFromDevice() const { - unsigned int version = m_device->deviceInfo.parsedDeviceVersion; + unsigned int version = m_device->deviceInfo.parsedDeviceVersion.major; if (version > 0) { - int majorVersion = (version >> 16) & 0xFF; - return majorVersion; + return version; } - // Fallback: parse from productVersion string - QString versionString = - QString::fromStdString(m_device->deviceInfo.productVersion); - QStringList parts = versionString.split('.'); - if (!parts.isEmpty()) { - bool ok; - int majorVersion = parts.first().toInt(&ok); - if (ok) { - return majorVersion; - } - } - - // return unknown version (will use ios26 wallpaper) return 0; } diff --git a/src/devicemenuwidget.cpp b/src/devicemenuwidget.cpp index d6e5f3a..444b303 100644 --- a/src/devicemenuwidget.cpp +++ b/src/devicemenuwidget.cpp @@ -18,14 +18,21 @@ */ #include "devicemenuwidget.h" +#include "Toast.h" +#include "cableinfowidget.h" +#include "devdiskimageswidget.h" #include "iDescriptor.h" +#include "livescreenwidget.h" #include "qprocessindicator.h" +#include "querymobilegestaltwidget.h" +#include "servicemanager.h" +#include "virtuallocationwidget.h" #include #include #include DeviceMenuWidget::DeviceMenuWidget(iDescriptorDevice *device, QWidget *parent) - : QWidget{parent}, device(device) + : QWidget{parent}, m_device(device) { QVBoxLayout *mainLayout = new QVBoxLayout(this); setContentsMargins(0, 0, 0, 0); @@ -54,19 +61,19 @@ void DeviceMenuWidget::init() { // Create and add widgets to the stacked widget - m_deviceInfoWidget = new DeviceInfoWidget(device, this); - // m_installedAppsWidget = new InstalledAppsWidget(device, this); - m_galleryWidget = new GalleryWidget(device, this); - // m_fileExplorerWidget = new FileExplorerWidget(device, this); + m_deviceInfoWidget = new DeviceInfoWidget(m_device, this); + m_installedAppsWidget = new InstalledAppsWidget(m_device, this); + m_galleryWidget = new GalleryWidget(m_device, this); + m_fileExplorerWidget = new FileExplorerWidget(m_device, this); // Set minimum heights m_galleryWidget->setMinimumHeight(300); - // m_fileExplorerWidget->setMinimumHeight(300); + m_fileExplorerWidget->setMinimumHeight(300); - stackedWidget->addWidget(m_deviceInfoWidget); // Index 0 - Info - // stackedWidget->addWidget(m_installedAppsWidget); // Index 1 - Apps - stackedWidget->addWidget(m_galleryWidget); // Index 2 - Gallery - // stackedWidget->addWidget(m_fileExplorerWidget); // Index 3 - Files + stackedWidget->addWidget(m_deviceInfoWidget); // Index 0 - Info + stackedWidget->addWidget(m_installedAppsWidget); // Index 1 - Apps + stackedWidget->addWidget(m_galleryWidget); // Index 2 - Gallery + stackedWidget->addWidget(m_fileExplorerWidget); // Index 3 - Files // Set default to Info tab stackedWidget->setCurrentWidget(m_deviceInfoWidget); @@ -83,19 +90,47 @@ void DeviceMenuWidget::init() QWidget *loadingWidget = stackedWidget->widget(0); stackedWidget->removeWidget(loadingWidget); loadingWidget->deleteLater(); + + if (m_device->deviceInfo.parsedDeviceVersion.major < 13) { + Toast *toast = new Toast(this); + toast->setAttribute(Qt::WA_DeleteOnClose); + toast->setDuration(8000); // Hide after 8 seconds + toast->setTitle("Not wireless compatible"); + toast->setText("This device is not wireless compatible."); + toast->setPosition(ToastPosition::BOTTOM_MIDDLE); + toast->show(); + } else { + if (m_device->deviceInfo.isWireless) + return; + bool enabled = ServiceManager::enableWirelessConnections(m_device); + Toast *toast = new Toast(this); + toast->setAttribute(Qt::WA_DeleteOnClose); + toast->setDuration(8000); // Hide after 8 seconds + toast->setPosition(ToastPosition::BOTTOM_MIDDLE); + if (enabled) { + toast->setTitle("Wireless connections enabled"); + toast->setText( + "You can now use wireless connections with this device."); + } else { + toast->setTitle("Failed to enable wireless connections"); + toast->setText( + "Could not enable wireless connections for this device."); + } + toast->show(); + } } void DeviceMenuWidget::switchToTab(const QString &tabName) { if (tabName == "Info") { stackedWidget->setCurrentWidget(m_deviceInfoWidget); - // } else if (tabName == "Apps") { - // stackedWidget->setCurrentWidget(m_installedAppsWidget); + } else if (tabName == "Apps") { + stackedWidget->setCurrentWidget(m_installedAppsWidget); } else if (tabName == "Gallery") { qDebug() << "Switching to Gallery tab"; stackedWidget->setCurrentWidget(m_galleryWidget); } else if (tabName == "Files") { - // stackedWidget->setCurrentWidget(m_fileExplorerWidget); + stackedWidget->setCurrentWidget(m_fileExplorerWidget); } else { qDebug() << "Tab not found:" << tabName; } @@ -104,4 +139,4 @@ void DeviceMenuWidget::switchToTab(const QString &tabName) DeviceMenuWidget::~DeviceMenuWidget() { qDebug() << "DeviceMenuWidget destructor called"; -} \ No newline at end of file +} diff --git a/src/devicemenuwidget.h b/src/devicemenuwidget.h index 326bb8d..985c2d1 100644 --- a/src/devicemenuwidget.h +++ b/src/devicemenuwidget.h @@ -20,10 +20,10 @@ #ifndef DEVICEMENUWIDGET_H #define DEVICEMENUWIDGET_H #include "deviceinfowidget.h" -// #include "fileexplorerwidget.h" +#include "fileexplorerwidget.h" #include "gallerywidget.h" #include "iDescriptor.h" -// #include "installedappswidget.h" +#include "installedappswidget.h" #include #include @@ -39,11 +39,11 @@ public: private: QStackedWidget *stackedWidget; // Pointer to the stacked widget - iDescriptorDevice *device; // Pointer to the iDescriptor device + iDescriptorDevice *m_device; // Pointer to the iDescriptor device DeviceInfoWidget *m_deviceInfoWidget; - // InstalledAppsWidget *m_installedAppsWidget; + InstalledAppsWidget *m_installedAppsWidget; GalleryWidget *m_galleryWidget; - // FileExplorerWidget *m_fileExplorerWidget; + FileExplorerWidget *m_fileExplorerWidget; signals: }; diff --git a/src/devicesidebarwidget.h b/src/devicesidebarwidget.h index 1ff9a0e..fe3ade1 100644 --- a/src/devicesidebarwidget.h +++ b/src/devicesidebarwidget.h @@ -128,6 +128,18 @@ struct DeviceSelection { uint64_t ecid = 0; QString section = "Info"; + bool valid() const + { + if (type == Normal) { + return !udid.empty(); + } else if (type == Recovery) { + return ecid != 0; + } else if (type == Pending) { + return !udid.empty(); + } + return false; + } + DeviceSelection(const std::string &deviceUdid, const QString &nav = "") : type(Normal), udid(deviceUdid), section(nav) { diff --git a/src/diskusagewidget.cpp b/src/diskusagewidget.cpp index b2fc4cf..60d89a3 100644 --- a/src/diskusagewidget.cpp +++ b/src/diskusagewidget.cpp @@ -27,10 +27,6 @@ #include #include -// #include -// #include -// #include - DiskUsageWidget::DiskUsageWidget(iDescriptorDevice *device, QWidget *parent) : QWidget(parent), m_device(device), m_state(Loading), m_totalCapacity(0), m_systemUsage(0), m_appsUsage(0), m_mediaUsage(0), m_othersUsage(0), @@ -404,90 +400,92 @@ void DiskUsageWidget::fetchData() QFuture future = QtConcurrent::run([this]() -> QVariantMap { QVariantMap result; - if (!m_device || !m_device->provider) { - result["error"] = "Invalid device."; - return result; - } + // if (!m_device || !m_device->provider) { + // result["error"] = "Invalid device."; + // return result; + // } - // Pre-populate with known info - result["totalCapacity"] = QVariant::fromValue( - m_device->deviceInfo.diskInfo.totalDiskCapacity); - result["freeSpace"] = QVariant::fromValue( - m_device->deviceInfo.diskInfo.totalDataAvailable); - result["systemUsage"] = QVariant::fromValue( - m_device->deviceInfo.diskInfo.totalSystemCapacity); + // // Pre-populate with known info + // result["totalCapacity"] = QVariant::fromValue( + // m_device->deviceInfo.diskInfo.totalDiskCapacity); + // result["freeSpace"] = QVariant::fromValue( + // m_device->deviceInfo.diskInfo.totalDataAvailable); + // result["systemUsage"] = QVariant::fromValue( + // m_device->deviceInfo.diskInfo.totalSystemCapacity); - // Create provider wrapper from existing handle - Provider provider = Provider::adopt(m_device->provider); + // // Create provider wrapper from existing handle + // TODO:remove + // Provider provider = Provider::adopt(m_device->provider); - // Apps usage - uint64_t totalAppsSpace = 0; - auto instproxy_res = IdeviceFFI::InstallationProxy::connect(provider); - if (instproxy_res.is_err()) { - result["error"] = - "Could not connect to installation proxy: " + - QString::fromStdString(instproxy_res.unwrap_err().message); - return result; - } - auto instproxy = std::move(instproxy_res.unwrap()); + // // Apps usage + // uint64_t totalAppsSpace = 0; + // auto instproxy_res = + // IdeviceFFI::InstallationProxy::connect(provider); if + // (instproxy_res.is_err()) { + // result["error"] = + // "Could not connect to installation proxy: " + + // QString::fromStdString(instproxy_res.unwrap_err().message); + // return result; + // } + // auto instproxy = std::move(instproxy_res.unwrap()); - plist_t client_opts = plist_new_dict(); - plist_dict_set_item(client_opts, "ApplicationType", - plist_new_string("User")); + // plist_t client_opts = plist_new_dict(); + // plist_dict_set_item(client_opts, "ApplicationType", + // plist_new_string("User")); - plist_t return_attrs = plist_new_array(); - plist_array_append_item(return_attrs, - plist_new_string("StaticDiskUsage")); - plist_array_append_item(return_attrs, - plist_new_string("DynamicDiskUsage")); - plist_dict_set_item(client_opts, "ReturnAttributes", return_attrs); + // plist_t return_attrs = plist_new_array(); + // plist_array_append_item(return_attrs, + // plist_new_string("StaticDiskUsage")); + // plist_array_append_item(return_attrs, + // plist_new_string("DynamicDiskUsage")); + // plist_dict_set_item(client_opts, "ReturnAttributes", return_attrs); - auto apps_result = instproxy.browse(client_opts); - if (apps_result.is_ok()) { - auto apps = std::move(apps_result.unwrap()); - for (const auto &app_info : apps) { - plist_t static_usage = - plist_dict_get_item(app_info, "StaticDiskUsage"); - if (static_usage && - plist_get_node_type(static_usage) == PLIST_UINT) { - uint64_t static_size = 0; - plist_get_uint_val(static_usage, &static_size); - totalAppsSpace += static_size; - } + // auto apps_result = instproxy.browse(client_opts); + // if (apps_result.is_ok()) { + // auto apps = std::move(apps_result.unwrap()); + // for (const auto &app_info : apps) { + // plist_t static_usage = + // plist_dict_get_item(app_info, "StaticDiskUsage"); + // if (static_usage && + // plist_get_node_type(static_usage) == PLIST_UINT) { + // uint64_t static_size = 0; + // plist_get_uint_val(static_usage, &static_size); + // totalAppsSpace += static_size; + // } - plist_t dynamic_usage = - plist_dict_get_item(app_info, "DynamicDiskUsage"); - if (dynamic_usage && - plist_get_node_type(dynamic_usage) == PLIST_UINT) { - uint64_t dynamic_size = 0; - plist_get_uint_val(dynamic_usage, &dynamic_size); - totalAppsSpace += dynamic_size; - } - } - } - result["appsUsage"] = QVariant::fromValue(totalAppsSpace); + // plist_t dynamic_usage = + // plist_dict_get_item(app_info, "DynamicDiskUsage"); + // if (dynamic_usage && + // plist_get_node_type(dynamic_usage) == PLIST_UINT) { + // uint64_t dynamic_size = 0; + // plist_get_uint_val(dynamic_usage, &dynamic_size); + // totalAppsSpace += dynamic_size; + // } + // } + // } + // result["appsUsage"] = QVariant::fromValue(totalAppsSpace); // plist_free(client_opts); // client_opts is consumed by browse, but // Media usage - uint64_t mediaSpace = 0; - IdeviceFFI::Lockdown lockdown = - IdeviceFFI::Lockdown::adopt(m_device->lockdown); - auto itunes_info_res = - lockdown.get_value("com.apple.mobile.iTunes", nullptr); - if (itunes_info_res.is_ok()) { - auto itunes_dict = std::move(itunes_info_res.unwrap()); - if (itunes_dict) { - plist_t media_node = - plist_dict_get_item(itunes_dict, "MediaLibrarySize"); - if (media_node && - plist_get_node_type(media_node) == PLIST_UINT) { - plist_get_uint_val(media_node, &mediaSpace); - } - } - } - result["mediaUsage"] = QVariant::fromValue(mediaSpace); + // uint64_t mediaSpace = 0; + // IdeviceFFI::Lockdown lockdown = + // IdeviceFFI::Lockdown::adopt(m_device->lockdown); + // auto itunes_info_res = + // lockdown.get_value("com.apple.mobile.iTunes", nullptr); + // if (itunes_info_res.is_ok()) { + // auto itunes_dict = std::move(itunes_info_res.unwrap()); + // if (itunes_dict) { + // plist_t media_node = + // plist_dict_get_item(itunes_dict, "MediaLibrarySize"); + // if (media_node && + // plist_get_node_type(media_node) == PLIST_UINT) { + // plist_get_uint_val(media_node, &mediaSpace); + // } + // } + // } + // result["mediaUsage"] = QVariant::fromValue(mediaSpace); return result; }); - watcher->setFuture(future); + // watcher->setFuture(future); } diff --git a/src/exportmanager.cpp b/src/exportmanager.cpp index c37ff01..adef72b 100644 --- a/src/exportmanager.cpp +++ b/src/exportmanager.cpp @@ -60,7 +60,7 @@ ExportManager::~ExportManager() QUuid ExportManager::startExport(iDescriptorDevice *device, const QList &items, const QString &destinationPath, - std::optional altAfc) + std::optional altAfc) { if (!device || !device->mutex) { qWarning() << "Invalid device provided to ExportManager"; @@ -193,116 +193,115 @@ void ExportManager::executeExportJob(ExportJob *job) emit exportFinished(job->jobId, summary); } - -ExportResult ExportManager::exportSingleItem(iDescriptorDevice *device, - const ExportItem &item, - const QString &destinationDir, - std::optional altAfc, - std::atomic &cancelRequested, - const QUuid &jobId) +// TODO: implement +ExportResult ExportManager::exportSingleItem( + iDescriptorDevice *device, const ExportItem &item, + const QString &destinationDir, std::optional altAfc, + std::atomic &cancelRequested, const QUuid &jobId) { ExportResult result; result.sourceFilePath = item.sourcePathOnDevice; - // Generate output path - QString outputPath = QDir(destinationDir).filePath(item.suggestedFileName); - outputPath = generateUniqueOutputPath(outputPath); - result.outputFilePath = outputPath; + // // Generate output path + // QString outputPath = + // QDir(destinationDir).filePath(item.suggestedFileName); outputPath = + // generateUniqueOutputPath(outputPath); result.outputFilePath = outputPath; - // Get file size first - char **info = nullptr; - afc_error_t infoResult = ServiceManager::safeAfcGetFileInfo( - device, item.sourcePathOnDevice.toUtf8().constData(), &info, altAfc); + // // Get file size first + // char **info = nullptr; + // afc_error_t infoResult = ServiceManager::safeAfcGetFileInfo( + // device, item.sourcePathOnDevice.toUtf8().constData(), &info, altAfc); - qint64 totalFileSize = 0; - if (infoResult == AFC_E_SUCCESS && info) { - for (int i = 0; info[i]; i += 2) { - if (strcmp(info[i], "st_size") == 0) { - totalFileSize = QString::fromUtf8(info[i + 1]).toLongLong(); - break; - } - } - afc_dictionary_free(info); - } + // qint64 totalFileSize = 0; + // if (infoResult == AFC_E_SUCCESS && info) { + // for (int i = 0; info[i]; i += 2) { + // if (strcmp(info[i], "st_size") == 0) { + // totalFileSize = QString::fromUtf8(info[i + 1]).toLongLong(); + // break; + // } + // } + // afc_dictionary_free(info); + // } - // Open file on device - uint64_t handle = 0; - afc_error_t openResult = ServiceManager::safeAfcFileOpen( - device, item.sourcePathOnDevice.toUtf8().constData(), AFC_FOPEN_RDONLY, - &handle, altAfc); + // // Open file on device + // uint64_t handle = 0; + // afc_error_t openResult = ServiceManager::safeAfcFileOpen( + // device, item.sourcePathOnDevice.toUtf8().constData(), + // AFC_FOPEN_RDONLY, &handle, altAfc); - if (openResult != AFC_E_SUCCESS) { - result.errorMessage = - QString("Failed to open file on device: %1 (AFC error: %2)") - .arg(item.sourcePathOnDevice) - .arg(static_cast(openResult)); - return result; - } + // if (openResult != AFC_E_SUCCESS) { + // result.errorMessage = + // QString("Failed to open file on device: %1 (AFC error: %2)") + // .arg(item.sourcePathOnDevice) + // .arg(static_cast(openResult)); + // return result; + // } - // Open local output file - QFile outputFile(outputPath); - if (!outputFile.open(QIODevice::WriteOnly)) { - result.errorMessage = QString("Failed to create local file: %1 (%2)") - .arg(outputPath) - .arg(outputFile.errorString()); - ServiceManager::safeAfcFileClose(device, handle, altAfc); - return result; - } + // // Open local output file + // QFile outputFile(outputPath); + // if (!outputFile.open(QIODevice::WriteOnly)) { + // result.errorMessage = QString("Failed to create local file: %1 (%2)") + // .arg(outputPath) + // .arg(outputFile.errorString()); + // ServiceManager::safeAfcFileClose(device, handle, altAfc); + // return result; + // } - char buffer[8192]; - uint32_t bytesRead = 0; - qint64 totalBytes = 0; + // char buffer[8192]; + // uint32_t bytesRead = 0; + // qint64 totalBytes = 0; - while (true) { - // Check for cancellation during file copy - if (cancelRequested.load()) { - outputFile.close(); - outputFile.remove(); // Clean up partial file - ServiceManager::safeAfcFileClose(device, handle, altAfc); - result.errorMessage = "Export cancelled by user"; - return result; - } + // while (true) { + // // Check for cancellation during file copy + // if (cancelRequested.load()) { + // outputFile.close(); + // outputFile.remove(); // Clean up partial file + // ServiceManager::safeAfcFileClose(device, handle, altAfc); + // result.errorMessage = "Export cancelled by user"; + // return result; + // } - afc_error_t readResult = ServiceManager::safeAfcFileRead( - device, handle, buffer, sizeof(buffer), &bytesRead, altAfc); + // afc_error_t readResult = ServiceManager::safeAfcFileRead( + // device, handle, buffer, sizeof(buffer), &bytesRead, altAfc); - if (readResult != AFC_E_SUCCESS || bytesRead == 0) { - break; // End of file or error - } + // if (readResult != AFC_E_SUCCESS || bytesRead == 0) { + // break; // End of file or error + // } - qint64 bytesWritten = outputFile.write(buffer, bytesRead); - if (bytesWritten != bytesRead) { - result.errorMessage = - QString("Write error: only wrote %1 of %2 bytes") - .arg(bytesWritten) - .arg(bytesRead); - outputFile.close(); - outputFile.remove(); // Clean up partial file - ServiceManager::safeAfcFileClose(device, handle, altAfc); - return result; - } + // qint64 bytesWritten = outputFile.write(buffer, bytesRead); + // if (bytesWritten != bytesRead) { + // result.errorMessage = + // QString("Write error: only wrote %1 of %2 bytes") + // .arg(bytesWritten) + // .arg(bytesRead); + // outputFile.close(); + // outputFile.remove(); // Clean up partial file + // ServiceManager::safeAfcFileClose(device, handle, altAfc); + // return result; + // } - totalBytes += bytesRead; + // totalBytes += bytesRead; - // Emit progress update every 64KB or at end of file - if (totalBytes % (64 * 1024) == 0 || totalBytes == totalFileSize) { - emit fileTransferProgress(jobId, item.suggestedFileName, totalBytes, - totalFileSize); - } - } + // // Emit progress update every 64KB or at end of file + // if (totalBytes % (64 * 1024) == 0 || totalBytes == totalFileSize) { + // emit fileTransferProgress(jobId, item.suggestedFileName, + // totalBytes, + // totalFileSize); + // } + // } - // Clean up - outputFile.close(); - ServiceManager::safeAfcFileClose(device, handle, altAfc); + // // Clean up + // outputFile.close(); + // ServiceManager::safeAfcFileClose(device, handle, altAfc); - if (totalBytes == 0) { - result.errorMessage = "No data read from device file"; - outputFile.remove(); // Clean up empty file - return result; - } + // if (totalBytes == 0) { + // result.errorMessage = "No data read from device file"; + // outputFile.remove(); // Clean up empty file + // return result; + // } result.success = true; - result.bytesTransferred = totalBytes; + // result.bytesTransferred = totalBytes; return result; } diff --git a/src/exportmanager.h b/src/exportmanager.h index 949d2e9..7594a18 100644 --- a/src/exportmanager.h +++ b/src/exportmanager.h @@ -78,7 +78,7 @@ public: QUuid startExport(iDescriptorDevice *device, const QList &items, const QString &destinationPath, - std::optional altAfc = std::nullopt); + std::optional altAfc = std::nullopt); void cancelExport(const QUuid &jobId); @@ -113,7 +113,7 @@ private: iDescriptorDevice *device = nullptr; QList items; QString destinationPath; - std::optional altAfc; + std::optional altAfc; std::atomic cancelRequested{false}; QFuture future; QFutureWatcher *watcher = nullptr; @@ -124,7 +124,7 @@ private: ExportResult exportSingleItem(iDescriptorDevice *device, const ExportItem &item, const QString &destinationDir, - std::optional altAfc, + std::optional altAfc, std::atomic &cancelRequested, const QUuid &jobId); diff --git a/src/gallerywidget.cpp b/src/gallerywidget.cpp index 924a85e..f0759ee 100644 --- a/src/gallerywidget.cpp +++ b/src/gallerywidget.cpp @@ -20,7 +20,7 @@ #include "gallerywidget.h" // #include "exportmanager.h" #include "iDescriptor.h" -// #include "mediapreviewdialog.h" +#include "mediapreviewdialog.h" #include "photomodel.h" #include "servicemanager.h" #include @@ -134,7 +134,7 @@ void GalleryWidget::setupControlsLayout() static_cast(PhotoModel::ImagesOnly)); m_filterComboBox->addItem("Videos Only", static_cast(PhotoModel::VideosOnly)); - m_filterComboBox->setCurrentIndex(0); // Default to All + m_filterComboBox->setCurrentIndex(2); // Default to All m_filterComboBox->setMinimumWidth(100); // Ensure text fits m_filterComboBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); @@ -408,11 +408,11 @@ void GalleryWidget::setupPhotoGalleryView() if (filePath.isEmpty()) return; - // qDebug() << "Opening preview for" << filePath; - // auto *previewDialog = new MediaPreviewDialog( - // m_device, m_device->afcClient, filePath, this); - // previewDialog->setAttribute(Qt::WA_DeleteOnClose); - // previewDialog->show(); + qDebug() << "Opening preview for" << filePath; + auto *previewDialog = new MediaPreviewDialog( + m_device, m_device->afcClient, filePath, this); + previewDialog->setAttribute(Qt::WA_DeleteOnClose); + previewDialog->show(); }); connect(m_listView, &QListView::customContextMenuRequested, this, @@ -627,21 +627,21 @@ void GalleryWidget::onPhotoContextMenu(const QPoint &pos) exportAction->setEnabled(m_listView->selectionModel()->hasSelection()); - // connect(previewAction, &QAction::triggered, this, [this, index]() { - // // Re-use the double-click logic - // if (!index.isValid()) - // return; + connect(previewAction, &QAction::triggered, this, [this, index]() { + // Re-use the double-click logic + if (!index.isValid()) + return; - // QString filePath = m_model->data(index, Qt::UserRole).toString(); - // if (filePath.isEmpty()) - // return; + QString filePath = m_model->data(index, Qt::UserRole).toString(); + if (filePath.isEmpty()) + return; - // qDebug() << "Opening preview for" << filePath; - // auto *previewDialog = new MediaPreviewDialog( - // m_device, m_device->afcClient, filePath, this); - // previewDialog->setAttribute(Qt::WA_DeleteOnClose); - // previewDialog->show(); - // }); + qDebug() << "Opening preview for" << filePath; + auto *previewDialog = new MediaPreviewDialog( + m_device, m_device->afcClient, filePath, this); + previewDialog->setAttribute(Qt::WA_DeleteOnClose); + previewDialog->show(); + }); connect(exportAction, &QAction::triggered, this, &GalleryWidget::onExportSelected); diff --git a/src/iDescriptor.h b/src/iDescriptor.h index dd52673..9409e02 100644 --- a/src/iDescriptor.h +++ b/src/iDescriptor.h @@ -66,6 +66,10 @@ ((((maj) & 0xFF) << 16) | (((min) & 0xFF) << 8) | ((patch) & 0xFF)) #include "devicemonitor.h" +#define DeviceLockedMountErrorCode -21 +#define NotFoundErrorCode -14 +#define DISK_IMAGE_TYPE_DEVELOPER "Developer" + struct BatteryInfo { QString health; uint64_t cycleCount; @@ -104,6 +108,12 @@ struct DiskInfo { uint64_t totalDataAvailable; }; +struct DeviceVersion { + unsigned int major; + unsigned int minor; + unsigned int patch; +}; + // Carefull not all the vars are initialized in init_device.cpp struct DeviceInfo { enum class ActivationState { @@ -180,8 +190,9 @@ struct DeviceInfo { std::string marketingName; std::string regionRaw; std::string region; - unsigned int parsedDeviceVersion; + DeviceVersion parsedDeviceVersion; std::string wifiMacAddress; + bool isWireless = false; }; struct iDescriptorDevice { @@ -192,8 +203,11 @@ struct iDescriptorDevice { AfcClientHandle *afcClient; AfcClientHandle *afc2Client; LockdowndClientHandle *lockdown; - bool is_iPhone; std::recursive_mutex *mutex; + ImageMounterHandle *imageMounter; + std::shared_ptr diagRelay; + ScreenshotrClientHandle *screenshotrClient; + LocationSimulationHandle *locationSimulation; }; struct iDescriptorInitDeviceResult { @@ -204,6 +218,10 @@ struct iDescriptorInitDeviceResult { AfcClientHandle *afcClient; AfcClientHandle *afc2Client; LockdowndClientHandle *lockdown; + ImageMounterHandle *imageMounter; + std::shared_ptr diagRelay; + ScreenshotrClientHandle *screenshotrClient; + LocationSimulationHandle *locationSimulation; }; // #ifdef ENABLE_RECOVERY_DEVICE_SUPPORT // struct iDescriptorRecoveryDevice { @@ -256,6 +274,25 @@ public: return PlistNavigator(next); } + PlistNavigator operator[](const std::string &key) + { + if (!current_node || plist_get_node_type(current_node) != PLIST_DICT) { + return PlistNavigator(nullptr); + } + plist_t next = plist_dict_get_item(current_node, key.c_str()); + return PlistNavigator(next); + } + + PlistNavigator operator[](const QString &key) + { + if (!current_node || plist_get_node_type(current_node) != PLIST_DICT) { + return PlistNavigator(nullptr); + } + plist_t next = + plist_dict_get_item(current_node, key.toUtf8().constData()); + return PlistNavigator(next); + } + // array index access PlistNavigator operator[](int index) { @@ -303,6 +340,47 @@ public: return result; } plist_t getNode() const { return current_node; } + + QVariant toQVariant() const + { + if (!current_node) { + return QVariant(); + } + // TODO: free + plist_type type = plist_get_node_type(current_node); + switch (type) { + case PLIST_BOOLEAN: { + uint8_t val; + plist_get_bool_val(current_node, &val); + return QVariant(static_cast(val)); + } + case PLIST_UINT: { + uint64_t val; + plist_get_uint_val(current_node, &val); + return QVariant(static_cast(val)); + } + case PLIST_STRING: { + char *val = nullptr; + plist_get_string_val(current_node, &val); + QString str_val = QString::fromUtf8(val); + if (val) + free(val); + return QVariant(str_val); + } + case PLIST_REAL: { + double val; + plist_get_real_val(current_node, &val); + return QVariant(val); + } + case PLIST_DICT: + case PLIST_ARRAY: + case PLIST_DATA: + case PLIST_DATE: + default: { + return QVariant("Unsupported Type"); + } + } + } }; // afc_error_t safe_afc_read_directory(afc_client_t afcClient, idevice_t device, @@ -351,33 +429,41 @@ init_idescriptor_device(const QString &udid, // TakeScreenshotResult take_screenshot(screenshotr_client_t shotr); -// mobile_image_mounter_error_t mount_dev_image(idevice_t device, -// unsigned int device_version, -// const char *image_dir_path); -// struct GetMountedImageResult { -// bool success; -// std::string sig; -// std::string message; -// }; +IdeviceFfiError *mount_dev_image(const iDescriptorDevice *device, + const char *image_file, + const char *signature_file); -// plist_t _get_mounted_image(const char *udid); +struct MountedImageInfo { + IdeviceFfiError *err; + uint8_t *signature; + size_t signature_len; +}; + +struct MountedImageResult { + bool success; + IdeviceFfiError *err; +}; + +MountedImageInfo _get_mounted_image(const iDescriptorDevice *device); + +void mounted_image_info_free(MountedImageInfo &info); // bool restart(std::string udid); -// enum class ImageCompatibility { -// Compatible, // Exact match or known compatible version -// MaybeCompatible, // Major version matches but minor doesn't -// NotCompatible // Not compatible -// }; +enum class ImageCompatibility { + Compatible, // Exact match or known compatible version + MaybeCompatible, // Major version matches but minor doesn't + NotCompatible // Not compatible +}; -// struct ImageInfo { -// QString version; -// QString dmgPath; -// QString sigPath; -// ImageCompatibility compatibility = ImageCompatibility::NotCompatible; -// bool isDownloaded = false; -// bool isMounted = false; -// }; +struct ImageInfo { + QString version; + QString dmgPath; + QString sigPath; + ImageCompatibility compatibility = ImageCompatibility::NotCompatible; + bool isDownloaded = false; + bool isMounted = false; +}; // /** // * @brief Compare two iPhone product types to determine which is newer @@ -428,19 +514,18 @@ bool is_product_type_newer(const std::string &productType, // std::string safeGetXML(const char *key, pugi::xml_node dict); -void get_battery_info(IdeviceProviderHandle *provider, plist_t &diagnostics); +void get_battery_info(DiagnosticsRelay *diagRelay, plist_t &diagnostics); // void parseOldDeviceBattery(PlistNavigator &ioreg, DeviceInfo &d); // void parseDeviceBattery(PlistNavigator &ioreg, DeviceInfo &d); -// void fetchAppIconFromApple(QNetworkAccessManager *manager, -// const QString &bundleId, -// std::function -// callback); +void fetchAppIconFromApple(QNetworkAccessManager *manager, + const QString &bundleId, + std::function callback); // afc_error_t afc2_client_new(idevice_t device, afc_client_t *afc); -// void get_cable_info(idevice_t device, plist_t &response); +void _get_cable_info(const iDescriptorDevice *device, plist_t &response); struct NetworkDevice { QString name; // service name @@ -462,8 +547,8 @@ QByteArray read_afc_file_to_byte_array(const iDescriptorDevice *device, bool isDarkMode(); -// instproxy_error_t install_IPA(idevice_t device, afc_client_t afc, -// const char *filePath); +IdeviceFfiError *_install_IPA(const iDescriptorDevice *device, + const char *filePath, const char *ipaName); // Helper struct for semantic version comparison struct AppVersion { @@ -542,4 +627,46 @@ inline QJsonObject getVersionedConfig(const QJsonObject &rootObj) } } return QJsonObject(); +} + +inline void free_directory_listing(char **entries, size_t count) +{ + if (!entries) + return; + for (size_t i = 0; i < count; i++) { + if (entries[i]) { + free(entries[i]); + } + } + free(entries); +} + +inline int read_file(const char *filename, uint8_t **data, size_t *length) +{ + FILE *file = fopen(filename, "rb"); + if (!file) { + perror("Failed to open file"); + return 0; + } + + fseek(file, 0, SEEK_END); + *length = ftell(file); + fseek(file, 0, SEEK_SET); + + *data = (uint8_t *)malloc(*length); + if (!*data) { + perror("Failed to allocate memory"); + fclose(file); + return 0; + } + + if (fread(*data, 1, *length, file) != *length) { + perror("Failed to read file"); + free(*data); + fclose(file); + return 0; + } + + fclose(file); + return 1; } \ No newline at end of file diff --git a/src/installedappswidget.cpp b/src/installedappswidget.cpp index 3786b4e..89d8778 100644 --- a/src/installedappswidget.cpp +++ b/src/installedappswidget.cpp @@ -34,11 +34,6 @@ #include #include #include -#include -#include -#include -#include -#include AppTabWidget::AppTabWidget(const QString &appName, const QString &bundleId, const QString &version, QWidget *parent) @@ -291,180 +286,181 @@ void InstalledAppsWidget::createContentWidget() // todo: move to services void InstalledAppsWidget::fetchInstalledApps() { - if (!m_device || !m_device->device) { + if (!m_device) { showErrorState("Invalid device"); return; } - QFuture future = QtConcurrent::run([this]() -> QVariantMap { - QVariantMap result; - QVariantList apps; + // QFuture future = QtConcurrent::run([this]() -> QVariantMap { + // QVariantMap result; + // QVariantList apps; - // result["success"] = true; - // result["apps"] = apps; - // return result; + // // result["success"] = true; + // // result["apps"] = apps; + // // return result; - instproxy_client_t instproxy = nullptr; - lockdownd_client_t lockdownClient = nullptr; - lockdownd_service_descriptor_t lockdowndService = nullptr; + // instproxy_client_t instproxy = nullptr; + // lockdownd_client_t lockdownClient = nullptr; + // lockdownd_service_descriptor_t lockdowndService = nullptr; - try { - if (lockdownd_client_new_with_handshake( - m_device->device, &lockdownClient, APP_LABEL) != - LOCKDOWN_E_SUCCESS) { - result["error"] = "Could not connect to lockdown service"; - return result; - } + // try { + // if (lockdownd_client_new_with_handshake( + // m_device->device, &lockdownClient, APP_LABEL) != + // LOCKDOWN_E_SUCCESS) { + // result["error"] = "Could not connect to lockdown service"; + // return result; + // } - if (lockdownd_start_service( - lockdownClient, "com.apple.mobile.installation_proxy", - &lockdowndService) != LOCKDOWN_E_SUCCESS) { - result["error"] = "Could not start installation proxy service"; - lockdownd_client_free(lockdownClient); - return result; - } + // if (lockdownd_start_service( + // lockdownClient, "com.apple.mobile.installation_proxy", + // &lockdowndService) != LOCKDOWN_E_SUCCESS) { + // result["error"] = "Could not start installation proxy + // service"; lockdownd_client_free(lockdownClient); return + // result; + // } - if (instproxy_client_new(m_device->device, lockdowndService, - &instproxy) != INSTPROXY_E_SUCCESS) { - result["error"] = "Could not connect to installation proxy"; - lockdownd_service_descriptor_free(lockdowndService); - lockdownd_client_free(lockdownClient); - return result; - } + // if (instproxy_client_new(m_device->device, lockdowndService, + // &instproxy) != INSTPROXY_E_SUCCESS) { + // result["error"] = "Could not connect to installation proxy"; + // lockdownd_service_descriptor_free(lockdowndService); + // lockdownd_client_free(lockdownClient); + // return result; + // } - lockdownd_service_descriptor_free(lockdowndService); - lockdowndService = nullptr; + // lockdownd_service_descriptor_free(lockdowndService); + // lockdowndService = nullptr; - // Get both User and System apps - QStringList appTypes = {"User", "System"}; + // // Get both User and System apps + // QStringList appTypes = {"User", "System"}; - for (const QString &appType : appTypes) { - plist_t client_opts = plist_new_dict(); - plist_dict_set_item( - client_opts, "ApplicationType", - plist_new_string(appType.toUtf8().constData())); + // for (const QString &appType : appTypes) { + // plist_t client_opts = plist_new_dict(); + // plist_dict_set_item( + // client_opts, "ApplicationType", + // plist_new_string(appType.toUtf8().constData())); - plist_t return_attrs = plist_new_array(); - plist_array_append_item(return_attrs, - plist_new_string("CFBundleIdentifier")); - plist_array_append_item( - return_attrs, plist_new_string("CFBundleDisplayName")); - plist_array_append_item( - return_attrs, - plist_new_string("CFBundleShortVersionString")); - plist_array_append_item(return_attrs, - plist_new_string("CFBundleVersion")); - plist_array_append_item( - return_attrs, plist_new_string("UIFileSharingEnabled")); + // plist_t return_attrs = plist_new_array(); + // plist_array_append_item(return_attrs, + // plist_new_string("CFBundleIdentifier")); + // plist_array_append_item( + // return_attrs, plist_new_string("CFBundleDisplayName")); + // plist_array_append_item( + // return_attrs, + // plist_new_string("CFBundleShortVersionString")); + // plist_array_append_item(return_attrs, + // plist_new_string("CFBundleVersion")); + // plist_array_append_item( + // return_attrs, plist_new_string("UIFileSharingEnabled")); - plist_dict_set_item(client_opts, "ReturnAttributes", - return_attrs); + // plist_dict_set_item(client_opts, "ReturnAttributes", + // return_attrs); - plist_t apps_plist = nullptr; - if (instproxy_browse(instproxy, client_opts, &apps_plist) == - INSTPROXY_E_SUCCESS && - apps_plist) { - if (plist_get_node_type(apps_plist) == PLIST_ARRAY) { - for (uint32_t i = 0; - i < plist_array_get_size(apps_plist); i++) { - plist_t app_info = - plist_array_get_item(apps_plist, i); - if (!app_info) - continue; + // plist_t apps_plist = nullptr; + // if (instproxy_browse(instproxy, client_opts, &apps_plist) == + // INSTPROXY_E_SUCCESS && + // apps_plist) { + // if (plist_get_node_type(apps_plist) == PLIST_ARRAY) { + // for (uint32_t i = 0; + // i < plist_array_get_size(apps_plist); i++) { + // plist_t app_info = + // plist_array_get_item(apps_plist, i); + // if (!app_info) + // continue; - QVariantMap appData; + // QVariantMap appData; - // Get bundle identifier - plist_t bundle_id = plist_dict_get_item( - app_info, "CFBundleIdentifier"); - if (bundle_id && plist_get_node_type(bundle_id) == - PLIST_STRING) { - char *bundle_id_str = nullptr; - plist_get_string_val(bundle_id, &bundle_id_str); - if (bundle_id_str) { - appData["bundleId"] = - QString(bundle_id_str); - free(bundle_id_str); - } - } + // // Get bundle identifier + // plist_t bundle_id = plist_dict_get_item( + // app_info, "CFBundleIdentifier"); + // if (bundle_id && plist_get_node_type(bundle_id) + // == + // PLIST_STRING) { + // char *bundle_id_str = nullptr; + // plist_get_string_val(bundle_id, + // &bundle_id_str); if (bundle_id_str) { + // appData["bundleId"] = + // QString(bundle_id_str); + // free(bundle_id_str); + // } + // } - // Get display name - plist_t display_name = plist_dict_get_item( - app_info, "CFBundleDisplayName"); - if (display_name && - plist_get_node_type(display_name) == - PLIST_STRING) { - char *display_name_str = nullptr; - plist_get_string_val(display_name, - &display_name_str); - if (display_name_str) { - appData["displayName"] = - QString(display_name_str); - free(display_name_str); - } - } + // // Get display name + // plist_t display_name = plist_dict_get_item( + // app_info, "CFBundleDisplayName"); + // if (display_name && + // plist_get_node_type(display_name) == + // PLIST_STRING) { + // char *display_name_str = nullptr; + // plist_get_string_val(display_name, + // &display_name_str); + // if (display_name_str) { + // appData["displayName"] = + // QString(display_name_str); + // free(display_name_str); + // } + // } - // Get version - plist_t version = plist_dict_get_item( - app_info, "CFBundleShortVersionString"); - if (version && - plist_get_node_type(version) == PLIST_STRING) { - char *version_str = nullptr; - plist_get_string_val(version, &version_str); - if (version_str) { - appData["version"] = QString(version_str); - free(version_str); - } - } + // // Get version + // plist_t version = plist_dict_get_item( + // app_info, "CFBundleShortVersionString"); + // if (version && + // plist_get_node_type(version) == PLIST_STRING) + // { char *version_str = nullptr; + // plist_get_string_val(version, &version_str); + // if (version_str) { + // appData["version"] = + // QString(version_str); free(version_str); + // } + // } - // Get file sharing enabled status - plist_t file_sharing = plist_dict_get_item( - app_info, "UIFileSharingEnabled"); - if (file_sharing && - plist_get_node_type(file_sharing) == - PLIST_BOOLEAN) { - uint8_t file_sharing_enabled = 0; - plist_get_bool_val(file_sharing, - &file_sharing_enabled); - appData["fileSharingEnabled"] = - (file_sharing_enabled != 0); - } else { - appData["fileSharingEnabled"] = false; - } + // // Get file sharing enabled status + // plist_t file_sharing = plist_dict_get_item( + // app_info, "UIFileSharingEnabled"); + // if (file_sharing && + // plist_get_node_type(file_sharing) == + // PLIST_BOOLEAN) { + // uint8_t file_sharing_enabled = 0; + // plist_get_bool_val(file_sharing, + // &file_sharing_enabled); + // appData["fileSharingEnabled"] = + // (file_sharing_enabled != 0); + // } else { + // appData["fileSharingEnabled"] = false; + // } - appData["type"] = appType; + // appData["type"] = appType; - if (!appData["bundleId"].toString().isEmpty()) { - apps.append(appData); - } - } - } - plist_free(apps_plist); - } - plist_free(client_opts); - } + // if (!appData["bundleId"].toString().isEmpty()) { + // apps.append(appData); + // } + // } + // } + // plist_free(apps_plist); + // } + // plist_free(client_opts); + // } - instproxy_client_free(instproxy); - lockdownd_client_free(lockdownClient); + // instproxy_client_free(instproxy); + // lockdownd_client_free(lockdownClient); - result["apps"] = apps; - result["success"] = true; + // result["apps"] = apps; + // result["success"] = true; - } catch (const std::exception &e) { - if (instproxy) - instproxy_client_free(instproxy); - if (lockdownClient) - lockdownd_client_free(lockdownClient); - if (lockdowndService) - lockdownd_service_descriptor_free(lockdowndService); + // } catch (const std::exception &e) { + // if (instproxy) + // instproxy_client_free(instproxy); + // if (lockdownClient) + // lockdownd_client_free(lockdownClient); + // if (lockdowndService) + // lockdownd_service_descriptor_free(lockdowndService); - result["error"] = QString("Exception: %1").arg(e.what()); - } + // result["error"] = QString("Exception: %1").arg(e.what()); + // } - return result; - }); + // return result; + // }); - m_watcher->setFuture(future); + // m_watcher->setFuture(future); } void InstalledAppsWidget::onAppsDataReady() @@ -610,7 +606,7 @@ void InstalledAppsWidget::filterApps(const QString &searchText) */ void InstalledAppsWidget::loadAppContainer(const QString &bundleId) { - if (!m_device || !m_device->device) { + if (!m_device) { return; } @@ -639,186 +635,188 @@ void InstalledAppsWidget::loadAppContainer(const QString &bundleId) m_containerLayout->addWidget(loadingWidget); - QFuture future = QtConcurrent::run([this, bundleId]() - -> QVariantMap { - QVariantMap result; + // QFuture future = QtConcurrent::run([this, bundleId]() + // -> QVariantMap { + // QVariantMap result; - afc_client_t afcClient = nullptr; - lockdownd_client_t lockdownClient = nullptr; - lockdownd_service_descriptor_t lockdowndService = nullptr; - house_arrest_client_t houseArrestClient = nullptr; - try { - if (lockdownd_client_new_with_handshake( - m_device->device, &lockdownClient, APP_LABEL) != - LOCKDOWN_E_SUCCESS) { - result["error"] = "Could not connect to lockdown service"; - return result; - } + // afc_client_t afcClient = nullptr; + // lockdownd_client_t lockdownClient = nullptr; + // lockdownd_service_descriptor_t lockdowndService = nullptr; + // house_arrest_client_t houseArrestClient = nullptr; + // try { + // if (lockdownd_client_new_with_handshake( + // m_device->device, &lockdownClient, APP_LABEL) != + // LOCKDOWN_E_SUCCESS) { + // result["error"] = "Could not connect to lockdown service"; + // return result; + // } - if (lockdownd_start_service( - lockdownClient, "com.apple.mobile.house_arrest", - &lockdowndService) != LOCKDOWN_E_SUCCESS) { - result["error"] = "Could not start house arrest service"; - lockdownd_client_free(lockdownClient); - return result; - } + // if (lockdownd_start_service( + // lockdownClient, "com.apple.mobile.house_arrest", + // &lockdowndService) != LOCKDOWN_E_SUCCESS) { + // result["error"] = "Could not start house arrest service"; + // lockdownd_client_free(lockdownClient); + // return result; + // } - if (house_arrest_client_new(m_device->device, lockdowndService, - &houseArrestClient) != - HOUSE_ARREST_E_SUCCESS) { - result["error"] = "Could not connect to house arrest"; - lockdownd_service_descriptor_free(lockdowndService); - lockdownd_client_free(lockdownClient); - return result; - } + // if (house_arrest_client_new(m_device->device, lockdowndService, + // &houseArrestClient) != + // HOUSE_ARREST_E_SUCCESS) { + // result["error"] = "Could not connect to house arrest"; + // lockdownd_service_descriptor_free(lockdowndService); + // lockdownd_client_free(lockdownClient); + // return result; + // } - lockdownd_service_descriptor_free(lockdowndService); - lockdowndService = nullptr; + // lockdownd_service_descriptor_free(lockdowndService); + // lockdowndService = nullptr; - // Send vendor container command - if (house_arrest_send_command( - houseArrestClient, "VendDocuments", - // if (house_arrest_send_command(houseArrestClient, - // "VendDocuments", - bundleId.toUtf8().constData()) != HOUSE_ARREST_E_SUCCESS) { - result["error"] = "Could not send VendDocuments command"; - house_arrest_client_free(houseArrestClient); - lockdownd_client_free(lockdownClient); - return result; - } + // // Send vendor container command + // if (house_arrest_send_command( + // houseArrestClient, "VendDocuments", + // // if (house_arrest_send_command(houseArrestClient, + // // "VendDocuments", + // bundleId.toUtf8().constData()) != HOUSE_ARREST_E_SUCCESS) + // { + // result["error"] = "Could not send VendDocuments command"; + // house_arrest_client_free(houseArrestClient); + // lockdownd_client_free(lockdownClient); + // return result; + // } - // Get result - plist_t dict = nullptr; - if (house_arrest_get_result(houseArrestClient, &dict) != - HOUSE_ARREST_E_SUCCESS || - !dict) { - result["error"] = "App container not available for this app"; - house_arrest_client_free(houseArrestClient); - lockdownd_client_free(lockdownClient); - return result; - } + // // Get result + // plist_t dict = nullptr; + // if (house_arrest_get_result(houseArrestClient, &dict) != + // HOUSE_ARREST_E_SUCCESS || + // !dict) { + // result["error"] = "App container not available for this app"; + // house_arrest_client_free(houseArrestClient); + // lockdownd_client_free(lockdownClient); + // return result; + // } - // Check for error in response - plist_t error_node = plist_dict_get_item(dict, "Error"); - if (error_node) { - char *error_str = nullptr; - plist_get_string_val(error_node, &error_str); - if (error_str) { - result["error"] = - QString("Container access denied: %1").arg(error_str); - free(error_str); - } else { - result["error"] = "Container access denied"; - } - plist_free(dict); - house_arrest_client_free(houseArrestClient); - lockdownd_client_free(lockdownClient); - return result; - } + // // Check for error in response + // plist_t error_node = plist_dict_get_item(dict, "Error"); + // if (error_node) { + // char *error_str = nullptr; + // plist_get_string_val(error_node, &error_str); + // if (error_str) { + // result["error"] = + // QString("Container access denied: + // %1").arg(error_str); + // free(error_str); + // } else { + // result["error"] = "Container access denied"; + // } + // plist_free(dict); + // house_arrest_client_free(houseArrestClient); + // lockdownd_client_free(lockdownClient); + // return result; + // } - plist_free(dict); + // plist_free(dict); - // Get AFC client for file access - if (afc_client_new_from_house_arrest_client( - houseArrestClient, &afcClient) != AFC_E_SUCCESS) { - result["error"] = - "Could not create AFC client for app container"; - house_arrest_client_free(houseArrestClient); - lockdownd_client_free(lockdownClient); - return result; - } + // // Get AFC client for file access + // if (afc_client_new_from_house_arrest_client( + // houseArrestClient, &afcClient) != AFC_E_SUCCESS) { + // result["error"] = + // "Could not create AFC client for app container"; + // house_arrest_client_free(houseArrestClient); + // lockdownd_client_free(lockdownClient); + // return result; + // } - // List root directory contents - char **list = nullptr; - if (afc_read_directory(afcClient, "/Documents", &list) != - AFC_E_SUCCESS) { - result["error"] = "Could not read app container directory"; - afc_client_free(afcClient); - house_arrest_client_free(houseArrestClient); - lockdownd_client_free(lockdownClient); - return result; - } + // // List root directory contents + // char **list = nullptr; + // if (afc_read_directory(afcClient, "/Documents", &list) != + // AFC_E_SUCCESS) { + // result["error"] = "Could not read app container directory"; + // afc_client_free(afcClient); + // house_arrest_client_free(houseArrestClient); + // lockdownd_client_free(lockdownClient); + // return result; + // } - QStringList files; - if (list) { - for (int i = 0; list[i]; i++) { - QString fileName = QString::fromUtf8(list[i]); - if (fileName != "." && fileName != "..") { - qDebug() << "Found file:" << fileName; - files.append(fileName); - } - } - afc_dictionary_free(list); - } - result["files"] = files; - result["afcClient"] = - QVariant::fromValue(reinterpret_cast(afcClient)); - result["houseArrestClient"] = QVariant::fromValue( - reinterpret_cast(houseArrestClient)); - result["success"] = true; + // QStringList files; + // if (list) { + // for (int i = 0; list[i]; i++) { + // QString fileName = QString::fromUtf8(list[i]); + // if (fileName != "." && fileName != "..") { + // qDebug() << "Found file:" << fileName; + // files.append(fileName); + // } + // } + // afc_dictionary_free(list); + // } + // result["files"] = files; + // result["afcClient"] = + // QVariant::fromValue(reinterpret_cast(afcClient)); + // result["houseArrestClient"] = QVariant::fromValue( + // reinterpret_cast(houseArrestClient)); + // result["success"] = true; - lockdownd_client_free(lockdownClient); + // lockdownd_client_free(lockdownClient); - } catch (const std::exception &e) { - if (afcClient) - afc_client_free(afcClient); - if (houseArrestClient) - house_arrest_client_free(houseArrestClient); - if (lockdownClient) - lockdownd_client_free(lockdownClient); - if (lockdowndService) - lockdownd_service_descriptor_free(lockdowndService); + // } catch (const std::exception &e) { + // if (afcClient) + // afc_client_free(afcClient); + // if (houseArrestClient) + // house_arrest_client_free(houseArrestClient); + // if (lockdownClient) + // lockdownd_client_free(lockdownClient); + // if (lockdowndService) + // lockdownd_service_descriptor_free(lockdowndService); - result["error"] = QString("Exception: %1").arg(e.what()); - } + // result["error"] = QString("Exception: %1").arg(e.what()); + // } - return result; - }); + // return result; + // }); - m_containerWatcher->setFuture(future); + // m_containerWatcher->setFuture(future); } void InstalledAppsWidget::onContainerDataReady() { QVariantMap result = m_containerWatcher->result(); - // todo - // Clear loading state - QLayoutItem *item; - while ((item = m_containerLayout->takeAt(0)) != nullptr) { - if (item->widget()) { - item->widget()->deleteLater(); - } - delete item; - } + // // todo + // // Clear loading state + // QLayoutItem *item; + // while ((item = m_containerLayout->takeAt(0)) != nullptr) { + // if (item->widget()) { + // item->widget()->deleteLater(); + // } + // delete item; + // } - if (!result.value("success", false).toBool()) { - qDebug() << "Error loading app container:" - << result.value("error").toString(); - QLabel *errorLabel = new QLabel("No data available for this app"); - errorLabel->setAlignment(Qt::AlignCenter); - m_containerLayout->addWidget(errorLabel); - return; - } + // if (!result.value("success", false).toBool()) { + // qDebug() << "Error loading app container:" + // << result.value("error").toString(); + // QLabel *errorLabel = new QLabel("No data available for this app"); + // errorLabel->setAlignment(Qt::AlignCenter); + // m_containerLayout->addWidget(errorLabel); + // return; + // } - // Get the AFC clients from the result and store them as member variables - m_houseArrestAfcClient = reinterpret_cast( - result.value("afcClient").value()); - m_houseArrestClient = reinterpret_cast( - result.value("houseArrestClient").value()); + // // Get the AFC clients from the result and store them as member variables + // m_houseArrestAfcClient = reinterpret_cast( + // result.value("afcClient").value()); + // m_houseArrestClient = reinterpret_cast( + // result.value("houseArrestClient").value()); - if (!m_houseArrestAfcClient) { - QLabel *errorLabel = - new QLabel("Failed to get AFC client for app container"); - m_containerLayout->addWidget(errorLabel); - return; - } + // if (!m_houseArrestAfcClient) { + // QLabel *errorLabel = + // new QLabel("Failed to get AFC client for app container"); + // m_containerLayout->addWidget(errorLabel); + // return; + // } - // Create AfcExplorerWidget with the house arrest AFC client - AfcExplorerWidget *explorer = new AfcExplorerWidget( - m_device, true, m_houseArrestAfcClient, "/Documents", this); - explorer->setStyleSheet("border :none;"); - m_containerLayout->addWidget(explorer); + // // Create AfcExplorerWidget with the house arrest AFC client + // AfcExplorerWidget *explorer = new AfcExplorerWidget( + // m_device, true, m_houseArrestAfcClient, "/Documents", this); + // explorer->setStyleSheet("border :none;"); + // m_containerLayout->addWidget(explorer); } void InstalledAppsWidget::onFileSharingFilterChanged(bool enabled) @@ -830,15 +828,15 @@ void InstalledAppsWidget::onFileSharingFilterChanged(bool enabled) void InstalledAppsWidget::cleanupHouseArrestClients() { - if (m_houseArrestAfcClient) { - afc_client_free(m_houseArrestAfcClient); - m_houseArrestAfcClient = nullptr; - } + // if (m_houseArrestAfcClient) { + // afc_client_free(m_houseArrestAfcClient); + // m_houseArrestAfcClient = nullptr; + // } - if (m_houseArrestClient) { - house_arrest_client_free(m_houseArrestClient); - m_houseArrestClient = nullptr; - } + // if (m_houseArrestClient) { + // house_arrest_client_free(m_houseArrestClient); + // m_houseArrestClient = nullptr; + // } } void InstalledAppsWidget::createLeftPanel() diff --git a/src/installedappswidget.h b/src/installedappswidget.h index 1f41a83..77ad366 100644 --- a/src/installedappswidget.h +++ b/src/installedappswidget.h @@ -41,8 +41,6 @@ #include #include #include -#include -#include // Custom App Tab Widget class AppTabWidget : public QGroupBox @@ -134,8 +132,8 @@ private: QFutureWatcher *m_watcher; QFutureWatcher *m_containerWatcher; QSplitter *m_splitter; - house_arrest_client_t m_houseArrestClient = nullptr; - afc_client_t m_houseArrestAfcClient = nullptr; + // house_arrest_client_t m_houseArrestClient = nullptr; + // afc_client_t m_houseArrestAfcClient = nullptr; // App data storage QList m_appTabs; AppTabWidget *m_selectedTab = nullptr; diff --git a/src/livescreenwidget.cpp b/src/livescreenwidget.cpp index aa3e726..16870c4 100644 --- a/src/livescreenwidget.cpp +++ b/src/livescreenwidget.cpp @@ -29,17 +29,14 @@ #include #include #include -#include -#include // todo add a retry button when failed LiveScreenWidget::LiveScreenWidget(iDescriptorDevice *device, QWidget *parent) - : QWidget{parent}, m_device(device), m_timer(nullptr), - m_shotrClient(nullptr), m_fps(20) + : QWidget{parent}, m_device(device), m_timer(nullptr), m_fps(15) { setWindowTitle("Live Screen - iDescriptor"); - unsigned int device_version = idevice_get_device_version(m_device->device); - unsigned int deviceMajorVersion = (device_version >> 16) & 0xFF; + unsigned int deviceMajorVersion = + m_device->deviceInfo.parsedDeviceVersion.major; if (deviceMajorVersion > 16) { QMessageBox::warning( @@ -83,8 +80,10 @@ LiveScreenWidget::LiveScreenWidget(iDescriptorDevice *device, QWidget *parent) connect(m_timer, &QTimer::timeout, this, &LiveScreenWidget::updateScreenshot); + startCapturing(); + // Defer the initialization to allow the main widget to show first - QTimer::singleShot(0, this, &LiveScreenWidget::startInitialization); + // QTimer::singleShot(0, this, &LiveScreenWidget::startInitialization); } void LiveScreenWidget::startInitialization() @@ -122,97 +121,99 @@ LiveScreenWidget::~LiveScreenWidget() m_timer->stop(); } - if (m_shotrClient) { - screenshotr_client_free(m_shotrClient); - m_shotrClient = nullptr; - } + // if (m/*_shotrClient) { + // screenshotr_client_free(m_shotrClient); + // m_shotrClient = nullptr; + // }*/ } bool LiveScreenWidget::initializeScreenshotService(bool notify) { - lockdownd_client_t lockdownClient = nullptr; - lockdownd_service_descriptor_t service = nullptr; + return true; + // lockdownd_client_t lockdownClient = nullptr; + // lockdownd_service_descriptor_t service = nullptr; - try { - m_statusLabel->setText("Connecting to screenshot service..."); + // try { + // m_statusLabel->setText("Connecting to screenshot service..."); - lockdownd_error_t ldret = lockdownd_client_new_with_handshake( - m_device->device, &lockdownClient, APP_LABEL); + // IdeviceFfiError * = + // screenshotr_take_screenshot(m_device->screenshotrClient,) - if (ldret != LOCKDOWN_E_SUCCESS) { - m_statusLabel->setText("Failed to connect to lockdown service"); - if (notify) - QMessageBox::critical(this, "Connection Failed", - "Could not connect to lockdown service.\n" - "Error code: " + - QString::number(ldret)); - return false; - } + // if (ldret != LOCKDOWN_E_SUCCESS) { + // m_statusLabel->setText("Failed to connect to lockdown service"); + // if (notify) + // QMessageBox::critical(this, "Connection Failed", + // "Could not connect to lockdown + // service.\n" "Error code: " + + // QString::number(ldret)); + // return false; + // } - lockdownd_error_t lerr = lockdownd_start_service( - lockdownClient, SCREENSHOTR_SERVICE_NAME, &service); + // lockdownd_error_t lerr = lockdownd_start_service( + // lockdownClient, SCREENSHOTR_SERVICE_NAME, &service); - lockdownd_client_free(lockdownClient); - lockdownClient = nullptr; + // lockdownd_client_free(lockdownClient); + // lockdownClient = nullptr; - if (lerr != LOCKDOWN_E_SUCCESS) { - m_statusLabel->setText("Failed to start screenshot service"); - qDebug() << lerr << "lockdownd_start_service"; - if (notify) - QMessageBox::critical( - this, "Service Failed", - "Could not start screenshot service on device.\n" - "Please ensure the developer disk image is properly " - "mounted."); - if (service) { - lockdownd_service_descriptor_free(service); - } - return false; - } + // if (lerr != LOCKDOWN_E_SUCCESS) { + // m_statusLabel->setText("Failed to start screenshot service"); + // qDebug() << lerr << "lockdownd_start_service"; + // if (notify) + // QMessageBox::critical( + // this, "Service Failed", + // "Could not start screenshot service on device.\n" + // "Please ensure the developer disk image is properly " + // "mounted."); + // if (service) { + // lockdownd_service_descriptor_free(service); + // } + // return false; + // } - screenshotr_error_t screrr = - screenshotr_client_new(m_device->device, service, &m_shotrClient); + // screenshotr_error_t screrr = + // screenshotr_client_new(m_device->device, service, + // &m_shotrClient); - lockdownd_service_descriptor_free(service); - service = nullptr; - qDebug() << screrr << "screenshotr_client_new"; - if (screrr != SCREENSHOTR_E_SUCCESS) { - m_statusLabel->setText("Failed to create screenshot client"); - if (notify) - QMessageBox::critical(this, "Client Failed", - "Could not create screenshot client.\n" - "Error code: " + - QString::number(screrr)); - return false; - } + // lockdownd_service_descriptor_free(service); + // service = nullptr; + // qDebug() << screrr << "screenshotr_client_new"; + // if (screrr != SCREENSHOTR_E_SUCCESS) { + // m_statusLabel->setText("Failed to create screenshot client"); + // if (notify) + // QMessageBox::critical(this, "Client Failed", + // "Could not create screenshot client.\n" + // "Error code: " + + // QString::number(screrr)); + // return false; + // } - // Successfully initialized, start capturing - m_statusLabel->setText("Capturing"); - startCapturing(); - return true; - } catch (const std::exception &e) { - m_statusLabel->setText("Exception occurred"); - if (notify) - QMessageBox::critical( - this, "Exception", - QString("Exception occurred: %1").arg(e.what())); + // // Successfully initialized, start capturing + // m_statusLabel->setText("Capturing"); + // startCapturing(); + // return true; + // } catch (const std::exception &e) { + // m_statusLabel->setText("Exception occurred"); + // if (notify) + // QMessageBox::critical( + // this, "Exception", + // QString("Exception occurred: %1").arg(e.what())); - if (lockdownClient) { - lockdownd_client_free(lockdownClient); - } - if (service) { - lockdownd_service_descriptor_free(service); - } - } + // if (lockdownClient) { + // lockdownd_client_free(lockdownClient); + // } + // if (service) { + // lockdownd_service_descriptor_free(service); + // } + // } } void LiveScreenWidget::startCapturing() { - if (!m_shotrClient) { - qWarning() - << "Cannot start capturing: screenshot client not initialized"; - return; - } + // if (!m_shotrClient) { + // qWarning() + // << "Cannot start capturing: screenshot client not initialized"; + // return; + // } if (m_timer) { m_timer->start(); @@ -222,22 +223,44 @@ void LiveScreenWidget::startCapturing() void LiveScreenWidget::updateScreenshot() { - if (!m_shotrClient) { - qWarning() << "Screenshot client not initialized"; - return; - } - + // if (!m_shotrClient) { + // qWarning() << "Screenshot client not initialized"; + // return; + // } + qDebug() << "Updating screenshot..."; try { - TakeScreenshotResult result = take_screenshot(m_shotrClient); + // TakeScreenshotResult result = take_screenshot(m_shotrClient); + ScreenshotData screenshot; + IdeviceFfiError *err = screenshotr_take_screenshot( + m_device->screenshotrClient, &screenshot); + if (!err && screenshot.data && screenshot.length > 0) { + qDebug() << "Screenshot captured, size:" << screenshot.length; + // QImage img(screenshot.data, // data + // static_cast(screenshot.length), // width + // 1, // height + // QImage::Format_ARGB32); // format - if (result.success && !result.img.isNull()) { - QPixmap pixmap = QPixmap::fromImage(result.img); + QByteArray byteArray( + reinterpret_cast(screenshot.data), + static_cast(screenshot.length)); + QImage image; + image.loadFromData(byteArray); + QPixmap pixmap = QPixmap::fromImage(image); m_imageLabel->setPixmap(pixmap.scaled(m_imageLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); } else { - qWarning() << "Failed to capture screenshot"; + qDebug() << "Failed to capture screenshot"; } + + // if (result.success && !result.img.isNull()) { + // QPixmap pixmap = QPixmap::fromImage(result.img); + // m_imageLabel->setPixmap(pixmap.scaled(m_imageLabel->size(), + // Qt::KeepAspectRatio, + // Qt::SmoothTransformation)); + // } else { + // qWarning() << "Failed to capture screenshot"; + // } } catch (const std::exception &e) { qWarning() << "Exception in updateScreenshot:" << e.what(); m_statusLabel->setText("Error capturing screenshot"); diff --git a/src/livescreenwidget.h b/src/livescreenwidget.h index 361608f..bc4c1f8 100644 --- a/src/livescreenwidget.h +++ b/src/livescreenwidget.h @@ -24,8 +24,6 @@ #include #include #include -#include -#include class LiveScreenWidget : public QWidget { @@ -44,7 +42,6 @@ private: QTimer *m_timer; QLabel *m_imageLabel; QLabel *m_statusLabel; - screenshotr_client_t m_shotrClient; int m_fps; private: diff --git a/src/main.cpp b/src/main.cpp index e16ea84..9f38c87 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,6 +19,7 @@ #include "mainwindow.h" // #include "settingsmanager.h" +#include "iDescriptor.h" #include #include #include @@ -36,7 +37,7 @@ int main(int argc, char *argv[]) QCoreApplication::setOrganizationName("iDescriptor"); QCoreApplication::setApplicationName("iDescriptor"); QCoreApplication::setApplicationVersion(APP_VERSION); - + // idevice_init_logger(Debug, Disabled, NULL); // if (a.arguments().contains("--reset-settings")) { // SettingsManager::sharedInstance()->clear(); // QMessageBox::information(nullptr, "Settings Reset", diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 19291ff..7c876a7 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -19,10 +19,10 @@ #include "mainwindow.h" #include "./ui_mainwindow.h" -// #include "detailwindow.h" -// #include "ifusediskunmountbutton.h" -// #include "ifusemanager.h" -// #include "settingswidget.h" +#include "detailwindow.h" +#include "ifusediskunmountbutton.h" +#include "ifusemanager.h" +#include "settingswidget.h" #include "appswidget.h" #include "devicemanagerwidget.h" @@ -42,7 +42,7 @@ #include #include "appcontext.h" -// #include "settingsmanager.h" +#include "settingsmanager.h" // #include "devicemonitor.h" #include "networkdevicemanager.h" #include "networkdeviceswidget.h" @@ -52,9 +52,9 @@ #include #include -// #ifdef WIN32 -// #include "platform/windows/check_deps.h" -// #endif +#ifdef WIN32 +#include "platform/windows/check_deps.h" +#endif using namespace IdeviceFFI; @@ -171,32 +171,26 @@ MainWindow::MainWindow(QWidget *parent) this, &MainWindow::updateNoDevicesConnected); m_ZTabWidget->addTab(m_mainStackedWidget, "iDevice"); - // auto *appsWidgetTab = - // m_ZTabWidget->addTab(AppsWidget::sharedInstance(), "Apps"); - // m_ZTabWidget->addTab(new ToolboxWidget(this), "Toolbox"); - m_ZTabWidget->addTab(new QWidget(), "Apps"); // Placeholder for Apps tab - m_ZTabWidget->addTab(new QWidget(), - "Toolbox"); // Placeholder for Toolbox tab - m_ZTabWidget->addTab(new QWidget(), - "Jailbroken"); // Placeholder for Jailbroken tab - - // auto *jailbrokenWidget = new JailbrokenWidget(this); - // m_ZTabWidget->addTab(jailbrokenWidget, "Jailbroken"); + auto *appsWidgetTab = + m_ZTabWidget->addTab(AppsWidget::sharedInstance(), "Apps"); + m_ZTabWidget->addTab(new ToolboxWidget(this), "Toolbox"); + auto *jailbrokenWidget = new JailbrokenWidget(this); + m_ZTabWidget->addTab(jailbrokenWidget, "Jailbroken"); m_ZTabWidget->finalizeStyles(); - // connect( - // appsWidgetTab, &ZTab::clicked, this, - // [this](int index) { AppsWidget::sharedInstance()->init(); }, - // Qt::SingleShotConnection); + connect( + appsWidgetTab, &ZTab::clicked, this, + [this](int index) { AppsWidget::sharedInstance()->init(); }, + Qt::SingleShotConnection); - // // settings button - // ZIconWidget *settingsButton = new ZIconWidget( - // QIcon(":/resources/icons/MingcuteSettings7Line.png"), "Settings"); - // settingsButton->setCursor(Qt::PointingHandCursor); - // settingsButton->setFixedSize(24, 24); - // connect(settingsButton, &ZIconWidget::clicked, this, [this]() { - // SettingsManager::sharedInstance()->showSettingsDialog(); - // }); + // settings button + ZIconWidget *settingsButton = new ZIconWidget( + QIcon(":/resources/icons/MingcuteSettings7Line.png"), "Settings"); + settingsButton->setCursor(Qt::PointingHandCursor); + settingsButton->setFixedSize(24, 24); + connect(settingsButton, &ZIconWidget::clicked, this, [this]() { + SettingsManager::sharedInstance()->showSettingsDialog(); + }); ZIconWidget *githubButton = new ZIconWidget( QIcon(":/resources/icons/MdiGithub.png"), "iDescriptor on GitHub"); @@ -218,30 +212,28 @@ MainWindow::MainWindow(QWidget *parent) "QLabel:hover { background-color : #13131319; }"); ui->statusbar->addPermanentWidget(appVersionLabel); ui->statusbar->addPermanentWidget(githubButton); - // ui->statusbar->addPermanentWidget(settingsButton); + ui->statusbar->addPermanentWidget(settingsButton); - // #ifdef __linux__ - // QList mounted_iFusePaths = iFuseManager::getMountPoints(); +#ifdef __linux__ + QList mounted_iFusePaths = iFuseManager::getMountPoints(); - // for (const QString &path : mounted_iFusePaths) { - // auto *p = new iFuseDiskUnmountButton(path); + for (const QString &path : mounted_iFusePaths) { + auto *p = new iFuseDiskUnmountButton(path); - // ui->statusbar->addPermanentWidget(p); - // connect(p, &iFuseDiskUnmountButton::clicked, this, [this, p, - // path]() { - // bool ok = iFuseManager::linuxUnmount(path); - // if (!ok) { - // QMessageBox::warning(nullptr, "Unmount Failed", - // "Failed to unmount iFuse at " + path - // + - // ". Please try again."); - // return; - // } - // ui->statusbar->removeWidget(p); - // p->deleteLater(); - // }); - // } - // #endif + ui->statusbar->addPermanentWidget(p); + connect(p, &iFuseDiskUnmountButton::clicked, this, [this, p, path]() { + bool ok = iFuseManager::linuxUnmount(path); + if (!ok) { + QMessageBox::warning(nullptr, "Unmount Failed", + "Failed to unmount iFuse at " + path + + ". Please try again."); + return; + } + ui->statusbar->removeWidget(p); + p->deleteLater(); + }); + } +#endif // #ifdef ENABLE_RECOVERY_DEVICE_SUPPORT // irecv_error_t res_recovery = @@ -450,6 +442,7 @@ MainWindow::MainWindow(QWidget *parent) connect(NetworkDeviceManager::sharedInstance(), &NetworkDeviceManager::deviceAdded, this, [this](const NetworkDevice &device) { + // return; // FIXME: disable for now const iDescriptorDevice *idevice = AppContext::sharedInstance()->getDeviceByMacAddress( device.macAddress); diff --git a/src/mediapreviewdialog.cpp b/src/mediapreviewdialog.cpp index 1a49f59..93f82d3 100644 --- a/src/mediapreviewdialog.cpp +++ b/src/mediapreviewdialog.cpp @@ -18,6 +18,8 @@ */ #include "mediapreviewdialog.h" +#include "appcontext.h" +#include "iDescriptor-ui.h" #include "mediastreamermanager.h" #include "photomodel.h" #include @@ -43,13 +45,9 @@ #include #include #include -#include "appcontext.h" -#include "iDescriptor-ui.h" - - MediaPreviewDialog::MediaPreviewDialog(iDescriptorDevice *device, - afc_client_t afcClient, + AfcClientHandle *afcClient, const QString &filePath, QWidget *parent) : QDialog(parent), m_device(device), m_filePath(filePath), m_isVideo(isVideoFile(filePath)), m_mainLayout(nullptr), @@ -76,11 +74,12 @@ MediaPreviewDialog::MediaPreviewDialog(iDescriptorDevice *device, setupUI(); loadMedia(); - connect(AppContext::sharedInstance(), &AppContext::deviceRemoved, this, [this](const std::string &udid) { - if (udid == m_device->udid) { - close(); - } - }); + connect(AppContext::sharedInstance(), &AppContext::deviceRemoved, this, + [this](const std::string &udid) { + if (udid == m_device->udid) { + close(); + } + }); } MediaPreviewDialog::~MediaPreviewDialog() diff --git a/src/mediapreviewdialog.h b/src/mediapreviewdialog.h index 5cf897d..b80771b 100644 --- a/src/mediapreviewdialog.h +++ b/src/mediapreviewdialog.h @@ -52,7 +52,8 @@ class MediaPreviewDialog : public QDialog public: explicit MediaPreviewDialog(iDescriptorDevice *device, - afc_client_t afcClient, const QString &filePath, + AfcClientHandle *afcClient, + const QString &filePath, QWidget *parent = nullptr); ~MediaPreviewDialog(); @@ -145,7 +146,7 @@ private: bool m_isDraggingTimeline; qint64 m_videoDuration; - afc_client_t m_afcClient; + AfcClientHandle *m_afcClient; }; #endif // MEDIAPREVIEWDIALOG_H diff --git a/src/mediastreamer.cpp b/src/mediastreamer.cpp index 45cb5eb..5e07248 100644 --- a/src/mediastreamer.cpp +++ b/src/mediastreamer.cpp @@ -28,10 +28,10 @@ #include #include #include -#include #include -MediaStreamer::MediaStreamer(iDescriptorDevice *device, afc_client_t afcClient, +MediaStreamer::MediaStreamer(iDescriptorDevice *device, + AfcClientHandle *afcClient, const QString &filePath, QObject *parent) : QTcpServer(parent), m_device(device), m_afcClient(afcClient), m_filePath(filePath), m_cachedFileSize(-1), m_fileSizeCached(false) @@ -274,16 +274,16 @@ void MediaStreamer::streamFileRange(QTcpSocket *socket, qint64 startByte, context->startByte = startByte; context->endByte = endByte; context->bytesRemaining = endByte - startByte + 1; - context->afcHandle = 0; + context->afcHandle = nullptr; qDebug() << "m_filepath" << m_filePath; - // Open file on device using ServiceManager const QByteArray pathBytes = m_filePath.toUtf8(); - afc_error_t openResult = ServiceManager::safeAfcFileOpen( - m_device, pathBytes.constData(), AFC_FOPEN_RDONLY, &context->afcHandle, - m_afcClient); - if (openResult != AFC_E_SUCCESS || context->afcHandle == 0) { + IdeviceFfiError *err_open = // Use distinct variable name + ServiceManager::safeAfcFileOpen(m_device, pathBytes.constData(), + AfcRdOnly, &context->afcHandle); + + if (err_open || context->afcHandle == 0) { qWarning() << "Failed to open file on device:" << m_filePath; delete context; socket->disconnectFromHost(); @@ -292,12 +292,18 @@ void MediaStreamer::streamFileRange(QTcpSocket *socket, qint64 startByte, // Seek to start position if needed if (startByte > 0) { - afc_error_t seekResult = ServiceManager::safeAfcFileSeek( - m_device, context->afcHandle, startByte, SEEK_SET, m_afcClient); - if (seekResult != AFC_E_SUCCESS) { + // FIXME: m_afcClient must be passed as alt afc + IdeviceFfiError *seek_err = ServiceManager::safeAfcFileSeek( + m_device, context->afcHandle, startByte, SEEK_SET); + + if (seek_err) { qWarning() << "Failed to seek in file:" << m_filePath; - ServiceManager::safeAfcFileClose(m_device, context->afcHandle, - m_afcClient); + // FIXME: m_afcClient must be passed as alt afc + IdeviceFfiError *err = + ServiceManager::safeAfcFileClose(m_device, context->afcHandle); + if (err) { + idevice_error_free(err); + } delete context; socket->disconnectFromHost(); return; @@ -351,28 +357,22 @@ qint64 MediaStreamer::getFileSize() } // Get file info from device using ServiceManager - char **info = nullptr; const QByteArray pathBytes = m_filePath.toUtf8(); - afc_error_t result = ServiceManager::safeAfcGetFileInfo( - m_device, pathBytes.constData(), &info, m_afcClient); - if (result != AFC_E_SUCCESS || !info) { + AfcFileInfo info = {}; + IdeviceFfiError *info_err = ServiceManager::safeAfcGetFileInfo( + m_device, pathBytes.constData(), &info); + + if (info_err || info.size == 0) { qWarning() << "Failed to get file info for:" << m_filePath; + idevice_error_free(info_err); return -1; } - qint64 fileSize = -1; - for (int i = 0; info[i]; i += 2) { - if (strcmp(info[i], "st_size") == 0) { - bool ok; - fileSize = QString(info[i + 1]).toLongLong(&ok); - if (!ok) - fileSize = -1; - break; - } - } + size_t fileSize = info.size; - afc_dictionary_free(info); + // FIXME : safe to free ? + // afc_file_info_free(&info); if (fileSize > 0) { m_cachedFileSize = fileSize; @@ -402,12 +402,11 @@ QString MediaStreamer::getMimeType() const void MediaStreamer::streamNextChunk(StreamingContext *context) { if (!context || !context->socket) { - return; // Invalid context, don't cleanup here + return; } - // Check if context has been marked for cleanup if (context->socket->property("streamingContext").isNull()) { - return; // Already cleaned up + return; } if (context->bytesRemaining <= 0) { @@ -417,39 +416,58 @@ void MediaStreamer::streamNextChunk(StreamingContext *context) return; } - // Check if socket is still valid if (context->socket->state() != QAbstractSocket::ConnectedState) { cleanupStreamingContext(context); return; } - const int CHUNK_SIZE = 64 * 1024; // 64KB chunks + const int CHUNK_SIZE = 256 * 1024; const uint32_t bytesToRead = static_cast( qMin(static_cast(CHUNK_SIZE), context->bytesRemaining)); - auto buffer = std::make_unique(bytesToRead); - uint32_t bytesRead = 0; + uint8_t *chunkData = nullptr; + size_t bytesRead = 0; - afc_error_t readResult = ServiceManager::safeAfcFileRead( - m_device, context->afcHandle, buffer.get(), bytesToRead, &bytesRead, - m_afcClient); + IdeviceFfiError *read_err = ServiceManager::safeAfcFileRead( + m_device, context->afcHandle, &chunkData, bytesToRead, &bytesRead); - if (readResult != AFC_E_SUCCESS || bytesRead == 0) { - qWarning() << "AFC read error or EOF during streaming"; + if (read_err) { + qWarning() << "AFC read error during streaming:" << read_err->message; + idevice_error_free(read_err); cleanupStreamingContext(context); return; } - const qint64 bytesWritten = context->socket->write(buffer.get(), bytesRead); - if (bytesWritten == -1) { - qWarning() << "Socket write error"; + if (bytesRead == 0 && bytesToRead > 0) { + qWarning() << "AFC read returned 0 bytes but expected to read:" + << bytesToRead; + if (chunkData) { + afc_file_read_data_free(chunkData, 0); + } cleanupStreamingContext(context); return; } - context->bytesRemaining -= bytesWritten; + if (bytesRead > 0 && chunkData) { + const qint64 bytesWritten = context->socket->write( + reinterpret_cast(chunkData), bytesRead); + + afc_file_read_data_free(chunkData, bytesRead); + + if (bytesWritten == -1) { + qWarning() << "Socket write error"; + cleanupStreamingContext(context); + return; + } + + context->bytesRemaining -= bytesWritten; + } else { + qWarning() << "AFC read error: No data or null chunkData despite " + "bytesRead > 0."; + cleanupStreamingContext(context); + return; + } - // If we're done, clean up if (context->bytesRemaining <= 0) { qDebug() << "Streaming completed for" << QFileInfo(context->filePath).fileName(); @@ -457,9 +475,7 @@ void MediaStreamer::streamNextChunk(StreamingContext *context) return; } - // If socket buffer is getting full, let bytesWritten signal handle the next - // chunk Otherwise, continue immediately for better performance - if (context->socket->bytesToWrite() >= 32768) { + if (context->socket->bytesToWrite() >= 1024 * 1024) { // Wait for bytesWritten signal return; } else { @@ -489,8 +505,9 @@ void MediaStreamer::cleanupStreamingContext(StreamingContext *context) } if (context->afcHandle != 0) { - ServiceManager::safeAfcFileClose(context->device, context->afcHandle, - m_afcClient); + // FIXME: m_afcClient must be passed as alt afc + ServiceManager::safeAfcFileClose(context->device, context->afcHandle + /*m_afcClient*/); context->afcHandle = 0; } diff --git a/src/mediastreamer.h b/src/mediastreamer.h index 0c8eba6..80f6169 100644 --- a/src/mediastreamer.h +++ b/src/mediastreamer.h @@ -47,8 +47,9 @@ class MediaStreamer : public QTcpServer Q_OBJECT public: - explicit MediaStreamer(iDescriptorDevice *device, afc_client_t afcClient, - const QString &filePath, QObject *parent = nullptr); + explicit MediaStreamer(iDescriptorDevice *device, + AfcClientHandle *afcClient, const QString &filePath, + QObject *parent = nullptr); ~MediaStreamer(); /** @@ -87,7 +88,7 @@ private: qint64 startByte; qint64 endByte; qint64 bytesRemaining; - uint64_t afcHandle; + AfcFileHandle *afcHandle; }; HttpRequest parseHttpRequest(const QByteArray &requestData); @@ -113,7 +114,7 @@ private: QList m_activeConnections; QMutex m_connectionsMutex; - afc_client_t m_afcClient; + AfcClientHandle *m_afcClient; }; #endif // MEDIASTREAMER_H diff --git a/src/mediastreamermanager.cpp b/src/mediastreamermanager.cpp index bf371cc..b2e96c2 100644 --- a/src/mediastreamermanager.cpp +++ b/src/mediastreamermanager.cpp @@ -31,7 +31,7 @@ MediaStreamerManager *MediaStreamerManager::sharedInstance() } QUrl MediaStreamerManager::getStreamUrl(iDescriptorDevice *device, - afc_client_t afcClient, + AfcClientHandle *afcClient, const QString &filePath) { QMutexLocker locker(&m_streamersMutex); diff --git a/src/mediastreamermanager.h b/src/mediastreamermanager.h index 0e53c90..6170bc7 100644 --- a/src/mediastreamermanager.h +++ b/src/mediastreamermanager.h @@ -49,7 +49,7 @@ public: * @param filePath The file path on the device * @return URL to stream the file, or empty URL if failed */ - QUrl getStreamUrl(iDescriptorDevice *device, afc_client_t afcClient, + QUrl getStreamUrl(iDescriptorDevice *device, AfcClientHandle *afcClient, const QString &filePath); /** diff --git a/src/photomodel.cpp b/src/photomodel.cpp index eb5e688..74dfed4 100644 --- a/src/photomodel.cpp +++ b/src/photomodel.cpp @@ -86,32 +86,38 @@ QPixmap PhotoModel::generateVideoThumbnailFFmpeg(iDescriptorDevice *device, AfcFileHandle *fileHandle = nullptr; - IdeviceFfiError *err = + IdeviceFfiError *err_open = // Use distinct variable name for clarity ServiceManager::safeAfcFileOpen(device, filePath.toUtf8().constData(), AfcFopenMode::AfcRdOnly, &fileHandle); - if (err || fileHandle == nullptr) { + if (err_open || fileHandle == nullptr) { qWarning() << "Failed to open video file for thumbnail:" << filePath; - if (err) { - // idevice_error_free(err); + if (err_open) { + idevice_error_free(err_open); } return {}; } // Get file size - AfcFileInfo *info = nullptr; - err = ServiceManager::safeAfcGetFileInfo( - device, filePath.toUtf8().constData(), info); + AfcFileInfo info = {}; + IdeviceFfiError + *err_info = // Use distinct variable name for the error from GetFileInfo + ServiceManager::safeAfcGetFileInfo( + device, filePath.toUtf8().constData(), &info); uint64_t fileSize = 0; - if (!err && info) { - fileSize = info->size; - // afc_file_info_free(info); - if (err) { - // idevice_error_free(err); - } + if (err_info) { + qWarning() << "Failed to get file info for thumbnail:" << filePath + << "Error:" << err_info->message; + idevice_error_free(err_info); + ServiceManager::safeAfcFileClose(device, fileHandle); + afc_file_info_free(&info); // Free internal strings of info + return {}; } + fileSize = info.size; + afc_file_info_free(&info); // Free internal strings of info after use + if (fileSize == 0) { ServiceManager::safeAfcFileClose(device, fileHandle); qWarning() << "Invalid video file size for thumbnail:" << filePath; @@ -139,31 +145,64 @@ QPixmap PhotoModel::generateVideoThumbnailFFmpeg(iDescriptorDevice *device, // Custom read function that reads from device on-demand auto readPacket = [](void *opaque, uint8_t *buf, int bufSize) -> int { - // StreamContext *ctx = static_cast(opaque); + StreamContext *ctx = static_cast(opaque); - // if (ctx->currentPos >= ctx->fileSize) { - // return AVERROR_EOF; - // } + if (ctx->currentPos >= ctx->fileSize) { + return AVERROR_EOF; + } - // uint32_t toRead = - // std::min(static_cast(bufSize), - // static_cast(ctx->fileSize - ctx->currentPos)); - // uint32_t bytesRead = 0; + uint32_t toRead = + std::min(static_cast(bufSize), + static_cast(ctx->fileSize - ctx->currentPos)); + size_t bytesRead = 0; + uint8_t *read_data_ptr = + nullptr; // Pointer to store the data allocated by safeAfcFileRead - // IdeviceFfiError *err = ServiceManager::safeAfcFileRead( - // ctx->device, ctx->fileHandle, reinterpret_cast(buf), - // toRead, &bytesRead); + // Call safeAfcFileRead to get the data into a newly allocated buffer + IdeviceFfiError *err = ServiceManager::safeAfcFileRead( + ctx->device, ctx->fileHandle, &read_data_ptr, toRead, &bytesRead); - // if (err || bytesRead == 0) { - // if (err) { - // idevice_error_free(err); - // } - // return AVERROR(EIO); - // } + if (err) { + qWarning() << "AFC read error in readPacket for file handle" + << ctx->fileHandle << ":" << err->message; + idevice_error_free(err); + return AVERROR(EIO); + } - // ctx->currentPos += bytesRead; - // return static_cast(bytesRead); - return 0; + if (bytesRead == 0) { + // If bytesRead is 0 but we expected to read more, it's an error. + // If currentPos is already at fileSize, it's EOF. + if (ctx->currentPos < ctx->fileSize) { + qWarning() << "AFC readPacket returned 0 bytes but not EOF, " + "expected toRead:" + << toRead << "currentPos:" << ctx->currentPos + << "fileSize:" << ctx->fileSize; + // Free the allocated pointer if safeAfcFileRead still allocates + // even for 0 bytes. + if (read_data_ptr) { + afc_file_read_data_free(read_data_ptr, 0); + } + return AVERROR(EIO); + } + // It's EOF + return AVERROR_EOF; + } + + // Copy the data from the newly allocated `read_data_ptr` into FFmpeg's + // `buf` + if (read_data_ptr) { + memcpy(buf, read_data_ptr, bytesRead); + afc_file_read_data_free( + read_data_ptr, + bytesRead); // Free the memory allocated by safeAfcFileRead + } else { + qWarning() << "AFC readPacket: read_data_ptr was null but " + "bytesRead > 0. This is unexpected."; + return AVERROR(EIO); + } + + ctx->currentPos += bytesRead; + return static_cast(bytesRead); }; // Custom seek function using AFC seek @@ -198,7 +237,8 @@ QPixmap PhotoModel::generateVideoThumbnailFFmpeg(iDescriptorDevice *device, ctx->device, ctx->fileHandle, newPos, seekWhence); if (err) { - qDebug() << "AFC seek error:" << err->message; + qDebug() << "AFC seek error:" << err->message + << "code:" << err->code; // idevice_error_free(err); return -1; } @@ -806,11 +846,9 @@ QDateTime PhotoModel::extractDateTimeFromFile(const QString &filePath) const IdeviceFfiError *err = ServiceManager::safeAfcGetFileInfo( m_device, filePath.toUtf8().constData(), info); if (!err && info) { - uint64_t birthtime_ns = info->st_birthtime; - // Convert nanoseconds since epoch to QDateTime - // The timestamp appears to be in nanoseconds since Unix epoch - uint64_t seconds = birthtime_ns / 1000000000ULL; - QDateTime dateTime = QDateTime::fromSecsSinceEpoch(seconds, Qt::UTC); + uint64_t creation_seconds = info->creation; + QDateTime dateTime = + QDateTime::fromSecsSinceEpoch(creation_seconds, Qt::UTC); // afc_file_info_free(info); if (dateTime.isValid()) { diff --git a/src/qballoontip.cpp b/src/qballoontip.cpp new file mode 100644 index 0000000..815afcb --- /dev/null +++ b/src/qballoontip.cpp @@ -0,0 +1,299 @@ +#include "qballoontip.h" +#include "iDescriptor.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static QBalloonTip *theSolitaryBalloonTip = nullptr; + +void QBalloonTip::showBalloon(const QIcon &icon, const QString &title, + const QString &message, QWidget *widget, + const QPoint &pos, int timeout, bool showArrow) +{ + hideBalloon(); + if (message.isEmpty() && title.isEmpty()) + return; + + theSolitaryBalloonTip = new QBalloonTip(icon, title, message, widget); + if (timeout < 0) + timeout = 10000; // 10 s default + theSolitaryBalloonTip->balloon(pos, timeout, showArrow); +} + +void QBalloonTip::hideBalloon() +{ + if (!theSolitaryBalloonTip) + return; + theSolitaryBalloonTip->hide(); + delete theSolitaryBalloonTip; + theSolitaryBalloonTip = nullptr; +} + +void QBalloonTip::updateBalloonPosition(const QPoint &pos) +{ + if (!theSolitaryBalloonTip) + return; + theSolitaryBalloonTip->hide(); + theSolitaryBalloonTip->balloon(pos, 0, theSolitaryBalloonTip->showArrow); +} + +bool QBalloonTip::isBalloonVisible() { return theSolitaryBalloonTip; } + +QBalloonTip::QBalloonTip(const QIcon &icon, const QString &title, + const QString &message, QWidget *widget) + : QWidget(nullptr, Qt::ToolTip), widget(widget), showArrow(true) +{ + setAttribute(Qt::WA_DeleteOnClose); + setAttribute(Qt::WA_TranslucentBackground); + if (widget) { + connect(widget, &QWidget::destroyed, this, &QBalloonTip::close); + } + + // Add drop shadow effect + QGraphicsDropShadowEffect *shadowEffect = + new QGraphicsDropShadowEffect(this); + shadowEffect->setBlurRadius(200); + shadowEffect->setColor(QColor(0, 0, 0, 80)); + shadowEffect->setOffset(0, 5); + setGraphicsEffect(shadowEffect); + + QLabel *titleLabel = new QLabel; + titleLabel->installEventFilter(this); + titleLabel->setText(title); + QFont f = titleLabel->font(); + f.setBold(true); + titleLabel->setFont(f); + titleLabel->setTextFormat(Qt::PlainText); // to maintain compat with windows + + const int iconSize = 18; + const int closeButtonSize = 15; + + QPushButton *closeButton = new QPushButton; + closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton)); + closeButton->setIconSize(QSize(closeButtonSize, closeButtonSize)); + closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + closeButton->setFixedSize(closeButtonSize, closeButtonSize); + QObject::connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); + + QLabel *msgLabel = new QLabel; + msgLabel->installEventFilter(this); + msgLabel->setText(message); + msgLabel->setTextFormat(Qt::PlainText); + msgLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); + + QGridLayout *layout = new QGridLayout; + if (!icon.isNull()) { + QLabel *iconLabel = new QLabel; + iconLabel->setPixmap( + icon.pixmap(QSize(iconSize, iconSize), devicePixelRatio())); + iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + iconLabel->setMargin(2); + layout->addWidget(iconLabel, 0, 0); + layout->addWidget(titleLabel, 0, 1); + } else { + layout->addWidget(titleLabel, 0, 0, 1, 2); + } + + layout->addWidget(closeButton, 0, 2); + + layout->addWidget(msgLabel, 1, 0, 1, 3); + layout->setSizeConstraint(QLayout::SetFixedSize); + layout->setContentsMargins(3, 3, 3, 3); + setLayout(layout); +} + +QBalloonTip::~QBalloonTip() { theSolitaryBalloonTip = nullptr; } + +void QBalloonTip::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + painter.drawPixmap(rect(), pixmap); +} + +void QBalloonTip::resizeEvent(QResizeEvent *ev) { QWidget::resizeEvent(ev); } + +void QBalloonTip::balloon(const QPoint &pos, int msecs, bool showArrow) +{ + this->showArrow = showArrow; + QScreen *screen = QGuiApplication::screenAt(pos); + if (!screen) + screen = QGuiApplication::primaryScreen(); + QRect screenRect = screen->geometry(); + QSize sh = sizeHint(); + const int border = 1; + const int ah = 18, aw = 18, rc = 7; + bool arrowAtTop = (pos.y() + sh.height() + ah < screenRect.height()); + + setContentsMargins(border + 3, border + (arrowAtTop ? ah : 0) + 2, + border + 3, border + (arrowAtTop ? 0 : ah) + 2); + updateGeometry(); + sh = sizeHint(); + + // Center the balloon relative to the pos point (button center) + int balloonX = pos.x() - sh.width() / 2; + + // Calculate arrow offset from left edge of balloon to center it + int ao = sh.width() / 2 - aw / 2; // Center the arrow on the balloon + + int ml, mr, mt, mb; + QSize sz = sizeHint(); + if (!arrowAtTop) { + ml = mt = 0; + mr = sz.width() - 1; + mb = sz.height() - ah - 1; + } else { + ml = 0; + mt = ah; + mr = sz.width() - 1; + mb = sz.height() - 1; + } + + QPainterPath path; + path.moveTo(ml + rc, mt); + if (arrowAtTop) { + if (showArrow) { + path.lineTo(ml + ao, mt); + path.lineTo(ml + ao + aw / 2, mt - ah); + path.lineTo(ml + ao + aw, mt); + } + move(qBound(screenRect.left() + 2, balloonX, + screenRect.right() - sh.width() - 2), + pos.y()); + } + path.lineTo(mr - rc, mt); + path.arcTo(QRect(mr - rc * 2, mt, rc * 2, rc * 2), 90, -90); + path.lineTo(mr, mb - rc); + path.arcTo(QRect(mr - rc * 2, mb - rc * 2, rc * 2, rc * 2), 0, -90); + if (!arrowAtTop) { + if (showArrow) { + path.lineTo(mr - ao - aw, mb); + path.lineTo(mr - ao - aw / 2, mb + ah); + path.lineTo(mr - ao, mb); + } + move(qBound(screenRect.left() + 2, balloonX, + screenRect.right() - sh.width() - 2), + pos.y() - sh.height()); + } + path.lineTo(ml + rc, mb); + path.arcTo(QRect(ml, mb - rc * 2, rc * 2, rc * 2), -90, -90); + path.lineTo(ml, mt + rc); + path.arcTo(QRect(ml, mt, rc * 2, rc * 2), 180, -90); + + // Set the mask + QBitmap bitmap = QBitmap(sizeHint()); + bitmap.fill(Qt::color0); + QPainter painter1(&bitmap); + painter1.setPen(QPen(Qt::color1, border)); + painter1.setBrush(QBrush(Qt::color1)); + painter1.drawPath(path); + setMask(bitmap); + + // Draw the border with background color + pixmap = QPixmap(sz); + pixmap.fill(Qt::transparent); + QPainter painter2(&pixmap); + painter2.setRenderHint(QPainter::Antialiasing); + bool isDark = isDarkMode(); + QColor lightColor = qApp->palette().color(QPalette::Light); + QColor darkColor = qApp->palette().color(QPalette::Dark); + QColor bgColor = isDark ? lightColor : darkColor; + painter2.setPen(QPen(bgColor.darker(160), border)); + painter2.setBrush(bgColor); + painter2.drawPath(path); + + if (msecs > 0) + timer.start(msecs, this); + + // Install event filter to detect clicks outside + qApp->installEventFilter(this); + + // Set initial scale and opacity for animation + setWindowOpacity(0.0); + + // Store the transform origin point (center of the widget) + QPoint center = rect().center(); + setProperty("transformOriginPoint", center); + + show(); + + // Create scale and opacity animations + QPropertyAnimation *scaleAnim = new QPropertyAnimation(this, "geometry"); + scaleAnim->setDuration(200); + scaleAnim->setEasingCurve(QEasingCurve::OutBack); + + // Calculate scaled geometry (start from 80% size) + QRect finalGeometry = geometry(); + QRect startGeometry = finalGeometry; + int widthDiff = finalGeometry.width() * 0.2; + int heightDiff = finalGeometry.height() * 0.2; + startGeometry.adjust(widthDiff / 2, heightDiff / 2, -widthDiff / 2, + -heightDiff / 2); + + scaleAnim->setStartValue(startGeometry); + scaleAnim->setEndValue(finalGeometry); + + QPropertyAnimation *opacityAnim = + new QPropertyAnimation(this, "windowOpacity"); + opacityAnim->setDuration(200); + opacityAnim->setStartValue(0.0); + opacityAnim->setEndValue(1.0); + opacityAnim->setEasingCurve(QEasingCurve::OutCubic); + + scaleAnim->start(QAbstractAnimation::DeleteWhenStopped); + opacityAnim->start(QAbstractAnimation::DeleteWhenStopped); +} + +void QBalloonTip::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == Qt::LeftButton) { + emit messageClicked(); + } +} + +void QBalloonTip::timerEvent(QTimerEvent *e) +{ + if (e->timerId() == timer.timerId()) { + timer.stop(); + if (!underMouse()) + close(); + return; + } + QWidget::timerEvent(e); +} + +bool QBalloonTip::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::MouseButtonPress) { + QMouseEvent *mouseEvent = static_cast(event); + // Check if click is outside the balloon + if (!geometry().contains(mouseEvent->globalPos())) { + close(); + return false; + } + } else if (event->type() == QEvent::WindowDeactivate) { + // Close when window loses focus + if (obj == this) { + close(); + return false; + } + } + return QWidget::eventFilter(obj, event); +} + +void QBalloonTip::hideEvent(QHideEvent *event) +{ + // Remove event filter when hiding + qApp->removeEventFilter(this); + QWidget::hideEvent(event); +} diff --git a/src/qballoontip.h b/src/qballoontip.h new file mode 100644 index 0000000..2c53374 --- /dev/null +++ b/src/qballoontip.h @@ -0,0 +1,44 @@ +#ifndef QBALLOONTIP_H +#define QBALLOONTIP_H + +#include +#include +#include +#include + +class QBalloonTip : public QWidget +{ + Q_OBJECT +public: + static void showBalloon(const QIcon &icon, const QString &title, + const QString &msg, QWidget *widget, + const QPoint &pos, int timeout, + bool showArrow = true); + static void hideBalloon(); + static bool isBalloonVisible(); + static void updateBalloonPosition(const QPoint &pos); + +private: + QBalloonTip(const QIcon &icon, const QString &title, const QString &msg, + QWidget *widget); + ~QBalloonTip(); + void balloon(const QPoint &, int, bool); + +signals: + void messageClicked(); + +protected: + void paintEvent(QPaintEvent *) override; + void resizeEvent(QResizeEvent *) override; + void mousePressEvent(QMouseEvent *e) override; + void timerEvent(QTimerEvent *e) override; + bool eventFilter(QObject *obj, QEvent *event) override; + void hideEvent(QHideEvent *event) override; + +private: + QWidget *widget; + QPixmap pixmap; + QBasicTimer timer; + bool showArrow; +}; +#endif // QBALLOONTIP_H \ No newline at end of file diff --git a/src/querymobilegestaltwidget.cpp b/src/querymobilegestaltwidget.cpp index 8b91489..6365f71 100644 --- a/src/querymobilegestaltwidget.cpp +++ b/src/querymobilegestaltwidget.cpp @@ -22,12 +22,22 @@ #include #include #include +#include #include QueryMobileGestaltWidget::QueryMobileGestaltWidget(iDescriptorDevice *device, QWidget *parent) : QWidget(parent), m_device(device) { + // todo: not tested on iOS 17,18 but it's deprecated on iOS 26 + // i am assuming it won't work + if (m_device->deviceInfo.parsedDeviceVersion.major > 16) { + QMessageBox::warning(this, "Unsupported iOS Version", + "Apple deprecated this protocol for Devices " + "running iOS 17 or later"); + close(); + return; + } setupUI(); populateKeys(); } @@ -1135,32 +1145,34 @@ QueryMobileGestaltWidget::queryMobileGestalt(const QStringList &keys) { char *xml = nullptr; uint32_t xmlLength = 0; - bool res = query_mobile_gestalt(m_device, keys, xmlLength, xml); - if (!res) { + + std::vector keys_vector; + for (const QString &key : keys) { + keys_vector.push_back(key.toUtf8().data()); + } + + auto res = m_device->diagRelay->mobilegestalt(keys_vector); + + if (!res.is_ok()) { qDebug() << "MobileGestalt query failed."; return {}; } - pugi::xml_document infoXml; - pugi::xml_parse_result result = infoXml.load_string(xml); - if (xml) - free(xml); - if (!result) { - qDebug() << "Failed to parse XML:" << result.description(); - return {}; - } - pugi::xml_node dictNode = - infoXml.child("plist").child("dict").child("key").next_sibling("dict"); - if (!dictNode) { - qDebug() << "No MobileGestalt node found in XML."; - return {}; - } + plist_t res_plist_raw = res.unwrap().unwrap(); + // TODO: safety ? + auto res_plist = PlistNavigator(res_plist_raw)["MobileGestalt"]; + plist_print(res_plist_raw); QMap results; for (const QString &key : keys) { - std::string value = safeGetXML(key.toStdString().c_str(), dictNode); - if (!value.empty()) { - results.insert(key, QString::fromStdString(value)); + // Use the new toQVariant method to get the value + QVariant value = res_plist[key.toStdString()].toQVariant(); + qDebug() << "Key:" << key + << "Value:" << value; // Print QVariant directly + if (!value.isNull()) { // Only insert if the QVariant is valid (key + // exists and value could be parsed) + results.insert(key, value); } } + plist_free(res_plist_raw); return results; } \ No newline at end of file diff --git a/src/recoverydeviceinfowidget.cpp b/src/recoverydeviceinfowidget.cpp index abbd8e3..56243cb 100644 --- a/src/recoverydeviceinfowidget.cpp +++ b/src/recoverydeviceinfowidget.cpp @@ -20,7 +20,6 @@ #include "recoverydeviceinfowidget.h" #include "iDescriptor-ui.h" #include "iDescriptor.h" -#include "libirecovery.h" #include #include #include @@ -29,139 +28,143 @@ #include #include -RecoveryDeviceInfoWidget::RecoveryDeviceInfoWidget( - const iDescriptorRecoveryDevice *info, QWidget *parent) +RecoveryDeviceInfoWidget::RecoveryDeviceInfoWidget(const void *info, + QWidget *parent) : QWidget{parent} { - ecid = info->ecid; // Assuming ecid is unique for each device + // ecid = info->ecid; // Assuming ecid is unique for each device - QVBoxLayout *mainLayout = new QVBoxLayout(this); - mainLayout->setContentsMargins(20, 20, 20, 20); - mainLayout->setSpacing(16); + // QVBoxLayout *mainLayout = new QVBoxLayout(this); + // mainLayout->setContentsMargins(20, 20, 20, 20); + // mainLayout->setSpacing(16); - // Device Information Group - QGroupBox *deviceInfoGroup = new QGroupBox("Device Information"); - QVBoxLayout *infoLayout = new QVBoxLayout(deviceInfoGroup); - infoLayout->setSpacing(8); - infoLayout->setContentsMargins(16, 16, 16, 16); + // // Device Information Group + // QGroupBox *deviceInfoGroup = new QGroupBox("Device Information"); + // QVBoxLayout *infoLayout = new QVBoxLayout(deviceInfoGroup); + // infoLayout->setSpacing(8); + // infoLayout->setContentsMargins(16, 16, 16, 16); - // Device name with larger font - QLabel *deviceNameLabel = - new QLabel(QString::fromStdString(info->displayName)); - QFont nameFont = deviceNameLabel->font(); - nameFont.setPointSize(nameFont.pointSize() + 2); - nameFont.setWeight(QFont::DemiBold); - deviceNameLabel->setFont(nameFont); - infoLayout->addWidget(deviceNameLabel); + // // Device name with larger font + // QLabel *deviceNameLabel = + // new QLabel(QString::fromStdString(info->displayName)); + // QFont nameFont = deviceNameLabel->font(); + // nameFont.setPointSize(nameFont.pointSize() + 2); + // nameFont.setWeight(QFont::DemiBold); + // deviceNameLabel->setFont(nameFont); + // infoLayout->addWidget(deviceNameLabel); - // Add spacing - infoLayout->addSpacing(8); + // // Add spacing + // infoLayout->addSpacing(8); - // Mode info - QString modeText = QString::fromStdString(parse_recovery_mode(info->mode)); - QLabel *modeLabel = new QLabel("Mode: " + modeText); - infoLayout->addWidget(modeLabel); + // // Mode info + // QString modeText = + // QString::fromStdString(parse_recovery_mode(info->mode)); QLabel + // *modeLabel = new QLabel("Mode: " + modeText); + // infoLayout->addWidget(modeLabel); - // ECID info - QLabel *ecidLabel = new QLabel("ECID: " + QString::number(info->ecid)); - infoLayout->addWidget(ecidLabel); + // // ECID info + // QLabel *ecidLabel = new QLabel("ECID: " + QString::number(info->ecid)); + // infoLayout->addWidget(ecidLabel); - // CPID info - QLabel *cpidLabel = new QLabel("CPID: " + QString::number(info->cpid)); - infoLayout->addWidget(cpidLabel); + // // CPID info + // QLabel *cpidLabel = new QLabel("CPID: " + QString::number(info->cpid)); + // infoLayout->addWidget(cpidLabel); - mainLayout->addWidget(deviceInfoGroup); + // mainLayout->addWidget(deviceInfoGroup); - // Actions Group - QGroupBox *actionsGroup = new QGroupBox("Actions"); - QVBoxLayout *actionsLayout = new QVBoxLayout(actionsGroup); - actionsLayout->setSpacing(12); - actionsLayout->setContentsMargins(16, 16, 16, 16); + // // Actions Group + // QGroupBox *actionsGroup = new QGroupBox("Actions"); + // QVBoxLayout *actionsLayout = new QVBoxLayout(actionsGroup); + // actionsLayout->setSpacing(12); + // actionsLayout->setContentsMargins(16, 16, 16, 16); - // Info label - QLabel *infoLabel = new QLabel( - "Exit recovery mode and restart the device into normal mode."); - infoLabel->setWordWrap(true); - QFont infoFont = infoLabel->font(); - infoFont.setPointSize(infoFont.pointSize() - 1); - infoLabel->setFont(infoFont); - QPalette infoPalette = infoLabel->palette(); - infoPalette.setColor(QPalette::WindowText, - infoPalette.color(QPalette::WindowText).lighter(120)); - infoLabel->setPalette(infoPalette); - actionsLayout->addWidget(infoLabel); + // // Info label + // QLabel *infoLabel = new QLabel( + // "Exit recovery mode and restart the device into normal mode."); + // infoLabel->setWordWrap(true); + // QFont infoFont = infoLabel->font(); + // infoFont.setPointSize(infoFont.pointSize() - 1); + // infoLabel->setFont(infoFont); + // QPalette infoPalette = infoLabel->palette(); + // infoPalette.setColor(QPalette::WindowText, + // infoPalette.color(QPalette::WindowText).lighter(120)); + // infoLabel->setPalette(infoPalette); + // actionsLayout->addWidget(infoLabel); - // Button layout - QHBoxLayout *buttonLayout = new QHBoxLayout(); - buttonLayout->addStretch(); + // // Button layout + // QHBoxLayout *buttonLayout = new QHBoxLayout(); + // buttonLayout->addStretch(); - QPushButton *exitRecoveryMode = new QPushButton("Exit Recovery Mode"); - exitRecoveryMode->setMinimumWidth(160); - exitRecoveryMode->setMinimumHeight(32); + // QPushButton *exitRecoveryMode = new QPushButton("Exit Recovery Mode"); + // exitRecoveryMode->setMinimumWidth(160); + // exitRecoveryMode->setMinimumHeight(32); - connect(exitRecoveryMode, &QPushButton::clicked, this, [this, info]() { - irecv_client_t client = NULL; - irecv_error_t ierr = irecv_open_with_ecid_and_attempts( - &client, info->ecid, RECOVERY_CLIENT_CONNECTION_TRIES); - irecv_error_t error = IRECV_E_SUCCESS; - if (ierr != IRECV_E_SUCCESS) { - QMessageBox::critical( - this, "Connection Error", - QString("Failed to open device with ECID %1:\n%2") - .arg(info->ecid) - .arg(irecv_strerror(ierr))); - return; - } + // connect(exitRecoveryMode, &QPushButton::clicked, this, [this, info]() { + // irecv_client_t client = NULL; + // irecv_error_t ierr = irecv_open_with_ecid_and_attempts( + // &client, info->ecid, RECOVERY_CLIENT_CONNECTION_TRIES); + // irecv_error_t error = IRECV_E_SUCCESS; + // if (ierr != IRECV_E_SUCCESS) { + // QMessageBox::critical( + // this, "Connection Error", + // QString("Failed to open device with ECID %1:\n%2") + // .arg(info->ecid) + // .arg(irecv_strerror(ierr))); + // return; + // } - if (client == NULL) { - QMessageBox::critical(this, "Error", - "Client is NULL after successful open"); - return; - } + // if (client == NULL) { + // QMessageBox::critical(this, "Error", + // "Client is NULL after successful open"); + // return; + // } - error = irecv_setenv(client, "auto-boot", "true"); - if (error != IRECV_E_SUCCESS) { - QMessageBox::critical( - this, "Error", - QString("Failed to set environment variable 'auto-boot':\n%1") - .arg(irecv_strerror(error))); - irecv_close(client); - return; - } + // error = irecv_setenv(client, "auto-boot", "true"); + // if (error != IRECV_E_SUCCESS) { + // QMessageBox::critical( + // this, "Error", + // QString("Failed to set environment variable + // 'auto-boot':\n%1") + // .arg(irecv_strerror(error))); + // irecv_close(client); + // return; + // } - error = irecv_saveenv(client); - if (error != IRECV_E_SUCCESS) { - QMessageBox::critical( - this, "Error", - QString("Failed to save environment variables:\n%1") - .arg(irecv_strerror(error))); - irecv_close(client); - return; - } + // error = irecv_saveenv(client); + // if (error != IRECV_E_SUCCESS) { + // QMessageBox::critical( + // this, "Error", + // QString("Failed to save environment variables:\n%1") + // .arg(irecv_strerror(error))); + // irecv_close(client); + // return; + // } - error = irecv_reboot(client); - if (error != IRECV_E_SUCCESS) { - QMessageBox::critical(this, "Error", - QString("Failed to send reboot command:\n%1") - .arg(irecv_strerror(error))); - irecv_close(client); - return; - } + // error = irecv_reboot(client); + // if (error != IRECV_E_SUCCESS) { + // QMessageBox::critical(this, "Error", + // QString("Failed to send reboot + // command:\n%1") + // .arg(irecv_strerror(error))); + // irecv_close(client); + // return; + // } - irecv_close(client); + // irecv_close(client); - auto *msgBox = - new QMessageBox(QMessageBox::Information, "Success", - "Device is exiting recovery mode and restarting...", - QMessageBox::Ok, QApplication::activeWindow()); - msgBox->setAttribute(Qt::WA_DeleteOnClose); - msgBox->open(); - }); + // auto *msgBox = + // new QMessageBox(QMessageBox::Information, "Success", + // "Device is exiting recovery mode and + // restarting...", QMessageBox::Ok, + // QApplication::activeWindow()); + // msgBox->setAttribute(Qt::WA_DeleteOnClose); + // msgBox->open(); + // }); - buttonLayout->addWidget(exitRecoveryMode); - buttonLayout->addStretch(); - actionsLayout->addLayout(buttonLayout); + // buttonLayout->addWidget(exitRecoveryMode); + // buttonLayout->addStretch(); + // actionsLayout->addLayout(buttonLayout); - mainLayout->addWidget(actionsGroup); - mainLayout->addStretch(); + // mainLayout->addWidget(actionsGroup); + // mainLayout->addStretch(); } diff --git a/src/recoverydeviceinfowidget.h b/src/recoverydeviceinfowidget.h index 67920ce..1d96e3d 100644 --- a/src/recoverydeviceinfowidget.h +++ b/src/recoverydeviceinfowidget.h @@ -27,7 +27,7 @@ class RecoveryDeviceInfoWidget : public QWidget Q_OBJECT public: - explicit RecoveryDeviceInfoWidget(const iDescriptorRecoveryDevice *info, + explicit RecoveryDeviceInfoWidget(const void *info, QWidget *parent = nullptr); uint64_t ecid; // Assuming ecid is unique for each device signals: diff --git a/src/servicemanager.cpp b/src/servicemanager.cpp index d006064..f85d454 100644 --- a/src/servicemanager.cpp +++ b/src/servicemanager.cpp @@ -29,22 +29,7 @@ ServiceManager::safeAfcReadDirectory(const iDescriptorDevice *device, return executeAfcClientOperation( device, [path, dirs, &count, device](AfcClientHandle *client) { - IdeviceFfiError *err = nullptr; - err = afc_list_directory(client, path, dirs, &count); - /*TODO -1 is unknown error*/ - if (err && (err->code == -1 || err->code == -11)) { - qDebug() << "Reconnecting AFC client for path:" << path; - // afc_client_free(client); - // err = afc_client_connect(device->provider, &client); - err = afc_client_connect( - device->provider, - &const_cast(device)->afcClient); - - err = afc_list_directory( - const_cast(device)->afcClient, path, - dirs, &count); - } - return err; + return afc_list_directory(client, path, dirs, &count); }, altAfc); } @@ -57,21 +42,7 @@ ServiceManager::safeAfcGetFileInfo(const iDescriptorDevice *device, return executeAfcClientOperation( device, [path, info, device](AfcClientHandle *client) { - IdeviceFfiError *err = nullptr; - err = afc_get_file_info(client, path, info); - /*TODO -1 is unknown error*/ - if (err && err->code == -1) { - // afc_client_free(client); - // err = afc_client_connect(device->provider, &client); - err = afc_client_connect( - device->provider, - &const_cast(device)->afcClient); - - err = afc_get_file_info( - const_cast(device)->afcClient, path, - info); - } - return err; + return afc_get_file_info(client, path, info); }, altAfc); } @@ -83,19 +54,7 @@ IdeviceFfiError *ServiceManager::safeAfcFileOpen( return executeAfcClientOperation( device, [path, mode, handle, device](AfcClientHandle *client) { - IdeviceFfiError *err = nullptr; - err = afc_file_open(client, path, mode, handle); - /*TODO -1 is unknown error*/ - if (err && err->code == -1) { - // afc_client_free(client); - err = afc_client_connect( - device->provider, - &const_cast(device)->afcClient); - err = afc_file_open( - const_cast(device)->afcClient, path, - mode, handle); - } - return err; + return afc_file_open(client, path, mode, handle); }, altAfc); } @@ -140,11 +99,11 @@ ServiceManager::safeAfcFileSeek(const iDescriptorDevice *device, AfcFileHandle *handle, int64_t offset, int whence) { - off_t *newPos = nullptr; + off_t newPos; return executeAfcOperation( device, - [offset, whence, newPos](AfcFileHandle *handle) { - return afc_file_seek(handle, offset, whence, newPos); + [offset, whence, &newPos](AfcFileHandle *handle) { + return afc_file_seek(handle, offset, whence, &newPos); }, handle); } @@ -178,4 +137,61 @@ AFCFileTree ServiceManager::safeGetFileTree(const iDescriptorDevice *device, device, [path, device, checkDir]() -> AFCFileTree { return get_file_tree(device, checkDir, path); }); +} + +MountedImageInfo +ServiceManager::getMountedImage(const iDescriptorDevice *device) +{ + return executeOperation( + device, + [device]() -> MountedImageInfo { return _get_mounted_image(device); }); +} + +IdeviceFfiError *ServiceManager::mountImage(const iDescriptorDevice *device, + const char *image_file, + const char *signature_file) +{ + return executeOperation( + device, [device, image_file, signature_file]() -> IdeviceFfiError * { + return mount_dev_image(device, image_file, signature_file); + }); +} + +void ServiceManager::getCableInfo(const iDescriptorDevice *device, + plist_t &response) +{ + executeOperation( + device, [device, &response]() { _get_cable_info(device, response); }); +} + +IdeviceFfiError *ServiceManager::install_IPA(const iDescriptorDevice *device, + const char *ipa_path, + const char *file_name) +{ + return executeOperation( + device, [device, ipa_path, file_name]() -> IdeviceFfiError * { + return _install_IPA(device, ipa_path, file_name); + }); +} + +bool ServiceManager::enableWirelessConnections(const iDescriptorDevice *device) +{ + return executeOperation(device, [device]() -> bool { + plist_t value = plist_new_bool(true); + bool success = false; + IdeviceFfiError *err = + lockdownd_set_value(device->lockdown, "EnableWifiConnections", + value, "com.apple.mobile.wireless_lockdown"); + + if (err != NULL) { + qDebug() << "Failed to enable wireless connections." << err->message + << "Code:" << err->code; + idevice_error_free(err); + } else { + success = true; + } + + plist_free(value); + return success; + }); } \ No newline at end of file diff --git a/src/servicemanager.h b/src/servicemanager.h index 27227ad..d8a8821 100644 --- a/src/servicemanager.h +++ b/src/servicemanager.h @@ -248,6 +248,17 @@ public: const char *path); static AFCFileTree safeGetFileTree(const iDescriptorDevice *device, const std::string &path, bool checkDir); + static MountedImageInfo getMountedImage(const iDescriptorDevice *device); + static IdeviceFfiError *mountImage(const iDescriptorDevice *device, + const char *image_file, + const char *signature_file); + static void getCableInfo(const iDescriptorDevice *device, + plist_t &response); + + static IdeviceFfiError *install_IPA(const iDescriptorDevice *device, + const char *filePath, + const char *fileName); + static bool enableWirelessConnections(const iDescriptorDevice *device); }; #endif // SERVICEMANAGER_H diff --git a/src/toolboxwidget.cpp b/src/toolboxwidget.cpp index 2bb55d0..a403d13 100644 --- a/src/toolboxwidget.cpp +++ b/src/toolboxwidget.cpp @@ -44,41 +44,6 @@ struct iDescriptorToolWidget { QString iconName; }; -bool enterRecoveryMode(iDescriptorDevice *device) -{ - lockdownd_client_t client = NULL; - lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR; - idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; - - if (LOCKDOWN_E_SUCCESS != - (ldret = lockdownd_client_new(device->device, &client, APP_LABEL))) { - printf("ERROR: Could not connect to lockdownd: %s (%d)\n", - lockdownd_strerror(ldret), ldret); - return false; - } - - ldret = lockdownd_enter_recovery(client); - if (ldret == LOCKDOWN_E_SESSION_INACTIVE) { - lockdownd_client_free(client); - client = NULL; - if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake( - device->device, &client, APP_LABEL))) { - printf("ERROR: Could not connect to lockdownd: %s (%d)\n", - lockdownd_strerror(ldret), ldret); - return false; - } - ldret = lockdownd_enter_recovery(client); - } - lockdownd_client_free(client); - if (ldret != LOCKDOWN_E_SUCCESS) { - printf("Failed to enter recovery mode.\n"); - return false; - } else { - printf("Device is successfully switching to recovery mode.\n"); - return true; - } -} - ToolboxWidget::ToolboxWidget(QWidget *parent) : QWidget{parent} { setupUI(); @@ -325,6 +290,7 @@ void ToolboxWidget::updateDeviceList() if (devices.isEmpty()) { m_deviceCombo->addItem("No device connected"); m_deviceCombo->setEnabled(false); + m_uuid.clear(); } else { m_deviceCombo->setEnabled(true); for (iDescriptorDevice *device : devices) { @@ -341,6 +307,14 @@ void ToolboxWidget::updateDeviceList() AppContext::sharedInstance()->getCurrentDeviceSelection()); m_deviceCombo->blockSignals(false); + + if (m_deviceCombo->count() > 0 && m_deviceCombo->currentIndex() >= 0) { + QString currentUdid = m_deviceCombo->currentData().toString(); + if (!currentUdid.isEmpty()) { + m_uuid = currentUdid.toStdString(); + qDebug() << "[toolboxwidget] Initialized m_uuid to:" << currentUdid; + } + } } void ToolboxWidget::updateToolboxStates() @@ -374,10 +348,24 @@ void ToolboxWidget::updateUI() void ToolboxWidget::onDeviceSelectionChanged() { QString selectedUdid = m_deviceCombo->currentData().toString(); + + // Clear m_uuid if no valid selection if (selectedUdid.isEmpty()) { + m_uuid.clear(); return; } + if (AppContext::sharedInstance()->getDevice(selectedUdid.toStdString()) == + nullptr) { + QMessageBox::warning(this, "Device Not Found", + "The selected device is no longer connected."); + m_uuid.clear(); // Clear stale UUID + updateDeviceList(); + return; + } + + m_uuid = selectedUdid.toStdString(); + qDebug() << "[toolboxwidget] Selected device UDID:" << selectedUdid; // Update the selected device in main menu AppContext::sharedInstance()->setCurrentDeviceSelection( DeviceSelection(selectedUdid.toStdString())); @@ -385,7 +373,7 @@ void ToolboxWidget::onDeviceSelectionChanged() void ToolboxWidget::onCurrentDeviceChanged(const DeviceSelection &selection) { - if (selection.type == DeviceSelection::Normal) { + if (selection.valid() && selection.type == DeviceSelection::Normal) { int index = m_deviceCombo->findData(QString::fromStdString(selection.udid)); if (index != -1) { @@ -395,15 +383,23 @@ void ToolboxWidget::onCurrentDeviceChanged(const DeviceSelection &selection) m_deviceCombo->blockSignals(false); m_uuid = selection.udid; - m_currentDevice = - AppContext::sharedInstance()->getDevice(selection.udid); } + } else { + // Clear m_uuid when selection is invalid + m_uuid.clear(); } } void ToolboxWidget::onToolboxClicked(iDescriptorTool tool) { - + // final check to make sure device is connected if required + iDescriptorDevice *device = AppContext::sharedInstance()->getDevice(m_uuid); + if (!device && m_requiresDevice[m_toolboxes.indexOf(sender())]) { + QMessageBox::warning(this, "Device Disconnected ?", + "Please select a device to use this tool."); + return; + } + qDebug() << "idevice exists:" << (device != nullptr) << m_uuid.c_str(); switch (tool) { case iDescriptorTool::Airplayer: { if (!m_airplayWindow) { @@ -421,25 +417,25 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool) } break; case iDescriptorTool::LiveScreen: { - LiveScreenWidget *liveScreen = new LiveScreenWidget(m_currentDevice); + LiveScreenWidget *liveScreen = new LiveScreenWidget(device); liveScreen->setAttribute(Qt::WA_DeleteOnClose); liveScreen->show(); } break; case iDescriptorTool::RecoveryMode: { // Handle entering recovery mode - bool success = enterRecoveryMode(m_currentDevice); - QMessageBox msgBox; - msgBox.setWindowTitle("Recovery Mode"); - if (success) { - msgBox.setText("Successfully entered recovery mode."); - } else { - msgBox.setText("Failed to enter recovery mode."); - } - msgBox.exec(); + // bool success = _enterRecoveryMode(device); + // QMessageBox msgBox; + // msgBox.setWindowTitle("Recovery Mode"); + // if (success) { + // msgBox.setText("Successfully entered recovery mode."); + // } else { + // msgBox.setText("Failed to enter recovery mode."); + // } + // msgBox.exec(); } break; case iDescriptorTool::MountDevImage: { DevDiskImageHelper *devDiskImageHelper = - new DevDiskImageHelper(m_currentDevice, this); + new DevDiskImageHelper(device, this); connect(devDiskImageHelper, &DevDiskImageHelper::mountingCompleted, this, [this, devDiskImageHelper](bool success) { @@ -458,22 +454,22 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool) } break; case iDescriptorTool::VirtualLocation: { // Handle virtual location functionality - VirtualLocation *virtualLocation = new VirtualLocation(m_currentDevice); + VirtualLocation *virtualLocation = new VirtualLocation(device); virtualLocation->setAttribute(Qt::WA_DeleteOnClose); virtualLocation->setWindowFlag(Qt::Window); virtualLocation->resize(800, 600); virtualLocation->show(); } break; case iDescriptorTool::Restart: { - restartDevice(m_currentDevice); + restartDevice(device); } break; case iDescriptorTool::Shutdown: { - shutdownDevice(m_currentDevice); + shutdownDevice(device); } break; case iDescriptorTool::QueryMobileGestalt: { // Handle querying MobileGestalt QueryMobileGestaltWidget *queryMobileGestaltWidget = - new QueryMobileGestaltWidget(m_currentDevice); + new QueryMobileGestaltWidget(device); queryMobileGestaltWidget->setAttribute(Qt::WA_DeleteOnClose); queryMobileGestaltWidget->setWindowFlag(Qt::Window); queryMobileGestaltWidget->resize(800, 600); @@ -481,7 +477,7 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool) } break; case iDescriptorTool::DeveloperDiskImages: { if (!m_devDiskImagesWidget) { - m_devDiskImagesWidget = new DevDiskImagesWidget(m_currentDevice); + m_devDiskImagesWidget = new DevDiskImagesWidget(device); m_devDiskImagesWidget->setAttribute(Qt::WA_DeleteOnClose); m_devDiskImagesWidget->setWindowFlag(Qt::Window); m_devDiskImagesWidget->resize(800, 600); @@ -510,9 +506,9 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool) #ifndef __APPLE__ case iDescriptorTool::iFuse: { if (!m_ifuseWidget) { - m_ifuseWidget = new iFuseWidget(m_currentDevice); + m_ifuseWidget = new iFuseWidget(device); qDebug() << "Created iFuseWidget" - << m_currentDevice->deviceInfo.productType.c_str(); + << device->deviceInfo.productType.c_str(); m_ifuseWidget->setAttribute(Qt::WA_DeleteOnClose); connect(m_ifuseWidget, &QObject::destroyed, this, [this]() { m_ifuseWidget = nullptr; }); @@ -526,7 +522,7 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool) } break; #endif case iDescriptorTool::CableInfoWidget: { - CableInfoWidget *cableInfoWidget = new CableInfoWidget(m_currentDevice); + CableInfoWidget *cableInfoWidget = new CableInfoWidget(device); cableInfoWidget->setAttribute(Qt::WA_DeleteOnClose); cableInfoWidget->setWindowFlag(Qt::Window); cableInfoWidget->resize(600, 400); @@ -566,10 +562,17 @@ void ToolboxWidget::restartDevice(iDescriptorDevice *device) return; } - if (!(restart(device->udid))) - warn("Failed to restart device"); - else { - warn("Device will restart once unplugged", "Success"); + // FIXME: move to servicemanager + auto res = device->diagRelay->restart(); + + if (res.is_err()) { + QMessageBox::warning( + nullptr, "Restart Failed", + "Failed to restart device: " + + QString::fromStdString(res.unwrap_err().message)); + } else { + QMessageBox::information(nullptr, "Restart Initiated", + "Device will restart once unplugged."); qDebug() << "Restarting device"; } } @@ -590,12 +593,17 @@ void ToolboxWidget::shutdownDevice(iDescriptorDevice *device) return; } - if (!(shutdown(device->device))) - // TODO: warn is a safe wrapper for QMessageBox but do we actually need - // it ? - warn("Failed to shutdown device"); - else { - warn("Device will shutdown once unplugged", "Success"); + // FIXME: move to servicemanager + auto res = device->diagRelay->shutdown(); + + if (res.is_err()) { + QMessageBox::warning( + nullptr, "Shutdown Failed", + "Failed to shutdown device: " + + QString::fromStdString(res.unwrap_err().message)); + } else { + QMessageBox::information(nullptr, "Shutdown Initiated", + "Device will shutdown once unplugged."); qDebug() << "Shutting down device"; } } @@ -616,13 +624,16 @@ void ToolboxWidget::_enterRecoveryMode(iDescriptorDevice *device) return; } - bool success = enterRecoveryMode(device); - QMessageBox _msgBox; - _msgBox.setWindowTitle("Recovery Mode"); - if (success) { - _msgBox.setText("Successfully entered recovery mode."); - } else { - _msgBox.setText("Failed to enter recovery mode."); - } - _msgBox.exec(); + // auto res = device->diagRelay->enterRecovery(); + // if (res.is_err()) { + // QMessageBox::warning( + // nullptr, "Enter Recovery Mode Failed", + // "Failed to enter recovery mode: " + + // QString::fromStdString(res.unwrap_err().message)); + // } else { + // QMessageBox::information(nullptr, "Enter Recovery Mode Initiated", + // "Device will enter recovery mode once + // unplugged."); + // qDebug() << "Entering recovery mode"; + // } } \ No newline at end of file diff --git a/src/toolboxwidget.h b/src/toolboxwidget.h index 1aa65b6..689dfe2 100644 --- a/src/toolboxwidget.h +++ b/src/toolboxwidget.h @@ -67,7 +67,6 @@ private: QGridLayout *m_gridLayout; QList m_toolboxes; QList m_requiresDevice; - iDescriptorDevice *m_currentDevice; std::string m_uuid; DevDiskImagesWidget *m_devDiskImagesWidget = nullptr; NetworkDevicesWidget *m_networkDevicesWidget = nullptr; diff --git a/src/virtuallocationwidget.cpp b/src/virtuallocationwidget.cpp index 4d781b7..c94fbf8 100644 --- a/src/virtuallocationwidget.cpp +++ b/src/virtuallocationwidget.cpp @@ -45,19 +45,6 @@ VirtualLocation::VirtualLocation(iDescriptorDevice *device, QWidget *parent) : QWidget{parent}, m_device(device) { - unsigned int device_version = idevice_get_device_version(m_device->device); - unsigned int deviceMajorVersion = (device_version >> 16) & 0xFF; - - if (deviceMajorVersion > 16) { - QMessageBox::warning( - this, "Unsupported iOS Version", - "Virtual Location feature requires iOS 16 or earlier.\n" - "Your device is running iOS " + - QString::number(deviceMajorVersion) + - ", which is not yet supported."); - QTimer::singleShot(0, this, &QWidget::close); - return; - } setWindowTitle("Virtual Location - iDescriptor"); // Create the main layout QHBoxLayout *mainLayout = new QHBoxLayout(this); @@ -200,6 +187,7 @@ void VirtualLocation::updateMapFromInputs() double latitude = m_latitudeEdit->text().toDouble(&latOk); double longitude = m_longitudeEdit->text().toDouble(&lonOk); + // FIXME: warn if not valid if (latOk && lonOk && latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180) { QQuickItem *rootObject = m_quickWidget->rootObject(); @@ -311,53 +299,68 @@ void VirtualLocation::onApplyClicked() m_applyButton->setEnabled(true); return; } - DevDiskImageHelper *devDiskImageHelper = - new DevDiskImageHelper(m_device, this); - connect(devDiskImageHelper, &DevDiskImageHelper::mountingCompleted, this, - [this, latitude, longitude, devDiskImageHelper](bool success) { - if (devDiskImageHelper) { - devDiskImageHelper->deleteLater(); - } + // DevDiskImageHelper *devDiskImageHelper = + // new DevDiskImageHelper(m_device, this); + // connect(devDiskImageHelper, &DevDiskImageHelper::mountingCompleted, this, + // [this, latitude, longitude, devDiskImageHelper](bool success) { + // if (devDiskImageHelper) { + // devDiskImageHelper->deleteLater(); + // } - if (!success) { - return; - } + // if (!success) { + // return; + // } - // Apply location - emit locationChanged(latitude, longitude); - updateMapFromInputs(); + // // Apply location + // emit locationChanged(latitude, longitude); + // updateMapFromInputs(); - // Visual feedback - m_applyButton->setText("Applied!"); + // // Visual feedback + // m_applyButton->setText("Applied!"); - bool locationSuccess = set_location( - m_device->device, - const_cast( - m_latitudeEdit->text().toStdString().c_str()), - const_cast( - m_longitudeEdit->text().toStdString().c_str())); + // bool locationSuccess = set_location( + // m_device->device, + // const_cast( + // m_latitudeEdit->text().toStdString().c_str()), + // const_cast( + // m_longitudeEdit->text().toStdString().c_str())); - if (!locationSuccess) { - QMessageBox::warning(this, "Error", - "Failed to set location on device"); - } else { - SettingsManager::sharedInstance()->saveRecentLocation( - latitude, longitude); + // if (!locationSuccess) { + // QMessageBox::warning(this, "Error", + // "Failed to set location on device"); + // } else { + // SettingsManager::sharedInstance()->saveRecentLocation( + // latitude, longitude); - QMessageBox::information(this, "Success", - "Location applied successfully!"); - } - }); - connect( - devDiskImageHelper, &DevDiskImageHelper::destroyed, this, - [this]() { - QTimer::singleShot(1000, this, [this]() { - m_applyButton->setText("Apply Location"); - m_applyButton->setEnabled(true); - }); - }, - Qt::SingleShotConnection); - devDiskImageHelper->start(); + // QMessageBox::information(this, "Success", + // "Location applied + // successfully!"); + // } + // }); + // connect( + // devDiskImageHelper, &DevDiskImageHelper::destroyed, this, + // [this]() { + // QTimer::singleShot(1000, this, [this]() { + // m_applyButton->setText("Apply Location"); + // m_applyButton->setEnabled(true); + // }); + // }, + // Qt::SingleShotConnection); + // devDiskImageHelper->start(); + // FIXME: create issue for c bindings + IdeviceFfiError *err = location_simulation_set(m_device->locationSimulation, + latitude, longitude); + if (err != nullptr) { + QMessageBox::warning(this, "Error", + "Failed to set location on device:\n" + + QString::fromStdString(err->message)); + // idevice_ffi_error_free(err); + } else { + // SettingsManager::sharedInstance()->saveRecentLocation( + // latitude, longitude); + QMessageBox::information(this, "Success", + "Location applied successfully!"); + } } void VirtualLocation::loadRecentLocations(QVBoxLayout *layout)