From 15a70c62ca889c57f5a7b368740168c9a41458bf Mon Sep 17 00:00:00 2001 From: uncor3 Date: Fri, 3 Oct 2025 20:43:44 +0000 Subject: [PATCH] WIP: implement avahi service --- CMakeLists.txt | 9 + src/core/services/avahi_service.cpp | 225 ++++++++++++++++++ src/core/services/avahi_service.h | 79 +++++++ src/jailbrokenwidget.cpp | 338 +++++++++++++++++++++++++--- src/jailbrokenwidget.h | 65 ++++-- 5 files changed, 669 insertions(+), 47 deletions(-) create mode 100644 src/core/services/avahi_service.cpp create mode 100644 src/core/services/avahi_service.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a382fd..b07fb33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,12 @@ find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Svg SvgWidgets Mu # Add QTermWidget pkg_check_modules(QTERMWIDGET REQUIRED IMPORTED_TARGET qtermwidget6) +# Add Avahi for network device discovery +pkg_check_modules(AVAHI_CLIENT REQUIRED IMPORTED_TARGET avahi-client) + +# pkg_check_modules(AVAHI_CLIENT REQUIRED IMPORTED_TARGET avahi-client) +# pkg_check_modules(AVAHI_COMMON REQUIRED IMPORTED_TARGET avahi-common) + # Link directly to libraries in /usr/local/lib instead of using pkg-config # Force NO_DEFAULT_PATH to only search in /usr/local/lib find_library(IMOBILEDEVICE_LIBRARY @@ -174,6 +180,7 @@ find_package(Qt6 REQUIRED COMPONENTS QuickWidgets) find_package(Qt6 REQUIRED COMPONENTS Widgets) find_package(Qt6 REQUIRED COMPONENTS Multimedia) find_package(Qt6 REQUIRED COMPONENTS MultimediaWidgets) +find_package(Qt6 REQUIRED COMPONENTS Widgets) qt_add_executable(iDescriptor @@ -211,6 +218,7 @@ target_link_libraries(iDescriptor PRIVATE PkgConfig::PLIST PkgConfig::QRENCODE PkgConfig::QTERMWIDGET + PkgConfig::AVAHI_CLIENT airplay ipatool-go ) @@ -221,6 +229,7 @@ if(APPLE) ${SECURITY_FRAMEWORK} ${COREFOUNDATION_FRAMEWORK} ) +target_link_libraries(iDescriptor PRIVATE Qt6::Widgets) endif() # Add compile definition for source directory diff --git a/src/core/services/avahi_service.cpp b/src/core/services/avahi_service.cpp new file mode 100644 index 0000000..54b46ba --- /dev/null +++ b/src/core/services/avahi_service.cpp @@ -0,0 +1,225 @@ +#include "avahi_service.h" +#include +#include +#include +#include + +AvahiService::AvahiService(QObject *parent) + : QObject(parent), m_simplePoll(nullptr), m_client(nullptr), + m_serviceBrowser(nullptr), m_pollTimer(new QTimer(this)), m_running(false) +{ + connect(m_pollTimer, &QTimer::timeout, this, &AvahiService::pollAvahi); +} + +AvahiService::~AvahiService() { stopBrowsing(); } + +void AvahiService::startBrowsing() +{ + if (m_running) + return; + + qDebug() << "Starting Avahi browsing for Apple devices"; + initializeAvahi(); + + if (m_simplePoll) { + m_pollTimer->start(100); // Poll every 100ms + m_running = true; + } +} + +void AvahiService::stopBrowsing() +{ + if (!m_running) + return; + + qDebug() << "Stopping Avahi browsing"; + m_running = false; + m_pollTimer->stop(); + cleanupAvahi(); + + QMutexLocker locker(&m_devicesMutex); + m_networkDevices.clear(); +} + +QList AvahiService::getNetworkDevices() const +{ + QMutexLocker locker(&m_devicesMutex); + return m_networkDevices; +} + +void AvahiService::pollAvahi() +{ + if (m_simplePoll && m_running) { + avahi_simple_poll_iterate(m_simplePoll, 0); // Non-blocking + } +} + +void AvahiService::initializeAvahi() +{ + int error; + + m_simplePoll = avahi_simple_poll_new(); + if (!m_simplePoll) { + qWarning() << "Failed to create Avahi simple poll"; + return; + } + + m_client = + avahi_client_new(avahi_simple_poll_get(m_simplePoll), + (AvahiClientFlags)0, clientCallback, this, &error); + if (!m_client) { + qWarning() << "Failed to create Avahi client:" << avahi_strerror(error); + cleanupAvahi(); + return; + } +} + +void AvahiService::cleanupAvahi() +{ + if (m_serviceBrowser) { + avahi_service_browser_free(m_serviceBrowser); + m_serviceBrowser = nullptr; + } + + if (m_client) { + avahi_client_free(m_client); + m_client = nullptr; + } + + if (m_simplePoll) { + avahi_simple_poll_free(m_simplePoll); + m_simplePoll = nullptr; + } +} + +void AvahiService::clientCallback(AvahiClient *client, AvahiClientState state, + void *userdata) +{ + AvahiService *service = static_cast(userdata); + + if (state == AVAHI_CLIENT_S_RUNNING) { + qDebug() << "Avahi client running, creating service browser"; + service->m_serviceBrowser = avahi_service_browser_new( + client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_apple-mobdev2._tcp", + nullptr, (AvahiLookupFlags)0, browseCallback, userdata); + + if (!service->m_serviceBrowser) { + qWarning() << "Failed to create service browser:" + << avahi_strerror(avahi_client_errno(client)); + } + } else if (state == AVAHI_CLIENT_FAILURE) { + qWarning() << "Avahi client failure:" + << avahi_strerror(avahi_client_errno(client)); + service->m_running = false; + } +} + +void AvahiService::browseCallback(AvahiServiceBrowser *browser, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, const char *name, + const char *type, const char *domain, + AvahiLookupResultFlags flags, void *userdata) +{ + Q_UNUSED(browser) + Q_UNUSED(flags) + + AvahiService *service = static_cast(userdata); + + switch (event) { + case AVAHI_BROWSER_NEW: + qDebug() << "Found Apple device:" << name; + if (!avahi_service_resolver_new(service->m_client, interface, protocol, + name, type, domain, AVAHI_PROTO_UNSPEC, + (AvahiLookupFlags)0, resolveCallback, + userdata)) { + qWarning() << "Failed to create resolver for" << name; + } + break; + + case AVAHI_BROWSER_REMOVE: + qDebug() << "Apple device removed:" << name; + emit service->deviceRemoved(QString::fromUtf8(name)); + + // Remove from our list + { + QMutexLocker locker(&service->m_devicesMutex); + service->m_networkDevices.removeIf( + [name](const NetworkDevice &dev) { + return dev.name == QString::fromUtf8(name); + }); + } + break; + + case AVAHI_BROWSER_FAILURE: + qWarning() << "Browser failure"; + break; + + default: + break; + } +} + +void AvahiService::resolveCallback( + AvahiServiceResolver *resolver, AvahiIfIndex interface, + AvahiProtocol protocol, AvahiResolverEvent event, const char *name, + const char *type, const char *domain, const char *host_name, + const AvahiAddress *address, uint16_t port, AvahiStringList *txt, + AvahiLookupResultFlags flags, void *userdata) +{ + Q_UNUSED(interface) + Q_UNUSED(protocol) + Q_UNUSED(type) + Q_UNUSED(domain) + Q_UNUSED(flags) + + AvahiService *service = static_cast(userdata); + + if (event == AVAHI_RESOLVER_FOUND) { + NetworkDevice device; + device.name = QString::fromUtf8(name); + device.hostname = QString::fromUtf8(host_name); + device.port = port > 0 ? port : 22; // Default to SSH port + + // Convert address to string + char addr_str[AVAHI_ADDRESS_STR_MAX]; + avahi_address_snprint(addr_str, sizeof(addr_str), address); + device.address = QString::fromUtf8(addr_str); + + // Parse TXT records + for (AvahiStringList *t = txt; t; t = t->next) { + char *key = nullptr; + char *value = nullptr; + avahi_string_list_get_pair(t, &key, &value, nullptr); + if (key) { + device.txt[key] = value ? value : ""; + avahi_free(key); + } + if (value) { + avahi_free(value); + } + } + + qDebug() << "Resolved Apple device:" << device.name << "at" + << device.address << ":" << device.port; + + // Add to our list if not already present + { + QMutexLocker locker(&service->m_devicesMutex); + bool exists = std::any_of(service->m_networkDevices.begin(), + service->m_networkDevices.end(), + [&device](const NetworkDevice &existing) { + return existing == device; + }); + if (!exists) { + service->m_networkDevices.append(device); + emit service->deviceAdded(device); + } + } + + } else if (event == AVAHI_RESOLVER_FAILURE) { + qWarning() << "Failed to resolve service" << name; + } + + avahi_service_resolver_free(resolver); +} \ No newline at end of file diff --git a/src/core/services/avahi_service.h b/src/core/services/avahi_service.h new file mode 100644 index 0000000..c9090a7 --- /dev/null +++ b/src/core/services/avahi_service.h @@ -0,0 +1,79 @@ +#ifndef AVAHI_SERVICE_H +#define AVAHI_SERVICE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct NetworkDevice { + QString name; // service name + QString hostname; // e.g., iPhone-2.local + QString address; // IPv4 or IPv6 address + uint16_t port = 22; // SSH port + std::map txt; // TXT records + + bool operator==(const NetworkDevice &other) const + { + return name == other.name && address == other.address; + } +}; + +class AvahiService : public QObject +{ + Q_OBJECT + +public: + explicit AvahiService(QObject *parent = nullptr); + ~AvahiService(); + + void startBrowsing(); + void stopBrowsing(); + QList getNetworkDevices() const; + +signals: + void deviceAdded(const NetworkDevice &device); + void deviceRemoved(const QString &deviceName); + +private slots: + void pollAvahi(); + +private: + void initializeAvahi(); + void cleanupAvahi(); + + static void clientCallback(AvahiClient *client, AvahiClientState state, + void *userdata); + static void browseCallback(AvahiServiceBrowser *browser, + AvahiIfIndex interface, AvahiProtocol protocol, + AvahiBrowserEvent event, const char *name, + const char *type, const char *domain, + AvahiLookupResultFlags flags, void *userdata); + static void resolveCallback(AvahiServiceResolver *resolver, + AvahiIfIndex interface, AvahiProtocol protocol, + AvahiResolverEvent event, const char *name, + const char *type, const char *domain, + const char *host_name, + const AvahiAddress *address, uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, void *userdata); + + AvahiSimplePoll *m_simplePoll; + AvahiClient *m_client; + AvahiServiceBrowser *m_serviceBrowser; + QTimer *m_pollTimer; + + mutable QMutex m_devicesMutex; + QList m_networkDevices; + bool m_running; +}; + +#endif // AVAHI_SERVICE_H \ No newline at end of file diff --git a/src/jailbrokenwidget.cpp b/src/jailbrokenwidget.cpp index 5e4e875..1f0c42e 100644 --- a/src/jailbrokenwidget.cpp +++ b/src/jailbrokenwidget.cpp @@ -1,15 +1,21 @@ #include "jailbrokenwidget.h" #include "appcontext.h" +#include "core/services/avahi_service.h" #include "iDescriptor-ui.h" #include "iDescriptor.h" +#include #include #include #include +#include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -36,10 +42,20 @@ JailbrokenWidget::JailbrokenWidget(QWidget *parent) : QWidget{parent} mainLayout->addWidget(graphicsView, 1); + // Connect to AppContext for device events connect(AppContext::sharedInstance(), &AppContext::deviceAdded, this, - [this](iDescriptorDevice *device) { deviceConnected(device); }); + &JailbrokenWidget::onWiredDeviceAdded); + connect(AppContext::sharedInstance(), &AppContext::deviceRemoved, this, + &JailbrokenWidget::onWiredDeviceRemoved); - // Right side: Info and Terminal + // Initialize Avahi service + m_avahiService = new AvahiService(this); + connect(m_avahiService, &AvahiService::deviceAdded, this, + &JailbrokenWidget::onWirelessDeviceAdded); + connect(m_avahiService, &AvahiService::deviceRemoved, this, + &JailbrokenWidget::onWirelessDeviceRemoved); + + // Right side: Device selection and Terminal QWidget *rightContainer = new QWidget(); rightContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -48,15 +64,7 @@ JailbrokenWidget::JailbrokenWidget(QWidget *parent) : QWidget{parent} rightLayout->setContentsMargins(15, 15, 15, 15); rightLayout->setSpacing(10); - m_infoLabel = new QLabel("Connect a jailbroken device"); - rightLayout->addWidget(m_infoLabel); - - m_connectButton = new QPushButton("Connect SSH Terminal"); - m_connectButton->setEnabled(false); - connect(m_connectButton, &QPushButton::clicked, this, - &JailbrokenWidget::onConnectSSH); - rightLayout->addWidget(m_connectButton); - + setupDeviceSelectionUI(rightLayout); setupTerminal(); rightLayout->addWidget(m_terminal, 1); @@ -71,6 +79,12 @@ JailbrokenWidget::JailbrokenWidget(QWidget *parent) : QWidget{parent} m_sshTimer = new QTimer(this); connect(m_sshTimer, &QTimer::timeout, this, &JailbrokenWidget::checkSshData); + + // Start scanning for wireless devices + m_avahiService->startBrowsing(); + + // Populate initial devices + updateDeviceList(); } void JailbrokenWidget::setupTerminal() @@ -78,9 +92,228 @@ void JailbrokenWidget::setupTerminal() m_terminal = new QTermWidget(0, this); m_terminal->setMinimumHeight(400); m_terminal->setScrollBarPosition(QTermWidget::ScrollBarRight); - m_terminal->setColorScheme("DarkPastels"); + m_terminal->setColorScheme("Linux"); + m_terminal->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_terminal, &QWidget::customContextMenuRequested, this, + [this](const QPoint &pos) { + QMenu menu(this); + QList actions = m_terminal->filterActions(pos); + if (!actions.isEmpty()) { + menu.addActions(actions); + menu.exec(m_terminal->mapToGlobal(pos)); + } + }); m_terminal->startTerminalTeletype(); m_terminal->hide(); + m_terminal->setStyleSheet("padding : 10px;"); +} + +void JailbrokenWidget::setupDeviceSelectionUI(QVBoxLayout *layout) +{ + // Create scroll area for device selection + QScrollArea *scrollArea = new QScrollArea(); + scrollArea->setWidgetResizable(true); + scrollArea->setMinimumHeight(200); + scrollArea->setMaximumHeight(300); + + QWidget *scrollContent = new QWidget(); + m_deviceLayout = new QVBoxLayout(scrollContent); + m_deviceLayout->setContentsMargins(5, 5, 5, 5); + m_deviceLayout->setSpacing(10); + + // Button group for device selection + m_deviceButtonGroup = new QButtonGroup(this); + connect(m_deviceButtonGroup, + QOverload::of(&QButtonGroup::buttonClicked), + this, &JailbrokenWidget::onDeviceSelected); + + // Wired devices group + m_wiredDevicesGroup = new QGroupBox("Connected Devices"); + m_wiredDevicesLayout = new QVBoxLayout(m_wiredDevicesGroup); + m_deviceLayout->addWidget(m_wiredDevicesGroup); + + // Wireless devices group + m_wirelessDevicesGroup = new QGroupBox("Network Devices"); + m_wirelessDevicesLayout = new QVBoxLayout(m_wirelessDevicesGroup); + m_deviceLayout->addWidget(m_wirelessDevicesGroup); + + scrollArea->setWidget(scrollContent); + layout->addWidget(scrollArea); + + // Info and connect button + m_infoLabel = new QLabel("Select a device to connect"); + layout->addWidget(m_infoLabel); + + m_connectButton = new QPushButton("Connect SSH Terminal"); + m_connectButton->setEnabled(false); + connect(m_connectButton, &QPushButton::clicked, this, + &JailbrokenWidget::onConnectSSH); + layout->addWidget(m_connectButton); +} + +void JailbrokenWidget::updateDeviceList() +{ + // Clear existing devices + clearDeviceButtons(); + + // Add wired devices + QList wiredDevices = + AppContext::sharedInstance()->getAllDevices(); + for (iDescriptorDevice *device : wiredDevices) { + addWiredDevice(device); + } + + // Add wireless devices + QList wirelessDevices = m_avahiService->getNetworkDevices(); + for (const NetworkDevice &device : wirelessDevices) { + addWirelessDevice(device); + } +} + +void JailbrokenWidget::clearDeviceButtons() +{ + // Remove all buttons from button group and layouts + for (QAbstractButton *button : m_deviceButtonGroup->buttons()) { + m_deviceButtonGroup->removeButton(button); + button->deleteLater(); + } + + // Clear layouts + QLayoutItem *item; + while ((item = m_wiredDevicesLayout->takeAt(0)) != nullptr) { + delete item->widget(); + delete item; + } + while ((item = m_wirelessDevicesLayout->takeAt(0)) != nullptr) { + delete item->widget(); + delete item; + } +} + +void JailbrokenWidget::addWiredDevice(iDescriptorDevice *device) +{ + QString deviceName = QString::fromStdString(device->deviceInfo.deviceName); + QString udid = QString::fromStdString(device->udid); + QString displayText = QString("%1\n%2").arg(deviceName, udid); + + QRadioButton *radioButton = new QRadioButton(displayText); + radioButton->setProperty("deviceType", "wired"); + radioButton->setProperty("devicePointer", + QVariant::fromValue(static_cast(device))); + radioButton->setProperty("udid", udid); + + m_deviceButtonGroup->addButton(radioButton); + m_wiredDevicesLayout->addWidget(radioButton); +} + +void JailbrokenWidget::addWirelessDevice(const NetworkDevice &device) +{ + QString displayText = QString("%1\n%2").arg(device.name, device.address); + + QRadioButton *radioButton = new QRadioButton(displayText); + radioButton->setProperty("deviceType", "wireless"); + radioButton->setProperty("deviceAddress", device.address); + radioButton->setProperty("deviceName", device.name); + radioButton->setProperty("devicePort", device.port); + + m_deviceButtonGroup->addButton(radioButton); + m_wirelessDevicesLayout->addWidget(radioButton); +} + +void JailbrokenWidget::onWiredDeviceAdded(iDescriptorDevice *device) +{ + addWiredDevice(device); +} + +void JailbrokenWidget::onWiredDeviceRemoved(const std::string &udid) +{ + QString qudid = QString::fromStdString(udid); + + // Find and remove the corresponding radio button + for (QAbstractButton *button : m_deviceButtonGroup->buttons()) { + if (button->property("deviceType").toString() == "wired" && + button->property("udid").toString() == qudid) { + m_deviceButtonGroup->removeButton(button); + button->deleteLater(); + break; + } + } + + // Reset selection if this device was selected + if (m_selectedDeviceType == DeviceType::Wired && m_selectedWiredDevice && + m_selectedWiredDevice->udid == udid) { + resetSelection(); + } +} + +void JailbrokenWidget::onWirelessDeviceAdded(const NetworkDevice &device) +{ + addWirelessDevice(device); +} + +void JailbrokenWidget::onWirelessDeviceRemoved(const QString &deviceName) +{ + // Find and remove the corresponding radio button + for (QAbstractButton *button : m_deviceButtonGroup->buttons()) { + if (button->property("deviceType").toString() == "wireless" && + button->property("deviceName").toString() == deviceName) { + m_deviceButtonGroup->removeButton(button); + button->deleteLater(); + break; + } + } + + // Reset selection if this device was selected + if (m_selectedDeviceType == DeviceType::Wireless && + m_selectedNetworkDevice.name == deviceName) { + resetSelection(); + } +} + +void JailbrokenWidget::onDeviceSelected(QAbstractButton *button) +{ + QString deviceType = button->property("deviceType").toString(); + + if (deviceType == "wired") { + m_selectedDeviceType = DeviceType::Wired; + m_selectedWiredDevice = static_cast( + button->property("devicePointer").value()); + + if (m_selectedWiredDevice->deviceInfo.jailbroken) { + m_infoLabel->setText("Jailbroken device selected"); + } else { + m_infoLabel->setText("Device selected (jailbreak status unknown)"); + } + } else if (deviceType == "wireless") { + m_selectedDeviceType = DeviceType::Wireless; + m_selectedNetworkDevice.name = + button->property("deviceName").toString(); + m_selectedNetworkDevice.address = + button->property("deviceAddress").toString(); + m_selectedNetworkDevice.port = button->property("devicePort").toUInt(); + + m_infoLabel->setText( + "Network device selected (jailbreak status unknown)"); + } + + m_connectButton->setEnabled(true); + m_connectButton->setText("Connect SSH Terminal"); +} + +void JailbrokenWidget::resetSelection() +{ + m_selectedDeviceType = DeviceType::None; + m_selectedWiredDevice = nullptr; + m_selectedNetworkDevice = NetworkDevice{}; + m_connectButton->setEnabled(false); + m_infoLabel->setText("Select a device to connect"); + + // Uncheck all radio buttons + if (m_deviceButtonGroup->checkedButton()) { + m_deviceButtonGroup->setExclusive(false); + m_deviceButtonGroup->checkedButton()->setChecked(false); + m_deviceButtonGroup->setExclusive(true); + } } void JailbrokenWidget::connectLibsshToTerminal() @@ -100,11 +333,14 @@ void JailbrokenWidget::connectLibsshToTerminal() void JailbrokenWidget::deviceConnected(iDescriptorDevice *device) { if (device->deviceInfo.jailbroken) { - m_device = device; m_infoLabel->setText("Jailbroken device connected"); - m_connectButton->setEnabled(true); - m_connectButton->setText("Connect SSH Terminal"); + } else { + m_infoLabel->setText( + "Connected device is not detected as jailbroken. Continue anyway?"); } + m_device = device; + m_connectButton->setEnabled(true); + m_connectButton->setText("Connect SSH Terminal"); } void JailbrokenWidget::onConnectSSH() @@ -114,24 +350,33 @@ void JailbrokenWidget::onConnectSSH() return; } - initWidget(); + if (m_selectedDeviceType == DeviceType::None) { + m_infoLabel->setText("Please select a device first"); + return; + } + + if (m_selectedDeviceType == DeviceType::Wired) { + initWiredDevice(); + } else { + initWirelessDevice(); + } } -void JailbrokenWidget::initWidget() +void JailbrokenWidget::initWiredDevice() { if (m_isInitialized) return; m_isInitialized = true; - if (!m_device) { - m_infoLabel->setText("Device is not jailbroken"); + if (!m_selectedWiredDevice) { + m_infoLabel->setText("No wired device selected"); return; } m_connectButton->setEnabled(false); m_infoLabel->setText("Setting up SSH tunnel..."); - // Start iproxy first + // Start iproxy first for wired devices iproxyProcess = new QProcess(this); iproxyProcess->setProcessChannelMode(QProcess::MergedChannels); QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); @@ -147,13 +392,22 @@ void JailbrokenWidget::initWidget() qDebug() << "iproxy error:" << error; }); - QTimer::singleShot( - 3000, this, - &JailbrokenWidget::startSSH); // Increased delay to 3 seconds + // Monitor iproxy output for readiness + connect(iproxyProcess, &QProcess::readyRead, this, [this]() { + QByteArray output = iproxyProcess->readAll(); + if (output.contains("waiting for connection")) { + // iproxy is ready, disconnect the signal to avoid multiple calls + disconnect(iproxyProcess, &QProcess::readyRead, this, nullptr); + startSSH("127.0.0.1", 3333); + } + }); - iproxyProcess->start("iproxy", QStringList() - << "-u" << m_device->udid.c_str() - << "3333" << "22"); + QStringList args; + args << "-u" << m_selectedWiredDevice->udid.c_str() << "3333" << "22"; + + qDebug() << "Starting iproxy with args:" << args; + + iproxyProcess->start("iproxy", args); // Check if iproxy started successfully if (!iproxyProcess->waitForStarted(5000)) { @@ -163,13 +417,26 @@ void JailbrokenWidget::initWidget() } } -void JailbrokenWidget::startSSH() +void JailbrokenWidget::initWirelessDevice() +{ + if (m_isInitialized) + return; + m_isInitialized = true; + + m_connectButton->setEnabled(false); + m_infoLabel->setText("Connecting to network device..."); + + // For wireless devices, connect directly without iproxy + startSSH(m_selectedNetworkDevice.address, m_selectedNetworkDevice.port); +} + +void JailbrokenWidget::startSSH(const QString &host, uint16_t port) { if (m_sshConnected) return; m_infoLabel->setText("Connecting to SSH server..."); - qDebug() << "Starting SSH connection to localhost:3333"; + qDebug() << "Starting SSH connection to" << host << ":" << port; // Create SSH session m_sshSession = ssh_new(); @@ -180,9 +447,10 @@ void JailbrokenWidget::startSSH() } // Configure SSH session - ssh_options_set(m_sshSession, SSH_OPTIONS_HOST, "localhost"); - int port = 3333; - ssh_options_set(m_sshSession, SSH_OPTIONS_PORT, &port); + QByteArray hostBytes = host.toUtf8(); + ssh_options_set(m_sshSession, SSH_OPTIONS_HOST, hostBytes.constData()); + int sshPort = static_cast(port); + ssh_options_set(m_sshSession, SSH_OPTIONS_PORT, &sshPort); ssh_options_set(m_sshSession, SSH_OPTIONS_USER, "root"); // Disable strict host key checking @@ -345,8 +613,12 @@ void JailbrokenWidget::disconnectSSH() } if (iproxyProcess) { - iproxyProcess->terminate(); - iproxyProcess->waitForFinished(3000); + iproxyProcess->terminate(); // Ask it to terminate nicely + if (!iproxyProcess->waitForFinished(1000)) { // Wait 1 sec + iproxyProcess->kill(); // Forcefully kill it + iproxyProcess->waitForFinished(1000); // Wait for it to die + } + delete iproxyProcess; // Avoid memory leak iproxyProcess = nullptr; } diff --git a/src/jailbrokenwidget.h b/src/jailbrokenwidget.h index 18e4963..90c9ad5 100644 --- a/src/jailbrokenwidget.h +++ b/src/jailbrokenwidget.h @@ -1,50 +1,87 @@ #ifndef JAILBROKENWIDGET_H #define JAILBROKENWIDGET_H +#include "core/services/avahi_service.h" #include "iDescriptor.h" +#include +#include +#include #include #include #include #include +#include #include #include -#include + +class QTermWidget; + +enum class DeviceType { None, Wired, Wireless }; class JailbrokenWidget : public QWidget { Q_OBJECT public: - explicit JailbrokenWidget(QWidget *parent = nullptr); + JailbrokenWidget(QWidget *parent = nullptr); ~JailbrokenWidget(); - void initWidget(); private slots: - void deviceConnected(iDescriptorDevice *device); void onConnectSSH(); - void startSSH(); void checkSshData(); + void onWiredDeviceAdded(iDescriptorDevice *device); + void onWiredDeviceRemoved(const std::string &udid); + void onWirelessDeviceAdded(const NetworkDevice &device); + void onWirelessDeviceRemoved(const QString &deviceName); + void onDeviceSelected(QAbstractButton *button); private: void setupTerminal(); - void connectLibsshToTerminal(); + void setupDeviceSelectionUI(QVBoxLayout *layout); + void updateDeviceList(); + void clearDeviceButtons(); + void addWiredDevice(iDescriptorDevice *device); + void addWirelessDevice(const NetworkDevice &device); + void resetSelection(); + + void initWiredDevice(); + void initWirelessDevice(); + void startSSH(const QString &host, uint16_t port); void disconnectSSH(); + void connectLibsshToTerminal(); + void deviceConnected(iDescriptorDevice *device); + QTermWidget *m_terminal; QLabel *m_infoLabel; - iDescriptorDevice *m_device = nullptr; - QProcess *iproxyProcess = nullptr; + QPushButton *m_connectButton; - // SSH session variables + // Device selection UI + QVBoxLayout *m_deviceLayout; + QGroupBox *m_wiredDevicesGroup; + QGroupBox *m_wirelessDevicesGroup; + QVBoxLayout *m_wiredDevicesLayout; + QVBoxLayout *m_wirelessDevicesLayout; + QButtonGroup *m_deviceButtonGroup; + + // Avahi service for network discovery + AvahiService *m_avahiService; + + // Selected device tracking + DeviceType m_selectedDeviceType = DeviceType::None; + iDescriptorDevice *m_selectedWiredDevice = nullptr; + NetworkDevice m_selectedNetworkDevice; + + // Legacy device pointer (kept for compatibility) + iDescriptorDevice *m_device = nullptr; + + // SSH components ssh_session m_sshSession; ssh_channel m_sshChannel; QTimer *m_sshTimer; + QProcess *iproxyProcess = nullptr; - // Terminal widgets - QTermWidget *m_terminal; - QPushButton *m_connectButton; - - bool m_isInitialized = false; bool m_sshConnected = false; + bool m_isInitialized = false; }; #endif // JAILBROKENWIDGET_H \ No newline at end of file