/*! * * Copyright (c) 2021-2024 Diality Inc. - All Rights Reserved. * \copyright * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * \file DeviceView.cpp * \author (last) Behrouz NematiPour * \date (last) 11-Sep-2023 * \author (original) Behrouz NematiPour * \date (original) 03-Jun-2021 * */ #include "DeviceView.h" // Qt // Project #include "ApplicationController.h" #include "GuiController.h" #include "DeviceController.h" #include "GuiGlobals.h" #include "encryption.h" #include "MWifiNetwork.h" VIEW_DEF_CLASS_EX(VDevice, QAbstractListModel) void VDevice::initConnections() { DEVICE_VIEW_INIT_CONNECTIONS_LIST connect(&_DeviceController , SIGNAL(didPOSTOSVersionData(QString)), this , SLOT( onPOSTOSVersionData(QString))); connect(&_DeviceController , SIGNAL(didWiFiIP(QString)), this , SLOT( onWiFiIP(QString))); } // ================================================================================================== // ================================================= developer implementation section // ================================================================================================== // ================================================= OS Version void VDevice::onPOSTOSVersionData(const QString &vOSVersion) { osVersion(vOSVersion); } // ================================================= Brightness void VDevice::doInitBrightness() { // DEBUG : qDebug() << "HERE Request" << vValue; DeviceBrightnessRequestData data; emit didAttributeRequest(data); } void VDevice::brightnessRequest(const quint8 &vValue) { // DEBUG : qDebug() << "HERE Request" << vValue; DeviceBrightnessRequestData data; data.mBrightness = vValue; emit didAttributeRequest(data); } void VDevice::onAttributeResponse(const DeviceBrightnessResponseData &vData) { // DEBUG : qDebug() << "HERE Response" << vData.mBrightness << brightness(); if ( vData.mCompleted ) { // for the brightness we will set what ever comes from the script either succeed or failed. brightness(vData.mBrightness); emit brightnessChanged(vData.mBrightness); } status (vData.mMessage ); accepted(vData.mAccepted); reason (vData.mReason ); // has to be the last one response(true); } // ================================================= Crypt Setup void VDevice::doInitCryptSetup() { // Nothing for now. } void VDevice::cryptSetupRequest(const QString &vCommand) { // DEBUG : qDebug() << "HERE Request" << vValue; cryptSetupEnabled(false); DeviceCryptSetupRequestData data; data.mCommand = vCommand; bool ok = false; data.mPassword = encryption::configurationsPassword( ok ); if ( ! ok ) { // not enough infromation to create a secure passowrd status(tr("Not enough secure information provided")); } else { emit didAttributeRequest(data); } } void VDevice::onAttributeResponse(const DeviceCryptSetupResponseData &vData) { // this has to be called to let Gui to set to old value that device controller provided. // this response is not updating the cryptsetup attribute. // cryptsetup attribute will containe the command to be sent to the cryptsetup script // and the retrurned message can be the model message in vData.mMessage status(vData.mMessage); accepted(vData.mAccepted); reason (vData.mReason ); // Only set to enable if request failed with a reason reported, disable otherwise //DEBUG qDebug()<< "accepted " << vData.mAccepted << " reason "<< vData.mReason; cryptSetupEnabled (!vData.mAccepted && vData.mReason != 0); // has to be the last one response(true); } // ================================================= Bluetooth Paired Reset void VDevice::doInitBluetoothPairedReset() { // DEBUG : qDebug() << "HERE Init"; DeviceBluetoothPairedResetRequestData data; emit didAttributeRequest(data); } void VDevice::bluetoothPairedResetRequest(const quint8 &) { // DEBUG : qDebug() << "HERE Request"; // Nothing to be done here. This property will not be assigned. } void VDevice::onAttributeResponse(const DeviceBluetoothPairedResetResponseData &vData) { // DEBUG : qDebug() << "HERE Response" << vData.mAccepted << vData.mReason << vData.mMessage; // this has to be called to let Gui to set to old value that device controller provided. status(vData.mMessage); accepted(vData.mAccepted); reason (vData.mReason ); // has to be the last one emit bluetoothPairedResetChanged(vData.mReason); response(true); } // ================================================= Bluetooth Paired Query void VDevice::doInitBluetoothPairedQuery() { // DEBUG : qDebug() << "HERE Init"; DeviceBluetoothPairedQueryRequestData data; emit didAttributeRequest(data); } void VDevice::bluetoothPairedQueryRequest(const QStringList &) { // DEBUG : qDebug() << "HERE Request"; // Nothing to be done here. This property will not be assigned. } void VDevice::onAttributeResponse(const DeviceBluetoothPairedQueryResponseData &vData) { // DEBUG : qDebug() << "HERE Response" << vData.mAccepted << vData.mReason << vData.mMessage; // this has to be called to let Gui to set to old value that device controller provided. status(vData.mMessage); accepted(vData.mAccepted); reason (vData.mReason ); // has to be the last one emit bluetoothPairedQueryChanged(vData.mInfo); response(true); } // ================================================= RootSSHAccess void VDevice::doInitRootSSHAccess() { // DEBUG : qDebug() << "HERE Request" << vValue; DeviceRootSSHAccessRequestData data; data.mIsGet = true; emit didAttributeRequest(data); } void VDevice::rootSSHAccessRequest(const Qt::CheckState &vValue) { // DEBUG : qDebug() << "HERE Request" << vValue; DeviceRootSSHAccessRequestData data; data.mIsGet = false; data.mRootSSHAccess = vValue; emit didAttributeRequest(data); } void VDevice::onAttributeResponse(const DeviceRootSSHAccessResponseData &vData) { if ( vData.mAccepted ) { rootSSHAccess(vData.mRootSSHAccess); status(""); } else { // this has to be called to let Gui to set to old value that device controller provided. emit rootSSHAccessChanged(vData.mRootSSHAccess); status(vData.mMessage); } accepted(vData.mAccepted); reason (vData.mReason ); // has to be the last one response(true); } // ================================================= Factory Reset void VDevice::doInitFactoryReset() { // Nothing for now. } void VDevice::factoryResetRequest(const QString &vCommand) { Q_UNUSED(vCommand) // DEBUG : qDebug() << "HERE Request" << vCommand; factoryResetEnabled(false); DeviceFactoryResetRequestData data; emit didAttributeRequest(data); } void VDevice::onAttributeResponse(const DeviceFactoryResetResponseData &vData) { // QDEBUG : qDebug() << "HERE Response " << Q_FUNC_INFO << " accepted: " << vData.mAccepted; // this has to be called to let Gui to set to old value that device controller provided. status(vData.mMessage); accepted(vData.mAccepted); reason (vData.mReason ); factoryResetEnabled(isCompleteResponse(vData)); // has to be the last one response(true); } // ================================================= Decommission void VDevice::doInitDecommission() { // Nothing for now. } void VDevice::decommissionRequest(const QString &vCommand) { Q_UNUSED(vCommand) // DEBUG : qDebug() << "HERE Request" << vCommand; decommissionEnabled(true); DeviceDecommissionRequestData data; bool ok = false; data.mPassword = encryption::configurationsPassword( ok ); if ( ! ok ) { // not enough infromation to create a secure passowrd status(tr("Not enough secure information provided")); } else { emit didAttributeRequest(data); } emit didAttributeRequest(data); } void VDevice::onAttributeResponse(const DeviceDecommissionResponseData &vData) { //QDEBUG : qDebug() << "HERE Response " << Q_FUNC_INFO << " accepted: "<< vData.mAccepted << vData.mReason; // this has to be called to let Gui to set to old value that device controller provided. status(vData.mMessage); accepted(vData.mAccepted); reason (vData.mReason ); decommissionEnabled(isCompleteResponse(vData)); // has to be the last one response(true); } // ================================================= WifiList void VDevice::doInitWifiList() { wifiListRequest({}); } void VDevice::wifiListRequest(const QStringList &) { status( "" ); wifiList({}); dataClear(); wifiListEnabled(false); DeviceWifiListRequestData data ; emit didAttributeRequest( data ); } void VDevice::onAttributeResponse(const DeviceWifiListResponseData &vData) { // DEBUG : qDebug() << " 2 ---------- " << __FUNCTION__ << vData.mMessage << result << vData.mCompleted; if ( vData.mCompleted ) { if ( vData.mAccepted ) { wifiListEnabled(true); parseWifiListResult(vData.mWifiList); } else { wifiList({}); } } accepted(vData.mAccepted); reason (vData.mReason ); status (vData.mMessage ); // has to be the last one response(true); } /*! * \brief Network::parseWifiListResult * \details Extract desired information from the WiFi scan output. Sorts by signal stength * \param vResult - (QString) output collected from QProcess execution * \return List of the Found SSIDs in a model. */ void View::VDevice::parseWifiListResult(const QString &vResult) { enum SSIDInfo_Enum { eSSID , eBSSID , eFREQ , eRATE , eSIGNAL , eSECURITY , eWPA_FLAGS , eRSN_FLAGS , eIN_USE , }; enum ValueUnit_Enum { eValue , eUnit , eCount , }; struct SSIDInfo { QString mSSID ; QString mBSSID ; quint16 mFREQ_Max ; quint16 mRATE_Max ; quint16 mSIGNAL_Max ; QString mSECURITY ; QString mFLAGS ; // eWPA_FLAGS, eRSN_FLAGS are exclusive bool mInUse = false ; bool mSupported = true ; QString toString() { QStringList fields = QStringList() << mSSID << mBSSID << QString::number(mFREQ_Max ) << QString::number(mRATE_Max ) << QString::number(mSIGNAL_Max ) << mSECURITY << mFLAGS << (mSupported ? "T" : "") << (mInUse ? "T" : ""); return fields.join(','); } }; QString mResult = vResult; QHash ssidInfoList; // the Freq, Rate units QString mSSID; const QString mFREQ_Unit = " MHz"; const QString mRATE_Unit = " Mbit/s"; // Removing the units once from the result to accelerate parsing. (will be added when done) mResult.remove(mFREQ_Unit); mResult.remove(mRATE_Unit); QStringList lines = mResult.split('\n'); for ( int row = 0; row < lines.count(); row++ ) { QString mWPA_FLAGS; QString mRSN_FLAGS; QStringList fields = lines[row].split(','); // DEBUG: qDebug() << fields.join("-"); // this will never fail since even an empty string in split at least has index 0=eSSID; mSSID = fields[eSSID].trimmed(); if ( mSSID.isEmpty() ) continue; //hidden networks, or an incorrect entry if ( ssidInfoList.contains( mSSID ) ) { for ( int index = 0; index < fields.count(); index++ ) { // the list has to be sorted and the script is written that way. QString field = fields[index].trimmed(); quint32 value = 0; switch (index) { // NOTE: NOT USED FOR NOW // Not sure if get the max independently for each row is a correct way. // since if we connect to one then we are bound to the specification of that Ap, so we cannot have a max freq from one and max rate from another one. case eFREQ : value = QString("%1").arg(field).toInt(); if ( ssidInfoList[mSSID].mFREQ_Max < value ) { ssidInfoList[mSSID].mFREQ_Max = value; } break; case eRATE : value = QString("%1").arg(field).toInt(); if ( ssidInfoList[mSSID].mRATE_Max < value ) { ssidInfoList[mSSID].mRATE_Max = value; } break; case eSIGNAL : value = QString("%1").arg(field).toInt(); if ( ssidInfoList[mSSID].mSIGNAL_Max < value ) { ssidInfoList[mSSID].mSIGNAL_Max = value; } break; case eIN_USE : value = field.contains("*"); ssidInfoList[mSSID].mInUse = value; break; default: break; } } } else { SSIDInfo ssidInfo; for ( int index = 0; index < fields.count(); index++ ) { // the list has to be sorted and the script is written that way. QString field = fields[index].trimmed(); switch (index) { // DO NOT USE default in this switch case eSSID : mSSID = field; break; case eBSSID :/* ssidInfo.mBSSID = field; */ break; // every row is different and not used now. case eFREQ : ssidInfo.mFREQ_Max = QString("%1").arg(field).toInt(); break; case eRATE : ssidInfo.mRATE_Max = QString("%1").arg(field).toInt(); break; case eSIGNAL : ssidInfo.mSIGNAL_Max = QString("%1").arg(field).toInt(); break; case eIN_USE : ssidInfo.mInUse = field.contains("*"); break; case eSECURITY : ssidInfo.mSECURITY = field; if ( field.isEmpty() ) { ssidInfo.mSupported = false; } else { ssidInfo.mSupported = field.remove(QRegExp("(WPA[23])")).trimmed().isEmpty(); } break; case eWPA_FLAGS : if ( field.contains("tkip") ) { mWPA_FLAGS += " TKIP" ; ssidInfo.mSupported = false; } if ( field.contains("ccmp") ) { mWPA_FLAGS += " AES" ; } if ( ! mWPA_FLAGS.isEmpty() ) { ssidInfo.mFLAGS = mWPA_FLAGS.trimmed(); } break; case eRSN_FLAGS : if ( field.contains("tkip") ) { mRSN_FLAGS += " TKIP" ; ssidInfo.mSupported = false; } if ( field.contains("ccmp") ) { mRSN_FLAGS += " AES" ; } if ( field.contains("sae" ) ) { mRSN_FLAGS += " SAE" ; } if ( ! mRSN_FLAGS.isEmpty() ) { ssidInfo.mFLAGS = mRSN_FLAGS.trimmed(); } break; } } ssidInfo .mSSID = mSSID; ssidInfoList[mSSID] = ssidInfo; } } _dataList.clear(); for (const auto &ssid: qAsConst(ssidInfoList)) { DataModel data; data.mWifiMacAddress = ssid.mBSSID; data.mWifiSSID = ssid.mSSID; data.mWifiSecurityTypes = ssid.mSECURITY; data.mWifiSignalLevel = ssid.mSIGNAL_Max; data.mWifiSupported = ssid.mSupported; data.mWifiConnected = ssid.mInUse ; dataAppend( data, ssid.mInUse, ssid.mSupported); } } void View::VDevice::dataAppend(const DataModel &vData, bool vFirst, bool vSecond) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); if ( vFirst && _dataList.count() >= 0 ) { _dataList.insert(0, vData); goto lOut; } if ( vSecond && _dataList.count() >= 1 ) { _dataList.insert(1, vData); goto lOut; } _dataList.append( vData); lOut: endInsertRows(); } void View::VDevice::dataClear() { beginRemoveRows(QModelIndex(), 0, rowCount()); _dataList.clear(); endRemoveRows(); } QVariant View::VDevice::data(const QModelIndex &vIndex, int vRole) const { if (! vIndex.isValid() || vIndex.row() >= _dataList.count()) return QVariant(); DataModel dataList = _dataList[vIndex.row()]; switch (vRole) { // ----- WiFi case eRole_WifiMacAddress : return dataList.mWifiMacAddress ; case eRole_WifiSsid : return dataList.mWifiSSID ; case eRole_WifiSecurityTypes : return dataList.mWifiSecurityTypes ; case eRole_WifiSignalLevel : return dataList.mWifiSignalLevel ; case eRole_WifiSupported : return dataList.mWifiSupported ; case eRole_WifiConnected : return dataList.mWifiConnected ; // ----- Bluetooth case eRole_BLE_UNUSED : return ""; ; } return QString("Wifi %1").arg(vIndex.row()); } bool View::VDevice::setData(const QModelIndex &vIndex, const QVariant& vValue, int vRole) { if (! vIndex.isValid() || vIndex.row() >= _dataList.count()) { return false; } DataModel &dataItem = _dataList[vIndex.row()]; switch (vRole) { case eRole_WifiConnected: { if (dataItem.mWifiConnected != vValue.toBool()) { dataItem.mWifiConnected = vValue.toBool(); } break; } default: return false; } // explicitly emit a dataChanged signal to notify anybody bound to this property (vRole) emit dataChanged(vIndex, vIndex, QVector(1, vRole)); return true; } QModelIndex View::VDevice::index(int vRow, int vColumn, const QModelIndex &vParent) const { return hasIndex(vRow, vColumn, vParent) ? createIndex(vRow, vColumn) : QModelIndex(); } // ================================================= WifiInfo void VDevice::doInitWifiInfo() { wifiInfoRequest({}); } void VDevice::wifiInfoRequest(const QStringList &) { DeviceWifiInfoRequestData data; wifiInfo({}); emit didAttributeRequest(data); } void VDevice::onAttributeResponse(const DeviceWifiInfoResponseData &vData) { if ( vData.mCompleted ) { if ( vData.mAccepted ) { parseWifiInfoResult(vData.mWifiInfo); } else { wifiInfo({}); } } accepted(vData.mAccepted); reason (vData.mReason ); status (vData.mMessage ); // has to be the last one response(true); } /*! * \brief Network::parseWifiInfoResult * \details Extract desired information from the WiFi information output. * \param vResult - (QString) output collected from QProcess execution */ void View::VDevice::parseWifiInfoResult(const QString &vResult) { enum WifiInfo_Enum { eSSID , eIPADDRESS , eSUBNETMASK , eGATEWAY , eDNS , eCount , }; QString mResult = vResult; QStringList fields = mResult.split(','); if (fields.size() < eCount) { status (tr("The WiFi info response error")); LOG_DEBUG(QStringLiteral("The WiFi info response length is not correct [%1 of %2]").arg(fields.size()).arg(eCount)); return; } if (! fields[eSSID].trimmed().isEmpty()) { ssid(fields[eSSID].trimmed()); ipAddress(fields[eIPADDRESS].trimmed()); subnetMask(fields[eSUBNETMASK].trimmed()); gateway(fields[eGATEWAY].trimmed()); QStringList dnsList = fields.mid(eDNS); dns(dnsList.join('\n')); } else { ssid(""); ipAddress(""); gateway(""); subnetMask(""); dns(""); } updateWifiList(); } void VDevice::updateWifiList() { for (int row = 0; row < rowCount(); row++) { setData(index(row,0), _dataList[row].mWifiSSID == ssid(), eRole_WifiConnected); } } // ================================================= WifiConnect void VDevice::doInitWifiConnect() { // Nothing for now. } void VDevice::wifiConnectRequest(const bool &) { // Nothing for now. } /*! * \brief Network::doWifiConnect * \details Method to connect/disconnect to desired Wi-Fi with SSID and password * \param vConnect - (bool) determine if user wants to connect or disconnect Wi-Fi * \param vSsid - (QString) SSID to connect or disconnect * \param vPassword - (QString) SSID password */ void VDevice::doWifiConnect(bool vConnect, const QString &vSsid, const QString &vPassword) { DeviceWifiConnectRequestData data; data.mConnect = vConnect; data.mSsid = vSsid; data.mPassword = vPassword; emit didAttributeRequest(data); } void VDevice::onAttributeResponse(const DeviceWifiConnectResponseData &vData) { if ( vData.mCompleted ) { if ( vData.mAccepted ) { wifiInfoRequest({}); } } status (vData.mMessage ); accepted(vData.mAccepted); reason (vData.mReason ); // has to be the last one response(true); } // ================================================= WiFi Indicator void VDevice::onWiFiIP (const QString &vData) { ipAddress(vData); }