Index: sources/cloudsync/CloudSyncController.cpp =================================================================== diff -u -r6445275f6426e45223901364b88de48c5845f2ed -re7d731932dbc46bb459190e8a46cb9f7c4b54f01 --- sources/cloudsync/CloudSyncController.cpp (.../CloudSyncController.cpp) (revision 6445275f6426e45223901364b88de48c5845f2ed) +++ sources/cloudsync/CloudSyncController.cpp (.../CloudSyncController.cpp) (revision e7d731932dbc46bb459190e8a46cb9f7c4b54f01) @@ -1,13 +1,13 @@ /*! * - * Copyright (c) 2021-2023 Diality Inc. - All Rights Reserved. + * 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 CloudSyncController.cpp * \author (last) Behrouz NematiPour - * \date (last) 18-Jul-2023 + * \date (last) 20-Nov-2023 * \author (original) Behrouz NematiPour * \date (original) 14-Oct-2021 * @@ -18,7 +18,6 @@ #include // Project -#include "MessageDispatcher.h" #include "ApplicationController.h" #include "DeviceController.h" #include "FileHandler.h" @@ -102,18 +101,22 @@ connect(&_ApplicationController , SIGNAL(didPOSTCloudSync(bool)), this , SLOT( onPOSTCloudSync(bool))); + connect(&_ApplicationController , SIGNAL(didActionReceive (GuiActionType , const QVariantList &)), + this , SLOT( onActionReceive (GuiActionType , const QVariantList &))); connect(&_DeviceController , SIGNAL(didCryptSetupMount(bool)), this , SLOT( onCryptSetupMount(bool))); + connect(&_DeviceController , SIGNAL(didPendingLog (const QString &, const QString &)), + this , SLOT( onPendingLog (const QString &, const QString &))); connect(&_DeviceController , SIGNAL(didWatchFileChange (const QString &)), this , SLOT( onWatchFileChange (const QString &))); connect(&_DeviceController , SIGNAL(didFactoryReset (bool)), this , SLOT( onFactoryReset (bool))); - connect(&_MessageDispatcher , SIGNAL(didActionReceive (GuiActionType , const QVariantList &)), - this , SLOT( onActionReceive (GuiActionType , const QVariantList &))); - connect(&_TreatmentLog , SIGNAL(didTxPending (const QString &)), - this , SLOT( onTxPending (const QString &))); + connect(&_DeviceController , SIGNAL(didDecommissioning (bool)), + this , SLOT( onDecommissioning (bool))); + connect(&_TreatmentLog , SIGNAL(didPendingTxr (const QString &)), + this , SLOT( onPendingTxr (const QString &))); connect(this , SIGNAL(didInitComplete ()), this , SLOT( onInitComplete ()),Qt::QueuedConnection); // it has to be queued connection, don't remove it. } @@ -161,8 +164,8 @@ */ void CloudSyncController::timerEvent(QTimerEvent *) { - // TODO: touch the inp file to as a check-in for CloudSync to know we are up - // a simple touch or a check-in message? + TIME_CALL(sendCheckIn(), _checkinIntervalSend); // call every x times/second - will be called on first call. + TIME_CALL(testCheckIn(), _checkinIntervalTest); // call every x times/second - will be called on first call. testWatchBuffDate(); testDeviceInfoWait(); } @@ -187,7 +190,14 @@ * Does nothing for now */ void CloudSyncController::onInitComplete() { - // Nothing for now + /* Development testability: + For testing if -C (gDisableCloudSyncFailStop) is used call the testReady + which is called + - when the encrypted partition is mounted + - a request for credential save received + testReady will set ok = true if -C (gDisableCloudSyncFailStop) is used. + */ + if ( gDisableCloudSyncFailStop ) testReady(); } /*! @@ -214,7 +224,7 @@ /*! * \brief CloudSyncController::onFactoryReset * \details this slot will be called when the DeviceController is done with the Factory Reset - * to let the UI request CS to do the Factory Reset and clean up all the Tokens. + * to let the UI request CS to do the Factory Reset and clean up all the Logs. * \param vPass - Device controller factory reset was successful. */ void CloudSyncController::onFactoryReset(bool vPass) @@ -225,6 +235,19 @@ } /*! + * \brief CloudSyncController::onDecommissioning + * \details this slot will be called when the DeviceController is done with the Decommissioning + * to let the UI request CS to do the Decommissioning and clean up all the Tokens. + * \param vPass - Device controller Decommissioning was successful. + */ +void CloudSyncController::onDecommissioning(bool vPass) +{ + if ( vPass ) { + csDecommissioning(); + } +} + +/*! * \brief CloudSyncController::onWatchFileChange * \details This slot will be called when the Device Controller identifies any changes in the watched files. * \param vFile - watched file @@ -249,15 +272,31 @@ */ bool CloudSyncController::addCSBuffWatch() { + // this makeFolder function call investigated for permissions + // during setup the folder if does not exists will be created (root) + // the lockdown.sh script will set the permissions for it. + // on power cycle to normal operation the folder with the correct exists. + // *** NOTE: the makeFolder returns true if the folder already exists. *** bool ok = Storage::FileHandler::makeFolder(_location); QVariantList args {}; Errors_Enum error = eError_Unknown; - if ( ! ok ) { error = eError_LogFolder; args = {{ _location }}; ok = false; goto lErr; } - _date_out_File = _location + // The location - _dateFormatted + _dateSeparator + _out_File; // The file name - // watching for the cloud sync output file buffer. - _DeviceController.doAddWatch(_date_out_File); + _date_out_File = _location + // The location + _dateFormatted + _dateSeparator + _out_File; // The file name + // watching for the cloud sync output file buffer. + if ( ! QFileInfo(_date_out_File).exists() ) { + // if the file does not exists, send a check-in to the CS and wait for the response. + // by CS sending the response it will create the out file and next time this function with start watching the file. + // since this class has a one second timer set, next call is next second + // TODO: during this less that 1s UI will not see messages from CS, since the file was not there to watch. + sendCheckIn(); + // send device state to make the CloudSync send back a message to create the out buff with its user to own the file + return ok; + } + _DeviceController.doAddWatch(_date_out_File, false); + _isWatching = true; // when the watch is added then the flag sets until next time the date changes. + // since the buff files will be deleted on each power cycle, when the out buf is created it means the CloudSync is running. + // we emit the ApplicationController to check the post.log for the CloudSync status check. return ok; lErr: @@ -393,6 +432,8 @@ case eError_CredentialEmpty : text = tr( "CS The credentials folder is empty." ) ; break; case eError_TxCodeNoParam : text = tr( "CS No Treatment Code provided." ) ; break; case eError_TxCodeEmpty : text = tr( "CS The provided Treatment Code is empty." ) ; break; + case eError_LogNameNoParam : text = tr( "CS No Log Name provided." ) ; break; + case eError_LogNameEmpty : text = tr( "CS The provided Log Name is empty." ) ; break; case eError_NotRegistered : text = tr( "CS Not Sent, Device not registered." ) ; break; } @@ -450,6 +491,8 @@ case eError_CredentialEmpty : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; case eError_TxCodeNoParam : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; case eError_TxCodeEmpty : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; + case eError_LogNameNoParam : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; + case eError_LogNameEmpty : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; case eError_NotRegistered : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; } return info; @@ -521,8 +564,9 @@ _secSinceEpoch = _datetime.toSecsSinceEpoch(); _timeFormatted = _datetime.toString(_timeFormat); QString dateFormatted = _datetime.toString(_dateFormat); - if (_dateFormatted != dateFormatted) { + if (_dateFormatted != dateFormatted || ! _isWatching ) { _dateFormatted = dateFormatted; + _isWatching = false; // first time the date changes and CSctrl needs a new watch this flag need to be reset until the watch is added. // TODO: do we need to remove previous watch? addCSBuffWatch(); // bool out is not used here and error handling is internal to the addCSBuffWatch } @@ -716,45 +760,99 @@ } } -void CloudSyncController::sendPendingLog(const QString &vFileName) +void CloudSyncController::sendPendingTxr(const QString &vFileName) { bool ok = true; Q_UNUSED(ok) QVariantList args ; Errors_Enum error = eError_Unknown; qint32 messageID = UI2CS(static_cast( eMessageID_TxReport )); - - if ( ! isRegistered() ) { error = eError_NotRegistered ; args = { vFileName }; ok = false; goto lErr; } + // DEBUG ok = false; // using the ok bool which is true as the debug flag to bypass the registration on debug testing. + if ( ok && ! isRegistered() ) { error = eError_NotRegistered ; args = { vFileName }; ok = false; goto lErr; } sendUIBuff(makeUIBuff( messageID , { vFileName } )); return; lErr: toLog(error, args); } -void CloudSyncController::onTxPending(const QString &vFileName) +void CloudSyncController::onPendingTxr(const QString &vFileName) { - sendPendingLog(vFileName); + sendPendingTxr(vFileName); } /*! + * \brief CloudSyncController::onPendingLog + * \details The signal handler to call the function to send the CloudSync a message to upload the pending file. + * \param vFileName - The pending file name + * \param vChecksum - The sha256sum of the file content + */ +void CloudSyncController::onPendingLog(const QString &vFileName, const QString vChecksum) +{ + sendPendingLog(vFileName, vChecksum); +} + +/*! + * \brief CloudSyncController::sendPendingLog + * \details The function to send CloudSync a message to uplaod the pending log file. + * \param vFileName - The pending file name + * \param vChecksum - The sha256sum of the file content + */ +void CloudSyncController::sendPendingLog(const QString &vFileName, const QString vChecksum) +{ + bool ok = true; Q_UNUSED(ok) + QVariantList args ; + Errors_Enum error = eError_Unknown; + qint32 messageID = UI2CS(static_cast( eMessageID_SendLogUpload )); + // DEBUG ok = false; // using the ok bool which is true as the debug flag to bypass the registration on debug testing. + if ( ok && ! isRegistered() ) { error = eError_NotRegistered ; args = { vFileName }; ok = false; goto lErr; } + sendUIBuff(makeUIBuff( messageID , { vFileName, vChecksum } )); + + return; +lErr: + toLog(error, args); +} + +/*! + * \brief CloudSyncController::rcvdPendingLog + * \details reads the received Log Name from CloudSync app and notifies with a signal. + * \param vMessage : message containing the uploaded Log name. + * \return true on successful extracting the Log Name. + */ +bool CloudSyncController::rcvdPendingLog(const Message &vMessage) +{ + bool ok = true; + QString mLogName; + // although it has been checked in the interpreter, we won't risk the crash and check the list empty. + if ( vMessage.params.isEmpty() ) { toLog(eError_LogNameNoParam , {}); ok = false; goto lOut; } + mLogName = vMessage.params[0].trimmed(); + if ( mLogName.isEmpty() ) { toLog(eError_LogNameEmpty , {}); ok = false; goto lOut; } + //DEBUG qDebug() << " ---------- " << mLogName; + emit didLogUpload( mLogName ); + + lOut: + return ok; +} + +/*! * \brief CloudSyncController::sendMessage * \details Makes and Sends the appropriate message for the vAction. * Some messages are sent out upon request form the last received on the history and will not be asked from FW. * \return true if there is a history for that message and no error happened. */ bool CloudSyncController::sendMessage(const Message &vMessage) { bool ok = false; - + //DEBUG qDebug() << Q_FUNC_INFO << vMessage.id; // this function is used in sendUIResponse, therefore the message IDs which are responses should be implemented here. switch (vMessage.id) { case eMessageID_DeviceRegister : /* No Request/Response */ break; - + case eMessageID_CheckIn : ok = takeCheckIn ( ); break; case eMessageID_DeviceInfo : ok = sendDeviceInfo ( ); break; case eMessageID_CredentialsSave : ok = sendCredentialsSave( vMessage ); break; case eMessageID_UIFactoryReset : ok = sendFactoryReset ( ); break; case eMessageID_DeviceState : ok = sendDeviceState ( ); break; case eMessageID_TxCodeDisplay : ok = sendTxCodeDisplay ( vMessage ); break; + case eMessageID_SendLogUpload : ok = rcvdPendingLog ( vMessage ); break; case eMessageID_TxReport : /* No Req/Rsp, it is event based */ break; // This message doesn't have the response since there is no request. UI will send when the data ready by HD. @@ -790,26 +888,54 @@ /*! * \brief CloudSyncController::csFactoryReset + * \details This function is requesting CloudSync to remove all the logs + * in the logs folder (/media/sd-card/cloudsync/log/) + * during Factory Reset + * It is a request from UI to CS since UI Linux user (denali) should not have access + * to remove the CloudSync logs. + */ +bool CloudSyncController::csFactoryReset() +{ + + bool ok = true; + return ok; // NOT IMPLEMENTED + + QVariantList args ; + Errors_Enum error = eError_Unknown; + qint32 messageID = UI2CS(static_cast( eMessageID_CSFactoryReset )); + + if ( ! isRegistered() ) { error = eError_NotRegistered ; args = {}; ok = false; goto lErr; } + sendUIBuff(makeUIBuff( messageID , { } )); + + return ok; +lErr: + toLog(error, args); + return ok; +} + +/*! + * \brief CloudSyncController::Decommissioning * \details This function is requesting CloudSync to remove all the credentials and configurations * in the settings partition (/var/configurations/CloudSync) * during decommissioning * which then it means the device needs to be re-registered to able to communicate with cloud. * It is a request from UI to CS since UI Linux user (denali) should not have access * to remove the CloudSync credentials. */ -void CloudSyncController::csFactoryReset() +bool CloudSyncController::csDecommissioning() { - bool ok = true; Q_UNUSED(ok) + bool ok = true; QVariantList args ; Errors_Enum error = eError_Unknown; - qint32 messageID = UI2CS(static_cast( eMessageID_CSFactoryReset )); + qint32 messageID = UI2CS(static_cast( eMessageID_CSDecommissioning )); if ( ! isRegistered() ) { error = eError_NotRegistered ; args = {}; ok = false; goto lErr; } sendUIBuff(makeUIBuff( messageID , { } )); - return; + return ok; lErr: toLog(error, args); + return ok; } /*! @@ -884,6 +1010,42 @@ } /*! + * \brief CloudSyncController::sendCheckIn + * \details Send a check-in message and expects the same check-in message from CS + * \return + */ +bool CloudSyncController::sendCheckIn() +{ + _checkinRcvd = false; + qint32 messageID = UI2CS(eMessageID_CheckIn); + return sendUIBuff( { QString("%1").arg( messageID ) ,"0" } ); +} + +/*! + * \brief CloudSyncController::testCheckIn + * \details + * \return + */ +void CloudSyncController::testCheckIn() +{ + if ( ! _checkinRcvd ) { + LOG_APPED_UI(QString("CloudSync check-in failed")); + } +} + +/*! + * \brief CloudSyncController::takeCheckIn + * \details if the check-in received this method will be called + * \return true - + */ +bool CloudSyncController::takeCheckIn() +{ + _checkinRcvd = true; + emit didCheckInReceive(); + return true; +} + +/*! * \brief CloudSyncController::sendTxCodeDisplay * \details reads the received Tx Code from CloudSync app and notifies with a signal. * \param vMessage : message containing the Tx Code. @@ -900,8 +1062,8 @@ mTxCode = vMessage.params[0].trimmed(); if ( mTxCode.isEmpty() ) { toLog(eError_TxCodeEmpty , {}); ok = false; goto lOut; } - emit didTxCodeReceive ( mTxCode ); //DEBUG qDebug() << " ---------- " << mTxCode; + emit didTxCodeReceive ( mTxCode ); lOut: return ok; @@ -938,9 +1100,13 @@ */ bool CloudSyncController::isRegistered() { +#ifdef BUILD_FOR_DESKTOP + return true; +#else QString source = QString(Storage::CloudSync_Base_Path_Name) + Storage::CloudSync_Credentials_Folder_Name; QFileInfoList fileInfos = QDir(source).entryInfoList(QDir::NoDotAndDotDot|QDir::Files); return !fileInfos.isEmpty(); +#endif } /*! @@ -950,7 +1116,9 @@ void CloudSyncController::testReady() { bool ok = _postPass && isRegistered(); - emit didCloudSyncStatus( ok ); + emit didCloudSyncStatus( ok + || gDisableCloudSyncFailStop // Development testability + ); } /*!