mirror of
https://github.com/iDescriptor/iDescriptor.git
synced 2026-06-22 03:45:51 +08:00
WIP: implement avahi service
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
#include "avahi_service.h"
|
||||
#include <QDebug>
|
||||
#include <QMutexLocker>
|
||||
#include <avahi-common/error.h>
|
||||
#include <avahi-common/malloc.h>
|
||||
|
||||
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<NetworkDevice> 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<AvahiService *>(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<AvahiService *>(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<AvahiService *>(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);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifndef AVAHI_SERVICE_H
|
||||
#define AVAHI_SERVICE_H
|
||||
|
||||
#include <QList>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <avahi-client/client.h>
|
||||
#include <avahi-client/lookup.h>
|
||||
#include <avahi-common/simple-watch.h>
|
||||
|
||||
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<std::string, std::string> 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<NetworkDevice> 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<NetworkDevice> m_networkDevices;
|
||||
bool m_running;
|
||||
};
|
||||
|
||||
#endif // AVAHI_SERVICE_H
|
||||
+305
-33
@@ -1,15 +1,21 @@
|
||||
#include "jailbrokenwidget.h"
|
||||
#include "appcontext.h"
|
||||
#include "core/services/avahi_service.h"
|
||||
#include "iDescriptor-ui.h"
|
||||
#include "iDescriptor.h"
|
||||
#include <QButtonGroup>
|
||||
#include <QDebug>
|
||||
#include <QGraphicsPixmapItem>
|
||||
#include <QGraphicsView>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QMenu>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QScrollArea>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
@@ -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<QAction *> 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<QAbstractButton *>::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<iDescriptorDevice *> wiredDevices =
|
||||
AppContext::sharedInstance()->getAllDevices();
|
||||
for (iDescriptorDevice *device : wiredDevices) {
|
||||
addWiredDevice(device);
|
||||
}
|
||||
|
||||
// Add wireless devices
|
||||
QList<NetworkDevice> 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<void *>(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<iDescriptorDevice *>(
|
||||
button->property("devicePointer").value<void *>());
|
||||
|
||||
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<int>(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;
|
||||
}
|
||||
|
||||
|
||||
+51
-14
@@ -1,50 +1,87 @@
|
||||
#ifndef JAILBROKENWIDGET_H
|
||||
#define JAILBROKENWIDGET_H
|
||||
|
||||
#include "core/services/avahi_service.h"
|
||||
#include "iDescriptor.h"
|
||||
#include <QAbstractButton>
|
||||
#include <QButtonGroup>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QProcess>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <libssh/libssh.h>
|
||||
#include <qtermwidget6/qtermwidget.h>
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user