#include "WifiInterface.h" // Qt #include #include #include // Project #include "main.h" #include "Logger.h" #include "StorageGlobals.h" using namespace Storage; WifiInterface::WifiInterface(QObject *parent) : QObject(parent) { _processScan.setParent(this); _processReadIPSettings.setParent(this); _processReadGateway.setParent(this); _processReadDNS.setParent(this); _processGenerateWPASupplicant.setParent(this); _processStartWPASupplicant.setParent(this); _processRequestAutoAssignedIP.setParent(this); } void WifiInterface::onInitConnections() { connect(this, SIGNAL(didError(const QString)), this, SLOT(onLogFailure(const QString))); connect(&_processScan, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onProcessFinishedScan(int,QProcess::ExitStatus))); connect(&_processReadIPSettings, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onProcessFinishedReadIP(int,QProcess::ExitStatus))); connect(&_processReadGateway, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onProcessFinishedReadGateway(int,QProcess::ExitStatus))); connect(&_processReadDNS, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onProcessFinishedReadDNS(int,QProcess::ExitStatus))); connect(&_processGenerateWPASupplicant, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onProcessFinishedGenerateWPASupplicant(int,QProcess::ExitStatus))); connect(&_processStartWPASupplicant, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onProcessFinishedStartWPASupplicant(int,QProcess::ExitStatus))); connect(&_processRequestAutoAssignedIP, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onProcessFinishedRequestAutoAssignedIP(int,QProcess::ExitStatus))); } /*! * \brief WifiInterface::init * \details Initialized the Class by calling the init() method first * And initializes the thread vThread by calling initThread * on success init(). * \param vThread - the thread * \return returns the return value of the init() method */ bool WifiInterface::init(QThread &vThread) { if (!init()) return false; initThread(vThread); return true; } /*! * \brief ApplicationController::initThread * \details Moves this object into the thread vThread. * And checks that this method is called from main thread. * Also connects quitThread to application aboutToQuit. * \param vThread - the thread */ void WifiInterface::initThread(QThread &vThread) { // runs in main thread Q_ASSERT_X(QThread::currentThread() == qApp->thread() , __func__, "The Class initialization must be done in Main Thread" ); _thread = &vThread; _thread->setObjectName(QString("%1_Thread").arg(metaObject()->className())); connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(quit())); _thread->start(); moveToThread(_thread); } /*! * \brief MessageAcknowModel::quit * \details quits the class * Calls quitThread */ void WifiInterface::quit() { // coco begin validated: Application termination is not correctly done in coco!!! // it has been tested and works perfectly fine in normal run. quitThread(); // validated } /*! * \brief MessageAcknowModel::quitThread * \details Moves this object to main thread to be handled by QApplicaiton * And to be destroyed there. */ void WifiInterface::quitThread() { // coco begin validated: Application termination is not correctly done in coco!!! // it has been tested and works perfectly fine in normal run. if (!_thread) return; // runs in thread moveToThread(qApp->thread()); // validated } // coco end /*! * \brief WifiInterface::init * \details Initializes the class by setting the connections * \return true on first initialization, false if it has already been initialized */ bool WifiInterface::init() { if (_init) return false; _init = true; onInitConnections(); LOG_EVENT("UI," + tr("%1 Initialized").arg(metaObject()->className())); return true; } /*! * \brief WifiInterface::quit * Called when the application is exiting. */ void WifiInterface::onQuit() { onQuitThread(); // verified } /*! * \brief WifiInterface::quitThread * \details Moves this object to main thread to be handled by QApplicaiton * It will also be destroyed there. */ void WifiInterface::onQuitThread() { if (!_thread) return; moveToThread(qApp->thread()); } /*! * \brief WifiInterface::timerEvent * Built in QObject timer * \param event (QTimerEvent*) - the event timer */ void WifiInterface::timerEvent(QTimerEvent *event) { Q_UNUSED(event); } /*! * \brief WifiInterface::doScan * Scans for Wifi Access Points */ void WifiInterface::doScan() { qDebug() << __FUNCTION__ << QThread::currentThread()->objectName(); if (_scanRunning) { LOG_DEBUG("Wifi network scan is already running."); return; } LOG_DEBUG("Scanning for Wifi Access Points..."); _processScan.setWorkingDirectory(Wifi_Scripts_Dir); _scanRunning = true; emit didScanStatusChanged(_scanRunning); _processScan.start(Wifi_Scan_For_Networks); } /*! * \brief WifiInterface::onScanFinished * Called when the scan process has finished * \param vExitCode - (int) the exit code * \param vExitStatus - (QProcess::ExitStatus) the exit status */ void WifiInterface::onProcessFinishedScan(int vExitCode, QProcess::ExitStatus vExitStatus) { Q_UNUSED(vExitCode) Q_UNUSED(vExitStatus) QString out = _processScan.readAllStandardOutput(); QString err = _processScan.readAllStandardError(); LOG_DEBUG(QString("stdout: %1").arg(out)); LOG_DEBUG(QString("stderr: %1").arg(err)); _scanRunning = false; emit didScanStatusChanged(_scanRunning); onParseWifiScan(out); } /*! * \brief Network::onParseWifiScan * Extract desired information from the wifi scan output. Sorts by signal stength * * \param vOutput - (QString) output collected from QProcess execution */ void WifiInterface::onParseWifiScan(const QString &vOutput) { QList networks; QStringList temp = vOutput.split("Cell"); const QString signalLevelSearchTerm = "Signal level="; const QString macAddressSearchTerm = "Address:"; const QString ssidSearchTerm = "ESSID:"; const QString groupCipherSearchTerm = "Group Cipher"; const QString authSuitesSearchTerm = "Authentication Suites"; for (const QString &line : temp) { if (line.contains(ssidSearchTerm) && line.contains(macAddressSearchTerm) && line.contains(signalLevelSearchTerm)) { QString ssid = line.split(ssidSearchTerm)[1].split("\n")[0].replace("\"", "").trimmed(); if (ssid == "") continue; QString macAddress = line.split(macAddressSearchTerm)[1].split("\n")[0].trimmed(); int signalLevel = line.split(signalLevelSearchTerm)[1].split("dBm")[0].trimmed().toInt(); bool isCCMP = line.split(groupCipherSearchTerm)[1].split("\n")[0].contains("CCMP"); bool isEnterprise = line.split(authSuitesSearchTerm)[1].split("\n")[0].contains("802.1x"); bool isPersonal = line.split(authSuitesSearchTerm)[1].split("\n")[0].contains("PSK"); Network::SECURITY_LEVEL securityLevel = Network::SECURITY_LEVEL::UNSUPPORTED; if (isPersonal && isCCMP) securityLevel = Network::SECURITY_LEVEL::WPA2_AES_PERSONAL; else if (isEnterprise && isCCMP) securityLevel = Network::SECURITY_LEVEL::WPA2_AES_ENTERPRISE; // TODO: Add support for the other security levels Network network(macAddress,ssid, securityLevel, Network::STATUS::NOT_CONNECTED, signalLevel); emit didAddNetwork(network); } } } /*! * \brief WifiInterface::onLogFailure * Ensures any failures reported are logged * \param vMessage (QString) the message detail of the failure */ void WifiInterface::onLogFailure(const QString &vMessage) { LOG_DEBUG(vMessage); } /*! * \brief WifiInterface::doJoinNetwork * Handles request to join a network * \param vMacAddress - (QString) the mac address of the network to join * \param vPassword - (QString) the password for the network provided by the user */ void WifiInterface::doJoinNetwork(const Network &vNetwork, const QString &vPassword) { _network = vNetwork; LOG_DEBUG(QString("Joining Network %1").arg(vNetwork.ssid())); _processGenerateWPASupplicant.start(Wifi_Generate_WPA_Supplicant, QStringList() << vNetwork.ssid() << vPassword << _wpaSupplicantConfPath); } /*! * \brief WifiInterface::onProcessFinishedGenerateWPASupplicant * Called when the process that generates the WPA supplicant file has finished * \param vExitCode - (int) the exit code * \param vExitStatus - (QProcess::ExitStatus) the exit status */ void WifiInterface::onProcessFinishedGenerateWPASupplicant(int vExitCode, QProcess::ExitStatus vExitStatus) { Q_UNUSED(vExitStatus) if (vExitCode != 0) { emit didError("Could not configure network."); return; } _processStartWPASupplicant.start(Wifi_Start_WPA_Supplicant, QStringList() << _iface << _wpaSupplicantConfPath); } /*! * \brief WifiInterface::onProcessFinishedStartWPASupplicant * Called when the process that starts the WPA supplicant has finished * \param vExitCode - (int) the exit code * \param vExitStatus - (QProcess::ExitStatus) the exit status */ void WifiInterface::onProcessFinishedStartWPASupplicant(int vExitCode, QProcess::ExitStatus vExitStatus) { Q_UNUSED(vExitStatus) if (vExitCode != 0) { emit didError("Could not configure network."); return; } _processRequestAutoAssignedIP.start(Wifi_Get_Auto_Assigned_IP, QStringList() << _iface); } /*! * \brief WifiInterface::onProcessFinishedRequestAutoAssignedIP * Called when the process that requests an auto-assigned IP has finished * \param vExitCode - (int) the exit code * \param vExitStatus - (QProcess::ExitStatus) the exit status */ void WifiInterface::onProcessFinishedRequestAutoAssignedIP(int vExitCode, QProcess::ExitStatus vExitStatus) { Q_UNUSED(vExitStatus) if (vExitCode != 0) { emit didError(QString("Could not connect to %1").arg(_network.ssid())); return; } doRequestIPSettings(); } /*! * \brief WifiInterface::doRequestIPSettings * Handles a request to read the IP settings of the device. */ void WifiInterface::doRequestIPSettings() { QString result = ""; _processReadIPSettings.start(Wifi_Read_IP_Settings, QStringList() << _iface); } /*! * \brief WifiInterface::onProcessFinishedReadIP * Parses the IP settings of the device when the process has finished * \param vExitCode - (int) the exit code * \param vExitStatus - (QProcess::ExitStatus) the exit status */ void WifiInterface::onProcessFinishedReadIP(int vExitCode, QProcess::ExitStatus vExitStatus) { Q_UNUSED(vExitCode); Q_UNUSED(vExitStatus); QString output = _processReadIPSettings.readAllStandardOutput(); _network.mIPSettings.mIPAddress = parseIP(output); _network.mIPSettings.mBroadcast = parseBroadcast(output); _network.mIPSettings.mSubnetMask = parseSubnetMask(output); _processReadGateway.start(Wifi_Read_Gateway); } /*! * \brief WifiInterface::onProcessFinishedReadGateway * Parses the gateway of the device when the process has finished then * initiates reading the DNS * \param vExitCode - (int) the exit code * \param vExitStatus - (QProcess::ExitStatus) the exit status */ void WifiInterface::onProcessFinishedReadGateway(int vExitCode, QProcess::ExitStatus vExitStatus) { Q_UNUSED(vExitCode); Q_UNUSED(vExitStatus); _network.mIPSettings.mGateway = parseGateway(_processReadGateway.readAllStandardOutput()); _processReadDNS.start(Wifi_Read_DNS); } /*! * \brief WifiInterface::onProcessFinishedReadDNS * Parses the DNS of the device when the process has finished then * emits the newtwork with all the ip address information filled * \param vExitCode - (int) the exit code * \param vExitStatus - (QProcess::ExitStatus) the exit status */ void WifiInterface::onProcessFinishedReadDNS(int vExitCode, QProcess::ExitStatus vExitStatus) { Q_UNUSED(vExitCode); Q_UNUSED(vExitStatus); _network.mIPSettings.mDNS = parseDNS(_processReadDNS.readAllStandardOutput()); emit didConnectToNetwork(_network); } /*! * \brief WifiInterface::doDisconnectNetwork * Disconnects from the specified network * \param vNetwork - (Network) the network to disconnect from */ void WifiInterface::doDisconnectNetwork(const Network &vNetwork) { LOG_DEBUG(QString("Disconnecting from Network %1").arg(vNetwork.ssid())); QProcess process; process.start(Wifi_Disconnect_Network, QStringList() << _iface); if (process.waitForFinished(_defaultTimeout)) emit didDisconnectNetwork(vNetwork); process.kill(); emit didError(tr("Failed to disconnect from %1").arg(vNetwork.ssid())); } /*! * \brief WifiInterface::onProcessFinishedDisconnectNetwork * Called when we disconnect from the wifi network * \param vExitCode - (int) the exit code * \param vExitStatus - (QProcess::ExitStatus) the exit status */ void WifiInterface::onProcessFinishedDisconnectNetwork(int vExitCode, QProcess::ExitStatus vExitStatus) { Q_UNUSED(vExitStatus) if (vExitCode != 0) { emit didError(tr("Failed to disconnect from %1").arg(_network.ssid())); return; } emit didDisconnectNetwork(_network); _network.clearSettings(); } /*! * \brief WifiInterface::parseIP * Parses the IP from the IP address output * \param vOutput - (QString) the console output * \return (QString) the IP Address only */ QString WifiInterface::parseIP(const QString &vOutput) { if (vOutput.contains("inet addr:") && vOutput.contains("Bcast:")) return vOutput.split("inet addr:")[1].split("Bcast:")[0].trimmed(); return ""; } /*! * \brief WifiInterface::parseBroadcast * Parses the Broadcast from the IP address output * \param vOutput - (QString) the console output * \return (QString) the Broadcast IP if found, "" otherwise */ QString WifiInterface::parseBroadcast(const QString &vOutput) { if (vOutput.contains("Bcast:") && vOutput.contains("Mask:")) return vOutput.split("Bcast:")[1].split("Mask:")[0].trimmed(); return ""; } /*! * \brief WifiInterface::parseSubnetMask * Parses the subnet mask from the provided output * \param vOutput - (QString) the console output * \return (QString) the subnet mask if found, "" otherwise */ QString WifiInterface::parseSubnetMask(const QString &vOutput) { if (vOutput.contains("Mask:")) return vOutput.split("Mask:")[1].trimmed(); return ""; } /*! * \brief WifiInterface::parseGateway * Parses the gateway from the provided output * \param vOutput - (QString) the console output * \return (QString) the gateway if found, "" otherwise */ QString WifiInterface::parseGateway(const QString &vOutput) { if (vOutput.contains("default via")) return vOutput.split("default via")[1].split("dev")[0].trimmed(); return ""; } /*! * \brief WifiInterface::parseDNS * Parses the DNS from the provided output * \param vOutput - (QString) the console output * \return (QString) the first DNS found, "" otherwise */ QString WifiInterface::parseDNS(const QString &vOutput) { if (vOutput.contains("nameserver")) return vOutput.split("nameserver")[1].split("\n")[0].trimmed(); return ""; }