diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4006ce75..d13e075f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -42,7 +42,7 @@ jobs: uses: actions/checkout@v2 - name: Install KLog dependencies run: - sudo apt-get install g++ make qt5-qmake qtbase5-dev qttools5-dev libqt5charts5-dev libqt5sql5-sqlite libhamlib-dev libqt5serialport5-dev + sudo apt-get install g++ make qt5-qmake qtbase5-dev qttools5-dev libqt5charts5-dev libqt5sql5-sqlite libhamlib-dev libqt5serialport5-dev libqt5keychain1 qt5keychain-dev # Initializes the CodeQL tools for scanning. diff --git a/.travis.yml b/.travis.yml index 27f3b948..857cf8b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ matrix: before_install: - sudo apt-get update - sudo apt-get -y install qtbase5-dev libsqlite3-dev - - sudo apt-get -y install libhamlib++-dev libqt5serialport5-dev libqt5charts5-dev qt5-default qttools5-dev-tools + - sudo apt-get -y install libhamlib++-dev libqt5serialport5-dev libqt5charts5-dev qt5-default qttools5-dev-tools libqt5keychain1 qt5keychain-dev compiler: - gcc script: diff --git a/src/INSTALL-linux b/src/INSTALL-linux index 7095c712..ac8b6fbd 100644 --- a/src/INSTALL-linux +++ b/src/INSTALL-linux @@ -16,8 +16,9 @@ details in the AUTHORS file). 1. Install the KLog dependencies - Install g++, make, qt5-qmake, qtbase5-dev, qttools5-dev, libqt5charts5-dev, libqt5sql5-sqlite, libqt5serialport5-dev & libhamlib-dev packages + libqt5keychain1 & qt5keychain-dev -In Debian it is done with: apt-get install g++ make qt5-qmake qtbase5-dev qttools5-dev libqt5charts5-dev libqt5sql5-sqlite libhamlib-dev libqt5serialport5-dev +In Debian it is done with: apt-get install g++ make qt5-qmake qtbase5-dev qttools5-dev libqt5charts5-dev libqt5sql5-sqlite libhamlib-dev libqt5serialport5-dev libqt5keychain1 qt5keychain-dev In your distribution, you should know how to install new packages :-) diff --git a/src/elogclublog.cpp b/src/elogclublog.cpp index c195d9cb..f752e0ac 100644 --- a/src/elogclublog.cpp +++ b/src/elogclublog.cpp @@ -34,11 +34,10 @@ //https://clublog.freshdesk.com/support/solutions/59800 eLogClubLog::eLogClubLog() + : SecureLogin("KLog-Clublog", tr("Please, Enter Clublog password")) { //qDebug()<< "eLogClubLog::eLogClubLog" << endl; - email = QString(); - pass = QString(); qsos.clear(); api = "9467beee93377e82a276b0a777d388b5c933d044"; currentQSO = -1; @@ -228,7 +227,6 @@ void eLogClubLog::slotErrorManagement(QNetworkReply::NetworkError networkError) int eLogClubLog::sendQSO(QStringList _qso) { - //qDebug() << "eLogClubLog::sendQSO: " << email << "/" << pass << "/" << api << endl; //qDebug()<< "eLogClubLog::sendQSO:: length = " << QString::number(_qso.length()) << endl; // First Data in the QStringList is the QSO id, not to be sent to clublog but used in the signal actionReturnDownload(const int _i, const int _qsoId); for(int i = 0; i<_qso.length(); i++) @@ -282,8 +280,6 @@ int eLogClubLog::sendDataParams(const QString &_clublogCall, const QUrlQuery &_p { //qDebug()<< "eLogClubLog::sendDataParams: Call: " << _clublogCall << endl; //qDebug()<< "eLogClubLog::sendDataParams: Params: " << _params.query(QUrl::FullyEncoded).toUtf8() << endl; - //qDebug()<< "eLogClubLog::sendDataParams: email = " << email << endl; - //qDebug()<< "eLogClubLog::sendDataParams: Pass = " << pass << endl; QUrl serviceUrl; if (_adding) @@ -298,8 +294,8 @@ int eLogClubLog::sendDataParams(const QString &_clublogCall, const QUrlQuery &_p QByteArray postData; QUrlQuery params; - params.addQueryItem("email",email); - params.addQueryItem("password",pass); + params.addQueryItem("email",getUser()); + params.addQueryItem("password",getPass()); if (_clublogCall.length()>2) { @@ -508,22 +504,15 @@ NOTES return qso; } -void eLogClubLog::setCredentials(const QString &_email, const QString &_pass, const QString _defaultStationCallsign) +void eLogClubLog::setDefaultStation(const QString _defaultStationCallsign) { - //qDebug()<< "eLogClubLog::setCredentials: email: " << _email << " / Pass: " << _pass << " / StationCallsign: " << _defaultStationCallsign << endl; + //qDebug()<< "eLogClubLog::setDefaultStation: << _defaultStationCallsign << endl; stationCallsign = _defaultStationCallsign; - email = _email; - pass = _pass; } - - int eLogClubLog::deleteQSO(QStringList _qso) { //qDebug()<< "eLogClubLog::deleteQSO: length = " << QString::number(_qso.length()) << endl; - //qDebug()<< "eLogClubLog::deleteQSO: " << email << "/" << pass << "/" << api << endl; - //qDebug()<< "eLogClubLog::deleteQSO: email = " << email << endl; - //qDebug()<< "eLogClubLog::deleteQSO: Pass = " << pass << endl; // email, password, callsign, dxcall, datetime (sqlite format, not ADIF), bandid (only the number, not ADIF), api if (_qso.length()!=18) @@ -730,18 +719,16 @@ void eLogClubLog::sendLogFile(const QString &_file, QList _qso, bool _overw } file->close(); // The rest of the form goes as usual - //qDebug()<< "eLogClubLog::sendLogFile: email: " << email << endl; - //qDebug()<< "eLogClubLog::sendLogFile: pass: " << pass << endl; //qDebug()<< "eLogClubLog::sendLogFile: stationcall: " << stationCallsign << endl; //qDebug()<< "eLogClubLog::sendLogFile: api: " << api << endl; QHttpPart emailPart; emailPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"email\"")); - emailPart.setBody(email.toUtf8()); + emailPart.setBody(getUser().toUtf8()); QHttpPart passPart; passPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"password\"")); - passPart.setBody(pass.toUtf8()); + passPart.setBody(getPass().toUtf8()); QHttpPart callPart; callPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"callsign\"")); diff --git a/src/elogclublog.h b/src/elogclublog.h index a89dff81..9377c052 100644 --- a/src/elogclublog.h +++ b/src/elogclublog.h @@ -36,16 +36,15 @@ #include #include #include "utilities.h" +#include "securelogin.h" -class eLogClubLog : public QObject { +class eLogClubLog : public SecureLogin { Q_OBJECT public: explicit eLogClubLog(); ~eLogClubLog(); - - void setCredentials(const QString &_email, const QString &_pass, const QString _defaultStationCallsign); + void setDefaultStation(const QString _defaultStationCallsign); int sendQSO(QStringList _qso); - int deleteQSO(QStringList _qso); //int deleteQSOid(const int _qsoId); int modifyQSO (QStringList _oldQSO, QStringList _newQSO); @@ -60,7 +59,7 @@ class eLogClubLog : public QObject { QString prepareToTranslate(const QString &_m); // Get the message and put it in a tr to be able to translate it - QString email, pass, api, stationCallsign; + QString api, stationCallsign; QNetworkAccessManager *manager; QNetworkReply* reply; diff --git a/src/elogqrzlog.cpp b/src/elogqrzlog.cpp index 9e38dfcf..097884ef 100644 --- a/src/elogqrzlog.cpp +++ b/src/elogqrzlog.cpp @@ -29,9 +29,11 @@ #include #include #include + //#include eLogQrzLog::eLogQrzLog(DataProxy_SQLite *dp, const QString &_parentFunction, const QString &_klogVersion) + : SecureLogin("KLog-QRZ", tr("Please, Enter QRZ.COM password")) { //qDebug()<< QString("eLogQrzLog::eLogQrzLog (%1) ").arg(_parentFunction) << endl; klogVersion = _klogVersion; @@ -183,7 +185,8 @@ void eLogQrzLog::parseXMLAnswer(QXmlStreamReader &xml) //qDebug() << "eLogQrzLog::parseXMLAnswer: Error: " << tdata << endl; if (tdata == "Username/password incorrect ") { - pass = QString(); + // do not erase a password when login is incorrect + //setPass(QString()); } emit dataFoundSignal("error", tdata); continue; @@ -608,42 +611,13 @@ void eLogQrzLog::login() return; } - //bool savePassword = true; - if (pass.length()<1) - { - //savePassword = false; - - bool ok; - pass = QInputDialog::getText(nullptr, tr("KLog - QRZ.com password needed"), tr("Please enter your QRZ.com password: "), QLineEdit::Password, "", &ok); - if (!ok) - { - //qDebug() << "eLogQrzLog::login - END 1" << endl; - return; - } - } - - - if ((user.length()<1) || (pass.length()<1)) - { - //qDebug()<< "eLogQrzLog::login error 2" << endl; - //if (!savePassword) - //{// We delete the password as soon as possible if the user is not willing to save it - // pass = QString(); - //} - return; - } - QUrlQuery params; - params.addQueryItem("username", user); - params.addQueryItem("password", pass); + params.addQueryItem("username", getUser()); + params.addQueryItem("password", getPass()); params.addQueryItem("agent", util->getGlobalAgent(klogVersion)); sendDataParams(params); - //if (!savePassword) - //{// We delete the password as soon as possible if the user is not willing to save it - // pass = QString(); - //} //qDebug()<< "eLogQrzLog::login - END" << endl; } @@ -664,20 +638,12 @@ int eLogQrzLog::sendDataParams(const QUrlQuery &_params) QNetworkRequest request(serviceUrl); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - //qDebug()<< "eLogQrzLog::sendDataParams: postData: " << postData << endl; manager->post(request, postData); //qDebug()<< "eLogQrzLog::sendDataParams - END" << endl; return -1; } -void eLogQrzLog::setCredentials(const QString &_user, const QString &_pass) -{ - //qDebug()<< "eLogQrzLog::setCredentials: user: " << _user << " / Pass: " << _pass << endl; - user = _user; - pass = _pass; -} - QString eLogQrzLog::prepareToTranslate(const QString &_m) { //qDebug()<< "eLogQrzLog:: = prepareToTranslate" << _m << endl; diff --git a/src/elogqrzlog.h b/src/elogqrzlog.h index 6b74a158..6d5ffb90 100644 --- a/src/elogqrzlog.h +++ b/src/elogqrzlog.h @@ -38,18 +38,19 @@ #include "utilities.h" #include "dataproxy_sqlite.h" #include "widgets/onlinemessagewidget.h" +#include "securelogin.h" // https://www.qrz.com/XML/current_spec.html // https://www.qrz.com/page/xml_data.html // https://www.qrz.com/docs/logbook/QRZLogbookAPI.html -class eLogQrzLog : public QObject { +class eLogQrzLog : public SecureLogin { Q_OBJECT public: explicit eLogQrzLog(DataProxy_SQLite *dp, const QString &_parentFunction, const QString &_klogVersion); ~eLogQrzLog(); void login(); - void setCredentials(const QString &_user, const QString &_pass); + void setLogBookKey(const QString &_key); bool hasLogBookKey(); int sendQSOs(QList _qsos); @@ -76,7 +77,6 @@ class eLogQrzLog : public QObject { bool sendingQSO; bool lastQSO; QString sessionkey, logbookkey; - QString user, pass; QString klogVersion; DataProxy_SQLite *dataProxy; QNetworkAccessManager *manager; diff --git a/src/eqslutilities.cpp b/src/eqslutilities.cpp index 9fab1e16..1a465bf9 100644 --- a/src/eqslutilities.cpp +++ b/src/eqslutilities.cpp @@ -34,17 +34,15 @@ eQSLUtilities::eQSLUtilities(const QString &_parentFunction) + : SecureLogin("KLog-eQSL", tr("Please, Enter eQSL password")) { //qDebug()<< "eQSLUtilities::eQSLUtilities" << endl; - user = QString(); - pass = QString(); qsos.clear(); currentQSO = -1; manager = new QNetworkAccessManager(this); connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotQsoUploadFinished(QNetworkReply*))); - stationCallsign = QString(); uploadingFile = false; util = new Utilities; //qDebug()<< "eQSLUtilities::eQSLUtilities - END" << endl; @@ -55,20 +53,6 @@ eQSLUtilities::~eQSLUtilities() //qDebug()<< "eQSLUtilities::~eQSLUtilities" << endl; } -void eQSLUtilities::setUser(const QString &_call) -{ - //qDebug() << "eQSLUtilities::setUser: " << _call << endl; - user = _call; - //qDebug() << "eQSLUtilities::setUser: END" << endl; -} - -void eQSLUtilities::setPass(const QString &_pass) -{ - //qDebug() << "eQSLUtilities::setPass: " << _pass << endl; - pass = _pass; - //qDebug() << "eQSLUtilities::setPass: END" << endl; -} - void eQSLUtilities::slotQsoUploadFinished(QNetworkReply *data) { //qDebug()<< "eQSLUtilities::slotQsoUploadFinished" << endl; @@ -162,14 +146,6 @@ void eQSLUtilities::slotErrorManagement(QNetworkReply::NetworkError networkError //actionError(result); } -void eQSLUtilities::setCredentials(const QString &_user, const QString &_pass, const QString _defaultStationCallsign) -{ - //qDebug()<< "eQSLUtilities::setCredentials: user: " << _user << " / Pass: " << _pass << " / StationCallsign: " << _defaultStationCallsign << endl; - stationCallsign = _defaultStationCallsign; - user = _user; - pass = _pass; -} - QStringList eQSLUtilities::prepareToTranslate(const QString &_m) { //qDebug()<< "eQSLUtilities:: = prepareToTranslate" << _m << endl; @@ -180,7 +156,6 @@ QStringList eQSLUtilities::prepareToTranslate(const QString &_m) { result << QString("Error"); result << QString(tr("eQSL Error: User or password incorrect")); - pass = QString(); } else if ( (_m.contains("Warning:")) && (_m.contains("Bad record: Duplicate") ) ) { @@ -233,27 +208,15 @@ void eQSLUtilities::sendLogFile(const QString &_file, QList _qso) } file->close(); // The rest of the form goes as usual - //qDebug()<< "eQSLUtilities::sendLogFile: e: " << user << endl; - //qDebug()<< "eQSLUtilities::sendLogFile: pass: " << pass << endl; //qDebug()<< "eQSLUtilities::sendLogFile: stationcall: " << stationCallsign << endl; QHttpPart userPart; userPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"eqsl_user\"")); - userPart.setBody(user.toUtf8()); - - if (pass.length()<1) - { - bool ok; - pass = QInputDialog::getText(nullptr, tr("KLog - eQSL.cc password needed"), tr("Please enter your eQSL.cc password: "), QLineEdit::Password, "", &ok); - if (!ok) - { - return; - } - } + userPart.setBody(getUser().toUtf8()); QHttpPart passPart; passPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"eqsl_pswd\"")); - passPart.setBody(pass.toUtf8()); + passPart.setBody(getPass().toUtf8()); QHttpPart filePart; QString aux = QString("form-data; name=\"Filename\"; filename=\"%1\"").arg(_file); diff --git a/src/eqslutilities.h b/src/eqslutilities.h index 361dd460..7d6fed47 100644 --- a/src/eqslutilities.h +++ b/src/eqslutilities.h @@ -37,15 +37,14 @@ #include #include #include "utilities.h" +#include "securelogin.h" + // https://www.eqsl.cc/qslcard/Programming.cfm -class eQSLUtilities : public QObject { +class eQSLUtilities : public SecureLogin { Q_OBJECT public: explicit eQSLUtilities(const QString &_parentFunction); ~eQSLUtilities(); - void setUser(const QString &_call); - void setPass(const QString &_pass); - void setCredentials(const QString &_user, const QString &_pass, const QString _defaultStationCallsign); //int sendQSO(QStringList _qso); //int deleteQSO(QStringList _qso); //int modifyQSO (QStringList _oldQSO, QStringList _newQSO); @@ -60,8 +59,6 @@ class eQSLUtilities : public QObject { QStringList prepareToTranslate(const QString &_m); // Get the message and put it in a tr to be able to translate it - QString user, pass, stationCallsign; - QNetworkAccessManager *manager; QNetworkReply* reply; int currentQSO; diff --git a/src/lotwutilities.cpp b/src/lotwutilities.cpp index 2aa8da2a..54b38f5b 100644 --- a/src/lotwutilities.cpp +++ b/src/lotwutilities.cpp @@ -32,6 +32,7 @@ //#include LoTWUtilities::LoTWUtilities(const QString &_klogDir, const QString &_klogVersion, const QString &_parentFunction, DataProxy_SQLite *dp) + : SecureLogin("KLog-LoTW", tr("Please, Enter LoTW password")) { //qDebug() << "LoTWUtilities::LoTWUtilities(): " << _klogDir << endl; dataProxy = dp; @@ -48,8 +49,6 @@ LoTWUtilities::LoTWUtilities(const QString &_klogDir, const QString &_klogVersio stationCallsign.clear(); startDate.clear(); lotwQuery.clear(); - lotwUser.clear(); - lotwPassword.clear(); fileName = "lotwDownload.adi"; pDialog = new QProgressDialog(nullptr); @@ -94,38 +93,21 @@ QString LoTWUtilities::getFileName() bool LoTWUtilities::selectQuery(const int _queryId) { //qDebug() << "LoTWUtilities::selectQuery: - Start: " << QString::number(_queryId) << endl; - bool savePassword = true; - if (lotwPassword.length()<1) - { - savePassword = false; - bool ok; - lotwPassword = QInputDialog::getText(nullptr, tr("KLog - LoTW password needed"), - tr("Please enter your LoTW password: "), QLineEdit::Password, "", &ok); - if (!ok) - { - //qDebug() << "LoTWUtilities::selectQuery: - END 1" << endl; - return false; - } - } switch (_queryId) { case 1: // Normal query - lotwQuery = QString("https://lotw.arrl.org/lotwuser/lotwreport.adi?login=%1&password=%2&qso_query=1&qso_qsl=no&qso_owncall=%3&qso_startdate=%4").arg(lotwUser).arg(lotwPassword).arg(stationCallsign).arg(startDate); + lotwQuery = QString("https://lotw.arrl.org/lotwuser/lotwreport.adi?login=%1&password=%2&qso_query=1&qso_qsl=no&qso_owncall=%3&qso_startdate=%4").arg(getUser()).arg(getPass()).arg(stationCallsign).arg(startDate); break; case 2: - lotwQuery = QString("https://lotw.arrl.org/lotwuser/lotwreport.adi?login=%1&password=%2&qso_query=1&qso_qsl=no&qso_owncall=%3&qso_startdate=%4").arg(lotwUser).arg(lotwPassword).arg(stationCallsign).arg(firstDate.toString("yyyyMMdd")); + lotwQuery = QString("https://lotw.arrl.org/lotwuser/lotwreport.adi?login=%1&password=%2&qso_query=1&qso_qsl=no&qso_owncall=%3&qso_startdate=%4").arg(getUser()).arg(getPass()).arg(stationCallsign).arg(firstDate.toString("yyyyMMdd")); break; default: { - lotwQuery = QString("https://lotw.arrl.org/lotwuser/lotwreport.adi?login=%1&password=%2&qso_query=1&qso_qsl=no&qso_owncall=%3&qso_startdate=%4").arg(lotwUser).arg(lotwPassword).arg(stationCallsign).arg(startDate); + lotwQuery = QString("https://lotw.arrl.org/lotwuser/lotwreport.adi?login=%1&password=%2&qso_query=1&qso_qsl=no&qso_owncall=%3&qso_startdate=%4").arg(getUser()).arg(getPass()).arg(stationCallsign).arg(startDate); } } - if (!savePassword) - {// We delete the password as soon as possible if the user is not willing to save it - lotwPassword = QString(); - } url = QUrl(lotwQuery); //qDebug() << "LoTWUtilities::selectQuery: - END" << endl; @@ -543,24 +525,10 @@ void LoTWUtilities::slotCancelDownload() //qDebug() << "LoTWUtilities::slotCancelDownload - END" << endl; } -void LoTWUtilities::setUser(const QString &_call) -{ - //qDebug() << "LoTWUtilities::setUser: " << _call << endl; - lotwUser = _call; - //qDebug() << "LoTWUtilities::setUser: END" << endl; -} - -void LoTWUtilities::setPass(const QString &_pass) -{ - //qDebug() << "LoTWUtilities::setPass: " << _pass << endl; - lotwPassword = _pass; - //qDebug() << "LoTWUtilities::setPass: END" << endl; -} - bool LoTWUtilities::getIsReady() { - //qDebug() << "LoTWUtilities::getIsReady: user/station: -" << lotwUser <<"/" << stationCallsign << "-" << endl; - if ((lotwUser.length()>1) && (stationCallsign.length()>1)) + //qDebug() << "LoTWUtilities::getIsReady: user/station: -" << getUser() <<"/" << stationCallsign << "-" << endl; + if ((getUser().length()>1) && (stationCallsign.length()>1)) { //qDebug() << "LoTWUtilities::getIsReady: true" << endl; return true; diff --git a/src/lotwutilities.h b/src/lotwutilities.h index 67b75ce7..ca502f62 100644 --- a/src/lotwutilities.h +++ b/src/lotwutilities.h @@ -40,10 +40,12 @@ #include #include "dataproxy_sqlite.h" #include "utilities.h" +#include "securelogin.h" + // https://lotw.arrl.org/lotw-help/developer-query-qsos-qsls/?lang=en class QSslError; -class LoTWUtilities : public QObject +class LoTWUtilities : public SecureLogin { Q_OBJECT @@ -53,8 +55,6 @@ class LoTWUtilities : public QObject bool setStationCallSign(const QString &_call); int download(); int fullDownload(); - void setUser(const QString &_call); - void setPass(const QString &_pass); bool getIsReady(); void setFileName(const QString &_fn); QString getFileName(); @@ -87,7 +87,6 @@ class LoTWUtilities : public QObject QString stationCallsign; QString startDate; QString lotwQuery; - QString lotwUser, lotwPassword; DataProxy_SQLite *dataProxy;//, *dataProxyPrepared; QCalendarWidget *calendar; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 76148dce..628eb5e3 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -91,17 +91,25 @@ MainWindow::MainWindow(const QString &_klogDir, const QString &tversion) dataProxy = new DataProxy_SQLite(Q_FUNC_INFO, softwareVersion); lotwUtilities = new LoTWUtilities(klogDir, softwareVersion, Q_FUNC_INFO, dataProxy); - eqslUtilities = new eQSLUtilities(Q_FUNC_INFO); + //connect debugLog as soon as possible + connect(lotwUtilities, SIGNAL(debugLog(QString, QString, DebugLogLevel)), this, SLOT(slotCaptureDebugLogs(QString, QString, DebugLogLevel)) ); + eqslUtilities = new eQSLUtilities(Q_FUNC_INFO); + //connect debugLog as soon as possible + connect(eqslUtilities, SIGNAL(debugLog(QString, QString, DebugLogLevel)), this, SLOT(slotCaptureDebugLogs(QString, QString, DebugLogLevel)) ); //qDebug() << "MainWindow::MainWindow: Before DXCCStatusWidget " << QTime::currentTime().toString("hh:mm:ss") << endl; dxccStatusWidget = new DXCCStatusWidget(dataProxy, Q_FUNC_INFO); //qDebug() << "MainWindow::MainWindow: After DXCCStatusWidget " << QTime::currentTime().toString("hh:mm:ss") << endl; //qDebug() << "MainWindow::MainWindow: 00081" << QTime::currentTime().toString("hh:mm:ss") << endl; elogClublog = new eLogClubLog(); + //connect debugLog as soon as possible + connect(elogClublog, SIGNAL(debugLog(QString, QString, DebugLogLevel)), this, SLOT(slotCaptureDebugLogs(QString, QString, DebugLogLevel)) ); //qDebug() << "MainWindow::MainWindow: 00082" << QTime::currentTime().toString("hh:mm:ss") << endl; elogQRZcom = new eLogQrzLog(dataProxy, Q_FUNC_INFO, softwareVersion); + //connect debugLog as soon as possible + connect(elogQRZcom, SIGNAL(debugLog(QString, QString, DebugLogLevel)), this, SLOT(slotCaptureDebugLogs(QString, QString, DebugLogLevel)) ); //qDebug() << "MainWindow::MainWindow: 00083" << QTime::currentTime().toString("hh:mm:ss") << endl; updateSatsData = new UpdateSatsData(dataProxy); @@ -153,7 +161,16 @@ MainWindow::MainWindow(const QString &_klogDir, const QString &tversion) //qDebug() << "MainWindow::MainWindow: xx " << QTime::currentTime().toString("hh:mm:ss") << endl; - setupDialog = new SetupDialog(dataProxy, configFileName, softwareVersion, 0, !configured, this); + setupDialog = new SetupDialog(dataProxy, + configFileName, + softwareVersion, + elogQRZcom, + elogClublog, + eqslUtilities, + lotwUtilities, + 0, + !configured, + this); //qDebug() << "MainWindow::MainWindow: satTabWidget to be created " << endl; satTabWidget = new MainWindowSatTab(dataProxy); @@ -348,9 +365,6 @@ void MainWindow::init() keepSatPage = false; //qDebug() << "MainWindow::init - 40" << endl; - //clublogUser = QString(); - clublogPass = QString(); - clublogEmail = QString(); clublogActive = false; clublogRealTime = false; @@ -361,9 +375,6 @@ void MainWindow::init() qrzcomActive = false; lotwActive = false; - qrzcomUser = QString(); - qrzcomPass = QString(); - callingUpdate = false; // to control whether the update is mannually launched or at the begining //previousQrz = ""; setModifying(false); @@ -4901,22 +4912,8 @@ void MainWindow::readConfigData() // I need to init the CLUBLOG if (clublogActive) { - elogClublog->setCredentials(clublogEmail, clublogPass, stationQRZ); - } - else - { - //qDebug() << "MainWindow::readConfigData: NOT Setting ClublogCredentials" << endl; + elogClublog->setDefaultStation(stationQRZ); } - //qDebug() << "MainWindow::readConfigData: QRZcom active????" << QTime::currentTime().toString("hh:mm:ss") << endl; - if (qrzcomActive) - { - //qDebug() << "MainWindow::readConfigData: QRZcom active"<< QTime::currentTime().toString("hh:mm:ss") << endl; - elogQRZcom->setCredentials(qrzcomUser, qrzcomPass); - //qDebug() << "MainWindow::readConfigData: login" << QTime::currentTime().toString("hh:mm:ss") << endl; - //elogQRZcom->login(); - //qDebug() << "MainWindow::readConfigData: after login" << QTime::currentTime().toString("hh:mm:ss") << endl; - } - //qDebug() << "MainWindow::readConfigData: calling checkIfNewBandOrMode" << QTime::currentTime().toString("hh:mm:ss") << endl; //qDebug() << "MainWindow::readConfigData: 100" << QTime::currentTime().toString("hh:mm:ss") << endl; util->setVersion(softwareVersion); @@ -5381,12 +5378,17 @@ bool MainWindow::processConfigLine(const QString &_line){ else if(field=="CLUBLOGPASS") { //qDebug() << "MainWindow::processConfigLine: clublogPass: " << value << endl; - clublogPass = value; + + /* Password is stored in a secure storage. Due to migration phase when old versions have^M + * stored passwords in config file, the option remains here.^M + */ + + elogClublog->setPass(value); } else if(field=="CLUBLOGEMAIL") { //qDebug() << "MainWindow::processConfigLine: clublogEmail: " << value << endl; - clublogEmail = value; + elogClublog->setUser(value); } else if(field=="QRZCOMACTIVE") { @@ -5405,11 +5407,14 @@ bool MainWindow::processConfigLine(const QString &_line){ } else if(field=="QRZCOMPASS") { - qrzcomPass = value; + /* Password is stored in a secure storage. Due to migration phase when old versions have + * stored passwords in config file, the option remains here. + */ + elogQRZcom->setPass(value); } else if(field=="QRZCOMUSER") { - qrzcomUser = value; + elogQRZcom->setUser(value); } else if (field =="QRZCOMLOGBOOKKEY"){ elogQRZcom->setLogBookKey(value); @@ -5430,6 +5435,8 @@ bool MainWindow::processConfigLine(const QString &_line){ } else if(field =="EQSLPASS"){ //qDebug() << "MainWindow::processConfigLine - EQSLPASS" << endl; + /* Password is stored in a secure storage. Due to migration phase when old versions have + * stored passwords in config file, the option remains here.*/ eqslUtilities->setPass(value); } else if(field =="EQSLUSESTATIONCALLSIGN"){ @@ -5462,6 +5469,9 @@ bool MainWindow::processConfigLine(const QString &_line){ //qDebug() << "MainWindow::processConfigLine - AFTER LOTWUSER" << endl; } else if(field =="LOTWPASS"){ + /* Password is stored in a secure storage. Due to migration phase when old versions have + * stored passwords in config file, the option remains here. + */ lotwUtilities->setPass(value); } else if(field=="VERSION") @@ -8555,6 +8565,10 @@ void MainWindow::setSeverity(const DebugLogLevel _sev) { logSeverity = _sev; setupDialog->setSeverity(logSeverity); + lotwUtilities->setSeverity(logSeverity); + eqslUtilities->setSeverity(logSeverity); + elogClublog->setSeverity(logSeverity); + elogQRZcom->setSeverity(logSeverity); } void MainWindow::slotCaptureDebugLogs(const QString &_func, const QString &_msg, DebugLogLevel _level) diff --git a/src/mainwindow.h b/src/mainwindow.h index 95141ace..e2de7fa1 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -725,7 +725,6 @@ private slots: // bool clublogActive, clublogRealTime, eQSLActive, eQSLRealTime, eQSLUseQSOStationCallSign; //clublogUseStationCallSign, - QString clublogPass, clublogEmail; //clublogUser, eLogClubLog *elogClublog; int clublogAnswer; @@ -735,7 +734,6 @@ private slots: // QRZ.com bool qrzcomActive; eLogQrzLog *elogQRZcom; - QString qrzcomUser, qrzcomPass; // QRz.com - END // Contest diff --git a/src/securelogin.cpp b/src/securelogin.cpp new file mode 100644 index 00000000..2422d20b --- /dev/null +++ b/src/securelogin.cpp @@ -0,0 +1,232 @@ +/*************************************************************************** + securelogin.cpp - description + ------------------- + begin : Jun 2021 + copyright : (C) 2021 by Ladislav Foldyna + ***************************************************************************/ + +/***************************************************************************** + * This file is part of KLog. * + * * + * KLog is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * KLog is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with KLog. If not, see . * + * * + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include "securelogin.h" + +/* + * SecureLogin::SecureLogin + * params: _secureStorageKey - Storage Key unique identificator + * _enterPasswordLabel - if a password is requested and no password is present + * then a dialog with enterPasswordLabel is displayed + */ +SecureLogin::SecureLogin(const QString &_secureStorageKey, const QString &_enterPasswordLabel) +{ + emit debugLog (Q_FUNC_INFO, "Start", Info); + secureStorageKey = _secureStorageKey; + enterPasswordLabel = _enterPasswordLabel; + emit debugLog (Q_FUNC_INFO, "STOP", Info); +} + +SecureLogin::~SecureLogin() +{ +} + +void SecureLogin::setUser(const QString _user) +{ + //qDebug() << "SecureLogin::setUser: " << _user << " - START" << endl; + + emit debugLog (Q_FUNC_INFO, QString("Start (%1)").arg(_user), Info); + // If a username is changed then it is necessary to delete an old record in secure storage. + // we do not want to store old records in the secure storage. + if ( _user != user) + { + delPass(); + } + user = _user; + //qDebug() << "SecureLogin::setUser: - END" << endl; + emit debugLog (Q_FUNC_INFO, "Stop", Info); +} + +void SecureLogin::setPass(const QString _pass) +{ + //qDebug() << "SecureLogin::setPass: " << _pass << " - START" << endl; + emit debugLog (Q_FUNC_INFO, QString("Start (%1)").arg(_pass.length()), Info); + pass = _pass; + + // Save the password immediately after the change + savePass(); + + emit debugLog (Q_FUNC_INFO, "Stop", Info); + //qDebug() << "SecureLogin::setPass - END" << endl; +} + +QString SecureLogin::getUser() +{ + return user; +} + +int SecureLogin::savePass() +{ + emit debugLog (Q_FUNC_INFO, QString("Start (%1: %2)").arg(user).arg(pass.length()), Info); + //qDebug() << "SecureLogin::savePass " << user << ";" << pass << " - START" << endl; + using namespace QKeychain; + + if (!savePassword) + { + //qDebug() << "SecureLogin::savePass - does not save a password - END" << endl; + emit debugLog (Q_FUNC_INFO, "Stop - do no save a password", Info); + return 0; + } + + if (user.isEmpty()) + { + //qDebug() << "SecureLogin::savePass - Empty Username - END" << endl; + + // it is not interesting to save password without username. + emit debugLog (Q_FUNC_INFO, "Stop - doe not save a passowrd - empty user", Info); + return 1; + } + + // write a password to Secure Storage + WritePasswordJob job(QLatin1String(secureStorageKey.toStdString().c_str())); + job.setAutoDelete(false); + job.setKey(user); + job.setTextData(pass); + QEventLoop loop; + job.connect(&job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit())); + job.start(); + loop.exec(); + + if (job.error()) + { + //qDebug() << "SecureLogin::savePass - password storage error " << job.errorString() << " - END" << endl; + emit debugLog (Q_FUNC_INFO, QString("Stop - job error (%1)").arg(job.errorString()), Info); + return 1; + } + + emit debugLog (Q_FUNC_INFO, "Stop", Info); + //qDebug() << "SecureLogin::savePass - END" << endl; + return 0; +} + +QString SecureLogin::getPass(bool askEmptyPass) +{ + using namespace QKeychain; + QString manual_pass; + + emit debugLog (Q_FUNC_INFO, QString("Start (%1; ask %2)").arg(user).arg(askEmptyPass), Info); + //qDebug() << "SecureLogin::getPass : " << askEmptyPass << " - START" << endl; + + // if password already loaded then return it immediately + if (!pass.isEmpty()) + { + emit debugLog (Q_FUNC_INFO, "Stop - password in cache", Info); + //qDebug() << "SecureLogin::getPass - password in cache: " << pass << " - END" << endl; + return pass; + } + + // get a password from Secure Storage + ReadPasswordJob job(QLatin1String(secureStorageKey.toStdString().c_str())); + job.setAutoDelete(false); + job.setKey(user); + QEventLoop loop; + job.connect(&job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit())); + job.start(); + loop.exec(); + + pass = job.textData(); + + if (job.error() || pass.isEmpty()) + { + // try to ask user when the password is empty or error. + if (askEmptyPass) + { + // no password or error, try to enter password manually + // do not store manually entered password + bool ok; + manual_pass = QInputDialog::getText(nullptr, + QObject::tr("KLog needs a password"), //add TR + enterPasswordLabel, + QLineEdit::Password, "", &ok); + if (!ok) + { + savePassword = true; + pass = QString(); + } + else + { + // do not save a password when the password is manually entered + savePassword = false; + emit debugLog (Q_FUNC_INFO, "Stop - manually entered", Info); + return manual_pass; + } + } + else + { + savePassword = true; + pass = QString(); + } + } + savePassword = true; + emit debugLog (Q_FUNC_INFO, "Stop", Info); + //qDebug() << "SecureLogin::getPass :" << pass << " - END" << endl; + return pass; +} + +void SecureLogin::delPass() +{ + using namespace QKeychain; + + emit debugLog (Q_FUNC_INFO, QString("Start (%1)").arg(user), Info); + //qDebug() << "SecureLogin::delPass " << user << ";" << pass << " - START" << endl; + + if (user.isEmpty()) + { + emit debugLog (Q_FUNC_INFO, "Stop - empty user", Info); + //qDebug() << "SecureLogin::delPass - User empty - END" << endl; + return; + } + + // delete password from Secure Storage + DeletePasswordJob job(QLatin1String(secureStorageKey.toStdString().c_str())); + job.setAutoDelete(false); + job.setKey(user); + QEventLoop loop; + job.connect(&job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit())); + job.start(); + loop.exec(); + + if (job.error()) + { + //nothing to do when an error. + emit debugLog (Q_FUNC_INFO, QString("Stop - job error (%1)").arg(job.errorString()), Info); + //qDebug() << "SecureLogin::delPass - password storage error " << job.errorString() << endl; + } + + pass = QString(); + //qDebug() << "SecureLogin::delPass - END" << endl; + emit debugLog (Q_FUNC_INFO, "Stop", Info); + return; +} + +void SecureLogin::setSeverity(const DebugLogLevel _sev) +{ + logSeverity = _sev; +} diff --git a/src/securelogin.h b/src/securelogin.h new file mode 100644 index 00000000..8eda7203 --- /dev/null +++ b/src/securelogin.h @@ -0,0 +1,57 @@ +#ifndef SECURELOGIN_H +#define SECURELOGIN_H +/*************************************************************************** + securelogin - description + ------------------- + begin : Jun 2021 + copyright : (C) 2021 by Ladislav Foldyna + ***************************************************************************/ + +/***************************************************************************** + * This file is part of KLog. * + * * + * KLog is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * KLog is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with KLog. If not, see . * + * * + *****************************************************************************/ +#include +#include +#include "klogdefinitions.h" + +class SecureLogin : public QObject +{ + Q_OBJECT + +public: + SecureLogin(const QString &_secureStorageKey, const QString &_enterPasswordLabel); + ~SecureLogin(); + void setUser(const QString _user); + void setPass(const QString _pass); + QString getUser(); + QString getPass(bool askEmptyPass=true); + void setSeverity(const DebugLogLevel _sev); + +signals: + void debugLog (QString _func, QString _msg, DebugLogLevel _level); + +private: + QString user, pass, secureStorageKey, enterPasswordLabel; + bool savePassword = true; + + int savePass(); + void delPass(); + + DebugLogLevel logSeverity; +}; + +#endif // SECURELOGIN_H diff --git a/src/setupdialog.cpp b/src/setupdialog.cpp index 8037c4a1..4f4c22df 100644 --- a/src/setupdialog.cpp +++ b/src/setupdialog.cpp @@ -33,7 +33,16 @@ This class calls all the othet "Setup..." to manage the configuration */ -SetupDialog::SetupDialog(DataProxy_SQLite *dp, const QString &_configFile, const QString &_softwareVersion, const int _page, const bool _firstTime, QWidget *parent) +SetupDialog::SetupDialog(DataProxy_SQLite *dp, + const QString &_configFile, + const QString &_softwareVersion, + eLogQrzLog *_qrz, + eLogClubLog *_clublog, + eQSLUtilities *_eqsl, + LoTWUtilities *_lotw, + const int _page, + const bool _firstTime, + QWidget *parent) { //qDebug() << "SetupDialog::SetupDialog 2: " << _configFile << "/" << _softwareVersion << "/" << QString::number(_page) << util->boolToQString(_firstTime) << endl ; @@ -46,6 +55,11 @@ SetupDialog::SetupDialog(DataProxy_SQLite *dp, const QString &_configFile, const configFileName = _configFile; version = _softwareVersion; pageRequested = _page; + qrz = _qrz; + clublog = _clublog; + eqsl = _eqsl; + lotw = _lotw; + int logsPageTabN=-1; //qDebug() << "SetupDialog::SetupDialog 01" << endl; @@ -553,16 +567,12 @@ void SetupDialog::slotOkButtonClicked() tmp = eLogPage->getClubLogEmail() ; if (tmp.length()>0) { + clublog->setUser(tmp); // username remains in config file stream << "ClubLogEmail=" << tmp << ";" << endl; } tmp = eLogPage->getClubLogPassword() ; - if (tmp.length()>0) - { - stream << "ClubLogPass=" << tmp << ";" << endl; - } - - //qDebug() << "SetupDialog::slotOkButtonClicked - 50" << endl; + clublog->setPass(tmp); // we can store also an empty password - the empty password means insert it manually // eQSL @@ -577,14 +587,12 @@ void SetupDialog::slotOkButtonClicked() tmp = eLogPage->getEQSLUser(); if (tmp.length()>0) { + eqsl->setUser(tmp); stream << "eQSLCall=" << tmp << ";" << endl; } tmp = eLogPage->getEQSLPassword() ; - if (tmp.length()>0) - { - stream << "eQSLPass=" << tmp << ";" << endl; - } + eqsl->setPass(tmp); // eQSL - END @@ -603,13 +611,12 @@ void SetupDialog::slotOkButtonClicked() tmp = eLogPage->getQRZCOMUser(); if (tmp.length()>0) { + qrz->setUser(tmp); // username remains in config file stream << "QRZcomUser=" << tmp << ";" << endl; } + tmp = eLogPage->getQRZCOMPassword(); - if (tmp.length()>0) - { - stream << "QRZcomPass=" << tmp << ";" << endl; - } + qrz->setPass(tmp); // we can store also an empty password - the empty password means insert it manually if (eLogPage->getQRZCOMAutoCheck()) { @@ -639,13 +646,11 @@ void SetupDialog::slotOkButtonClicked() tmp = eLogPage->getLoTWUser(); if (tmp.length()>0) { + lotw->setUser(tmp); stream << "LoTWUSer=" << tmp << ";" << endl; } tmp = eLogPage->getLoTWPass(); - if (tmp.length()>0) - { - stream << "LoTWPass=" << tmp << ";" << endl; - } + lotw->setPass(tmp); // LOTW //qDebug() << "SetupDialog::slotOkButtonClicked - 70" << endl; @@ -1054,13 +1059,9 @@ bool SetupDialog::processConfigLine(const QString &_line) //clubLogPage->setClubLogRealTime(value); eLogPage->setClubLogRealTime(util->trueOrFalse(value)); } - else if(tab =="CLUBLOGPASS"){ - //clubLogPage->setPassword(value); - eLogPage->setClubLogPassword(value); - } else if(tab =="CLUBLOGEMAIL"){ - //clubLogPage->setEmail(value); - eLogPage->setClubLogEmail(value); + eLogPage->setClubLogEmail(clublog->getUser()); + eLogPage->setClubLogPassword(clublog->getPass(false)); } else if(tab =="EQSLACTIVE"){ //eQSLPage->setActive(value); @@ -1068,25 +1069,20 @@ bool SetupDialog::processConfigLine(const QString &_line) } else if(tab =="EQSLCALL"){ //eQSLPage->setCallsign(value); - eLogPage->setEQSLUser(value); - } - else if(tab =="EQSLPASS"){ - //eQSLPage->setPassword(value); - eLogPage->setEQSLPassword(value); + eLogPage->setEQSLUser(eqsl->getUser()); + eLogPage->setQRZCOMPassword(eqsl->getPass(false)); } else if(tab =="QRZCOMACTIVE"){ //eQSLPage->setActive(value); eLogPage->setQRZCOMActive(value); } else if(tab =="QRZCOMUSER"){ - eLogPage->setQRZCOMUser(value); + eLogPage->setQRZCOMUser(qrz->getUser()); + eLogPage->setQRZCOMPassword(qrz->getPass(false)); } else if(tab =="QRZCOMAUTO"){ eLogPage->setQRZCOMAutoCheck(value); } - else if(tab =="QRZCOMPASS"){ - eLogPage->setQRZCOMPassword(value); - } else if(tab =="QRZCOMLOGBOOKKEY"){ eLogPage->setQRZCOMLogBookKEY(value); } @@ -1097,10 +1093,10 @@ bool SetupDialog::processConfigLine(const QString &_line) eLogPage->setTQSLPath(value); } else if(tab =="LOTWUSER"){ - eLogPage->setLoTWUser(value); + eLogPage->setLoTWUser(lotw->getUser()); } else if(tab =="LOTWPASS"){ - eLogPage->setLoTWPass(value); + eLogPage->setLoTWPass(lotw->getPass(false)); } else if(tab =="MAINWINDOWSIZE"){ QStringList values; diff --git a/src/setupdialog.h b/src/setupdialog.h index a4808a92..118efe4f 100644 --- a/src/setupdialog.h +++ b/src/setupdialog.h @@ -46,7 +46,10 @@ //#include "setuppages/setuppageinterfaceswindows.h" #include "utilities.h" #include "locator.h" - +#include "elogqrzlog.h" +#include "elogclublog.h" +#include "eqslutilities.h" +#include "lotwutilities.h" class QListWidget; class QListWidgetItem; @@ -58,7 +61,16 @@ class SetupDialog : public QDialog public: //SetupDialog(DataProxy_SQLite *dp, const bool _firstTime=true, QWidget *parent = nullptr); - SetupDialog(DataProxy_SQLite *dp, const QString &_configFile, const QString &_softwareVersion, const int _page=0, const bool _firstTime = true, QWidget *parent = nullptr); + SetupDialog(DataProxy_SQLite *dp, + const QString &_configFile, + const QString &_softwareVersion, + eLogQrzLog *_qrz, + eLogClubLog *_clublog, + eQSLUtilities *_eqsl, + LoTWUtilities *_lotw, + const int _page=0, + const bool _firstTime = true, + QWidget *parent = nullptr); ~SetupDialog(); void setData(const QString &_configFile, const QString &_softwareVersion, const int _page, const bool _firstTime=true); @@ -151,6 +163,11 @@ private slots: int constrid; // Just an id for the constructor to check who is being executed at one specific time DebugLogLevel logSeverity; // Manages as syslog, the severity of the application debug log + + eLogQrzLog *qrz; + eLogClubLog *clublog; + eQSLUtilities *eqsl; + LoTWUtilities *lotw; }; diff --git a/src/src.pro b/src/src.pro index 39278746..02a48b23 100644 --- a/src/src.pro +++ b/src/src.pro @@ -119,6 +119,7 @@ HEADERS += setupdialog.h \ hamlibclass.h \ tipsdialog.h \ worldmapwidget.h \ + securelogin.h \ setuppages/setuppagemisc.h \ setuppages/setuppageuserdata.h \ setuppages/setuppagedxcluster.h \ @@ -189,6 +190,7 @@ SOURCES += main.cpp \ dxcluster.cpp \ locator.cpp \ awards.cpp \ + securelogin.cpp \ setuppages/setuppagemisc.cpp \ setuppages/setuppageuserdata.cpp \ setuppages/setuppagedxcluster.cpp \ @@ -323,7 +325,7 @@ unix:!mac { datafiles.files = $$DISTFILES INSTALLS += translations INSTALLS += datafiles - LIBS += -lhamlib + LIBS += -lhamlib -lqt5keychain } @@ -334,14 +336,14 @@ macx: { #INCLUDEPATH +=../../../hamlib/include/ #LIBS += -L"../../../hamlib/lib" -lhamlib INCLUDEPATH +=/usr/local/include/ - LIBS += -L"/usr/local/lib" -lhamlib + LIBS += -L"/usr/local/lib" -lhamlib -lqt5keychain } win32: { RC_ICONS = klog.ico TARGET = klog QMAKE_TARGET_COMPANY = EA4K QMAKE_TARGET_DESCRIPTION = Hamradio logging - LIBS += -L"$$PWD/../../libs/hamlib/lib/gcc" -lhamlib + LIBS += -L"$$PWD/../../libs/hamlib/lib/gcc" -lhamlib -lqt5keychain INCLUDEPATH += "$$PWD/../../libs/hamlib/include/" #LIBS += -L"$$PWD/../../libs/hamlib-w32-4.0rc2/lib/gcc" -lhamlib #INCLUDEPATH += "$$PWD/../../libs/hamlib-w32-4.0rc2/include/"