diff --git a/.installer/configuration/config.xml b/.installer/configuration/config.xml index c3c81bb..bfab375 100644 --- a/.installer/configuration/config.xml +++ b/.installer/configuration/config.xml @@ -11,7 +11,7 @@ @HomeDir@/.clipbird Clipbird Installer Clipbird - 1.0.0 + 1.1.0 Sri Lakshmi Kanthan P Clipbird diff --git a/.installer/packages/clipbird/meta/install.qs b/.installer/packages/clipbird/meta/install.qs index 0b40585..5a46971 100644 --- a/.installer/packages/clipbird/meta/install.qs +++ b/.installer/packages/clipbird/meta/install.qs @@ -61,7 +61,7 @@ Component.prototype.createOperations = function () { "@TargetDir@/clipbird.exe", "@StartMenuDir@/ClipBird.lnk", "workingDirectory=@TargetDir@", - "iconPath=@TargetDir@/logo.png", + "iconPath=@TargetDir@/logo.ico", "IconId=0", "description=ClipBird" ); diff --git a/.installer/packages/clipbird/meta/package.xml b/.installer/packages/clipbird/meta/package.xml index 58bcd0b..9bdaaa7 100644 --- a/.installer/packages/clipbird/meta/package.xml +++ b/.installer/packages/clipbird/meta/package.xml @@ -15,7 +15,7 @@ on your network. 2023-07-29 - 1.0.0 + 1.1.0 diff --git a/.scripts/package.ps1 b/.scripts/package.ps1 index 6cf0954..864d1a5 100644 --- a/.scripts/package.ps1 +++ b/.scripts/package.ps1 @@ -24,11 +24,11 @@ Remove-Item -Recurse -Force $ClipbirDir/* -Exclude .gitignore # Copy All openssl dlls to the package directory Write-Host "Copying $env:OPENSSL_ROOT_DIR /bin/*.dll to $ClipbirDir" -ForegroundColor Green -Copy-Item "$env:OPENSSL_ROOT_DIR /bin/*.dll" $ClipbirDir +Copy-Item "$env:OPENSSL_ROOT_DIR/bin/*.dll" $ClipbirDir # copy the Logo to the package directory -Write-Host "Copying ./assets/images/logo.png to $ClipbirDir" -ForegroundColor Green -Copy-Item ./assets/images/logo.png $ClipbirDir +Write-Host "Copying ./assets/images/* to $ClipbirDir" -ForegroundColor Green +Copy-Item ./assets/images/* $ClipbirDir # Create the package as BuildType (to lower) version Write-Host "Creating the package as Release version" -ForegroundColor Green diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index b6b63aa..ad8a802 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,28 +1,5 @@ { "configurations": [ - { - "compilerPath": "/usr/bin/gcc", - "name": "linux", - "includePath": [ - "${workspaceFolder}/build/_deps/singleapplication-src", - "${workspaceFolder}/build", - "${workspaceFolder}/", - "${workspaceFolder}/build/_deps/**", - "/opt/Qt/6.5.2/gcc_64/include/QtGui", - "/opt/Qt/6.5.2/gcc_64/include/QtCore", - "/opt/Qt/6.5.2/gcc_64/include/**", - "${HOME}/.kderoot/include/KF6/KDNSSD" - ], - "defines": [ - "_UNICODE", - "_DEBUG", - "UNICODE" - ], - "cStandard": "c17", - "cppStandard": "c++17", - "intelliSenseMode": "linux-gcc-x64", - "configurationProvider": "ms-vscode.cpptools" - }, { "compilerPath": "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.33.31629/bin/Hostx64/x64/cl.exe", "name": "Win32", @@ -38,11 +15,7 @@ "C:\\Program Files\\Bonjour SDK\\Include", "C:\\kderoot\\include\\KF6\\KGuiAddons\\**" ], - "defines": [ - "_UNICODE", - "_DEBUG", - "UNICODE" - ], + "defines": [ "_UNICODE", "_DEBUG", "UNICODE" ], "cStandard": "c17", "cppStandard": "c++17", "intelliSenseMode": "msvc-x64", diff --git a/.vscode/settings.json b/.vscode/settings.json index 02c288e..1c6dca2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,7 +14,8 @@ "pubkey", "qobject", "qvariant", - "SSLCONFIG" + "SSLCONFIG", + "unauthed" ], "files.associations": { ".env*": "dotenv", @@ -148,6 +149,11 @@ "*.qs": "javascript", ".qs": "javascript", "qsystemtrayicon": "cpp", - "qsettings": "cpp" + "qsettings": "cpp", + "queue": "cpp", + "stack": "cpp", + "qsslcertificate": "cpp", + "qapplication": "cpp", + "singleapplication": "cpp" }, } diff --git a/CMakeLists.txt b/CMakeLists.txt index 37fb66e..0ca572f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) # set project name and version -project(clipbird VERSION 0.0.1 LANGUAGES CXX) +project(clipbird VERSION 1.1.0 LANGUAGES CXX) # set c++ standard 17 set(CMAKE_CXX_STANDARD 17) @@ -105,6 +105,11 @@ qt_standard_project_setup() qt_add_resources(RESOURCES ${PROJECT_SOURCE_DIR}/assets/resources.qrc) +# Add windows.rc to project +if (WIN32) + set (RESOURCES ${RESOURCES} ${PROJECT_SOURCE_DIR}/assets/windows.rc) +endif() + # Add executable qt_add_executable(clipbird ${main_cpp} ${RESOURCES}) @@ -114,7 +119,6 @@ target_link_libraries(clipbird PRIVATE SingleApplication::SingleApplication PRIVATE Qt6::Widgets PRIVATE Qt6::Network - PRIVATE Qt6::Sql PRIVATE OpenSSL::SSL PRIVATE OpenSSL::Crypto) @@ -146,10 +150,8 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") target_compile_options(clipbird PRIVATE -std=c++17) endif() -# add target linker options MSVC /NODEFAULTLIB:library # add target compiler options MSVC /std:c++17 if(MSVC) - target_link_options(clipbird PRIVATE /NODEFAULTLIB:library) target_compile_options(clipbird PRIVATE /std:c++17) endif() diff --git a/README.md b/README.md index 09c4432..1f2df85 100644 --- a/README.md +++ b/README.md @@ -110,14 +110,6 @@ In the following steps, we will see how to build the project in the Windows plat * Qt6 * OpenSSL -#### Installing Qt6 - -Go to [Qt](https://www.qt.io/download-qt-installer) and download the Qt installer for windows, then install it in your system. After installing Qt, you need to set the environment variable `QT_CMAKE_DIR` to the Qt cmake directory. - -#### Installing Bonjour - -Just go to [bonjour](https://developer.apple.com/bonjour/) and downlod the bonjour SDK for windows, then install it in your system. It should set the environment variable `BONJOUR_SDK_HOME` to the Bonjour SDK directory. - #### Installing OpenSSL Install OpenSSL using the following command and set the environment variable `OPENSSL_ROOT_DIR` to the OpenSSL installation directory. @@ -126,6 +118,14 @@ Install OpenSSL using the following command and set the environment variable `OP choco install openssl ~~~ +#### Installing Qt6 + +Go to [Qt](https://www.qt.io/download-qt-installer) and download the Qt installer for windows, then install it in your system. After installing Qt, you need to set the environment variable `QT_CMAKE_DIR` to the Qt cmake directory. + +#### Installing Bonjour + +Just go to [bonjour](https://developer.apple.com/bonjour/) and downlod the bonjour SDK for windows, then install it in your system. It should set the environment variable `BONJOUR_SDK_HOME` to the Bonjour SDK directory. + #### Environment Variables | Variable | Value | diff --git a/assets/images/logo.ico b/assets/images/logo.ico new file mode 100644 index 0000000..11658fb Binary files /dev/null and b/assets/images/logo.ico differ diff --git a/assets/windows.rc b/assets/windows.rc new file mode 100644 index 0000000..f49adf4 --- /dev/null +++ b/assets/windows.rc @@ -0,0 +1,6 @@ +// Copyright (c) 2023 Sri Lakshmi Kanthan P +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +appIcon ICON "./assets/images/logo.ico" diff --git a/constants/constants.cpp b/constants/constants.cpp index ab65515..52e241d 100644 --- a/constants/constants.cpp +++ b/constants/constants.cpp @@ -111,7 +111,6 @@ std::string getAppIssuePage() { * @return std::string */ std::string getMDnsServiceName() { - std::cout << QSysInfo::machineHostName().toStdString() << std::endl; return QSysInfo::machineHostName().toStdString(); } diff --git a/controller/clipbird/clipbird.cpp b/controller/clipbird/clipbird.cpp index 59a9d52..18ea667 100644 --- a/controller/clipbird/clipbird.cpp +++ b/controller/clipbird/clipbird.cpp @@ -7,46 +7,82 @@ namespace srilakshmikanthanp::clipbirdesk::controller { /** - * @brief Handle On Server Authenticated the Client (From client) + * @brief Handle On Client State Changed (From server) + */ +void ClipBird::handleClientStateChanged(types::device::Device client, bool connected) { + // if the host is not server then throw error + if (!std::holds_alternative(m_host)) { + throw std::runtime_error("Host is not server"); + } + + // if not Connected the return + if (!connected) return; + + // get the device certificate from the server + auto cert = std::get(m_host).getClientCert(client); + + // get the store instance to store certificate + auto &store = storage::Storage::instance(); + + // store the client certificate + store.setClientCert(client.name, cert.toPem()); + + // add ca certificates + this->m_sslConfig.addCaCertificate(cert); + + // set new config for Server + std::get(m_host).setSslConfiguration(this->m_sslConfig); +} + +/** + * @brief Handle On Server Disconnect from Client (From client) * * @param isConnected is connected to the server */ -void ClipBird::handleServerAuthentication(bool isSuccess) { +void ClipBird::handleServerStatusChanged(bool status) { // if the host is not client then throw error if (!std::holds_alternative(m_host)) { throw std::runtime_error("Host is not client"); } // get the client and disconnect the signals - const auto signal = &clipboard::Clipboard::OnClipboardChange; - auto *client = &std::get(m_host); - const auto slot_n = &Client::syncItems; + auto signal = &clipboard::Clipboard::OnClipboardChange; + auto *client = &std::get(m_host); + auto slot_n = &Client::syncItems; + auto &store = storage::Storage::instance(); // if the client is connected then connect the signals - if (isSuccess) { + if (!status) { + disconnect(&m_clipboard, signal, client, slot_n); + } else { connect(&m_clipboard, signal, client, slot_n); + auto cert = client->getConnectedServerCertificate(); + auto name = client->getConnectedServer().name; + store.setServerCert(name, cert.toPem()); + this->m_sslConfig.addCaCertificate(cert); + client->setSslConfiguration(this->m_sslConfig); } } /** - * @brief Handle On Server Disconnect from Client (From client) - * - * @param isConnected is connected to the server + * @brief Handle the Server Found (From client) */ -void ClipBird::handleServerStatusChanged(bool status) { +void ClipBird::handleServerFound(types::device::Device server) { // if the host is not client then throw error if (!std::holds_alternative(m_host)) { throw std::runtime_error("Host is not client"); } - // get the client and disconnect the signals - const auto signal = &clipboard::Clipboard::OnClipboardChange; - auto *client = &std::get(m_host); - const auto slot_n = &Client::syncItems; + // if already connected then return + if (std::get(m_host).isConnected()) return; - // if the client is connected then connect the signals - if (!status) { - disconnect(&m_clipboard, signal, client, slot_n); + // get the client and store the server + auto *client = &std::get(m_host); + auto &store = storage::Storage::instance(); + + // if the server is not found then return + if (store.hasServerCert(server.name)) { + client->connectToServerSecured(server); } } @@ -57,8 +93,7 @@ void ClipBird::handleServerStatusChanged(bool status) { * @param board clipboard that is managed * @param parent parent object */ -ClipBird::ClipBird(QSslConfiguration config, QObject *parent) - : QObject(parent), m_clipboard(this), m_sslConfig(config) {} +ClipBird::ClipBird(QObject *parent) : QObject(parent), m_clipboard(this) {} //---------------------- public slots -----------------------// @@ -71,22 +106,25 @@ void ClipBird::setCurrentHostAsServer() { auto *server = &m_host.emplace(this); // Set the QSslConfiguration - server->setSSLConfiguration(m_sslConfig); + server->setSslConfiguration(m_sslConfig); // Connect the onClientStateChanged signal to the signal + const auto slot_hcs = &ClipBird::handleClientStateChanged; const auto signal_cs = &Server::OnCLientStateChanged; const auto slot_cs = &ClipBird::OnCLientStateChanged; connect(server, signal_cs, this, slot_cs); + connect(server, signal_cs, this, slot_hcs); - // Connect the onNewHostConnected signal to the signal - const auto signal_nh = &Server::OnNewHostConnected; - const auto slot_nh = &ClipBird::OnNewHostConnected; - connect(server, signal_nh, this, slot_nh); + // connect OnAuthRequest to clipbird OnAuthRequest + // Connect the onSyncRequest signal to the clipboard + const auto signal_sa = &Server::OnAuthRequest; + const auto slot_sa = &ClipBird::OnAuthRequest; + connect(server, signal_sa, this, slot_sa); // Connect the onSyncRequest signal to the clipboard const auto signal_sr = &Server::OnSyncRequest; - const auto slot_bs = &clipboard::Clipboard::set; - connect(server, signal_sr, &m_clipboard, slot_bs); + const auto slot_sr = &clipboard::Clipboard::set; + connect(server, signal_sr, &m_clipboard, slot_sr); // connect the OnClipboardChange signal to the server const auto signal_cc = &clipboard::Clipboard::OnClipboardChange; @@ -103,6 +141,12 @@ void ClipBird::setCurrentHostAsServer() { const auto slot_ss = &ClipBird::OnServerStateChanged; connect(server, signal_ss, this, slot_ss); + // get the storage instance + auto &store = storage::Storage::instance(); + + // set the host is client + store.setHostIsServer(true); + // Start the server to listen and accept the client server->startServer(); } @@ -114,6 +158,9 @@ void ClipBird::setCurrentHostAsClient() { // Emplace the client into the m_host variant variable auto *client = &m_host.emplace(this); + // Set the QSslConfiguration + client->setSslConfiguration(m_sslConfig); + // Connect the onServerListChanged signal to the signal const auto signal_sl = &Client::OnServerListChanged; const auto slot_sl = &ClipBird::OnServerListChanged; @@ -121,9 +168,16 @@ void ClipBird::setCurrentHostAsClient() { // Connect the onServerFound signal to the signal const auto signal_fn = &Client::OnServerFound; + const auto slot_hfn = &ClipBird::handleServerFound; const auto slot_fn = &ClipBird::OnServerFound; + connect(client, signal_fn, this, slot_hfn); connect(client, signal_fn, this, slot_fn); + // Connect the OnServerGone signal to the signal + const auto signal_sg = &Client::OnServerGone; + const auto slot_sg = &ClipBird::OnServerGone; + connect(client, signal_sg, this, slot_sg); + // Connect the onServerStateChanged signal to the signal const auto slot_hc = &ClipBird::handleServerStatusChanged; const auto signal_sc = &Client::OnServerStatusChanged; @@ -131,22 +185,125 @@ void ClipBird::setCurrentHostAsClient() { connect(client, signal_sc, this, slot_sc); connect(client, signal_sc, this, slot_hc); - // Connect the onServerAuthentication signal to the signal - const auto slot_ha = &ClipBird::handleServerAuthentication; - const auto signal_au = &Client::OnServerAuthentication; - const auto slot_au = &ClipBird::OnServerAuthentication; - connect(client, signal_au, this, slot_au); - connect(client, signal_au, this, slot_ha); - // Connect the onSyncRequest signal to the clipboard const auto signal_rq = &Client::OnSyncRequest; const auto slot_rq = &clipboard::Clipboard::set; connect(client, signal_rq, &m_clipboard, slot_rq); + // get the storage instance + auto &store = storage::Storage::instance(); + + // set the host is client + store.setHostIsServer(false); + // Start the Discovery client->startBrowsing(); } +/** + * @brief Set the SSL Configuration object + */ +void ClipBird::setSslConfiguration(QSslConfiguration config) { + // storage instance + storage::Storage &store = storage::Storage::instance(); + + // Ca Certificates + QList caCerts; + + // set all ca Client certs from store + for (auto certByte : store.getAllClientCert()) { + caCerts.append(QSslCertificate(certByte, QSsl::Pem)); + } + + // set all ca Server certs from store + for (auto certByte : store.getAllServerCert()) { + caCerts.append(QSslCertificate(certByte, QSsl::Pem)); + } + + // set ca certificates + config.setCaCertificates(caCerts); + + // set the ssl configuration + this->m_sslConfig = config; +} + +/** + * @brief Get the SSL Configuration object + */ +QSslConfiguration ClipBird::getSslConfiguration() const { + return m_sslConfig; +} + +/** + * @brief Clear the Server Certificates + */ +void ClipBird::clearServerCertificates() { + // storage instance + storage::Storage &store = storage::Storage::instance(); + + // Ca Certificates + QList caCerts; + + // clear all ca Server certs from m_sslconfig + this->m_sslConfig.setCaCertificates(caCerts); + + // set all ca Client certs from store + for (auto certByte : store.getAllClientCert()) { + caCerts.append(QSslCertificate(certByte, QSsl::Pem)); + } + + // set the ca certificates + this->m_sslConfig.setCaCertificates(caCerts); + + // clear all ca Server certs from store + store.clearAllServerCert(); + + // if the host is server then set the ssl configuration + if (std::holds_alternative(m_host)) { + std::get(m_host).setSslConfiguration(this->m_sslConfig); + } + + // if the host is client then set the ssl configuration + if (std::holds_alternative(m_host)) { + std::get(m_host).setSslConfiguration(this->m_sslConfig); + } +} + +/** + * @brief Clear the Client Certificates + */ +void ClipBird::clearClientCertificates() { + // storage instance + storage::Storage &store = storage::Storage::instance(); + + // Ca Certificates + QList caCerts; + + // clear all ca Server certs from m_sslconfig + this->m_sslConfig.setCaCertificates(caCerts); + + // set all ca Client certs from store + for (auto certByte : store.getAllServerCert()) { + caCerts.append(QSslCertificate(certByte, QSsl::Pem)); + } + + // set the ca certificates + this->m_sslConfig.setCaCertificates(caCerts); + + // clear all ca Server certs from store + store.clearAllServerCert(); + + // if the host is server then set the ssl configuration + if (std::holds_alternative(m_host)) { + std::get(m_host).setSslConfiguration(this->m_sslConfig); + } + + // if the host is client then set the ssl configuration + if (std::holds_alternative(m_host)) { + std::get(m_host).setSslConfiguration(this->m_sslConfig); + } +} + //---------------------- Server functions -----------------------// /** @@ -154,7 +311,7 @@ void ClipBird::setCurrentHostAsClient() { * * @return QList List of clients */ -QList> ClipBird::getConnectedClientsList() const { +QList ClipBird::getConnectedClientsList() const { if (std::holds_alternative(m_host)) { return std::get(m_host).getConnectedClientsList(); } else { @@ -168,7 +325,7 @@ QList> ClipBird::getConnectedClientsList() const { * @param host ip address of the client * @param ip port number of the client */ -void ClipBird::disconnectClient(const QPair &client) { +void ClipBird::disconnectClient(const types::device::Device &client) { // if the host is not server then throw if (!std::holds_alternative(m_host)) { throw std::runtime_error("Host is not server"); @@ -176,7 +333,7 @@ void ClipBird::disconnectClient(const QPair &client) { // find the client with the given host and ip const auto match = [&](auto i) -> auto{ - return i.first == client.first && i.second == client.second; + return (i.ip == client.ip) && (i.port == client.port); }; // get the list of clients @@ -205,38 +362,38 @@ void ClipBird::disconnectAllClients() { } /** - * @brief The function that is called when the client is authenticated - * - * @param client the client that is currently processed + * @brief Get the server QHostAddress and port */ -void ClipBird::authSuccess(const QPair &client) { +types::device::Device ClipBird::getServerInfo() const { if (std::holds_alternative(m_host)) { - std::get(m_host).authSuccess(client); + return std::get(m_host).getServerInfo(); } else { throw std::runtime_error("Host is not server"); } } /** - * @brief The function that is called when the client it not - * authenticated + * @brief The function that is called when the client is authenticated * * @param client the client that is currently processed */ -void ClipBird::authFailed(const QPair &client) { +void ClipBird::authSuccess(const types::device::Device &client) { if (std::holds_alternative(m_host)) { - std::get(m_host).authFailed(client); + std::get(m_host).authSuccess(client); } else { throw std::runtime_error("Host is not server"); } } /** - * @brief Get the server QHostAddress and port + * @brief The function that is called when the client it not + * authenticated + * + * @param client the client that is currently processed */ -QPair ClipBird::getServerInfo() const { +void ClipBird::authFailed(const types::device::Device &client) { if (std::holds_alternative(m_host)) { - return std::get(m_host).getServerInfo(); + std::get(m_host).authFailed(client); } else { throw std::runtime_error("Host is not server"); } @@ -247,9 +404,9 @@ QPair ClipBird::getServerInfo() const { /** * @brief Get the Server List object * - * @return QList> List of servers + * @return QList List of servers */ -QList> ClipBird::getServerList() const { +QList ClipBird::getServerList() const { if (std::holds_alternative(m_host)) { return std::get(m_host).getServerList(); } else { @@ -264,7 +421,7 @@ QList> ClipBird::getServerList() const { * @param host Host address * @param port Port number */ -void ClipBird::connectToServer(const QPair &host) { +void ClipBird::connectToServer(const types::device::Device &host) { if (std::holds_alternative(m_host)) { std::get(m_host).connectToServer(host); } else { @@ -273,26 +430,24 @@ void ClipBird::connectToServer(const QPair &host) { } /** - * @brief get the connected server address and port - * - * @return QPair address and port + * @brief Is Client Connected */ -QPair ClipBird::getConnectedServer() const { +bool ClipBird::isConnectedToServer() { if (std::holds_alternative(m_host)) { - return std::get(m_host).getConnectedServer(); + return std::get(m_host).isConnected(); } else { throw std::runtime_error("Host is not client"); } } /** - * @brief get the connected server address and port or empty + * @brief get the connected server address and port * - * @return QPair address and port + * @return types::device::Device address and port */ -QPair ClipBird::getConnectedServerOrEmpty() const { +types::device::Device ClipBird::getConnectedServer() const { if (std::holds_alternative(m_host)) { - return std::get(m_host).getConnectedServerOrEmpty(); + return std::get(m_host).getConnectedServer(); } else { throw std::runtime_error("Host is not client"); } @@ -301,7 +456,7 @@ QPair ClipBird::getConnectedServerOrEmpty() const { /** * @brief Disconnect from the server */ -void ClipBird::disconnectFromServer(const QPair &host) { +void ClipBird::disconnectFromServer(const types::device::Device &host) { // if the host is not client then throw error if (!std::holds_alternative(m_host)) { throw std::runtime_error("Host is not client"); @@ -309,7 +464,7 @@ void ClipBird::disconnectFromServer(const QPair &host) { // find the server with the given host and ip const auto match = [&](auto i) -> auto{ - return i.first == host.first && i.second == host.second; + return (i.ip == host.ip) && (i.port == host.port); }; // get the connected server @@ -324,26 +479,9 @@ void ClipBird::disconnectFromServer(const QPair &host) { } /** - * @brief Get the Authed Server object - * @return QPair - */ -QPair ClipBird::getAuthedServer() const { - if (std::holds_alternative(m_host)) { - return std::get(m_host).getAuthedServer(); - } else { - throw std::runtime_error("Host is not client"); - } -} - -/** - * @brief Get the Authed Server object Or Empty - * @return QPair + * @brief IS the Host is Lastly Server */ -QPair ClipBird::getAuthedServerOrEmpty() const { - if (std::holds_alternative(m_host)) { - return std::get(m_host).getAuthedServerOrEmpty(); - } else { - throw std::runtime_error("Host is not client"); - } +bool ClipBird::isLastlyHostIsServer() const { + return storage::Storage::instance().getHostIsServer(); } } // namespace srilakshmikanthanp::clipbirdesk::controller diff --git a/controller/clipbird/clipbird.hpp b/controller/clipbird/clipbird.hpp index b34541c..b535dde 100644 --- a/controller/clipbird/clipbird.hpp +++ b/controller/clipbird/clipbird.hpp @@ -19,6 +19,8 @@ #include "clipboard/clipboard.hpp" #include "network/syncing/client/client.hpp" #include "network/syncing/server/server.hpp" +#include "store/storage.hpp" +#include "types/device/device.hpp" namespace srilakshmikanthanp::clipbirdesk::controller { class ClipBird : public QObject { @@ -26,37 +28,37 @@ class ClipBird : public QObject { signals: // signals for this class /// @brief On Server List Changed (From Client) - void OnServerListChanged(QList> servers); + void OnServerListChanged(QList servers); signals: // signals for this class /// @brief On Server Found (From Client) - void OnServerFound(QPair server); + void OnServerFound(types::device::Device server); signals: // signals for this class - /// @brief On Server state changed (From Client) - void OnServerStatusChanged(bool isConnected); + /// @brief On Server Gone + void OnServerGone(types::device::Device); signals: // signals for this class - /// @brief On Server Auth Succeed (From Client) - void OnServerAuthentication(bool isSuccessful); + /// @brief On Server state changed (From Client) + void OnServerStatusChanged(bool isConnected); //----------------------- server Signals ------------------------// signals: // signals /// @brief On client state changed (From Server) - void OnCLientStateChanged(QPair client, bool connected); - - signals: // signals for this class - /// @brief On New Host Connected - void OnNewHostConnected(QPair client); + void OnCLientStateChanged(types::device::Device client, bool connected); signals: // signals for this class /// @brief On Server state changed (From Server) void OnServerStateChanged(bool isStarted); + signals: // signals for this class + /// @brief On Sync Request + void OnAuthRequest(types::device::Device client); + signals: // signals for this class /// @brief On Sync Request (From Server) - void OnClientListChanged(QList> clients); + void OnClientListChanged(QList clients); private: // typedefs for this class @@ -75,12 +77,15 @@ class ClipBird : public QObject { private: // private slots - /// @brief Handle On Server Authenticated the client (From client) - void handleServerAuthentication(bool isConnected); + /// @brief Handle Client State Changes (From server) + void handleClientStateChanged(types::device::Device client, bool connected); - /// @brief Handle On Server Disconnect from Client (From client) + /// @brief Handle On Server Disconnect (From client) void handleServerStatusChanged(bool status); + /// @brief Handle the Server Found (From client) + void handleServerFound(types::device::Device server); + public: // Member functions /** @@ -90,14 +95,14 @@ class ClipBird : public QObject { * @param board clipboard that is managed * @param parent parent object */ - ClipBird(QSslConfiguration config, QObject *parent = nullptr); + ClipBird(QObject *parent = nullptr); /** * @brief Destroy the ClipBird object */ virtual ~ClipBird() = default; - //---------------------- public slots ----------------------// + //------------------------- public slots -------------------------// /** * @brief set the host as server and start listening @@ -110,14 +115,38 @@ class ClipBird : public QObject { */ void setCurrentHostAsClient(); - //---------------------- Server functions -----------------------// + //--------------------- General functions ----------------------// + + /** + * @brief Set the SSL Configuration object + */ + void setSslConfiguration(QSslConfiguration config); + + /** + * @brief Get the SSL Configuration object + */ + QSslConfiguration getSslConfiguration() const; + + //------------------- Store functions ------------------------// + + /** + * @brief Clear Server Certificates + */ + void clearServerCertificates(); + + /** + * @brief Clear Client Certificates + */ + void clearClientCertificates(); + + //------------------- Server functions ------------------------// /** * @brief Get the Clients that are connected to the server * * @return QList List of clients */ - QList> getConnectedClientsList() const; + QList getConnectedClientsList() const; /** * @brief Disconnect the client from the server and delete @@ -125,19 +154,24 @@ class ClipBird : public QObject { * @param host ip address of the client * @param ip port number of the client */ - void disconnectClient(const QPair &client); + void disconnectClient(const types::device::Device &client); /** * @brief Disconnect the all the clients from the server */ void disconnectAllClients(); + /** + * @brief Get the server QHostAddress and port + */ + types::device::Device getServerInfo() const; + /** * @brief The function that is called when the client is authenticated * * @param client the client that is currently processed */ - void authSuccess(const QPair &client); + void authSuccess(const types::device::Device &client); /** * @brief The function that is called when the client it not @@ -145,21 +179,16 @@ class ClipBird : public QObject { * * @param client the client that is currently processed */ - void authFailed(const QPair &client); - - /** - * @brief Get the server QHostAddress and port - */ - QPair getServerInfo() const; + void authFailed(const types::device::Device &client); //---------------------- Client functions -----------------------// /** * @brief Get the Server List object * - * @return QList> List of servers + * @return QList List of servers */ - QList> getServerList() const; + QList getServerList() const; /** * @brief Connect to the server with the given host and port @@ -168,37 +197,30 @@ class ClipBird : public QObject { * @param host Host address * @param port Port number */ - void connectToServer(const QPair &host); + void connectToServer(const types::device::Device &host); /** - * @brief get the connected server address and port - * - * @return QPair address and port + * @brief Is Client Connected */ - QPair getConnectedServer() const; + bool isConnectedToServer(); /** - * @brief get the connected server address and port or empty + * @brief get the connected server address and port * - * @return QPair address and port + * @return types::device::Device address and port */ - QPair getConnectedServerOrEmpty() const; + types::device::Device getConnectedServer() const; /** * @brief Disconnect from the server */ - void disconnectFromServer(const QPair &host); + void disconnectFromServer(const types::device::Device &host); - /** - * @brief Get the Authed Server object - * @return QPair - */ - QPair getAuthedServer() const; + //---------------------- General functions -----------------------// /** - * @brief Get the Authed Server object Or Empty - * @return QPair + * @brief IS the Host is Lastly Server */ - QPair getAuthedServerOrEmpty() const; + bool isLastlyHostIsServer() const; }; } // namespace srilakshmikanthanp::clipbirdesk::controller diff --git a/docs/design/Protocol.md b/docs/design/Protocol.md index 9f90820..8f68186 100644 --- a/docs/design/Protocol.md +++ b/docs/design/Protocol.md @@ -33,7 +33,7 @@ Clipbird utilizes a variety of packet types for different purposes. These packet ### What are the packets Required for Clipbird -First we need to Send the authenticate Request to the server, for that we need to send the **Authentication** packet to the server. This packet may contains the JWT token if it has a valid JWT token the server will accept the connection without user intervention. If the token is invalid the server will send the **Authentication** packet with **AuthStatus** set to 0x00. If the token is valid the server will send the **Authentication** packet with **AuthStatus** set to 0x01. If the server is not configured to use JWT token it will send the **Authentication** packet with **AuthStatus** set to 0x01. If the server is configured to use JWT token but the client does not send the **Authentication** packet with JWT token the server will send the **Authentication** packet with **AuthStatus** set to 0x00. +When Client connected to server The serve need to send an Response Called Authentication Success or failed for this purpose we use **Authentication**. Once the server has been identified, clipboard data is transmitted between the client and the server using a single type of packet known as the **SyncingPacket**. This packet is responsible for transferring clipboard data from the client to the server and vice versa, ensuring seamless sharing of clipboard content between the two devices. @@ -71,7 +71,7 @@ The **InvalidRequest** is used to indicate that the packet is invalid. This pack | Error Code | Error Message | |------------|---------------| | 0x01 | Coding Error | -| 0x02 | TLS Error | +| 0x02 | InvalidPacket | ### Authentication @@ -84,9 +84,9 @@ The **Authentication** is used to indicate the authentication process to the cli #### Body -- **AuthStatus**: This field specifies the status of the authentication process. - - AuthFailed: This field specifies the status of the authentication process, which is set to 0x00 if the authentication fails. - - AuthSuccess: This field specifies the status of the authentication process, which is set to 0x01 if the authentication succeeds. +- **AuthStatus**: This field specifies the status of the authentication process. it can be one of the following values. + - 0x00: Auth Failed + - 0x01: Auth Success #### Structure diff --git a/main.cpp b/main.cpp index 61d74e1..c5b1542 100644 --- a/main.cpp +++ b/main.cpp @@ -4,6 +4,8 @@ // https://opensource.org/licenses/MIT // Qt Headers +#include +#include #include #include #include @@ -28,8 +30,82 @@ namespace srilakshmikanthanp::clipbirdesk { * for ClipBird Application */ class ClipbirdApplication : public SingleApplication { + private: // Member Variables and Objects + + // file path to store the certificate and key + std::string certPath = (std::filesystem::path(constants::getAppHome()) / "cert.pem").string(); + std::string keyPath = (std::filesystem::path(constants::getAppHome()) / "key.pem").string(); + private: // Member Functions + /** + * @brief Get the certificate from App Home + */ + QSslConfiguration getOldSslConfiguration() { + // QFile to read the certificate and key + QFile certFile(certPath.c_str()), pkeyFile(keyPath.c_str()); + + // open the certificate and key + if(!certFile.open(QIODevice::ReadOnly) || !pkeyFile.open(QIODevice::ReadOnly)) { + throw std::runtime_error("Can't Create Certificate"); + } + + // read the certificate and key + QSslCertificate cert = QSslCertificate(certFile.readAll(), QSsl::Pem); + QSslKey key = QSslKey(pkeyFile.readAll(), QSsl::Rsa); + QSslConfiguration sslConfig; + + // set the certificate and key + sslConfig.setLocalCertificate(cert); + sslConfig.setPrivateKey(key); + + // if the configuration is null + if (cert.isNull() || key.isNull() || sslConfig.isNull()) { + throw std::runtime_error("Can't Create QSslConfiguration"); + } + + // set peer verify + sslConfig.setPeerVerifyMode(QSslSocket::VerifyPeer); + + // return the configuration + return sslConfig; + } + + /** + * @brief Get the certificate by creating new one + */ + QSslConfiguration getNewSslConfiguration() { + // QFile to read the certificate and key + QFile certFile(certPath.c_str()), pkeyFile(keyPath.c_str()); + + // open the certificate and key + if(!certFile.open(QIODevice::WriteOnly) || !pkeyFile.open(QIODevice::WriteOnly)) { + throw std::runtime_error("Can't Create Certificate"); + } + + // generate the certificate and key + auto sslConfig = utility::functions::getQSslConfiguration(); + + // write the certificate and key + certFile.write(sslConfig.localCertificate().toPem()); + pkeyFile.write(sslConfig.privateKey().toPem()); + + // return the configuration + return sslConfig; + } + + /** + * @brief Get the certificate from App Home + * or generate new one and store it + */ + QSslConfiguration getSslConfiguration() { + if (!std::filesystem::exists(certPath) || !std::filesystem::exists(keyPath)) { + return getNewSslConfiguration(); + } else { + return getOldSslConfiguration(); + } + } + /** * @brief On Tray Icon Clicked */ @@ -69,7 +145,7 @@ class ClipbirdApplication : public SingleApplication { Q_DISABLE_COPY_MOVE(ClipbirdApplication); - public: // Constructors and Destructors + public: // Constructors and Destructors /** * @brief Construct a new Clipbird Application object @@ -79,9 +155,14 @@ class ClipbirdApplication : public SingleApplication { */ ClipbirdApplication(int &argc, char **argv) : SingleApplication(argc, argv) { // create the objects of the class - controller = new controller::ClipBird(utility::functions::getQSslConfiguration()); - content = new ui::gui::Content(controller); - window = new ui::gui::Window(); + controller = new controller::ClipBird(); + + // set the ssl configuration + controller->setSslConfiguration(this->getSslConfiguration()); + + // create the objects of the class + content = new ui::gui::Content(controller); + window = new ui::gui::Window(); // set the signal handler for all os signal(SIGTERM, [](int sig) { qApp->quit(); }); @@ -117,6 +198,11 @@ class ClipbirdApplication : public SingleApplication { // using some classes using ui::gui::Content; + // tray icon click from content + const auto signal_tic = &QSystemTrayIcon::activated; + const auto slot_tic = &ClipbirdApplication::onTrayIconClicked; + QObject::connect(content->getTrayIcon(), signal_tic, this, slot_tic); + // set the signal for instance Started const auto signal_is = &SingleApplication::instanceStarted; const auto slot_is = &Content::show; @@ -126,11 +212,6 @@ class ClipbirdApplication : public SingleApplication { const auto signal = &QStyleHints::colorSchemeChanged; const auto slot = &ClipbirdApplication::setQssFile; QObject::connect(QGuiApplication::styleHints(), signal, this, slot); - - // tray icon click from content - const auto signal_tic = &QSystemTrayIcon::activated; - const auto slot_tic = &ClipbirdApplication::onTrayIconClicked; - QObject::connect(content->getTrayIcon(), signal_tic, this, slot_tic); } /** @@ -156,7 +237,7 @@ class ClipbirdApplication : public SingleApplication { * @return int Status code */ auto main(int argc, char **argv) -> int { - // using ClipbirdApplication class + // using ClipbirdApplication class from namespace using srilakshmikanthanp::clipbirdesk::ClipbirdApplication; using srilakshmikanthanp::clipbirdesk::constants::getAppHome; using srilakshmikanthanp::clipbirdesk::constants::getAppLogFile; @@ -167,14 +248,25 @@ auto main(int argc, char **argv) -> int { QDir().mkdir(getAppHome().c_str()); } +#ifdef NDEBUG // log file to record the logs QFile logfile(QString::fromStdString(getAppLogFile())); // open the log file logfile.open(QIODevice::WriteOnly | QIODevice::Append); + // open QStream to write the log file + QTextStream logstream(&logfile); + // Set the log file - Logger::setLogFile(&logfile); + Logger::setLogStream(&logstream); +#else + // open QStream to write the log file + QTextStream logstream(stdout); + + // Set the log file as std::cout + Logger::setLogStream(&logstream); +#endif // Set the custom message handler qInstallMessageHandler(Logger::handler); diff --git a/network/packets/authentication/authentication.cpp b/network/packets/authentication/authentication.cpp index 6447101..e17ab38 100644 --- a/network/packets/authentication/authentication.cpp +++ b/network/packets/authentication/authentication.cpp @@ -52,12 +52,12 @@ quint8 Authentication::getPacketType() const noexcept { * @param status */ void Authentication::setAuthStatus(quint8 status) { - if (status == types::enums::AuthStatus::AuthFailed) { + if (status == types::enums::AuthStatus::AuthFail) { this->authStatus = status; return; } - if (status == types::enums::AuthStatus::AuthSuccess) { + if (status == types::enums::AuthStatus::AuthOkay) { this->authStatus = status; return; } @@ -125,8 +125,14 @@ QDataStream& operator>>(QDataStream& stream, Authentication& packet) { // auth status const auto authStatus = packet.authStatus; + // allowed status + const QList allowedStatus = { + AuthStatus::AuthOkay, + AuthStatus::AuthFail, + }; + // Check the auth status - if (authStatus != AuthStatus::AuthFailed && authStatus != AuthStatus::AuthSuccess) { + if (!allowedStatus.contains(authStatus)) { throw MalformedPacket(ErrorCode::CodingError, "Invalid Auth Status"); } diff --git a/network/packets/invalidrequest/invalidrequest.cpp b/network/packets/invalidrequest/invalidrequest.cpp index bceff4d..5977c29 100644 --- a/network/packets/invalidrequest/invalidrequest.cpp +++ b/network/packets/invalidrequest/invalidrequest.cpp @@ -50,11 +50,17 @@ quint8 InvalidRequest::getPacketType() const noexcept { * @param code */ void InvalidRequest::setErrorCode(quint8 code) { - if (code != types::enums::ErrorCode::CodingError) { - throw std::invalid_argument("Invalid Error Code"); + if (code == types::enums::ErrorCode::InvalidPacket) { + this->errorCode = code; + return; } - this->errorCode = code; + if (code == types::enums::ErrorCode::CodingError) { + this->errorCode = code; + return; + } + + throw std::invalid_argument("Invalid Error Code"); } /** @@ -172,8 +178,14 @@ QDataStream& operator>>(QDataStream& stream, InvalidRequest& packet) { // using some types using types::enums::ErrorCode; + // allowed + const auto allowed = std::vector{ + ErrorCode::InvalidPacket, + ErrorCode::CodingError + }; + // check the error code - if (packet.errorCode != ErrorCode::CodingError && packet.errorCode != ErrorCode::SSLError) { + if (std::find(allowed.begin(), allowed.end(), packet.errorCode) == allowed.end()) { throw MalformedPacket(types::enums::CodingError, "Invalid Error Code"); } diff --git a/network/service/mdns/browser/browser.cpp b/network/service/mdns/browser/browser.cpp index eef31bc..15ae2cf 100644 --- a/network/service/mdns/browser/browser.cpp +++ b/network/service/mdns/browser/browser.cpp @@ -14,19 +14,21 @@ namespace srilakshmikanthanp::clipbirdesk::network::service::mdns { * @param isAdded * @param const QHostInfo& info */ -void Browser::onHostResolved(bool isAdded, quint16 port, const QHostInfo& info) { +void Browser::onHostResolved(bool isAdded, quint16 port, QString srvName, const QHostInfo& info) { // check for error if (info.error() != QHostInfo::NoError || info.addresses().isEmpty()) { - // log the error with qt qWarning() << LOG("Unable to resolve service"); return; } + // remove the .local from srv name + srvName.replace(".local.", ""); + // get the ip address auto ip = info.addresses().first(); // emit the signal - isAdded ? emit onServiceAdded({ip, port}) : emit onServiceRemoved({ip, port}); + isAdded ? emit onServiceAdded({ip, port, srvName}) : emit onServiceRemoved({ip, port, srvName}); } /** @@ -144,6 +146,7 @@ void Browser::addedCallback( browserObj, // this true, // Removed ntohs(port), // port + QString::fromUtf8(hosttarget), // srvName std::placeholders::_1 // QHostInfo ); @@ -190,6 +193,7 @@ void Browser::removeCallback( browserObj, // this false, // Removed ntohs(port), // port + QString::fromUtf8(hosttarget), // srvName std::placeholders::_1 // QHostInfo ); diff --git a/network/service/mdns/browser/browser.hpp b/network/service/mdns/browser/browser.hpp index 7eb35c6..c643f68 100644 --- a/network/service/mdns/browser/browser.hpp +++ b/network/service/mdns/browser/browser.hpp @@ -27,6 +27,7 @@ // Local headers #include "constants/constants.hpp" #include "types/enums/enums.hpp" +#include "types/device/device.hpp" #include "utility/functions/ipconv/ipconv.hpp" namespace srilakshmikanthanp::clipbirdesk::network::service::mdns { @@ -59,7 +60,7 @@ class Browser : public QObject { * @param isAdded * @param const QHostInfo& info */ - void onHostResolved(bool isAdded, quint16 port, const QHostInfo& info); + void onHostResolved(bool isAdded, quint16 port, QString srvName, const QHostInfo& info); /** * @brief Callback function for DNSServiceBrowse function @@ -142,7 +143,7 @@ class Browser : public QObject { * @param host Host address * @param port Port number */ - virtual void onServiceAdded(QPair) = 0; + virtual void onServiceAdded(types::device::Device) = 0; /** * @brief On Server Removed abstract function that @@ -151,7 +152,7 @@ class Browser : public QObject { * @param host Host address * @param port Port number */ - virtual void onServiceRemoved(QPair) = 0; + virtual void onServiceRemoved(types::device::Device) = 0; }; } // namespace srilakshmikanthanp::clipbirdesk::network::service::dnsd #endif diff --git a/network/service/index.hpp b/network/service/service.hpp similarity index 100% rename from network/service/index.hpp rename to network/service/service.hpp diff --git a/network/syncing/client/client.cpp b/network/syncing/client/client.cpp index 158551f..3ee4b03 100644 --- a/network/syncing/client/client.cpp +++ b/network/syncing/client/client.cpp @@ -6,19 +6,141 @@ #include "client.hpp" namespace srilakshmikanthanp::clipbirdesk::network::syncing { +/** + * @brief Verify Server + */ +bool Client::verifyCert(const QSslCertificate& certificate) { + // check the basic conditions + if (certificate.isNull() || certificate.subjectInfo(QSslCertificate::CommonName).isEmpty()) { + return false; + } + + // get the client details + const auto name = certificate.subjectInfo(QSslCertificate::CommonName).constFirst(); + const auto addr = this->m_ssl_socket->peerAddress(); + const auto port = this->m_ssl_socket->peerPort(); + + // matcher lambda function + auto matcher = [&](const auto &elm) { + return elm.ip == addr && elm.port == port; + }; + + // if name of device not equal to subject name + auto device = std::find_if(m_servers.begin(), m_servers.end(), matcher); + + // if device not found + if (device == nullptr) { + return false; + } + + // name matches + if (device->name == name) { + return true; + } + + // name not matches + return false; +} + +/** + * @brief Process SSL Errors + */ +void Client::processSslErrors(const QList& errors) { + // List of ignored SslErrors + QList ignoredErrors; + + // Ignore the errors + ignoredErrors.append(QSslError::SelfSignedCertificate); + ignoredErrors.append(QSslError::CertificateUntrusted); + ignoredErrors.append(QSslError::HostNameMismatch); + + // make copy of errors + auto errorsCopy = errors; + + // remove all the ignored errors + auto itr = std::remove_if(errorsCopy.begin(), errorsCopy.end(), [&](auto error) { + return ignoredErrors.contains(error.error()); + }); + + // remove the ignored errors + errorsCopy.erase(itr, errorsCopy.end()); + + // log the errors + for (auto error : errorsCopy) { + qWarning() << (LOG(std::to_string(error.error()).c_str())); + } + + // if errorsCopy is not empty + if (!errorsCopy.isEmpty()) { + return this->m_ssl_socket->abort(); + } + + // get certificate + const auto certificate = this->m_ssl_socket->peerCertificate(); + + // if any error occurred + if (errors.length() > 0 && !this->verifyCert(certificate)) { + return this->m_ssl_socket->abort(); + } + + // ignore the errors + this->m_ssl_socket->ignoreSslErrors(); +} + +/** + * @brief Process Ssl Error Secured + */ +void Client::processSslErrorsSecured(const QList& errors) { + // List of ignored SslErrors + QList ignoredErrors; + + // Ignore the errors + ignoredErrors.append(QSslError::HostNameMismatch); + + // make copy of errors + auto errorsCopy = errors; + + // remove all the ignored errors + auto itr = std::remove_if(errorsCopy.begin(), errorsCopy.end(), [&](auto error) { + return ignoredErrors.contains(error.error()); + }); + + // remove the ignored errors + errorsCopy.erase(itr, errorsCopy.end()); + + // log the errors + for (auto error : errorsCopy) { + qWarning() << (LOG(std::to_string(error.error()).c_str())); + } + + if (!errorsCopy.isEmpty()) { + return this->m_ssl_socket->abort(); + } else { + this->m_ssl_socket->ignoreSslErrors(); + } +} + /** * @brief Process the Authentication Packet from the server * * @param packet Authentication */ void Client::processAuthentication(const packets::Authentication& packet) { - if (packet.getAuthStatus() == types::enums::AuthStatus::AuthSuccess) { - emit OnServerAuthentication((this->m_is_authed = true)); - } else { - emit OnServerAuthentication((this->m_is_authed = false)); + if (packet.getAuthStatus() == types::enums::AuthStatus::AuthOkay) { + emit OnServerStatusChanged(true); } } +/** + * @brief Process the Invalid packet that has been received + * from the server and emit the signal + * + * @param packet Invalid packet + */ +void Client::processInvalidPacket(const packets::InvalidRequest& packet) { + qWarning() << (LOG(packet.getErrorMessage().toStdString())); +} + /** * @brief Process the packet that has been received * from the server and emit the signal @@ -38,16 +160,6 @@ void Client::processSyncingPacket(const packets::SyncingPacket& packet) { emit OnSyncRequest(items); } -/** - * @brief Process the Invalid packet that has been received - * from the server and emit the signal - * - * @param packet Invalid packet - */ -void Client::processInvalidPacket(const packets::InvalidRequest& packet) { - qWarning() << (LOG(packet.getErrorMessage().toStdString())); -} - /** * @brief Process the packet that has been received * from the server @@ -89,8 +201,7 @@ void Client::processReadyRead() { auto bytes = get.readRawData(start, toRead); if (bytes == -1) { - get.rollbackTransaction(); - return; + return get.rollbackTransaction(); } toRead -= bytes; @@ -156,31 +267,6 @@ void Client::processReadyRead() { qWarning() << (LOG("Unknown Packet Found")); } -/** - * @brief Process the SSL error and emit the signal - * - * @param errors List of SSL errors - */ -void Client::processSslError(const QList& errors) { - for (auto& error : errors) { - qWarning() << (LOG(error.errorString().toStdString())); - } -} - -/** - * @brief Handle client connected - */ -void Client::handleConnected() { - emit OnServerStatusChanged(true); -} - -/** - * @brief Handle client disconnected - */ -void Client::handleDisconnected() { - emit OnServerStatusChanged(false); -} - /** * @brief Construct a new Syncing Client object * and connect the signals and slots and start @@ -190,33 +276,31 @@ void Client::handleDisconnected() { * @param parent Parent */ Client::Client(QObject* parent) : service::mdnsBrowser(parent) { - // connect the signal to emit the signal for - // server state changed - const auto signal_c = &QSslSocket::connected; - const auto slot_c = &Client::handleConnected; - connect(m_ssl_socket, signal_c, this, slot_c); - // connect the signals and slots for the socket // readyRead signal to process the packet const auto signal_r = &QSslSocket::readyRead; const auto slot_r = &Client::processReadyRead; connect(m_ssl_socket, signal_r, this, slot_r); - // connect the signals and slots for the socket - // sslErrors signal to emit the signal for - // OnErrorOccurred - const auto signal_s = &QSslSocket::sslErrors; - const auto slot_s = &Client::processSslError; - connect(m_ssl_socket, signal_s, this, slot_s); - // disconnected signal to emit the signal for // server state changed const auto signal_d = &QSslSocket::disconnected; - const auto slot_d = &Client::handleDisconnected; + const auto slot_d = [=] {emit OnServerStatusChanged(false); }; connect(m_ssl_socket, signal_d, this, slot_d); +} - // set the peer verify mode to none - m_ssl_socket->setPeerVerifyMode(QSslSocket::VerifyNone); +/** + * @brief Set SSL configuration + */ +void Client::setSslConfiguration(QSslConfiguration config) { + this->m_ssl_config = config; +} + +/** + * @brief get the SSL configuration + */ +QSslConfiguration Client::getSslConfiguration() const { + return this->m_ssl_config; } /** @@ -236,7 +320,9 @@ void Client::syncItems(QVector> items) { using utility::functions::createPacket; // create the packet - SyncingPacket packet = createPacket({SyncingPacket::PacketType::SyncPacket, items}); + SyncingPacket packet = createPacket({ + SyncingPacket::PacketType::SyncPacket, items + }); // send the packet to the server this->sendPacket(packet); @@ -247,17 +333,8 @@ void Client::syncItems(QVector> items) { * * @return QList> List of servers */ -QList> Client::getServerList() const { - // Host address and port number - QList> list; - - // iterate and add the server - for (auto& [host, port] : m_servers) { - list.append({host, port}); - } - - // return the list - return list; +QList Client::getServerList() const { + return m_servers; } /** @@ -267,82 +344,124 @@ QList> Client::getServerList() const { * @param host Host address * @param port Port number */ -void Client::connectToServer(QPair client) { - // check if the SSL configuration is set - if (m_ssl_socket->sslConfiguration().isNull()) { - throw std::runtime_error("SSL Configuration is not set"); +void Client::connectToServer(types::device::Device server) { + // if Discover Configuration is null the return + if (this->m_ssl_config.isNull()) { + throw std::runtime_error("SSL Config Config is not set"); } + // set the ssl Configuration + m_ssl_socket->setSslConfiguration(m_ssl_config); + // check if the socket is connected - if (m_ssl_socket->isOpen()) { + if (this->isConnected()) { this->disconnectFromServer(); } + // disconnect the signals and slots for the socket + const auto signal_ss = &QSslSocket::sslErrors; + const auto slot_ss = &Client::processSslErrorsSecured; + disconnect(m_ssl_socket, signal_ss, this, slot_ss); + + // connect the signals and slots for the socket + const auto signal_su = &QSslSocket::sslErrors; + const auto slot_su = &Client::processSslErrors; + connect(m_ssl_socket, signal_su, this, slot_su); + // create the host address - const auto host = client.first.toString(); - const auto port = client.second; + const auto host = server.ip.toString(); + const auto port = server.port; + + // log host and port + qInfo() << (LOG("Connecting to server: " + host.toStdString() + ":" + std::to_string(port))); // connect to the server as encrypted m_ssl_socket->connectToHostEncrypted(host, port); } /** - * @brief Get the Connection Host and Port object - * @return QPair + * @brief Connect to server Secured */ -QPair Client::getConnectedServer() const { - if (this->m_ssl_socket->state() != QAbstractSocket::ConnectedState) { - throw std::runtime_error("Socket is not connected"); +void Client::connectToServerSecured(types::device::Device server) { + // if Discover Configuration is null the return + if (this->m_ssl_config.isNull()) { + throw std::runtime_error("SSL Config Config is not set"); } - return {m_ssl_socket->peerAddress(), m_ssl_socket->peerPort()}; -} + // set the ssl Configuration + m_ssl_socket->setSslConfiguration(m_ssl_config); -/** - * @brief Get the Connection Host and Port object Or Empty - * @return QPair - */ -QPair Client::getConnectedServerOrEmpty() const { - try { - return this->getConnectedServer(); - } catch (const std::exception&) { - return QPair(); + // check if the socket is connected + if (this->isConnected()) { + this->disconnectFromServer(); } + + // disconnect the signals and slots for the socket + const auto signal_su = &QSslSocket::sslErrors; + const auto slot_su = &Client::processSslErrors; + disconnect(m_ssl_socket, signal_su, this, slot_su); + + // connect the signals and slots for the socket + const auto signal_ss = &QSslSocket::sslErrors; + const auto slot_ss = &Client::processSslErrorsSecured; + connect(m_ssl_socket, signal_ss, this, slot_ss); + + // create the host address + const auto host = server.ip.toString(); + const auto port = server.port; + + // log host and port + qInfo() << (LOG("Connecting to server: " + host.toStdString() + ":" + std::to_string(port))); + + // connect to the server as encrypted + m_ssl_socket->connectToHostEncrypted(host, port); } /** - * @brief Disconnect from the server + * @brief IS connected to the server */ -void Client::disconnectFromServer() { - m_ssl_socket->disconnectFromHost(); +bool Client::isConnected() const { + return m_ssl_socket->state() == QAbstractSocket::ConnectedState; } /** - * @brief Get the Authed Server object + * @brief Get the Connection Host and Port object * @return QPair */ -QPair Client::getAuthedServer() const { +types::device::Device Client::getConnectedServer() const { + // check if the socket is connected else throw error if (this->m_ssl_socket->state() != QAbstractSocket::ConnectedState) { throw std::runtime_error("Socket is not connected"); } - if (!this->m_is_authed) { - throw std::runtime_error("Socket is not authenticated"); - } + // peer server details + auto addr = m_ssl_socket->peerAddress(); + auto port = m_ssl_socket->peerPort(); + auto cert = m_ssl_socket->peerCertificate(); + auto name = cert.subjectInfo(QSslCertificate::CommonName).constFirst(); - return {m_ssl_socket->peerAddress(), m_ssl_socket->peerPort()}; + // return the host address and port number + return { addr, port, name }; } /** - * @brief Get the Authed Server object Or Empty - * @return QPair + * @brief Disconnect from the server */ -QPair Client::getAuthedServerOrEmpty() const { - try { - return this->getAuthedServer(); - } catch (const std::exception&) { - return QPair(); +void Client::disconnectFromServer() { + m_ssl_socket->disconnectFromHost(); +} + +/** + * @brief Get the Server Certificate + */ +QSslCertificate Client::getConnectedServerCertificate() const{ + // if not connected throw error + if (!this->isConnected()) { + throw std::runtime_error("Socket is not connected"); } + + // return the server certificate + return m_ssl_socket->peerCertificate(); } /** @@ -352,12 +471,17 @@ QPair Client::getAuthedServerOrEmpty() const { * @param host Host address * @param port Port number */ -void Client::onServiceAdded(QPair server) { - // emit the signal for server found event +void Client::onServiceAdded(types::device::Device server) { + // if Discover Configuration is null the return + if (this->m_ssl_config.isNull()) { + throw std::runtime_error("SSL Config Config is not set"); + } + + // emit server found emit OnServerFound(server); - // add the server to the list - m_servers.append({server.first, server.second}); + // add to list + this->m_servers.append(server); // emit the signal emit OnServerListChanged(getServerList()); @@ -367,12 +491,29 @@ void Client::onServiceAdded(QPair server) { * @brief On server removed function that That Called by the * discovery client when the server is removed */ -void Client::onServiceRemoved(QPair server) { - // emit the signal for server gone event - emit OnServerGone(server); +void Client::onServiceRemoved(types::device::Device server) { + // matcher to get the Device from servers list + auto matcher = [server](const types::device::Device& device) { + return device == server; + }; + + // start and end + auto start = m_servers.begin(); + auto end = m_servers.end(); + + // Get the Device + auto device = std::find_if(start, end, matcher); + + // if device is not found + if (device == m_servers.end()) { + return; + } + + // emit server gone + emit OnServerGone(*device); // remove the server from the list - m_servers.removeAll({server.first, server.second}); + m_servers.erase(device); // emit the signal emit OnServerListChanged(getServerList()); diff --git a/network/syncing/client/client.hpp b/network/syncing/client/client.hpp index bfd3407..781f948 100644 --- a/network/syncing/client/client.hpp +++ b/network/syncing/client/client.hpp @@ -7,22 +7,26 @@ // Qt headers #include +#include #include #include #include #include +#include #include #include #include #include +#include // standard headers #include #include // Local headers -#include "network/service/index.hpp" +#include "network/service/service.hpp" #include "types/enums/enums.hpp" +#include "types/device/device.hpp" #include "utility/functions/ipconv/ipconv.hpp" #include "utility/functions/nbytes/nbytes.hpp" #include "utility/functions/packet/packet.hpp" @@ -35,24 +39,20 @@ namespace srilakshmikanthanp::clipbirdesk::network::syncing { class Client : public service::mdnsBrowser { signals: // signals for this class /// @brief On Server List Changed - void OnServerListChanged(QList> servers); + void OnServerListChanged(QList servers); signals: // signals for this class /// @brief On Server Found - void OnServerFound(QPair); + void OnServerFound(types::device::Device); signals: // signals for this class /// @brief On Server Gone - void OnServerGone(QPair); + void OnServerGone(types::device::Device); signals: // signals for this class /// @brief On Server state changed void OnServerStatusChanged(bool isConnected); - signals: // signals for this class - /// @brief On Server Auth Succeed - void OnServerAuthentication(bool isSuccessful); - signals: // signals for this class /// @brief On Sync Request void OnSyncRequest(QVector> items); @@ -69,14 +69,14 @@ class Client : public service::mdnsBrowser { private: // Member variables - /// @brief List of Found servers and timestamp - QList> m_servers; - - /// @brief SSL socket + /// @brief SSL socket for the client QSslSocket* m_ssl_socket = new QSslSocket(this); - /// @brief is Server Authenticated Me - bool m_is_authed = false; + /// @brief SSL configuration + QSslConfiguration m_ssl_config; + + /// @brief List of Found servers + QList m_servers; private: // private functions @@ -101,25 +101,27 @@ class Client : public service::mdnsBrowser { // write the packet length while (wrote < data.size()) { - // try to write the data auto bytes = stream.writeRawData(data.data() + wrote, data.size() - wrote); - - // if no error occurred - if (bytes != -1) { - wrote += bytes; continue; - } - - // abort the transaction - stream.abortTransaction(); - - // log - qWarning() << LOG(m_ssl_socket->errorString().toStdString()); + wrote = wrote + bytes; + if (bytes == -1) { stream.abortTransaction(); break; } } // commit the transaction stream.commitTransaction(); } + void processSslErrorsSecured(const QList& errors); + + /** + * @brief Verify Server + */ + bool verifyCert(const QSslCertificate& certificate); + + /** + * @brief Process SSL Errors + */ + void processSslErrors(const QList& errors); + /** * @brief Process the Authentication Packet from the server * @@ -128,20 +130,20 @@ class Client : public service::mdnsBrowser { void processAuthentication(const packets::Authentication& packet); /** - * @brief Process the packet that has been received + * @brief Process the Invalid packet that has been received * from the server and emit the signal * - * @param packet Syncing packet + * @param packet Invalid packet */ - void processSyncingPacket(const packets::SyncingPacket& packet); + void processInvalidPacket(const packets::InvalidRequest& packet); /** - * @brief Process the Invalid packet that has been received + * @brief Process the packet that has been received * from the server and emit the signal * - * @param packet Invalid packet + * @param packet Syncing packet */ - void processInvalidPacket(const packets::InvalidRequest& packet); + void processSyncingPacket(const packets::SyncingPacket& packet); /** * @brief Process the packet that has been received @@ -149,21 +151,6 @@ class Client : public service::mdnsBrowser { */ void processReadyRead(); - /** - * @brief Process the ssl error - */ - void processSslError(const QList& errors); - - /** - * @brief Handle client connected - */ - void handleConnected(); - - /** - * @brief Handle client disconnected - */ - void handleDisconnected(); - public: /** @@ -181,6 +168,16 @@ class Client : public service::mdnsBrowser { */ ~Client() override = default; + /** + * @brief Set SSL configuration + */ + void setSslConfiguration(QSslConfiguration config); + + /** + * @brief get the SSL configuration + */ + QSslConfiguration getSslConfiguration() const; + /** * @brief Send the items to the server to sync the * clipboard data @@ -194,7 +191,7 @@ class Client : public service::mdnsBrowser { * * @return QList> List of servers */ - QList> getServerList() const; + QList getServerList() const; /** * @brief Connect to the server with the given host and port @@ -203,53 +200,36 @@ class Client : public service::mdnsBrowser { * @param host Host address * @param port Port number */ - void connectToServer(QPair client); - - /** - * @brief Get the Connection Host and Port object - * @return QPair - */ - QPair getConnectedServer() const; + void connectToServerSecured(types::device::Device server); /** - * @brief Get the Connection Host and Port object Or Empty - * @return QPair + * @brief Connect to the server with the given host and port + * number + * + * @param host Host address + * @param port Port number */ - QPair getConnectedServerOrEmpty() const; + void connectToServer(types::device::Device client); /** - * @brief Disconnect from the server + * @brief IS connected to the server */ - void disconnectFromServer(); + bool isConnected() const; /** - * @brief Get the Authed Server object - * @return QPair + * @brief Get the Connection Host and Port object */ - QPair getAuthedServer() const; + types::device::Device getConnectedServer() const; /** - * @brief Get the Authed Server object Or Empty - * @return QPair + * @brief Disconnect from the server */ - QPair getAuthedServerOrEmpty() const; + void disconnectFromServer(); /** - * @brief Send To Connected Server - * - * @tparam Packet - * @param pack + * @brief Get the Server Certificate */ - template - void sendToConnectedServer(const Packet& pack) { - // if not connected - if (m_ssl_socket->state() != QAbstractSocket::ConnectedState) { - throw std::runtime_error("Not connected to the server"); - } - - // send the packet - sendPacket(pack); - } + QSslCertificate getConnectedServerCertificate() const; protected: // abstract functions from the base class @@ -260,7 +240,7 @@ class Client : public service::mdnsBrowser { * @param host Host address * @param port Port number */ - void onServiceAdded(QPair server) override; + void onServiceAdded(types::device::Device server) override; /** * @brief On server removed function that That Called by the @@ -268,6 +248,6 @@ class Client : public service::mdnsBrowser { * * @param server */ - void onServiceRemoved(QPair server) override; + void onServiceRemoved(types::device::Device server) override; }; } // namespace srilakshmikanthanp::clipbirdesk::network::syncing diff --git a/network/syncing/server/server.cpp b/network/syncing/server/server.cpp index 1313a10..4ad5da8 100644 --- a/network/syncing/server/server.cpp +++ b/network/syncing/server/server.cpp @@ -6,6 +6,138 @@ #include "server.hpp" namespace srilakshmikanthanp::clipbirdesk::network::syncing { +/** + * @brief Process the connections that are pending + */ +void Server::processPendingConnections() { + while (m_server->hasPendingConnections()) { + // Get the client that has been connected + auto client = qobject_cast(m_server->nextPendingConnection()); + + // Connect the disconnected signal to the processDisconnection function + const auto signal_d = &QSslSocket::disconnected; + const auto slot_d = &Server::processDisconnection; + QObject::connect(client, signal_d, this, slot_d); + + // Connect the readyRead signal to the processReadyRead function + const auto signal_r = &QSslSocket::readyRead; + const auto slot_r = &Server::processReadyRead; + QObject::connect(client, signal_r, this, slot_r); + + // get the device info of the client + auto addr = client->peerAddress(); + auto port = client->peerPort(); + auto cert = client->peerCertificate(); + auto name = cert.subjectInfo(QSslCertificate::CommonName).constFirst(); + + // create device object of the client + auto device = types::device::Device { + addr, port, name + }; + + // add to un authenticated list + m_unauthenticatedClients.append(client); + + // Add the client to the list of authenticated clients + if (!client->sslHandshakeErrors().isEmpty()) { + emit OnAuthRequest(device); + } else { + this->authSuccess(device); + } + } +} + +/** + * @brief Process SSL Errors + */ +void Server::processSslErrors(QSslSocket *socket, const QList& errors) { + // List of ignored SslErrors + QList ignoredErrors; + + // Ignore the errors + ignoredErrors.append(QSslError::SelfSignedCertificate); + ignoredErrors.append(QSslError::CertificateUntrusted); + + // make copy of errors + auto errorsCopy = errors; + + // remove all the ignored errors + auto itr = std::remove_if(errorsCopy.begin(), errorsCopy.end(), [&](auto error) { + return ignoredErrors.contains(error.error()); + }); + + // remove the ignored errors + errorsCopy.erase(itr, errorsCopy.end()); + + // log the errors + for (auto error : errorsCopy) { + qWarning() << (LOG(std::to_string(error.error()))); + } + + // if errorsCopy is not empty + if (!errorsCopy.isEmpty()) { + return socket->abort(); + } + + // peer info of the client that is connected + auto addr = socket->peerAddress(); + auto port = socket->peerPort(); + auto cert = socket->peerCertificate(); + auto name = cert.subjectInfo(QSslCertificate::CommonName); + + // if certificate is null or common name is empty + if (cert.isNull() || name.isEmpty()) { + return socket->abort(); + } + + // create the device + auto device = types::device::Device { + addr, port, name.constFirst() + }; + + // just ignore wait for user + socket->ignoreSslErrors(); +} + +/** + * @brief Process the disconnection from the client + */ +void Server::processDisconnection() { + // Get the client that was disconnected + auto client = qobject_cast(sender()); + + // if in unauthenticated list + if (m_unauthenticatedClients.removeOne(client)) { + return; + } + + // if not in authenticated list + if (!m_clients.contains(client)) return; + + // peer information of the client + auto addr = client->peerAddress(); + auto port = client->peerPort(); + auto cert = client->peerCertificate(); + auto name = cert.subjectInfo(QSslCertificate::CommonName).constFirst(); + + // create the device + auto device = types::device::Device { + addr, port, name + }; + + // Remove the client from the list of clients + m_clients.removeOne(client); + + // Notify the listeners that the client is disconnected + emit OnCLientStateChanged(device, false); + + // get connected client list + auto list = getConnectedClientsList(); + + // Notify the listeners that the client list is changed + emit OnClientListChanged(list); +} + /** * @brief Process the SyncingPacket from the client * @@ -22,38 +154,12 @@ void Server::processSyncingPacket(const packets::SyncingPacket &packet) { // Notify the listeners to sync the data emit OnSyncRequest(items); -} - -/** - * @brief Process the connections that are pending - */ -void Server::processConnections() { - while (m_ssl_server->hasPendingConnections()) { - // Get the client that has been connected - auto client_tcp = (m_ssl_server->nextPendingConnection()); - auto client_tls = qobject_cast(client_tcp); - - // If the client is not SSL client then disconnect - if (client_tls == nullptr) { - const auto packType = packets::InvalidRequest::PacketType::RequestFailed; - const auto code = types::enums::ErrorCode::SSLError; - const auto message = "Client is not SSL client"; - - using utility::functions::createPacket; - this->sendPacket(client_tls, createPacket({packType, code, message})); - client_tcp->disconnectFromHost(); - } else { - // add the client to unauthenticated list - m_un_authed_clients.append(client_tls); - // get the peer address ans port - const auto peerAddress = client_tls->peerAddress(); - const auto peerPort = client_tls->peerPort(); + // get the Sender of the packet + auto client = qobject_cast(sender()); - // emit the signal that new host is connected - emit OnNewHostConnected({peerAddress, peerPort}); - } - } + // send the response packet + this->sendPacket(packet, client); } /** @@ -64,6 +170,9 @@ void Server::processReadyRead() { // Get the client that was ready to read auto client = qobject_cast(sender()); + // if not authenticated then return + if (!m_clients.contains(client)) return; + // using the fromQByteArray from namespace using utility::functions::createPacket; using utility::functions::fromQByteArray; @@ -115,10 +224,12 @@ void Server::processReadyRead() { return; } + // if not in authenticated client + if (!this->m_clients.contains(client)) return; + // Deserialize the data to SyncingPacket try { this->processSyncingPacket(fromQByteArray(data)); - this->sendPacket(fromQByteArray(data), client); return; } catch (const types::except::MalformedPacket &e) { const auto type = packets::InvalidRequest::PacketType::RequestFailed; @@ -141,33 +252,6 @@ void Server::processReadyRead() { this->sendPacket(client, createPacket({type, code, msg})); } -/** - * @brief Process the disconnection from the client - */ -void Server::processDisconnection() { - // Get the client that was disconnected - auto client = qobject_cast(sender()); - - // convert to QPair - auto info = QPair(client->peerAddress(), client->peerPort()); - - // Notify the listeners that the client is disconnected - emit OnCLientStateChanged(info, false); - - // Remove the client from the list of clients - m_authed_clients.removeOne(client); - - // Notify the listeners that the client list is changed - emit OnClientListChanged(getConnectedClientsList()); -} - -/** - * @brief Called when the service is registered - */ -void Server::OnServiceRegistered() { - emit OnServerStateChanged(true); -} - /** * @brief Construct a new Syncing Server object and * bind to any available port and any available @@ -181,13 +265,19 @@ Server::Server(QObject *parent) : service::mdnsRegister(parent) { // process the connections when the socket is ready // to read so the listener can be notified const auto signal_c = &QSslServer::pendingConnectionAvailable; - const auto slot_c = &Server::processConnections; - QObject::connect(m_ssl_server, signal_c, this, slot_c); + const auto slot_c = &Server::processPendingConnections; + QObject::connect(m_server, signal_c, this, slot_c); // Notify the listeners that the server is started const auto signal = &service::mdnsRegister::OnServiceRegistered; - const auto slot = &Server::OnServiceRegistered; + const auto slot = [=] { emit OnServerStateChanged(true); }; QObject::connect(this, signal, this, slot); + + // Connect the socket to the callback function that + // process the SSL errors + const auto signal_e = &QSslServer::sslErrors; + const auto slot_e = &Server::processSslErrors; + QObject::connect(m_server, signal_e, this, slot_e); } /** @@ -205,11 +295,23 @@ void Server::syncItems(QVector> items) { * * @return QList List of clients */ -QList> Server::getConnectedClientsList() const { - QList> list; - for (auto client : m_authed_clients) { - list.append({client->peerAddress(), client->peerPort()}); +QList Server::getConnectedClientsList() const { + // List of clients that are connected + QList list; + + // iterate through the list of clients + for (auto c : m_clients) { + // Peer info of the client that is connected + auto addr = c->peerAddress(); + auto port = c->peerPort(); + auto cert = c->peerCertificate(); + auto name = cert.subjectInfo(QSslCertificate::CommonName).constFirst(); + + // add the device to the list + list.append({addr, port, name}); } + + // return the list return list; } @@ -219,14 +321,14 @@ QList> Server::getConnectedClientsList() const { * * @param client Client to disconnect */ -void Server::disconnectClient(QPair client) { +void Server::disconnectClient(types::device::Device client) { // matcher lambda function to find the client const auto matcher = [&client](QSslSocket *c) { - return (c->peerAddress() == client.first) && (c->peerPort() == client.second); + return (c->peerAddress() == client.ip) && (c->peerPort() == client.port); }; // find the client from the list of clients - for (auto c : m_authed_clients) { + for (auto c : m_clients) { if (matcher(c)) { return c->disconnectFromHost(); } @@ -237,16 +339,23 @@ void Server::disconnectClient(QPair client) { * @brief Disconnect the all the clients from the server */ void Server::disconnectAllClients() { - for (auto client : m_authed_clients) client->disconnectFromHost(); + for (auto client : m_clients) client->disconnectFromHost(); } /** * @brief Get the Server QHostAddress & Port * - * @return QPair + * @return types::device::Device */ -QPair Server::getServerInfo() const { - return {m_ssl_server->serverAddress(), m_ssl_server->serverPort()}; +types::device::Device Server::getServerInfo() const { + // server information + auto addr = m_server->serverAddress(); + auto port = m_server->serverPort(); + auto cert = m_server->sslConfiguration().localCertificate(); + auto name = cert.subjectInfo(QSslCertificate::CommonName).constFirst(); + + // Return info + return { addr, port, name }; } /** @@ -254,8 +363,8 @@ QPair Server::getServerInfo() const { * * @param config SSL Configuration */ -void Server::setSSLConfiguration(QSslConfiguration config) { - m_ssl_server->setSslConfiguration(config); +void Server::setSslConfiguration(QSslConfiguration config) { + m_server->setSslConfiguration(config); } /** @@ -264,7 +373,65 @@ void Server::setSSLConfiguration(QSslConfiguration config) { * @return QSslConfiguration */ QSslConfiguration Server::getSSLConfiguration() const { - return m_ssl_server->sslConfiguration(); + return m_server->sslConfiguration(); +} + +/** + * @brief Start the server + */ +void Server::startServer() { + // check if the SSL configuration is set + if (m_server->sslConfiguration().isNull()) { + throw std::runtime_error("SSL Configuration is not set"); + } + + // start the server + if (!m_server->listen()) { + throw std::runtime_error("Failed to start the server"); + } + + // log port + qInfo() << (LOG("Server started at port: " + std::to_string(m_server->serverPort()))); + + // start the discovery server + this->registerServiceAsync(); +} + +/** + * @brief Stop the server + */ +void Server::stopServer() { + // stop the discovery server + this->unregisterService(); + + // disconnect all the clients + this->disconnectAllClients(); + + // stop the server + m_server->close(); + + // Notify the listeners + emit OnServerStateChanged(false); +} + +/** + * @brief Get the Dei=vice Certificate + */ +QSslCertificate Server::getClientCert(types::device::Device device) const { + // matcher lambda function to find the client + const auto matcher = [&device](QSslSocket *c) { + return (c->peerAddress() == device.ip) && (c->peerPort() == device.port); + }; + + // find the client from the list of clients + for (auto c : m_clients) { + if (matcher(c)) { + return c->peerCertificate(); + } + } + + // if not found + throw std::runtime_error("Client not found"); } /** @@ -272,54 +439,33 @@ QSslConfiguration Server::getSSLConfiguration() const { * * @param client the client that is currently processed */ -void Server::authSuccess(const QPair &client) { +void Server::authSuccess(types::device::Device device) { // Matcher Lambda Function to find the client - const auto matcher = [&client](QSslSocket *c) { - return (c->peerAddress() == client.first) && (c->peerPort() == client.second); + const auto matcher = [&device](QSslSocket *c) { + return (c->peerAddress() == device.ip) && (c->peerPort() == device.port); }; // Get the iterator to the start and end of the list - auto start = m_un_authed_clients.begin(); - auto end = m_un_authed_clients.end(); + auto start = m_unauthenticatedClients.begin(); + auto end = m_unauthenticatedClients.end(); // Get the client from the unauthenticated list and remove it auto client_itr = std::find_if(start, end, matcher); // If the client is not found then return from the function - if (client_itr == m_un_authed_clients.end()) return; + if (client_itr == m_unauthenticatedClients.end()) return; // Get the client from the iterator auto client_tls = *client_itr; // Remove the client from the unauthenticated list - m_un_authed_clients.erase(client_itr); - - // Connect the client to the callback function that process - // the disconnection when the client is disconnected - // so the listener can be notified - const auto signal_d = &QSslSocket::disconnected; - const auto slot_d = &Server::processDisconnection; - QObject::connect(client_tls, signal_d, this, slot_d); - - // Connect the socket to the callback function that - // process the ready read when the socket is ready - // to read so the listener can be notified - const auto signal_r = &QSslSocket::readyRead; - const auto slot_r = &Server::processReadyRead; - QObject::connect(client_tls, signal_r, this, slot_r); - - // Peer info - const auto peerAddress = client_tls->peerAddress(); - const auto peerPort = client_tls->peerPort(); + m_unauthenticatedClients.erase(client_itr); - // convert to QPair - auto client_info = QPair(peerAddress, peerPort); + // Add the client to the list of clients + m_clients.append(client_tls); // Notify the listeners that the client is connected - emit OnCLientStateChanged(client_info, true); - - // Add the client to the list of clients - m_authed_clients.append(client_tls); + emit OnCLientStateChanged(device, true); // Notify the listeners that the client list is changed emit OnClientListChanged(getConnectedClientsList()); @@ -330,7 +476,7 @@ void Server::authSuccess(const QPair &client) { // create the Authentication packet packets::Authentication packet = createPacket({ packets::Authentication::PacketType::AuthStatus, - types::enums::AuthStatus::AuthSuccess, + types::enums::AuthStatus::AuthOkay, }); // send the packet to the client @@ -342,24 +488,24 @@ void Server::authSuccess(const QPair &client) { * * @param client the client that is currently processed */ -void Server::authFailed(const QPair &client) { +void Server::authFailed(types::device::Device device) { // Matcher Lambda Function to find the client - const auto matcher = [&client](QSslSocket *c) { - return (c->peerAddress() == client.first) && (c->peerPort() == client.second); + const auto matcher = [&device](QSslSocket *c) { + return (c->peerAddress() == device.ip) && (c->peerPort() == device.port); }; // Get the iterator to the start and end of the list - auto start = m_un_authed_clients.begin(); - auto end = m_un_authed_clients.end(); + auto start = m_unauthenticatedClients.begin(); + auto end = m_unauthenticatedClients.end(); // Get the client from the unauthenticated list and remove it auto client_itr = std::find_if(start, end, matcher); // If the client is not found then return from the function - if (client_itr == m_un_authed_clients.end()) return; + if (client_itr == m_unauthenticatedClients.end()) return; // Remove the client from the unauthenticated list - m_un_authed_clients.erase(client_itr); + m_unauthenticatedClients.erase(client_itr); // using the create packet from namespace using utility::functions::createPacket; @@ -367,7 +513,7 @@ void Server::authFailed(const QPair &client) { // create the Authentication packet packets::Authentication packet = createPacket({ packets::Authentication::PacketType::AuthStatus, - types::enums::AuthStatus::AuthFailed, + types::enums::AuthStatus::AuthFail, }); // send the packet to the client @@ -375,44 +521,6 @@ void Server::authFailed(const QPair &client) { // disconnect and delete the client (*client_itr)->disconnectFromHost(); - - // delete the client after the client is disconnected - (*client_itr)->deleteLater(); -} - -/** - * @brief Start the server - */ -void Server::startServer() { - // check if the SSL configuration is set - if (m_ssl_server->sslConfiguration().isNull()) { - throw std::runtime_error("SSL Configuration is not set"); - } - - // start the server - if (!m_ssl_server->listen()) { - throw std::runtime_error("Failed to start the server"); - } - - // start the discovery server - this->registerServiceAsync(); -} - -/** - * @brief Stop the server - */ -void Server::stopServer() { - // stop the discovery server - this->unregisterService(); - - // disconnect all the clients - this->disconnectAllClients(); - - // stop the server - m_ssl_server->close(); - - // Notify the listeners - emit OnServerStateChanged(false); } /** @@ -425,6 +533,6 @@ void Server::stopServer() { * @throw Any Exception If any error occurs */ quint16 Server::getPort() const { - return m_ssl_server->serverPort(); + return m_server->serverPort(); } } // namespace srilakshmikanthanp::clipbirdesk::network::syncing diff --git a/network/syncing/server/server.hpp b/network/syncing/server/server.hpp index 9042009..b72af86 100644 --- a/network/syncing/server/server.hpp +++ b/network/syncing/server/server.hpp @@ -13,8 +13,8 @@ #include #include -#include "network/packets/authentication/authentication.hpp" -#include "network/service/index.hpp" +#include "network/service/service.hpp" +#include "types/device/device.hpp" #include "types/enums/enums.hpp" #include "utility/functions/ipconv/ipconv.hpp" #include "utility/functions/nbytes/nbytes.hpp" @@ -28,24 +28,23 @@ namespace srilakshmikanthanp::clipbirdesk::network::syncing { class Server : public service::mdnsRegister { signals: // signals /// @brief On client state changed - void OnCLientStateChanged(QPair, bool connected); - - signals: // signals for this class - /// @brief On New Host Connected - void OnNewHostConnected(QPair); + void OnCLientStateChanged(types::device::Device, bool connected); signals: // signals for this class /// @brief On Server State Changed void OnServerStateChanged(bool started); + signals: // signals for this class + /// @brief On Sync Request + void OnAuthRequest(types::device::Device client); + signals: // signals /// @brief On Sync Request void OnSyncRequest(QVector> items); signals: // signals for this class /// @brief On Sync Request - void OnClientListChanged(QList> clients); - + void OnClientListChanged(QList clients); private: // just for Qt @@ -58,14 +57,14 @@ class Server : public service::mdnsRegister { private: // members of the class - /// @brief SSL server - QSslServer* m_ssl_server = new QSslServer(this); - - /// @brief List of clients unauthenticated - QList m_un_authed_clients; + /// @brief List of clients Unauthenticated + QList m_unauthenticatedClients; /// @brief List of clients Authenticated - QList m_authed_clients; + QList m_clients; + + /// @brief SSL server + QSslServer* m_server = new QSslServer(this); private: // some typedefs @@ -91,23 +90,13 @@ class Server : public service::mdnsRegister { stream.startTransaction(); // write the data to the stream - auto wrote = 0; + auto wrote = 0L; // write the packet length while (wrote < data.size()) { - // try to write the data auto bytes = stream.writeRawData(data.data() + wrote, data.size() - wrote); - - // if no error occurred - if (bytes != -1) { - wrote += bytes; continue; - } - - // abort the transaction - stream.abortTransaction(); - - // Notifies the error occurred - qWarning() << (LOG(client->errorString().toStdString())); + wrote = wrote + bytes; + if (bytes == -1) { stream.abortTransaction(); break; } } // commit the transaction @@ -122,28 +111,20 @@ class Server : public service::mdnsRegister { */ template void sendPacket(const Packet& pack, QSslSocket* except = nullptr) { - for (auto client : m_authed_clients) { + for (auto client : m_clients) { if (client != except) sendPacket(client, pack); } } - /** - * @brief Process the SyncingPacket from the client - * - * @param packet SyncingPacket - */ - void processSyncingPacket(const packets::SyncingPacket& packet); - /** * @brief Process the connections that are pending */ - void processConnections(); + void processPendingConnections(); /** - * @brief Callback function that process the ready - * read from the client + * @brief Process SSL Errors */ - void processReadyRead(); + void processSslErrors(QSslSocket *, const QList& errors); /** * @brief Process the disconnection from the client @@ -151,9 +132,17 @@ class Server : public service::mdnsRegister { void processDisconnection(); /** - * @brief On Service Registered + * @brief Process the SyncingPacket from the client + * + * @param packet SyncingPacket */ - void OnServiceRegistered(); + void processSyncingPacket(const packets::SyncingPacket& packet); + + /** + * @brief Callback function that process the ready + * read from the client + */ + void processReadyRead(); public: // constructors and destructors @@ -181,10 +170,8 @@ class Server : public service::mdnsRegister { /** * @brief Get the Clients that are connected to the server - * - * @return QList List of clients */ - QList> getConnectedClientsList() const; + QList getConnectedClientsList() const; /** * @brief Disconnect the client from the server and delete @@ -192,7 +179,7 @@ class Server : public service::mdnsRegister { * * @param client Client to disconnect */ - void disconnectClient(QPair client); + void disconnectClient(types::device::Device client); /** * @brief Disconnect the all the clients from the server @@ -202,16 +189,17 @@ class Server : public service::mdnsRegister { /** * @brief Get the Server QHostAddress & Port * - * @return QPair + * @return types::device::Device */ - QPair getServerInfo() const; + types::device::Device getServerInfo() const; /** * @brief Set the SSL Configuration object * * @param config SSL Configuration */ - void setSSLConfiguration(QSslConfiguration config); + void setSslConfiguration(QSslConfiguration config); + /** * @brief Get the SSL Configuration object * @@ -220,58 +208,34 @@ class Server : public service::mdnsRegister { QSslConfiguration getSSLConfiguration() const; /** - * @brief The function that is called when the client is authenticated - * - * @param client the client that is currently processed + * @brief Start the server */ - void authSuccess(const QPair&); + void startServer(); /** - * @brief The function that is called when the client it not - * authenticated - * - * @param client the client that is currently processed + * @brief Stop the server */ - void authFailed(const QPair&); + void stopServer(); /** - * @brief Start the server + * @brief Get the Dei=vice Certificate */ - void startServer(); + QSslCertificate getClientCert(types::device::Device device) const; /** - * @brief Stop the server + * @brief The function that is called when the client is authenticated + * + * @param client the client that is currently processed */ - void stopServer(); + void authSuccess(types::device::Device device); /** - * @brief Send the packet to the client + * @brief The function that is called when the client it not + * authenticated * - * @param packet Packet to send - * @param QPair {QHostAddress, port} + * @param client the client that is currently processed */ - template - void sendToClient(const Packet &packet, const QPair &client) { - // Matcher Lambda Function to find the client - const auto matcher = [&client](QSslSocket *c) { - return (c->peerAddress() == client.first) && (c->peerPort() == client.second); - }; - - // Get the iterator to the start and end of the list - auto start = m_authed_clients.begin(); - auto end = m_authed_clients.end(); - - // Get the client from the unauthenticated list and remove it - auto client_itr = std::find_if(start, end, matcher); - - // If the client is not found then return from the function - if (client_itr == m_authed_clients.end()) { - throw std::runtime_error("Client not found"); - } - - // send the packet to the client - this->sendPacket(packet, (*client_itr)); - } + void authFailed(types::device::Device device); protected: // override functions from the base class diff --git a/store/storage.cpp b/store/storage.cpp index fe37953..a33a0b2 100644 --- a/store/storage.cpp +++ b/store/storage.cpp @@ -16,89 +16,139 @@ Storage::Storage(QObject *parent) : QObject(parent) { } /** - * @brief Store Client hostname and JWT token + * @brief Store Client name and JWT token * - * @param hostname + * @param name * @param token */ -void Storage::setClientCert(const QString &hostname, const QString &token) { +void Storage::setClientCert(const QString &name, const QByteArray &token) { settings->beginGroup(clientGroup); - settings->setValue(hostname, token); + settings->setValue(name, token); settings->endGroup(); } /** - * @brief has the cert for the hostname + * @brief has the cert for the name */ -bool Storage::hasClientCert(const QString &hostname) { +bool Storage::hasClientCert(const QString &name) { settings->beginGroup(clientGroup); - auto token = settings->value(hostname); + auto token = settings->value(name); settings->endGroup(); return !token.isNull(); } /** - * @brief Get the JWT token for the hostname + * @brief Get the JWT token for the name * - * @param hostname + * @param name * @return QString * - * @throw std::invalid_argument if hostname not found + * @throw std::invalid_argument if name not found */ -QString Storage::getClientCert(const QString &hostname) { +QByteArray Storage::getClientCert(const QString &name) { settings->beginGroup(clientGroup); - auto token = settings->value(hostname); + auto token = settings->value(name); settings->endGroup(); if (token.isNull()) { - throw std::invalid_argument("hostname not found"); + throw std::invalid_argument("name not found"); } - return token.toString(); + return token.toByteArray(); } /** - * @brief Store the server hostname and JWT token + * @brief Get All Client Certificates + */ +QList Storage::getAllClientCert() { + QList list; + + settings->beginGroup(clientGroup); + for (auto &name : settings->childKeys()) { + auto cert = settings->value(name); + list.append(cert.toByteArray()); + } + settings->endGroup(); + + return list; +} + +/** + * @brief Store the server name and JWT token * - * @param hostname + * @param name * @param token */ -void Storage::setServerCert(const QString &hostname, const QString &token) { +void Storage::setServerCert(const QString &name, const QByteArray &token) { settings->beginGroup(serverGroup); - settings->setValue(hostname, token); + settings->setValue(name, token); settings->endGroup(); } /** - * @brief has the cert for the hostname + * @brief has the cert for the name */ -bool Storage::hasServerCert(const QString &hostname) { +bool Storage::hasServerCert(const QString &name) { settings->beginGroup(serverGroup); - auto token = settings->value(hostname); + auto token = settings->value(name); settings->endGroup(); return !token.isNull(); } /** - * @brief Get the JWT token for the hostname + * @brief Clear the client cert + */ +void Storage::clearClientCert(const QString &name) { + settings->beginGroup(clientGroup); + settings->remove(name); + settings->endGroup(); +} + +/** + * @brief Clear the client cert + */ +void Storage::clearAllClientCert() { + settings->beginGroup(clientGroup); + settings->remove(""); + settings->endGroup(); +} + +/** + * @brief Get the JWT token for the name * - * @param hostname + * @param name * @return QString * - * @throw std::invalid_argument if hostname not found + * @throw std::invalid_argument if name not found */ -QString Storage::getServerCert(const QString &hostname) { +QByteArray Storage::getServerCert(const QString &name) { settings->beginGroup(serverGroup); - auto token = settings->value(hostname); + auto token = settings->value(name); settings->endGroup(); if (token.isNull()) { - throw std::invalid_argument("hostname not found"); + throw std::invalid_argument("name not found"); } - return token.toString(); + return token.toByteArray(); +} + +/** + * @brief Get All Server Certificates + */ +QList Storage::getAllServerCert() { + QList list; + + settings->beginGroup(serverGroup); + for (auto &name : settings->childKeys()) { + auto cert = settings->value(name); + list.append(cert.toByteArray()); + } + settings->endGroup(); + + return list; } /** @@ -112,6 +162,24 @@ void Storage::setHostIsServer(bool isServer) { settings->endGroup(); } +/** + * @brief Clear the server cert + */ +void Storage::clearServerCert(const QString &name) { + settings->beginGroup(serverGroup); + settings->remove(name); + settings->endGroup(); +} + +/** + * @brief Clear the server cert + */ +void Storage::clearAllServerCert() { + settings->beginGroup(serverGroup); + settings->remove(""); + settings->endGroup(); +} + /** * @brief Get the current state of the server or client * @@ -126,7 +194,15 @@ bool Storage::getHostIsServer() { if (isServer.isNull()) { return false; } - + const auto s = isServer.toBool(); return isServer.toBool(); } + +/** + * @brief Instance of the storage + */ +Storage& Storage::instance() { + static Storage instance; + return instance; +} } // namespace srilakshmikanthanp::clipbirdesk::storage diff --git a/store/storage.hpp b/store/storage.hpp index 3fc892e..9d0bf3c 100644 --- a/store/storage.hpp +++ b/store/storage.hpp @@ -1,3 +1,5 @@ +#pragma once // Header guard see https://en.wikipedia.org/wiki/Include_guard + // Copyright (c) 2023 Sri Lakshmi Kanthan P // // This software is released under the MIT License. @@ -24,47 +26,83 @@ class Storage : public QObject { Q_OBJECT - public: // constructor + private: // constructor /** * @brief Construct a new SQLStore object */ Storage(QObject *parent = nullptr); + private: // constructor + + Q_DISABLE_COPY_MOVE(Storage) + + public: // methods + /** * @brief Destroy the SQLStore object */ virtual ~Storage() = default; /** - * @brief Store Client hostname and JWT cert + * @brief Store Client name and JWT cert + */ + void setClientCert(const QString &name, const QByteArray &cert); + + /** + * @brief has the cert for the name + */ + bool hasClientCert(const QString &name); + + /** + * @brief Clear the client cert + */ + void clearClientCert(const QString &name); + + /** + * @brief Clear the client cert */ - void setClientCert(const QString &hostname, const QString &cert); + void clearAllClientCert(); /** - * @brief has the cert for the hostname + * @brief Get the JWT cert for the name */ - bool hasClientCert(const QString &hostname); + QByteArray getClientCert(const QString &name); /** - * @brief Get the JWT cert for the hostname + * @brief Get All Client Certificates */ - QString getClientCert(const QString &hostname); + QList getAllClientCert(); /** - * @brief Store the server hostname and JWT cert + * @brief Store the server name and JWT cert */ - void setServerCert(const QString &hostname, const QString &cert); + void setServerCert(const QString &name, const QByteArray &cert); /** - * @brief has the cert for the hostname + * @brief has the cert for the name */ - bool hasServerCert(const QString &hostname); + bool hasServerCert(const QString &name); /** - * @brief Get the JWT cert for the hostname + * @brief Clear the server cert */ - QString getServerCert(const QString &hostname); + void clearServerCert(const QString &name); + + /** + * @brief Clear the server cert + */ + void clearAllServerCert(); + + /** + * @brief Get the JWT cert for the name + */ + QByteArray getServerCert(const QString &name); + + /** + * @brief Get All Server Certificates + */ + QList getAllServerCert(); /** * @brief Set the current state of the server or client @@ -75,6 +113,11 @@ class Storage : public QObject { * @brief Get the current state of the server or client */ bool getHostIsServer(); + + /** + * @brief Instance of the storage + */ + static Storage& instance(); }; } // namespace srilakshmikanthanp::clipbirdesk::storage diff --git a/tests/network/packets/authentication.hpp b/tests/network/packets/authentication.hpp deleted file mode 100644 index f989fbf..0000000 --- a/tests/network/packets/authentication.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once // Header guard see https://en.wikipedia.org/wiki/Include_guard - -// Copyright (c) 2023 Sri Lakshmi Kanthan P -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -// Google test header files -#include - -// Qt header files -#include - -// Local header files -#include "network/packets/authentication/authentication.hpp" -#include "types/enums/enums.hpp" -#include "utility/functions/nbytes/nbytes.hpp" -#include "utility/functions/packet/packet.hpp" - -/** - * @brief testing the AuthenticationPacket - */ -TEST(AuthenticationTest, TestingAuthentication) { - // using the AuthenticationPacket - using srilakshmikanthanp::clipbirdesk::network::packets::Authentication; - - // using the ErrorCode - using srilakshmikanthanp::clipbirdesk::types::enums::AuthStatus; - - // using functions namespace - using namespace srilakshmikanthanp::clipbirdesk::utility::functions; - - // creating the packet - Authentication packet_send, packet_recv; - - // constant values - const auto packetType = Authentication::PacketType::AuthStatus; - - // setting the packet type - packet_send = createPacket({ packetType, AuthStatus::AuthSuccess }); - - // load the packet from network byte order - packet_recv = fromQByteArray(toQByteArray(packet_send)); - - // check the packet type - EXPECT_EQ(packet_recv.getPacketType(), packetType); - - // check the packet length - EXPECT_EQ(packet_recv.getPacketLength(), packet_send.size()); - - // check the status code - EXPECT_EQ(packet_recv.getAuthStatus(), AuthStatus::AuthSuccess); -} diff --git a/tests/test.cpp b/tests/test.cpp index 9376c3b..9dca502 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -7,7 +7,6 @@ #include // Local header files -#include "tests/network/packets/authentication.hpp" #include "tests/network/packets/invalidrequest.hpp" #include "tests/network/packets/syncingpacket.hpp" diff --git a/types/device/device.cpp b/types/device/device.cpp new file mode 100644 index 0000000..43e406c --- /dev/null +++ b/types/device/device.cpp @@ -0,0 +1,22 @@ +// Copyright (c) 2023 Sri Lakshmi Kanthan P +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#include "device.hpp" + +namespace srilakshmikanthanp::clipbirdesk::types::device { +/** + * @brief operator overloading for == + */ +bool Device::operator==(const Device& other) const { + return ip == other.ip && port == other.port && name == other.name; +} + +/** + * @brief operator overloading for != + */ +bool Device::operator!=(const Device& other) const { + return !(*this == other); +} +} // namespace srilakshmikanthanp::types::device diff --git a/types/device/device.hpp b/types/device/device.hpp new file mode 100644 index 0000000..40a7333 --- /dev/null +++ b/types/device/device.hpp @@ -0,0 +1,25 @@ +#pragma once // Header guard see https://en.wikipedia.org/wiki/Include_guard + +// Copyright (c) 2023 Sri Lakshmi Kanthan P +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#include +#include + +namespace srilakshmikanthanp::clipbirdesk::types::device { +/** + * @brief Device Class That Represents Device + */ +struct Device { + // operator overloading for comparison + bool operator==(const Device& other) const; + bool operator!=(const Device& other) const; + + // public members + QHostAddress ip; + quint16 port; + QString name; +}; +} // namespace srilakshmikanthanp::types::device diff --git a/types/enums/enums.hpp b/types/enums/enums.hpp index 8b0c6a1..aec36d4 100644 --- a/types/enums/enums.hpp +++ b/types/enums/enums.hpp @@ -10,20 +10,25 @@ namespace srilakshmikanthanp::clipbirdesk::types::enums { /// @brief IP type used in the discovery packet enum class IPType : quint8 { - IPv4 = 0, - IPv6 = 1, + IPv4 = 0x00, + IPv6 = 0x01, }; /// @brief Allowed Error Codes enum ErrorCode : quint8 { CodingError = 0x01, - SSLError = 0x02, - InvalidPacket = 0x03, + InvalidPacket = 0x02, +}; + +/// @brief Allowed Authentication Types +enum AuthType : quint8 { + AuthReq = 0x00, + AuthRes = 0x01, }; /// @brief Allowed Authentication Status enum AuthStatus : quint8 { - AuthFailed = 0x00, - AuthSuccess = 0x01, + AuthOkay = 0x00, + AuthFail = 0x01, }; } // namespace srilakshmikanthanp::clipbirdesk::types::enums diff --git a/ui/gui/components/device/device.cpp b/ui/gui/components/device/device.cpp index 1d8574e..5727ff7 100644 --- a/ui/gui/components/device/device.cpp +++ b/ui/gui/components/device/device.cpp @@ -6,19 +6,6 @@ #include "device.hpp" namespace srilakshmikanthanp::clipbirdesk::ui::gui::components { -/** - * @brief Called when host is resolved - * - * @param info - */ -void Device::onHostResolved(const QHostInfo &info) { - if (info.error() != QHostInfo::NoError) { - this->hostName->setText("Unknown"); - } - - this->hostName->setText(info.hostName()); -} - /** * @brief Construct a new Device object * with parent as QWidget @@ -27,7 +14,7 @@ void Device::onHostResolved(const QHostInfo &info) { Device::Device(QWidget *parent) : QWidget(parent) { // connect the button signal to this signal QObject::connect(actBtn, &QPushButton::clicked, [this]() { - emit onAction({address, port, action}); + emit onAction({device, action}); }); // vertical alignment of the labels as center @@ -51,17 +38,14 @@ Device::Device(QWidget *parent) : QWidget(parent) { /** * @brief Set the Device - * - * @param QPair */ void Device::setHost(Device::Value host) { // set the address and port - this->address = std::get<0>(host); - this->port = std::get<1>(host); - this->action = std::get<2>(host); + this->device = std::get<0>(host); + this->action = std::get<1>(host); // set the host name - this->hostName->setText("Resolving..."); + this->hostName->setText(device.name); // action text to set const auto a = action == Action::Disconnect ? disconnect : connect; @@ -71,17 +55,12 @@ void Device::setHost(Device::Value host) { // set the object name to identify this->actBtn->setObjectName(a); - - // lookup the host name and change - QHostInfo::lookupHost(address.toString(), this, &Device::onHostResolved); } /** * @brief Get the Device - * - * @return QPair */ Device::Value Device::getHost() const { - return std::make_tuple(address, port, action); + return std::make_tuple(device, action); } } // namespace srilakshmikanthanp::clipbirdesk::ui::gui::components diff --git a/ui/gui/components/device/device.hpp b/ui/gui/components/device/device.hpp index 3538c2c..e3bed3f 100644 --- a/ui/gui/components/device/device.hpp +++ b/ui/gui/components/device/device.hpp @@ -17,10 +17,12 @@ #include #include - // C++ Headers #include +// local headers +#include "types/device/device.hpp" + namespace srilakshmikanthanp::clipbirdesk::ui::gui::components { class Device : public QWidget { private: // constant values for text @@ -33,19 +35,14 @@ class Device : public QWidget { public: // typedefs - using Value = std::tuple; + using Value = std::tuple; signals: // Signals - void onAction(std::tuple); - - private: // Private Functions - - void onHostResolved(const QHostInfo &info); + void onAction(std::tuple); private: // Member variable - QHostAddress address; - quint16 port; + types::device::Device device; Action action; private: // Member variable @@ -68,15 +65,11 @@ class Device : public QWidget { /** * @brief Set the Host - * - * @param QPair */ void setHost(Value host); /** * @brief Get the Host - * - * @return QPair */ Value getHost() const; diff --git a/ui/gui/content/content.cpp b/ui/gui/content/content.cpp index a6e17ab..6b59f90 100644 --- a/ui/gui/content/content.cpp +++ b/ui/gui/content/content.cpp @@ -12,16 +12,16 @@ namespace srilakshmikanthanp::clipbirdesk::ui::gui { * @brief handle the host action from the window */ void Content::handleHostAction(Tabs t, components::Device::Value h) { - if (t == Tabs::Server && std::get<2>(h) == Action::Disconnect) { - controller->disconnectClient({std::get<0>(h), std::get<1>(h)}); + if (t == Tabs::Server && std::get<1>(h) == Action::Disconnect) { + controller->disconnectClient(std::get<0>(h)); } - if (t == Tabs::Client && std::get<2>(h) == Action::Connect) { - controller->connectToServer({std::get<0>(h), std::get<1>(h)}); + if (t == Tabs::Client && std::get<1>(h) == Action::Connect) { + controller->connectToServer(std::get<0>(h)); } - if (t == Tabs::Client && std::get<2>(h) == Action::Disconnect) { - controller->disconnectFromServer({std::get<0>(h), std::get<1>(h)}); + if (t == Tabs::Client && std::get<1>(h) == Action::Disconnect) { + controller->disconnectFromServer(std::get<0>(h)); } } @@ -66,13 +66,13 @@ void Content::handleTabChangeForServer(Tabs tab) { /** * @brief Handle the Client List Item Clicked */ -void Content::handleClientListChange(QList> clients) { +void Content::handleClientListChange(QList clients) { // Create a list of tuple with Action QList clients_m; // Add the clients to the list for (auto c : clients) { - clients_m.append({c.first, c.second, Action::Disconnect}); + clients_m.append({c, Action::Disconnect}); } // set the client list to the window @@ -87,18 +87,10 @@ void Content::handleClientListChange(QList> clients */ void Content::handleServerStateChange(bool isStarted) { // infer the status from the server state - auto groupName = QString("-"); + auto groupName = isStarted ? controller->getServerInfo().name : QString("-"); auto status_m = isStarted ? Status::Active : Status::Inactive; auto clients = controller->getConnectedClientsList(); - // if the server is started - if (isStarted) { - auto info = controller->getServerInfo(); - auto ip = info.first.toString(); - auto port = info.second; - groupName = QHostInfo::fromName(ip).hostName(); - } - // set the server status this->setGroupName(s_groupNameKey, groupName); this->setStatus(s_statusKey, status_m); @@ -109,7 +101,7 @@ void Content::handleServerStateChange(bool isStarted) { // Add the clients to the list for (auto c : clients) { - clients_m.append({c.first, c.second, Action::Disconnect}); + clients_m.append({c, Action::Disconnect}); } // set the client list to the window @@ -121,60 +113,54 @@ void Content::handleServerStateChange(bool isStarted) { * * @param client */ -void Content::handleNewHostConnected(const QPair& client) { - // callback for host info looked up - const auto onHostInfoFound = [=](const QHostInfo& info) -> void { - // get the message to show - // clang-format off - auto message = QString( - "A New client Attempting to connect\n" - "Host: %1\n" - "Accept the connection?" - ).arg( - info.hostName() - ); - // clang-format on - - // get the user input - auto dialog = new QMessageBox(); - - // icon for the dialog - auto icon = QIcon(constants::getAppLogo().c_str()); - - // set the icon - dialog->setWindowIcon(icon); - - // set the title - dialog->setWindowTitle("Clipbird"); - - // set the message - dialog->setText(message); - - // set delete on close - dialog->setAttribute(Qt::WA_DeleteOnClose); - - // set the buttons - dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No); - - // set the default button - dialog->setDefaultButton(QMessageBox::No); - - // show the dialog - dialog->show(); - - // connect the dialog to window AuthSuccess signal - const auto signal_s = &QMessageBox::accepted; - const auto slot_s = [=] { controller->authSuccess(client); }; - connect(dialog, signal_s, slot_s); - - // connect the dialog to window AuthFail signal - const auto signal_f = &QMessageBox::rejected; - const auto slot_f = [=] { controller->authFailed(client); }; - connect(dialog, signal_f, slot_f); - }; +void Content::handleAuthRequest(const types::device::Device& client) { + // get the message to show + // clang-format off + auto message = QString( + "A New client Attempting to connect\n" + "Host: %1\n" + "Accept the connection?" + ).arg( + client.name + ); + // clang-format on + + // get the user input + auto dialog = new QMessageBox(); + + // icon for the dialog + auto icon = QIcon(constants::getAppLogo().c_str()); + + // set the icon + dialog->setWindowIcon(icon); + + // set the title + dialog->setWindowTitle("Clipbird"); + + // set the message + dialog->setText(message); + + // set delete on close + dialog->setAttribute(Qt::WA_DeleteOnClose); - // lookup the host info - QHostInfo::lookupHost(client.first.toString(), onHostInfoFound); + // set the buttons + dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No); + + // set the default button + dialog->setDefaultButton(QMessageBox::No); + + // show the dialog + dialog->show(); + + // connect the dialog to window AuthSuccess signal + const auto signal_s = &QMessageBox::accepted; + const auto slot_s = [=] { controller->authSuccess(client); }; + connect(dialog, signal_s, slot_s); + + // connect the dialog to window AuthFail signal + const auto signal_f = &QMessageBox::rejected; + const auto slot_f = [=] { controller->authFailed(client); }; + connect(dialog, signal_f, slot_f); } //----------------------------- slots for Client --------------------------// @@ -182,21 +168,29 @@ void Content::handleNewHostConnected(const QPair& client) /** * @brief Handle the Server List Item Clicked */ -void Content::handleServerListChange(QList> servers) { +void Content::handleServerListChange(QList servers) { // Create a list of tuple with Action QList servers_m; - // get the connected server - const auto server = controller->getAuthedServerOrEmpty(); - // get the action for the server - const auto getAction = [&server](const auto& s) { - return s == server ? Action::Disconnect : Action::Connect; + const auto getAction = [=](const auto& s) { + // if client is connected to server + if (!controller->isConnectedToServer()) return Action::Connect; + + // get the server + auto server = controller->getConnectedServer(); + + // infer + if (s.ip == server.ip && s.port == server.port) { + return Action::Disconnect; + } else { + return Action::Connect; + } }; // add the server to the list for (auto s : servers) { - servers_m.append({s.first, s.second, getAction(s)}); + servers_m.append({s, getAction(s)}); } // set the server list to the window @@ -207,21 +201,28 @@ void Content::handleServerListChange(QList> servers } /** - * @brief Handle the server authentication + * @brief Handle the server status change */ -void Content::handleServerAuthentication(bool isAuthed) { +void Content::handleServerStatusChanged(bool isConnected) { // infer the status from the server state - auto groupName = QString("-"); - auto status_m = isAuthed ? Status::Connected : Status::Disconnected; - auto servers = controller->getServerList(); - - // if the client is Authed - if (isAuthed) { - auto server = controller->getAuthedServer(); - auto ip = server.first.toString(); - auto port = server.second; - groupName = QHostInfo::fromName(ip).hostName(); - } + auto groupName = isConnected ? controller->getConnectedServer().name : QString("-"); + auto servers = controller->getServerList(); + auto status_m = isConnected ? Status::Connected : Status::Disconnected; + + // get the action for the server + const auto getAction = [=](const auto& s) { + if (!controller->isConnectedToServer()) return Action::Connect; + + // get the server + auto server = controller->getConnectedServer(); + + // infer + if (s.ip == server.ip && s.port == server.port) { + return Action::Disconnect; + } else { + return Action::Connect; + } + }; // set the server status this->setGroupName(c_groupNameKey, groupName); @@ -231,47 +232,9 @@ void Content::handleServerAuthentication(bool isAuthed) { // Create a list of tuple with Action QList servers_m; - // get the connected server - const auto server = controller->getAuthedServerOrEmpty(); - - // get the action for the server - const auto getAction = [&server](const auto& s) { - return s == server ? Action::Disconnect : Action::Connect; - }; - // add the server to the list for (auto s : servers) { - servers_m.append({s.first, s.second, getAction(s)}); - } - - // set the server list to the window - this->setServerList(servers_m); -} - -/** - * @brief Handle the server status change - */ -void Content::handleServerStatusChanged(bool status) { - if (status) return; // ignore the TSL connection and leave it to authentication - - // if the server is disconnected - this->setStatus(c_statusKey, Status::Disconnected); - this->setGroupName(c_groupNameKey, "-"); - - // Create a list of tuple with Action - QList servers_m; - - // get the connected server - const auto server = controller->getAuthedServerOrEmpty(); - - // get the action for the server - const auto getAction = [&server](const auto& s) { - return s == server ? Action::Disconnect : Action::Connect; - }; - - // add the server to the list - for (auto s : controller->getServerList()) { - servers_m.append({s.first, s.second, getAction(s)}); + servers_m.append({s, getAction(s)}); } // set the server list to the window @@ -292,6 +255,14 @@ void Content::onIssueClicked() { QDesktopServices::openUrl(QUrl(constants::getAppIssuePage().c_str())); } +/** + * @brief On Reset Clicked + */ +void Content::onResetClicked() { + controller->clearServerCertificates(); + controller->clearClientCertificates(); +} + /** * @brief Construct a new Content object * with parent as QWidget @@ -367,10 +338,25 @@ Content::Content(Content::ClipBird* c, QWidget* p) : QFrame(p), controller(c) { // set tooltip trayIcon->setToolTip(constants::getAppName().c_str()); - // set the signal for on new Host - const auto signal_nh = &controller::ClipBird::OnNewHostConnected; - const auto slot_nh = &Content::handleNewHostConnected; - QObject::connect(controller, signal_nh, this, slot_nh); + // set the signal for menus About click + const auto signal_ac = &ui::gui::content::TrayMenu::OnAboutClicked; + const auto slot_ac = &Content::onAboutClicked; + QObject::connect(trayMenu, signal_ac, this, slot_ac); + + // set the signal for menus Issue click + const auto signal_ic = &ui::gui::content::TrayMenu::OnIssueClicked; + const auto slot_ic = &Content::onIssueClicked; + QObject::connect(trayMenu, signal_ic, this, slot_ic); + + // set the signal for menus Reset click + const auto signal_rc = &ui::gui::content::TrayMenu::OnResetClicked; + const auto slot_rc = &Content::onResetClicked; + QObject::connect(trayMenu, signal_rc, this, slot_rc); + + // set the signal for menus Quit click + const auto signal_qc = &ui::gui::content::TrayMenu::OnExitClicked; + const auto slot_qc = [] { QApplication::quit(); }; + QObject::connect(trayMenu, signal_qc, this, slot_qc); // connect server list signal const auto signal_so = &content::DeviceList::onAction; @@ -387,11 +373,6 @@ Content::Content(Content::ClipBird* c, QWidget* p) : QFrame(p), controller(c) { const auto slot_cl = &Content::handleClientListChange; connect(controller, signal_cl, this, slot_cl); - // Connect the signal and slot for server authentication - const auto signal_sa = &ClipBird::OnServerAuthentication; - const auto slot_sa = &Content::handleServerAuthentication; - connect(controller, signal_sa, this, slot_sa); - // Connect the signal and slot for server list change const auto signal_sl = &ClipBird::OnServerListChanged; const auto slot_sl = &Content::handleServerListChange; @@ -402,6 +383,11 @@ Content::Content(Content::ClipBird* c, QWidget* p) : QFrame(p), controller(c) { const auto slot_st = &Content::handleServerStateChange; connect(controller, signal_st, this, slot_st); + // connect signal and slot for OnAuthRequest + const auto signal_or = &ClipBird::OnAuthRequest; + const auto slot_or = &Content::handleAuthRequest; + connect(controller, signal_or, this, slot_or); + // Connect the signal and slot for server status change const auto signal_ss = &ClipBird::OnServerStatusChanged; const auto slot_ss = &Content::handleServerStatusChanged; @@ -422,23 +408,12 @@ Content::Content(Content::ClipBird* c, QWidget* p) : QFrame(p), controller(c) { const auto slot_ts = &Content::handleTabChangeForServer; connect(this, signal_ts, this, slot_ts); - // set the signal for menus About click - const auto signal_ac = &ui::gui::content::TrayMenu::OnAboutClicked; - const auto slot_ac = &Content::onAboutClicked; - QObject::connect(trayMenu, signal_ac, this, slot_ac); - - // set the signal for menus Issue click - const auto signal_ic = &ui::gui::content::TrayMenu::OnIssueClicked; - const auto slot_ic = &Content::onIssueClicked; - QObject::connect(trayMenu, signal_ic, this, slot_ic); - - // set the signal for menus Quit click - const auto signal_qc = &ui::gui::content::TrayMenu::OnExitClicked; - const auto slot_qc = [] { qApp->quit(); }; - QObject::connect(trayMenu, signal_qc, this, slot_qc); - - // Set As Client - this->setTabAsClient(); + // if host is lastly server + if (controller->isLastlyHostIsServer()) { + this->onTabChanged(Tabs::Server); + } else { + this->setTabAsClient(); + } // show tray icon trayIcon->show(); diff --git a/ui/gui/content/content.hpp b/ui/gui/content/content.hpp index 0fd53aa..2bf1480 100644 --- a/ui/gui/content/content.hpp +++ b/ui/gui/content/content.hpp @@ -31,6 +31,7 @@ // project headers #include "controller/clipbird/clipbird.hpp" +#include "types/device/device.hpp" #include "ui/gui/components/device/device.hpp" #include "ui/gui/components/status/status.hpp" #include "ui/gui/content/deviceinfo/deviceinfo.hpp" @@ -87,7 +88,7 @@ class Content : public QFrame { ClipBird* controller; signals: // signals - void onHostAction(Tabs tab, std::tuple); + void onHostAction(Tabs tab, std::tuple); signals: // signals void onTabChanged(Tabs tab); // emit when tab changed @@ -113,7 +114,7 @@ class Content : public QFrame { /** * @brief handle the host action from the window */ - void handleHostAction(Tabs t, std::tuple h); + void handleHostAction(Tabs t, std::tuple h); /** * @brief On Tab Changed for Client @@ -130,7 +131,7 @@ class Content : public QFrame { /** * @brief Handle the Client List Item Clicked */ - void handleClientListChange(QList> clients); + void handleClientListChange(QList clients); /** * @brief Handle the Server State Change @@ -138,23 +139,16 @@ class Content : public QFrame { void handleServerStateChange(bool isStarted); /** - * @brief On New Host Connected - * - * @param client + * @brief Handle the Client auth Request */ - void handleNewHostConnected(const QPair &client); + void handleAuthRequest(const types::device::Device& client); //----------------------------- slots for Client --------------------------// /** * @brief Handle the Server List Item Clicked */ - void handleServerListChange(QList> servers); - - /** - * @brief Handle the Server Authentication - */ - void handleServerAuthentication(bool isAuthed); + void handleServerListChange(QList servers); /** * @brief Handle the server status change @@ -173,6 +167,11 @@ class Content : public QFrame { */ void onIssueClicked(); + /** + * @brief On Reset Clicked + */ + void onResetClicked(); + public: /** @@ -231,22 +230,22 @@ class Content : public QFrame { /** * @brief Set the Server List object */ - void setClientList(const QList>& hosts); + void setClientList(const QList>& hosts); /** * @brief Get the Server List object */ - QList> getClientList(); + QList> getClientList(); /** * @brief Add Server to the list */ - void addClient(std::tuple host); + void addClient(std::tuple host); /** * @brief Remove a Server from the list */ - void removeClient(std::tuple host); + void removeClient(std::tuple host); /** * @brief Remove all servers from the list @@ -258,22 +257,22 @@ class Content : public QFrame { /** * @brief Set the Server List object */ - void setServerList(const QList>& hosts); + void setServerList(const QList>& hosts); /** * @brief Get the Server List from the tab */ - QList> getServerList(); + QList> getServerList(); /** * @brief Add Server to the list */ - void addServer(std::tuple host); + void addServer(std::tuple host); /** * @brief Remove a Server from the list */ - void removeServer(std::tuple host); + void removeServer(std::tuple host); /** * @brief Remove all servers from the list diff --git a/ui/gui/content/traymenu/traymenu.cpp b/ui/gui/content/traymenu/traymenu.cpp index 67a6079..df0d7a8 100644 --- a/ui/gui/content/traymenu/traymenu.cpp +++ b/ui/gui/content/traymenu/traymenu.cpp @@ -14,6 +14,7 @@ namespace srilakshmikanthanp::clipbirdesk::ui::gui::content { TrayMenu::TrayMenu(QWidget* parent) : QMenu(parent) { // set the Menu Items this->addAction("About", this, &TrayMenu::OnAboutClicked); + this->addAction("Reset", this, &TrayMenu::OnResetClicked); this->addAction("Issue", this, &TrayMenu::OnIssueClicked); this->addSeparator(); this->addAction("Exit", this, &TrayMenu::OnExitClicked); diff --git a/ui/gui/content/traymenu/traymenu.hpp b/ui/gui/content/traymenu/traymenu.hpp index 5b2ccc8..fa1a96d 100644 --- a/ui/gui/content/traymenu/traymenu.hpp +++ b/ui/gui/content/traymenu/traymenu.hpp @@ -25,6 +25,9 @@ class TrayMenu : public QMenu { signals: // signals void OnIssueClicked(); + signals: // signals + void OnResetClicked(); + signals: // signals void OnExitClicked(); diff --git a/utility/functions/sslcert/sslcert.cpp b/utility/functions/sslcert/sslcert.cpp index 5edb592..653f465 100644 --- a/utility/functions/sslcert/sslcert.cpp +++ b/utility/functions/sslcert/sslcert.cpp @@ -62,7 +62,8 @@ std::shared_ptr generateRSAKey(int bits) { * @return X509* - shared pointer to certificate */ std::shared_ptr generateX509(std::shared_ptr pkey) { - // Allocate memory for the X509 structure. + // Allocate memory for the X509 structure & BIGNUM + std::unique_ptr sn(BN_new(), ::BN_free); std::shared_ptr x509(X509_new(), X509_free); // Expiry time @@ -73,6 +74,59 @@ std::shared_ptr generateX509(std::shared_ptr pkey) { throw std::runtime_error("Can't Create X509"); } + // if sn is null then throw an error + if (sn.get() == NULL) { + throw std::runtime_error("Can't Create BIGNUM"); + } + + // Set the version to 2 + if (!X509_set_version(x509.get(), 2)) { + throw std::runtime_error("Can't Set Version"); + } + + // Generate random number + if (!BN_rand(sn.get(), 160, -1, 0)) { + throw std::runtime_error("Can't Generate Random Number"); + } + + // Set the serial number + if (!BN_to_ASN1_INTEGER(sn.get(), X509_get_serialNumber(x509.get()))) { + throw std::runtime_error("Can't Set Serial Number"); + } + + // get local host name + auto cname = constants::getMDnsServiceName(); + auto org = constants::getAppOrgName(); + auto unit = constants::getAppName(); + + // get the subject name + X509_NAME *name = X509_get_subject_name(x509.get()); + + // set the common name + if(!X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (const unsigned char *)cname.data(), -1, -1, 0)) { + throw std::runtime_error("Can't Set Subject Name"); + } + + // set the organization name + if(!X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (const unsigned char *)org.c_str(), -1, -1, 0)) { + throw std::runtime_error("Can't Set Subject Name"); + } + + // set the unit name + if(!X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, (const unsigned char *)unit.c_str(), -1, -1, 0)) { + throw std::runtime_error("Can't Set Subject Name"); + } + + // try to set the subject name + if (!X509_set_subject_name(x509.get(), name)) { + throw std::runtime_error("Can't Set Subject Name"); + } + + // Set the issuer name + if (!X509_set_issuer_name(x509.get(), name)) { + throw std::runtime_error("Can't Set Issuer Name"); + } + // set Expiry date to 1 year X509_gmtime_adj(X509_get_notBefore(x509.get()), 0); X509_gmtime_adj(X509_get_notAfter(x509.get()), expiry); @@ -162,8 +216,8 @@ QSslConfiguration getQSslConfiguration(int bits) { throw std::runtime_error("Can't Create QSslConfiguration"); } - // set the protocol to TLSv1_2 - sslConfig.setProtocol(QSsl::TlsV1_2); + // set peer verify + sslConfig.setPeerVerifyMode(QSslSocket::VerifyPeer); // return the configuration return sslConfig; diff --git a/utility/functions/sslcert/sslcert.hpp b/utility/functions/sslcert/sslcert.hpp index e7c3a65..8a95eff 100644 --- a/utility/functions/sslcert/sslcert.hpp +++ b/utility/functions/sslcert/sslcert.hpp @@ -12,6 +12,7 @@ #include #include #include +#include // C++ headers #include @@ -22,6 +23,10 @@ // openssl headers #include #include +#include + +// local headers +#include "constants/constants.hpp" namespace srilakshmikanthanp::clipbirdesk::utility::functions::internal { /** diff --git a/utility/logging/logging.cpp b/utility/logging/logging.cpp index e91a393..14d64e2 100644 --- a/utility/logging/logging.cpp +++ b/utility/logging/logging.cpp @@ -24,15 +24,15 @@ void Logger::handler(QtMsgType type, const QMessageLogContext &context, const QS .arg(msg) .toLocal8Bit(); - mutex.lock(); // lock the file so another thread can't write to it - logs->write(message); // write the message - mutex.unlock(); // unlock the file so another thread can write to it + mutex.lock(); // lock the file so another thread can't write to it + (*logs) << message << Qt::endl; // write the message to the log file + mutex.unlock(); // unlock the file so another thread can write to it } /** * @brief Set the log file */ -void Logger::setLogFile(QFile *file) { +void Logger::setLogStream(QTextStream *file) { Logger::logs = file; } } // namespace srilakshmikanthanp::clipbirdesk::logging diff --git a/utility/logging/logging.hpp b/utility/logging/logging.hpp index 5e995f1..c7d185d 100644 --- a/utility/logging/logging.hpp +++ b/utility/logging/logging.hpp @@ -30,7 +30,7 @@ class Logger { }; /// @brief log file to log the messages - inline static QFile *logs = Q_NULLPTR; + inline static QTextStream *logs = Q_NULLPTR; /// @brief mutex to lock the file inline static QMutex mutex; @@ -40,11 +40,15 @@ class Logger { /** * @brief Custom Logger message handler for Qt */ - static void handler(QtMsgType type, const QMessageLogContext &context, const QString &msg); + static void handler( + QtMsgType type, // type of the message + const QMessageLogContext &context, // context of the message + const QString &msg // message + ); /** * @brief Set the log file */ - static void setLogFile(QFile *file); + static void setLogStream(QTextStream *file); }; } // namespace srilakshmikanthanp::clipbirdesk::logging