/*! * * Copyright (c) 2021-2023 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 Settings.cpp * \author (last) Behrouz NematiPour * \date (last) 28-Jul-2023 * \author (original) Behrouz NematiPour * \date (original) 29-Mar-2021 * */ // Qt #include #include #include // Project #include "StorageGlobals.h" #include "FileHandler.h" #include "MSettings.h" #include "Logger.h" // namespace using namespace Storage; #include "Settings.h" bool Settings::isValid(const QString &vSettingFile) { int err = Settings::Settings_Error::eError_None; if (! QFileInfo::exists(vSettingFile)) { err = Settings::Settings_Error::eError_SettingNotExists; LOG_DEBUG(errorMessage(err).arg(vSettingFile)); return false; } return true; } /*! * \brief Settings::fileName * \details returns the conf file by the settings information provided. * \return QString configuration/settings file name */ QString Settings::fileName(const QString &vCategory) { return QString("%1%2.%3").arg(Storage::Settings_Path()).arg(vCategory).arg(_settingsExt); } /*! * \brief Settings::doRead * \details Reads all the configuration files * \return non-zero, error value on error, and zero on success. */ int Settings::read() { int err = Settings::Settings_Error::eError_None; QStringList fileFilter = QStringList() << QString("*.%1").arg(_settingsExt); QFileInfoList settingFiles = FileHandler::find (Storage::Settings_Path(), fileFilter); QStringList settingFolders = FileHandler::subFolders(Storage::Settings_Path()); if ( ! settingFolders.count() ) { err = Settings::Settings_Error::eError_No_SettingsFolder; LOG_APPED_PO(errorMessage(err).arg(Storage::Settings_Path())); return err; } for ( QString &settingFolder : settingFolders ) { QString folder = settingFolder.prepend(Storage::Settings_Path()); settingFiles += FileHandler::find(folder, fileFilter); } // DEBUG: // settingFiles = // { // QFileInfo("/home/denali/Projects/application/resources/settings/Confirm/Confirm.conf"), // QFileInfo("/home/denali/Projects/application/resources/settings/Alarms/Alarms.conf") // }; if ( ! settingFiles.count() ) { err = Settings::Settings_Error::eError_No_SettingsFile; LOG_APPED_PO(errorMessage(err).arg(Storage::Settings_Path())); return err; } QList details; for (const auto &settingFile: settingFiles) { if (! isValid(settingFile.absoluteFilePath())) continue; QFile file(settingFile.absoluteFilePath()); if (! file.open(QIODevice::ReadOnly | QIODevice::Text)) { err = Settings::Settings_Error::eError_Read; LOG_APPED_PO(errorMessage(err).arg(Storage::Settings_Path())); return err; } Detail detail; detail.content = file.readAll().trimmed(); if (detail.content.isEmpty()) { //TODO Do not error out for now, the list of the config files which can be empty or not needs to be defined. // err = Settings::Settings_Error::eError_Empty; LOG_APPED_PO(errorMessage(Settings::Settings_Error::eError_Empty).arg(settingFile.fileName())); continue; } detail.location = settingFile.absolutePath() + "/"; detail.category = QString(detail.location + settingFile.baseName()).remove(Storage::Settings_Path()); details += detail; } for (const auto &detail : details) { bool enableDuplicateKey = false; QString attribute = QString("%1%2").arg(_config_attribute_tag); QString group = ""; QStringList lines = detail.content.split('\n'); for (QString line : lines) { // ----- trim the line line = line.trimmed(); // ----- ignore empty lines if ( line.isEmpty() ) continue; // ----- find comments int commentPosition = line.indexOf('#'); // ----- ignore comment line or find attributes if ( commentPosition == 0 ) { // ----- find the configuration file attribute int attributeTagPosition = line.indexOf(_config_attribute_tag); if ( attributeTagPosition == 0 ) { // ----- find the attribute : duplicate_key_... if ( line == attribute.arg( _duplicate_key_on ) ) { enableDuplicateKey = true ;} else if ( line == attribute.arg(_duplicate_key_off ) ) { enableDuplicateKey = false ;} else { LOG_APPED_PO(( "Unknown '" + line + "' attribute in %1").arg(detail.category)); } } // next line continue; } // ----- remove inline comment if ( commentPosition > 0 ) line.truncate(commentPosition); line = line.trimmed(); // ----- find group if (line.startsWith("[") && line.endsWith("]")) { line.replace("[","").replace("]", ""); group = line; } else { if ( group.isEmpty() ) { continue; } else { if ( ! line.isEmpty() ) { QString key = ""; QString value = ""; if ( line.contains('=') ) { QStringList keyValue = line.split('='); key = keyValue[0].trimmed().replace("\\n","\n"); value = keyValue[1].trimmed().replace("\\n","\n"); } else { key = line; } _Settings.add(detail.category, group, key, QVariant(value), enableDuplicateKey); // DEBUG: qDebug() << group << key << value << location << category; } } } // DEBUG: qDebug() << group << line; } } return err; } /*! * \brief Settings::save * \details Writes the setting in the configuration files * \return */ int Settings::save(const QString &vCategory, const QString &vGroup, const QString &vKey, const QString &vValue) { // qDebug() << vCategory // << vGroup // << vKey // << vValue; QString mFileName = fileName(vCategory); QString mContent; int err = Settings_Error::eError_None; // -------------------------------------------------------------------------------------------------------------- //Note: the configuration files which can be saved, are like settings and should not have duplicate values. // as an example the Alarm volume can't have two separate duplicate entry in the settings. // -------------------------------------------------------------------------------------------------------------- _Settings.add(vCategory, vGroup, vKey, vValue, false); QString mPath = QFileInfo(mFileName).absolutePath(); if ( mPath.trimmed().isEmpty() ) { err = Settings_Error::eError_PathEmpty; LOG_DEBUG(errorMessage(err)); return err; } if ( ! FileHandler::makeFolder(mPath) ) { err = Settings_Error::eError_MkDir; LOG_DEBUG(errorMessage(err).arg(mPath)); return err; } for ( const auto &group : _Settings.groups(vCategory) ) { mContent += QString("\n[%1]\n").arg(group); for ( const auto &key : _Settings.keys(vCategory, group) ) { mContent += QString("%1 = %2\n").arg(key).arg(_Settings.value(vCategory, group, key).toString()); } } if ( ! FileHandler::write(mFileName,mContent, false) ) { err = Settings_Error::eError_Write; LOG_DEBUG(errorMessage(err).arg(mFileName)); return err; } return err; } /*! * \brief Settings::configurationsMove * \details After the encrypted partition is ready the configuration files are moved from the root home to denali home. * \param vMessage * \return */ int Settings::configurationsMove(QString *vMessage, bool vIsUpdate) { int err = Settings_Error::eError_None ; Location_Enum loc = Location_Enum ::eInit ; QString src = Settings::location(Location_Enum ::eInit ); QString dst = Settings::location(Location_Enum ::eSecured ); QString msg = ""; QStringList lstExclude; if ( vIsUpdate ) { lstExclude << QFileInfo( src + Storage::Settings_Category_SettingsSystem ).absolutePath(); } if ( ! Settings ::configurationsPOST(loc )) { err = Settings_Error::eError_Remove; msg = errorMessage(err ); LOG_DEBUG(msg); goto lOut; } if ( ! FileHandler ::makeFolder ( dst )) { err = Settings_Error::eError_Copy ; msg = errorMessage(err ); LOG_DEBUG(msg); goto lOut; } for( QString dir : FileHandler::subFolders(src)) { QString sub = src + dir; if ( ! lstExclude.contains( sub ) ) { if ( FileHandler ::copyFolder (sub, dst )) { err = Settings_Error::eError_MkDir ; msg = errorMessage(err, dir); LOG_DEBUG(msg); goto lOut; } } if ( FileHandler ::removeFolder (sub )) { err = Settings_Error::eError_POST ; msg = errorMessage(err, dir); LOG_DEBUG(msg); goto lOut; } } Storage::Settings_Secured(); lOut: if ( vMessage ) { *vMessage = msg; } return err; }