refactor AppImage deployment and enhance device pairing logic

- Updated AppImage zip path in build workflow.
- Updated submodule references for ipatool-go and zupdater.
- Fix deploy-appimage.sh by manully deploying geoservices plugin
- Added retry logic for app downloads in AppDownloadBaseDialog.
- Fixed cancel download functionality in AppStoreManager.
- Added default jailbroken root password settings in SettingsManager and UI.
- Updated device sidebar item selection handling.
- General code cleanup and UI improvements across various components.
This commit is contained in:
uncor3
2025-11-23 04:47:38 +00:00
parent 6d2e0d6f41
commit ebd256eae0
21 changed files with 269 additions and 82 deletions
+1 -1
View File
@@ -172,4 +172,4 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: iDescriptor-AppImage
path: iDescriptor-*.AppImage
path: iDescriptor-*.AppImage.zip
+31 -2
View File
@@ -1,6 +1,14 @@
#!/bin/bash
# if you get errors try
# QMAKE=/usr/lib/qt6/bin/qmake NO_STRIP=1 ./scripts/deploy-appimage.sh 1.0.0
# QMAKE=/usr/lib/qt6/bin/qmake NO_STRIP=1 ./scripts/deploy-appimage.sh v1.0.0
# or even more explicit
#export QT_HOME=~/Qt/$YOUR_QT_VERSION/gcc_64
#export PATH="$QT_HOME/bin:$PATH"
#export LD_LIBRARY_PATH="$QT_HOME/lib"
#export QML2_IMPORT_PATH="$QT_HOME/qml"
#export QT_PLUGIN_PATH="$QT_HOME/plugins"
#QMAKE="$QT_HOME/bin/qmake6" ./scripts/deploy-appimage.sh v0.1.0
set -e
VERSION=$1
if [ -z "$VERSION" ]; then
@@ -135,9 +143,29 @@ chmod +x "$APPDIR/apprun-hooks/linuxdeploy-plugin-env.sh"
# .desktop file
cp iDescriptor.desktop "$APPDIR/usr/share/applications/"
# Manually deploy geoservices plugins (workaround for linuxdeploy-plugin-qt not finding them)
if [ -n "$Qt6_DIR" ] && [ -d "$Qt6_DIR/plugins/geoservices" ]; then
echo "Manually deploying geoservices plugins from $Qt6_DIR/plugins/geoservices"
mkdir -p "$APPDIR/usr/plugins/geoservices"
cp -v "$Qt6_DIR/plugins/geoservices"/*.so "$APPDIR/usr/plugins/geoservices/" || echo "Warning: Could not copy geoservices plugins"
echo "Setting RPATH for geoservices plugins"
for plugin in "$APPDIR/usr/plugins/geoservices"/*.so; do
if [ -f "$plugin" ]; then
echo "Setting rpath for $plugin"
patchelf --set-rpath '$ORIGIN/../../lib' "$plugin"
fi
done
else
echo "Warning: Could not find geoservices plugins directory"
echo "Qt6_DIR=$Qt6_DIR"
echo "QT_HOME=$QT_HOME"
fi
export LD_LIBRARY_PATH="$APPDIR/usr/local/lib:$LD_LIBRARY_PATH"
export LINUXDEPLOY_EXCLUDED_LIBRARIES="*sql*"
export QML_SOURCES_PATHS="./qml"
export EXTRA_QT_MODULES="geoservices;position"
./linuxdeploy-x86_64.AppImage \
@@ -154,7 +182,8 @@ APPIMAGE_FILE=$(find . -maxdepth 1 -name "iDescriptor*.AppImage")
if [ -n "$APPIMAGE_FILE" ]; then
mv "$APPIMAGE_FILE" "iDescriptor-${VERSION}-Linux_x86_64.AppImage"
chmod +x "iDescriptor-${VERSION}-Linux_x86_64.AppImage"
echo "Renamed AppImage to iDescriptor-${VERSION}-Linux_x86_64.AppImage"
zip -r "iDescriptor-${VERSION}-Linux_x86_64.AppImage.zip" "iDescriptor-${VERSION}-Linux_x86_64.AppImage"
echo "AppImage created and zipped: iDescriptor-${VERSION}-Linux_x86_64.AppImage.zip"
else
echo "Error: Could not find generated AppImage file."
exit 1
+18 -12
View File
@@ -56,12 +56,13 @@ void AppContext::addDevice(QString udid, idevice_connection_type conn_type,
m_pendingDevices.append(udid);
emit devicePasswordProtected(udid);
emit deviceChange();
// After 30 seconds, if the device is still pending,
// consider the pairing expired
QTimer::singleShot(30000, this, [this, udid]() {
QTimer::singleShot(
SettingsManager::sharedInstance()->connectionTimeout() *
1000,
this, [this, udid]() {
if (m_pendingDevices.contains(udid)) {
qDebug()
<< "Pairing expired for device UDID: " << udid;
qDebug() << "Pairing expired for device UDID: "
<< udid;
m_pendingDevices.removeAll(udid);
emit devicePairingExpired(udid);
emit deviceChange();
@@ -74,12 +75,15 @@ void AppContext::addDevice(QString udid, idevice_connection_type conn_type,
m_pendingDevices.append(udid);
emit devicePairPending(udid);
emit deviceChange();
// After 30 seconds, if the device is still pending,
// consider the pairing expired
QTimer::singleShot(30000, this, [this, udid]() {
qDebug() << "Pairing timer fired for device UDID: " << udid;
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;
qDebug()
<< "Pairing expired for device UDID: " << udid;
m_pendingDevices.removeAll(udid);
emit devicePairingExpired(udid);
emit deviceChange();
@@ -104,7 +108,6 @@ void AppContext::addDevice(QString udid, idevice_connection_type conn_type,
};
m_devices[device->udid] = device;
if (addType == AddType::Regular) {
// Apply settings-based behaviors
SettingsManager::sharedInstance()->doIfEnabled(
SettingsManager::Setting::AutoRaiseWindow, []() {
if (MainWindow *mainWindow = MainWindow::sharedInstance()) {
@@ -173,6 +176,8 @@ void AppContext::removeDevice(QString _udid)
if (device->afcClient)
afc_client_free(device->afcClient);
if (device->afc2Client)
afc_client_free(device->afc2Client);
idevice_free(device->device);
delete device->mutex;
delete device;
@@ -285,7 +290,8 @@ void AppContext::setCurrentDeviceSelection(const DeviceSelection &selection)
<< " Type:" << selection.type
<< " UDID:" << QString::fromStdString(selection.udid)
<< " ECID:" << selection.ecid << " Section:" << selection.section;
if (m_currentSelection.udid == selection.udid &&
if (m_currentSelection.type == selection.type &&
m_currentSelection.udid == selection.udid &&
m_currentSelection.ecid == selection.ecid &&
m_currentSelection.section == selection.section) {
qDebug() << "setCurrentDeviceSelection: No change in selection";
+48 -20
View File
@@ -24,6 +24,7 @@
#include <QFutureWatcher>
#include <QLabel>
#include <QMessageBox>
#include <QPointer>
#include <QProgressBar>
#include <QPushButton>
#include <QVBoxLayout>
@@ -54,7 +55,7 @@ AppDownloadBaseDialog::AppDownloadBaseDialog(const QString &appName,
const QString &bundleId,
QWidget *parent)
: QDialog(parent), m_appName(appName), m_downloadProcess(nullptr),
m_progressTimer(nullptr)
m_progressTimer(nullptr), m_bundleId(bundleId)
{
// Common UI: progress bar and action button
m_layout = new QVBoxLayout(this);
@@ -73,7 +74,6 @@ void AppDownloadBaseDialog::startDownloadProcess(const QString &bundleId,
int index,
bool promptToOpenDir)
{
bool acquireLicense = true;
if (bundleId.isEmpty()) {
QMessageBox::critical(this, "Error", "Bundle ID not provided.");
@@ -86,7 +86,13 @@ void AppDownloadBaseDialog::startDownloadProcess(const QString &bundleId,
m_actionButton->setEnabled(false);
m_operationInProgress = true;
tryToDownload(bundleId, outputDir, promptToOpenDir);
}
void AppDownloadBaseDialog::tryToDownload(const QString &bundleId,
const QString &outputDir,
bool promptToOpenDir)
{
AppStoreManager *manager = AppStoreManager::sharedInstance();
if (!manager) {
QMessageBox::critical(this, "Error",
@@ -94,34 +100,44 @@ void AppDownloadBaseDialog::startDownloadProcess(const QString &bundleId,
reject();
return;
}
bool acquireLicense = true;
m_operationInProgress = true;
QPointer<AppDownloadBaseDialog> safeThis = this;
auto progressCallback = [this](long long current, long long total) {
auto progressCallback = [safeThis](long long current, long long total) {
if (!safeThis) {
return;
}
int percentage = 0;
if (total > 0) {
percentage = static_cast<int>((current * 100) / total);
}
updateProgressBar(percentage);
safeThis->updateProgressBar(percentage);
};
manager->downloadApp(
bundleId, outputDir, "", acquireLicense,
[this, promptToOpenDir, outputDir](int result) {
m_operationInProgress = false;
[safeThis, promptToOpenDir, outputDir](int result) {
if (!safeThis) {
return;
}
safeThis->m_operationInProgress = false;
if (result == 0) { // Success
emit downloadFinished(true, "Success");
m_progressBar->setValue(100);
emit safeThis->downloadFinished(true, "Success");
if (safeThis->m_progressBar)
safeThis->m_progressBar->setValue(100);
if (promptToOpenDir) {
if (QMessageBox::Yes ==
QMessageBox::question(
this, "Download Successful",
safeThis, "Download Successful",
QString("Successfully downloaded. Would you like "
"to open the output directory: %1?")
.arg(outputDir))) {
QDir dir(outputDir);
if (!dir.exists()) {
QMessageBox::warning(
this, "Directory Not Found",
safeThis, "Directory Not Found",
QString("The directory %1 does not exist.")
.arg(outputDir));
} else {
@@ -129,18 +145,28 @@ void AppDownloadBaseDialog::startDownloadProcess(const QString &bundleId,
QUrl::fromLocalFile(outputDir));
}
}
accept();
}
safeThis->accept();
} else { // Failure
emit downloadFinished(false, "Failed");
// 3 attempts
if (safeThis->m_tries < 3) {
safeThis->m_tries++;
qDebug()
<< "Retrying download for" + safeThis->m_bundleId +
"Attempt:" + QString::number(safeThis->m_tries);
safeThis->tryToDownload(safeThis->m_bundleId, outputDir,
promptToOpenDir);
return;
}
emit safeThis->downloadFinished(false, "Failed");
// if (promptToOpenDir)
QMessageBox::critical(
this, "Download Failed",
safeThis, "Download Failed",
QString("Failed to download %1. Try signing out and back "
"in. Error code: %2")
.arg(m_appName)
.arg(safeThis->m_appName)
.arg(result));
reject();
safeThis->reject();
}
},
progressCallback);
@@ -148,11 +174,13 @@ void AppDownloadBaseDialog::startDownloadProcess(const QString &bundleId,
void AppDownloadBaseDialog::reject()
{
// FIXME: we need to cancel download if it gets closed
// if (m_operationInProgress) {
// AppStoreManager *manager = AppStoreManager::sharedInstance();
// m_operationInProgress = false;
// }
if (m_operationInProgress) {
AppStoreManager *manager = AppStoreManager::sharedInstance();
if (manager) {
manager->cancelDownload(m_bundleId);
}
m_operationInProgress = false;
}
cleanup();
QDialog::reject();
+4 -1
View File
@@ -49,6 +49,8 @@ protected:
const QString &appName,
const QString &outputDir);
void addProgressBar(int index);
void tryToDownload(const QString &bundleId, const QString &outputDir,
bool promptToOpenDir);
QProgressBar *m_progressBar;
QTimer *m_progressTimer;
QProcess *m_downloadProcess;
@@ -56,7 +58,8 @@ protected:
QPushButton *m_actionButton;
QVBoxLayout *m_layout;
bool m_operationInProgress = false;
QString m_bundleId;
uint m_tries = 0;
private slots:
void cleanup();
};
+10
View File
@@ -254,3 +254,13 @@ void AppStoreManager::downloadApp(
});
watcher->setFuture(future);
}
void AppStoreManager::cancelDownload(const QString &bundleId)
{
if (!m_initialized) {
return;
}
qDebug() << "[AppStoreManager::cancelDownload] : Cancelling download for"
<< bundleId;
IpaToolCancelDownload(bundleId.toUtf8().data());
}
+5 -4
View File
@@ -43,11 +43,12 @@ public:
void searchApps(
const QString &searchTerm, int limit,
std::function<void(bool success, const QString &results)> callback);
void downloadApp(const QString &bundleId, const QString &outputDir,
void
downloadApp(const QString &bundleId, const QString &outputPath,
const QString &externalVersionId, bool acquireLicense,
std::function<void(int result)> callback,
std::function<void(long long current, long long total)>
progressCallback = nullptr);
std::function<void(int)> completionCallback,
std::function<void(long long, long long)> progressCallback);
void cancelDownload(const QString &bundleId);
signals:
void loginSuccessful(const QJsonObject &accountInfo);
+8 -5
View File
@@ -35,6 +35,11 @@ DevDiskImageHelper::DevDiskImageHelper(iDescriptorDevice *device,
setAttribute(Qt::WA_DeleteOnClose);
setWindowTitle("Developer Disk Image - iDescriptor");
setupUI();
connect(this, &QDialog::accepted, this,
[this]() { emit mountingCompleted(true); });
connect(this, &QDialog::rejected, this,
[this]() { emit mountingCompleted(false); });
}
void DevDiskImageHelper::setupUI()
@@ -55,8 +60,8 @@ void DevDiskImageHelper::setupUI()
// Status label
m_statusLabel = new QLabel("Checking developer disk image...");
m_statusLabel->setAlignment(Qt::AlignCenter);
m_statusLabel->setWordWrap(true);
m_statusLabel->setAlignment(Qt::AlignCenter);
mainLayout->addWidget(m_statusLabel);
// Button layout
@@ -107,6 +112,7 @@ void DevDiskImageHelper::start()
finishWithError("Failed to download compatible image.");
}
});
qDebug() << "isMountAvailable:" << isMountAvailable;
if (!isMountAvailable) {
finishWithError("Failed to download compatible image.");
}
@@ -273,14 +279,11 @@ void DevDiskImageHelper::showStatus(const QString &message, bool isError)
void DevDiskImageHelper::finishWithSuccess()
{
m_loadingIndicator->stop();
emit mountingCompleted(true);
QTimer::singleShot(0, this, [this]() { accept(); });
accept();
}
void DevDiskImageHelper::finishWithError(const QString &errorMessage)
{
m_loadingIndicator->stop();
showStatus(errorMessage, true);
emit mountingCompleted(false);
}
+8 -2
View File
@@ -72,8 +72,7 @@ void DevDiskManager::populateImageList()
"Image list will be empty until network fetch succeeds.";
}
}
QUrl url("https://raw.githubusercontent.com/iDescriptor/iDescriptor/refs/"
"heads/main/DeveloperDiskImages.json");
QUrl url(DEVELOPER_DISK_IMAGE_JSON_URL);
QNetworkRequest request(url);
auto *reply = m_networkManager->get(request);
@@ -335,6 +334,13 @@ bool DevDiskManager::downloadCompatibleImage(iDescriptorDevice *device,
QList<ImageInfo> images =
parseImageList(path, deviceMajorVersion, deviceMinorVersion, "", 0);
if (images.isEmpty()) {
qDebug() << "No images found for device version:" << deviceMajorVersion
<< "." << deviceMinorVersion;
callback(false);
return false;
}
for (const ImageInfo &info : images) {
if (info.compatibility != ImageCompatibility::Compatible &&
info.compatibility != ImageCompatibility::MaybeCompatible) {
+33 -9
View File
@@ -141,15 +141,11 @@ void DeviceSidebarItem::setupUI()
updateToggleButton();
toggleCollapse();
setStyleSheet("DeviceSidebarItem { border: "
"1px solid #e0e0e0; border-radius: 5px; }");
setSelected(false);
}
void DeviceSidebarItem::setSelected(bool selected)
{
if (m_selected == selected)
return;
m_selected = selected;
// todo : bug the first device selected style is not applied
if (selected) {
@@ -233,8 +229,6 @@ void RecoveryDeviceSidebarItem::setupUI()
mainLayout->addWidget(headerWidget);
// Set initial style
// Set initial style
setStyleSheet("RecoveryDeviceSidebarItem { border: "
"1px solid #e0e0e0; border-radius: 5px; }");
}
@@ -322,6 +316,9 @@ DevicePendingSidebarItem *
DeviceSidebarWidget::addPendingDevice(const QString &uuid)
{
DevicePendingSidebarItem *item = new DevicePendingSidebarItem(uuid, this);
connect(item, &DevicePendingSidebarItem::clicked, this, [this, uuid]() {
onItemSelected(DeviceSelection::pending(uuid.toStdString()));
});
m_pendingItems[uuid.toStdString()] = item;
m_contentLayout->insertWidget(m_contentLayout->count() - 1, item);
return item;
@@ -390,6 +387,9 @@ void DeviceSidebarWidget::updateSelection()
for (auto item : m_recoveryItems) {
item->setSelected(false);
}
for (auto item : m_pendingItems) {
item->setSelected(false);
}
// Set selection based on current selection
if (m_currentSelection.type == DeviceSelection::Normal &&
@@ -398,15 +398,18 @@ void DeviceSidebarWidget::updateSelection()
} else if (m_currentSelection.type == DeviceSelection::Recovery &&
m_recoveryItems.contains(m_currentSelection.ecid)) {
m_recoveryItems[m_currentSelection.ecid]->setSelected(true);
} else if (m_currentSelection.type == DeviceSelection::Pending &&
m_pendingItems.contains(m_currentSelection.udid)) {
m_pendingItems[m_currentSelection.udid]->setSelected(true);
}
}
DevicePendingSidebarItem::DevicePendingSidebarItem(const QString &udid,
QWidget *parent)
: QFrame(parent)
: QFrame(parent), m_udid(udid)
{
QHBoxLayout *layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setContentsMargins(10, 10, 10, 10);
layout->setSpacing(1);
QProcessIndicator *spinner = new QProcessIndicator(this);
@@ -420,4 +423,25 @@ DevicePendingSidebarItem::DevicePendingSidebarItem(const QString &udid,
layout->addWidget(spinner);
setLayout(layout);
setSelected(false);
}
void DevicePendingSidebarItem::setSelected(bool selected)
{
m_selected = selected;
if (selected) {
setStyleSheet(QString("DevicePendingSidebarItem { border: "
"2px solid %1; border-radius: 5px; }")
.arg(COLOR_BLUE.name()));
} else {
setStyleSheet("DevicePendingSidebarItem { border: "
"1px solid #e0e0e0; border-radius: 5px; }");
}
}
void DevicePendingSidebarItem::mousePressEvent(QMouseEvent *event)
{
emit clicked();
QFrame::mousePressEvent(event);
}
+12 -1
View File
@@ -82,9 +82,20 @@ class DevicePendingSidebarItem : public QFrame
{
Q_OBJECT
public:
explicit DevicePendingSidebarItem(const QString &deviceName,
explicit DevicePendingSidebarItem(const QString &udid,
QWidget *parent = nullptr);
void setSelected(bool selected);
bool isSelected() const { return m_selected; }
signals:
void clicked();
protected:
void mousePressEvent(QMouseEvent *event) override;
private:
QString m_udid;
bool m_selected = false;
};
#endif // DEVICEPENDINGSIDEBARITEM_H
+5 -1
View File
@@ -41,7 +41,8 @@
#define TOOL_NAME "iDescriptor"
#define APP_LABEL "iDescriptor"
#define APP_COPYRIGHT "© 2025 Uncore. All rights reserved."
#define APP_COPYRIGHT \
"© 2025 The iDescriptor Project contributors. See AUTHORS for details."
#define AFC2_SERVICE_NAME "com.apple.afc2"
#define RECOVERY_CLIENT_CONNECTION_TRIES 3
#define APPLE_VENDOR_ID 0x05ac
@@ -49,6 +50,9 @@
#define SPONSORS_JSON_URL \
"https://raw.githubusercontent.com/iDescriptor/iDescriptor/refs/heads/" \
"main/sponsors.json"
#define DEVELOPER_DISK_IMAGE_JSON_URL \
"https://raw.githubusercontent.com/iDescriptor/iDescriptor/refs/heads/" \
"main/DeveloperDiskImages.json"
// This is because afc_read_directory accepts "/var/mobile/Media" as "/"
#define POSSIBLE_ROOT "../../../../"
+7 -4
View File
@@ -299,8 +299,10 @@ MainWindow::MainWindow(QWidget *parent)
true,
false,
true,
"AppImage is not updateable.New version is downloaded to "
"\"Downloads\".You can start using the new version by launching it "
"AppImages we ship are not updateable. New version is downloaded "
"to "
"\"Downloads\". You can start using the new version by launching "
"it "
"from there. You can delete this AppImage version if you like.",
"Update downloaded would you like to quit and open the new "
"version?",
@@ -337,8 +339,9 @@ void MainWindow::createMenus()
QAction *aboutAct = new QAction("&About iDescriptor", this);
connect(aboutAct, &QAction::triggered, this, [this]() {
QMessageBox::about(this, "iDescriptor",
"A free and open-source idevice management tool.");
QMessageBox::about(
this, "iDescriptor",
"A free, open-source, and cross-platform iDevice management tool.");
});
actionsMenu->addAction(aboutAct);
#endif
+13
View File
@@ -179,6 +179,18 @@ void SettingsManager::setShowKeychainDialog(bool show)
m_settings->sync();
}
QString SettingsManager::defaultJailbrokenRootPassword() const
{
return m_settings->value("defaultJailbrokenRootPassword", "alpine")
.toString();
}
void SettingsManager::setDefaultJailbrokenRootPassword(const QString &password)
{
m_settings->setValue("defaultJailbrokenRootPassword", password);
m_settings->sync();
}
void SettingsManager::doIfEnabled(Setting setting, std::function<void()> action)
{
bool shouldExecute = false;
@@ -222,6 +234,7 @@ void SettingsManager::resetToDefaults()
setTheme("System Default");
setConnectionTimeout(30);
setShowKeychainDialog(true);
setDefaultJailbrokenRootPassword("alpine");
}
void SettingsManager::saveFavoritePlace(const QString &path,
+3
View File
@@ -89,6 +89,9 @@ public:
bool showKeychainDialog() const;
void setShowKeychainDialog(bool show);
QString defaultJailbrokenRootPassword() const;
void setDefaultJailbrokenRootPassword(const QString &password);
// Utility method for conditional execution
void doIfEnabled(Setting setting, std::function<void()> action);
+40
View File
@@ -134,6 +134,38 @@ void SettingsWidget::setupUI()
securityLayout->addWidget(m_useUnsecureBackend);
scrollLayout->addWidget(securityGroup);
// === JAILBROKEN SETTINGS ===
auto *jailbrokenGroup = new QGroupBox("Jailbroken");
auto *jailbrokenLayout = new QVBoxLayout(jailbrokenGroup);
// Default jailbroken root password
auto *passwordLayout = new QHBoxLayout();
passwordLayout->addWidget(new QLabel("Default Jailbroken Root Password:"));
m_defaultJailbrokenRootPassword = new QLineEdit();
m_defaultJailbrokenRootPassword->setEchoMode(QLineEdit::PasswordEchoOnEdit);
m_defaultJailbrokenRootPassword->setMaximumWidth(200);
m_defaultJailbrokenRootPassword->setToolTip(
"Default password used for SSH root authentication on jailbroken "
"devices: Default is 'alpine'.");
passwordLayout->addWidget(m_defaultJailbrokenRootPassword);
passwordLayout->addStretch();
jailbrokenLayout->addLayout(passwordLayout);
scrollLayout->addWidget(jailbrokenGroup);
scrollLayout->addSpacing(30);
// Add a footer Author & Version & app info & app description
auto *footerLabel = new QLabel(
QString(
"iDescriptor v%1\n"
"A free, open-source, and cross-platform iDevice management tool.\n"
"© 2025 See AUTHORS for details. Licensed under AGPLv3.")
.arg(APP_VERSION));
footerLabel->setAlignment(Qt::AlignCenter);
footerLabel->setStyleSheet("color: gray; font-size: 10pt;");
scrollLayout->addWidget(footerLabel);
// Add stretch to push everything to the top
scrollLayout->addStretch();
@@ -188,6 +220,9 @@ void SettingsWidget::loadSettings()
m_connectionTimeout->setValue(sm->connectionTimeout());
m_useUnsecureBackend->setChecked(sm->useUnsecureBackend());
m_defaultJailbrokenRootPassword->setText(
sm->defaultJailbrokenRootPassword());
// Disable apply button initially
m_applyButton->setEnabled(false);
}
@@ -230,6 +265,9 @@ void SettingsWidget::connectSignals()
onSettingChanged();
}
});
connect(m_defaultJailbrokenRootPassword, &QLineEdit::textChanged, this,
&SettingsWidget::onSettingChanged);
}
void SettingsWidget::onBrowseButtonClicked()
@@ -304,6 +342,8 @@ void SettingsWidget::saveSettings()
sm->setTheme(m_themeCombo->currentText());
sm->setConnectionTimeout(m_connectionTimeout->value());
sm->setDefaultJailbrokenRootPassword(
m_defaultJailbrokenRootPassword->text());
m_applyButton->setEnabled(false);
}
+3
View File
@@ -63,6 +63,9 @@ private:
// Device Connection
QSpinBox *m_connectionTimeout;
// Jailbroken
QLineEdit *m_defaultJailbrokenRootPassword;
// Buttons
QPushButton *m_checkUpdatesButton;
QPushButton *m_resetButton;
+6 -2
View File
@@ -19,6 +19,7 @@
#include "sshterminalwidget.h"
#include "qprocessindicator.h"
#include "settingsmanager.h"
#include <QDebug>
#include <QDir>
#include <QFile>
@@ -384,8 +385,11 @@ void SSHTerminalWidget::startSSH(const QString &host, uint16_t port)
qDebug() << "SSH connected successfully, attempting authentication...";
// Authenticate with password
rc = ssh_userauth_password(m_sshSession, nullptr, "alpine");
QString defaultPassword =
SettingsManager::sharedInstance()->defaultJailbrokenRootPassword();
QByteArray passwordBytes = defaultPassword.toUtf8();
rc =
ssh_userauth_password(m_sshSession, nullptr, passwordBytes.constData());
if (rc != SSH_AUTH_SUCCESS) {
showError(QString("SSH authentication failed: %1")
.arg(ssh_get_error(m_sshSession)));
-4
View File
@@ -398,10 +398,6 @@ void ToolboxWidget::onCurrentDeviceChanged(const DeviceSelection &selection)
m_currentDevice =
AppContext::sharedInstance()->getDevice(selection.udid);
}
} else {
// Handle recovery, pending, or no device selection
m_uuid.clear();
m_currentDevice = nullptr;
}
}