/*! * * 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 VSettings.cpp * \author (last) Behrouz NematiPour * \date (last) 10-Jul-2024 * \author (original) Behrouz NematiPour * \date (original) 29-Mar-2021 * */ #include "VSettings.h" // Qt #include #include // Project #include "GuiController.h" #include "MSettings.h" #include "Settings.h" #include VIEW_DEF_CLASS(VSettings) void VSettings::initConnections() { ACTION_VIEW_CONNECTION (SettingsData ); connect(&_GuiController, SIGNAL(didActionReceive (GuiActionType, const QVariantList &)), this , SLOT( onActionReceive (GuiActionType, const QVariantList &))); } void VSettings::initConnectionsSettings() { static bool init = false; if ( init ) return; PROPERTY_POST_CONNECTION(VSettings, servicePass ); PROPERTY_POST_CONNECTION(VSettings, alarmVolume ); PROPERTY_POST_CONNECTION(VSettings, roWaterMode ); PROPERTY_POST_CONNECTION(VSettings, userMode ); PROPERTY_POST_CONNECTION(VSettings, integratedBPCuff ); PROPERTY_POST_CONNECTION(VSettings, heparinSyringePump ); init = true; } void VSettings::servicePass_post(const QString &vservicePass) { //TODO The Settings shall be the Singleton SettingsController and modify the MSettings like the others. if ( Storage::Settings::save(servicePassGroup(), servicePassKey(), vservicePass) != 0 ) { servicePass(""); // FIXME: Notify UI with a message } } void VSettings::roWaterMode_post(const bool &vroWaterMode_post) { //TODO The Settings shall be the Singleton SettingsController and modify the MSettings like the others. if ( Storage::Settings::save(roWaterModeGroup(), roWaterModeKey(), QString::number(vroWaterMode_post)) != 0 ) { roWaterMode(false); // FIXME: Notify UI with a message } } void VSettings::userMode_post(const bool &vuserMode_post) { //TODO The Settings shall be the Singleton SettingsController and modify the MSettings like the others. if ( Storage::Settings::save(userModeGroup(), userModeKey(), QString::number(vuserMode_post)) != 0 ) { userMode(false); // FIXME: Notify UI with a message } } void VSettings::integratedBPCuff_post(const bool &vintegratedBPCuff_post) { //TODO The Settings shall be the Singleton SettingsController and modify the MSettings like the others. if ( Storage::Settings::save(integratedBPCuffGroup(), integratedBPCuffKey(), QString::number(vintegratedBPCuff_post)) != 0 ) { integratedBPCuff(false); // FIXME: Notify UI with a message } } void VSettings::heparinSyringePump_post(const bool &vheparinSyringePump_post) { //TODO The Settings shall be the Singleton SettingsController and modify the MSettings like the others. if ( Storage::Settings::save(heparinSyringePumpGroup(), heparinSyringePumpKey(), QString::number(vheparinSyringePump_post)) != 0 ) { heparinSyringePump(false); // FIXME: Notify UI with a message } } void VSettings::alarmVolume_post(const quint8 &valarmVolume) { //TODO The Settings shall be the Singleton SettingsController and modify the MSettings like the others. Storage::Settings::save(alarmVolumeGroup(), alarmVolumeKey(), QString::number(valarmVolume)); } void VSettings::onActionReceive(const SettingsData &) { // TODO: this function needs to be moved to the controller, to execute in settings thread not the main thread. // it should then send the output here to update the specific properties defined. QStringList mCategorys = _Settings.categorys(); for (const auto &category : mCategorys) { QStringList groups = _Settings.groups(category); for (const auto &group : groups) { QStringList keys = _Settings.keys (category, group); QVariantList values = _Settings.values (category, group); if ( Storage::Settings::isCategoryInstructions( category ) ) { TKeysList keysList = updateReplacements(group, keys ); updateInstructions(group, keysList, values ); updateInstructions(group ); } else { //TODO: Since it is global system settings, move this to the settings controller so the C++ backend can also use it. like Date/Time formats. for (const auto &key : qAsConst(keys)) { // DEBUG: qDebug() << " ~~~~~~~~~~ " << category << group << key << _Settings.value(category, group, key).toString (); QVariantMap keyValue; if ( isservicePass (category, group, key) ) { QString mServicePass; mServicePass = _Settings.value(category, group, key).toString (); keyValue[key] = mServicePass ; servicePass ( mServicePass); } else if ( isroWaterMode (category, group, key) ) { bool mRoWaterMode; mRoWaterMode = _Settings.value(category, group, key).toBool (); keyValue[key] = mRoWaterMode ; roWaterMode ( mRoWaterMode); } else if ( isuserMode (category, group, key) ) { bool mUserMode; mUserMode = _Settings.value(category, group, key).toBool (); keyValue[key] = mUserMode ; userMode ( mUserMode); } else if ( isintegratedBPCuff (category, group, key) ) { bool mIntegratedBPCuff; mIntegratedBPCuff = _Settings.value(category, group, key).toBool (); keyValue[key] = mIntegratedBPCuff ; integratedBPCuff ( mIntegratedBPCuff ); } else if ( isheparinSyringePump (category, group, key) ) { bool mHeparinSyringePump; mHeparinSyringePump = _Settings.value(category, group, key).toBool (); keyValue[key] = mHeparinSyringePump ; heparinSyringePump ( mHeparinSyringePump ); } else if ( isalarmVolume (category, group, key) ) { quint8 mAlarmVolume; mAlarmVolume = _Settings.value(category, group, key).toInt (); // returns 0 if fails, so no error checking needed. keyValue[key] = mAlarmVolume ; alarmVolume ( mAlarmVolume); } else { keyValue[key] = _Settings.value(category, group, key); } } } } } categorys (mCategorys); emit instructionsChanged(_instructions); // If the configuration exits, then it has been set, and this call internally will be neutral, // otherwise will use the default value and will notify the update. servicePass ( _servicePass ); alarmVolume ( _alarmVolume ); roWaterMode ( _roWaterMode ); //DEBUG qDebug() << servicePass() << roWaterMode() << alarmVolume(); adjustment(true); initConnectionsSettings(); } VSettings::TKeysList VSettings::updateReplacements(const QString &vGroup, const QStringList &vKeys) { TKeysList keysList; for ( quint16 keyIndex = 0; keyIndex < vKeys.count(); keyIndex++ ) { QRegExp regx("\\{\\s*\\d+\\s*:\\s*\\d+\\s*:\\s*\\w*\\s*:\\s*\\d*\\s*\\}"); QString key = vKeys[keyIndex]; int replacementCount = key.count(regx); QStringList keyList; for ( int j = 0; j < replacementCount; j++ ) { int pos = key.indexOf(regx); int len = regx.matchedLength(); QString blk = key.mid(pos+1, len-2); keyList += key.mid(0, pos); keyList += ""; // value placeholder key.remove(0, pos + regx.matchedLength()); QStringList lst = blk.split(":"); quint16 id = lst[0].toUInt(); quint16 ix = lst[1].toUInt(); QString ex = lst[2] ; quint16 rd = lst[3].toUInt(); TLocation loc; loc.group = vGroup ; loc.keyIndex = keyIndex ; loc.locIndex = j * 2 + 1 ; loc.prmIndex = ix ; loc.prmExtra = ex ; loc.prmRound = rd ; _replacements[id] += loc; //DEBUG: qDebug() << "#" << replacementCount << id << loc.group << loc.keyIndex << loc.locIndex << loc.prmIndex; } keyList += key; keysList += keyList; } return keysList; } void VSettings::updateInstructions(const QString &vGroup, const TKeysList &vKeysList, const QVariantList &vValues) { TInstruction mInstruction; mInstruction.location = _location; mInstruction.keys = vKeysList; mInstruction.values = vValues ; _instructionMap[vGroup] = mInstruction; } void VSettings::updateInstructions(const QString &vGroup) { //TODO: this function or even the structure may need to update to send less data to qml and only the updated part. TKeysList keysList = _instructionMap[vGroup].keys ; QVariantList values = _instructionMap[vGroup].values; QStringList keyList; for ( const auto &keys : keysList) { keyList += keys.join(""); } QVariantMap details; details["location"] = _location ; details["keys" ] = keyList ; details["values" ] = values ; _instructions[vGroup] = details ; } /*! * \brief VSettings::onActionReceive * \details emits didActionReceive signal to notify other classes (Gui) * , an action has been received. * \param vAction - the action * \param vData - the action data */ void VSettings::onActionReceive (GuiActionType vAction, const QVariantList &vData) { bool isChanged = false; quint16 id = qFromBigEndian((quint16)vAction); if (_replacements.contains(id)) { isChanged = true; //DEBUG: qDebug() << _replacements[id].count(); for ( const auto &item : _replacements[id]) { //DEBUG: qDebug() << id << item.group << item.keyIndex << item.locIndex << item.prmIndex; QString value; quint16 len = vData.length(); if ( 0 < item.prmIndex && item.prmIndex <= len ) { if ( item.prmRound > 0 ) { value = QString::number(vData[item.prmIndex - 1].toFloat(), 'f', item.prmRound) + item.prmExtra; } else { value = vData[item.prmIndex - 1].toString() + item.prmExtra; } } else { LOG_DEBUG(QString("Given prmIndex %1 is out of message %2 parameters count %3 on instruction group '%4' and key index %5") .arg(item.prmIndex).arg(id).arg(len).arg(item.group).arg(item.keyIndex)); continue; } _instructionMap[item.group].keys[item.keyIndex][item.locIndex] = value; updateInstructions(item.group); } } if ( isChanged ) emit instructionsChanged(_instructions); } /*! * \brief VSettings::isPasswordValid * \details Validates the passed string for conforming to the * - at least one capital letter, * - at least one digit, * - at least one symbol * rule * \param vPassword - the string to check * \returns true if string pass conforms, false otherwise */ bool View::VSettings::isPasswordValid(const QString &vPassword) { int minLen = 10 ; // in the SRS the max is required to be 12 which seems incorrect and I didn't set it here. QString pla = "(?=.*[%1])" ; // positive look ahead QString regSntnc = "^%1$" ; // regular expression sentence with has a start/end QString regUpper = "A-Z" ; QString regLower = "a-z" ; QString regDigit = "0-9" ; QString regSymbl = "!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_`{\\|}~"; QString rln = "[%1]{%2,}" ; // "^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{\|}~])[A-Za-z0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{\|}~]{10,}$" // !"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_`{\\|}~ QString regStr = regSntnc.arg( pla.arg(regUpper) + pla.arg(regLower) + pla.arg(regDigit) + pla.arg(regSymbl) + rln.arg(regUpper + regLower + regDigit + regSymbl) .arg(minLen ) ); QRegularExpression passwordRegex(regStr); bool ok; //DEBUG: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ //DEBUG: vPassword = "Ab0!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" ok = passwordRegex.match(vPassword).hasMatch(); return ok; } QString View::VSettings::hashedPassword(const QString &vPassword, bool vIsService) { bool ok; QString hashed = encryption::hashedString(vPassword, ok, ! vIsService); encryptionPass(ok); return ok ? hashed : ""; } /*! * \brief View::VSettings::isServicePasswordMatch * \details matches the given password string, vPassword, with the service password. * \param vPassword - the given password string * \return true if it matches. */ bool View::VSettings::isServicePasswordMatch(const QString &vPassword) { //DEBUG qDebug() << __FUNCTION__ << _servicePass << hashedPassword(vPassword, true); bool isNotEmpty = ( ! vPassword.trimmed().isEmpty() ); bool isMatch = ( _servicePass == hashedPassword(vPassword, true) ); bool isValid = isMatch && isNotEmpty; if ( isValid ) { LOG_DEBUG("Valid Service Password Entered!"); } else { LOG_DEBUG("Invalid Service Password Entered!"); } return isValid; } /*! * \brief View::VSettings::updateServicePassword * \details Updatest the service password with the given password. * \param vPassword - the password to be saved as the service password. */ void View::VSettings::updateServicePassword(const QString &vPassword) { servicePass(hashedPassword(vPassword, true)); } void View::VSettings::checkServicePasswordSet() { bool ok; isDefaultServicePassword(encryption::isDefaultServicePassword(servicePass(), ok)); //DEBUG qDebug() << __FUNCTION__ << ok; encryptionPass(ok); }