Verified Commit a4c6def7 authored by Linus Jahn's avatar Linus Jahn

Rewrite Kaidan, ClientWorker and RosterManager to QXmpp

parent 0e5a4562
......@@ -40,12 +40,10 @@ include(FeatureSummary)
kde_enable_exceptions()
# Find packages
find_package(PkgConfig REQUIRED)
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Qml Quick Svg Sql QuickControls2)
find_package(KF5Kirigami2 REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_search_module(GLOOX REQUIRED gloox)
include_directories(${GLOOX_INCLUDE_DIRS})
pkg_check_modules(QXmpp REQUIRED qxmpp>=0.9)
# Optional QWidget style integration (not on mobile)
if(NOT UBUNTU_TOUCH AND NOT ANDROID)
......@@ -125,11 +123,6 @@ add_executable(${PROJECT_NAME}
"kaidan_qml.qrc"
)
#
# Linker Flags
#
target_link_libraries(${PROJECT_NAME}
Qt5::Core
Qt5::Sql
......@@ -138,7 +131,15 @@ target_link_libraries(${PROJECT_NAME}
Qt5::Svg
Qt5::Network
${__Qt5Widgets_LIBRARIES}
${GLOOX_LIBRARIES} ${GLOOX_LDFLAGS}
${QXmpp_LIBRARIES}
)
target_include_directories(${PROJECT_NAME} PUBLIC
${QXmpp_INCLUDE_DIRS}
)
target_compile_options(${PROJECT_NAME} PUBLIC
${QXmpp_CFLAGS_OTHER}
)
if (ANDROID OR WIN32)
......@@ -152,14 +153,14 @@ endif()
if (WIN32 AND STATIC_BUILD)
pkg_check_modules(QT5ALL Qt5Gui Qt5Quick Qt5QuickControls2 Qt5Network Qt5Qml Qt5Svg Qt5Sql)
foreach(plugin ${Qt5Gui_PLUGINS} ${Qt5Network_PLUGINS} ${Qt5Qml_PLUGINS} ${Qt5Svg_PLUGINS} ${Qt5Sql_PLUGINS})
get_target_property(_loc ${plugin} LOCATION)
message("Plugin ${plugin} is at location ${_loc}")
set(plugin_libs ${plugin_libs} ${_loc})
get_target_property(_loc ${plugin} LOCATION)
message("Plugin ${plugin} is at location ${_loc}")
set(plugin_libs ${plugin_libs} ${_loc})
endforeach()
pkg_search_module(SQLITE REQUIRED sqlite3)
set(QT_QML_PATH ${_qt5Quick_install_prefix})
find_library(KIRIGAMI_PLUGIN kirigamiplugin PATHS ${CMAKE_PREFIX_PATH}/${KDE_INSTALL_QMLDIR}/org/kde/kirigami.2)
find_library(QUICK_PLUGIN qtquick2plugin PATHS ${QT_QML_PATH}/qml/QtQuick.2)
find_library(LABS_PLATFORM_PLUGIN qtlabsplatformplugin PATHS ${QT_QML_PATH}/qml/Qt/labs/platform)
......
......@@ -4,25 +4,24 @@ set(CURDIR ${CMAKE_CURRENT_LIST_DIR})
set(KAIDAN_SOURCES
${CURDIR}/main.cpp
${CURDIR}/Kaidan.cpp
${CURDIR}/ClientThread.cpp
# ${CURDIR}/ClientThread.cpp
${CURDIR}/ClientWorker.cpp
${CURDIR}/AvatarFileStorage.cpp
${CURDIR}/Database.cpp
${CURDIR}/RosterModel.cpp
${CURDIR}/RosterManager.cpp
${CURDIR}/RosterUpdater.cpp
${CURDIR}/MessageHandler.cpp
${CURDIR}/MessageSessionHandler.cpp
# ${CURDIR}/MessageHandler.cpp
# ${CURDIR}/MessageSessionHandler.cpp
${CURDIR}/MessageModel.cpp
${CURDIR}/Notifications.cpp
${CURDIR}/PresenceCache.cpp
${CURDIR}/PresenceHandler.cpp
${CURDIR}/ServiceDiscoveryManager.cpp
${CURDIR}/UploadHandler.cpp
${CURDIR}/VCardManager.cpp
${CURDIR}/XmlLogHandler.cpp
# ${CURDIR}/PresenceHandler.cpp
# ${CURDIR}/ServiceDiscoveryManager.cpp
# ${CURDIR}/UploadHandler.cpp
# ${CURDIR}/VCardManager.cpp
# ${CURDIR}/XmlLogHandler.cpp
${CURDIR}/StatusBar.cpp
${CURDIR}/QtHttpUploader.cpp
# ${CURDIR}/QtHttpUploader.cpp
# SingleApplication
${CURDIR}/singleapp/singleapplication.cpp
......@@ -32,16 +31,16 @@ set(KAIDAN_SOURCES
${CURDIR}/Enums.h
# kaidan gloox extensions (need to be merged into gloox upstream)
${CURDIR}/gloox-extensions/httpuploadmanager.cpp
${CURDIR}/gloox-extensions/httpuploadrequest.cpp
${CURDIR}/gloox-extensions/httpuploadslot.cpp
${CURDIR}/gloox-extensions/bitsofbinarydata.cpp
${CURDIR}/gloox-extensions/bitsofbinarymanager.cpp
${CURDIR}/gloox-extensions/bitsofbinarymemorycache.cpp
${CURDIR}/gloox-extensions/reference.cpp
${CURDIR}/gloox-extensions/processinghints.cpp
${CURDIR}/gloox-extensions/hash.cpp
${CURDIR}/gloox-extensions/thumb.cpp
${CURDIR}/gloox-extensions/jinglefile.cpp
${CURDIR}/gloox-extensions/sims.cpp
# ${CURDIR}/gloox-extensions/httpuploadmanager.cpp
# ${CURDIR}/gloox-extensions/httpuploadrequest.cpp
# ${CURDIR}/gloox-extensions/httpuploadslot.cpp
# ${CURDIR}/gloox-extensions/bitsofbinarydata.cpp
# ${CURDIR}/gloox-extensions/bitsofbinarymanager.cpp
# ${CURDIR}/gloox-extensions/bitsofbinarymemorycache.cpp
# ${CURDIR}/gloox-extensions/reference.cpp
# ${CURDIR}/gloox-extensions/processinghints.cpp
# ${CURDIR}/gloox-extensions/hash.cpp
# ${CURDIR}/gloox-extensions/thumb.cpp
# ${CURDIR}/gloox-extensions/jinglefile.cpp
# ${CURDIR}/gloox-extensions/sims.cpp
)
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2018 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ClientThread.h"
// Kaidan
#include "ClientWorker.h"
#include "AvatarFileStorage.h"
#include "RosterManager.h"
#include "PresenceCache.h"
#include "PresenceHandler.h"
#include "ServiceDiscoveryManager.h"
#include "Database.h"
#include "MessageSessionHandler.h"
#include "MessageHandler.h"
#include "VCardManager.h"
#include "XmlLogHandler.h"
#include "UploadHandler.h"
#include "Kaidan.h"
// Qt
#include <QDebug>
#include <QMutexLocker>
#include <QTimer>
// gloox
#include <gloox/rostermanager.h>
#include <gloox/receipt.h>
#include <gloox/forward.h>
#include <gloox/carbons.h>
#include <gloox/vcardmanager.h>
#include <gloox/vcardupdate.h>
#include <gloox/delayeddelivery.h>
#include "gloox-extensions/httpuploadrequest.h"
#include "gloox-extensions/httpuploadslot.h"
#include "gloox-extensions/reference.h"
// package fetch interval in ms
static const unsigned int KAIDAN_CLIENT_LOOP_INTERVAL = 30;
ClientThread::ClientThread(RosterModel *rosterModel, MessageModel *messageModel,
AvatarFileStorage *avatarStorage, PresenceCache *presenceCache,
Credentials creds, QSettings *settings, Kaidan *kaidan,
QGuiApplication *app, bool enableLogging, QObject *parent)
: QThread(parent), rosterModel(rosterModel), messageModel(messageModel),
avatarStorage(avatarStorage), presenceCache(presenceCache), creds(creds),
settings(settings), connState(ConnectionState::StateNone), kaidan(kaidan),
enableLogging(enableLogging)
{
// Set custom thread name
setObjectName("XmppClient");
QString jid = QString("%1/%2.%3").arg(creds.jid, creds.jidResource,
generateRandomString());
client = new GlooxClient(gloox::JID(jid.toStdString()),
creds.password.toStdString());
client->bindResource(creds.jidResource.toStdString()); // set resource / device name
client->setTls(gloox::TLSRequired); // require encryption
worker = new ClientWorker(client, this, app);
connect(this, &ClientThread::connectRequested, worker, &ClientWorker::xmppConnect);
connect(this, &ClientThread::disconnectRequested, worker, &ClientWorker::xmppDisconnect);
connect(this, &ClientThread::stopWorkTimerRequested, worker, &ClientWorker::stopWorkTimer);
// The constructor is executed in the main thread, so we still need to move
// all QObject-based objects to the client thread.
worker->moveToThread(this);
workTimer.moveToThread(this);
}
ClientThread::~ClientThread()
{
// This is being executed in the main thread, not the client thread itself
// stop client loop timer from client thread
emit stopWorkTimerRequested();
exit(); // exit event loop
wait(); // wait for run() to finish
delete worker;
delete messageSessionHandler;
if (xmlLogHandler)
delete xmlLogHandler;
}
void ClientThread::run()
{
// initialize random generator
qsrand(time(NULL));
//
// Construct client and subclasses
//
// components
messageSessionHandler = new MessageSessionHandler(client, messageModel, rosterModel);
vCardManager = new VCardManager(client, avatarStorage, rosterModel);
rosterManager = new RosterManager(kaidan, client, rosterModel, vCardManager);
presenceHandler = new PresenceHandler(client, presenceCache);
uploadHandler = new UploadHandler(
client, messageSessionHandler->getMessageHandler(), messageModel
);
serviceDiscoveryManager = new ServiceDiscoveryManager(
client, client->disco(), uploadHandler->getUploadManager()
);
if (enableLogging)
xmlLogHandler = new XmlLogHandler(client);
// Register Stanza Extensions
client->registerStanzaExtension(new gloox::Receipt(gloox::Receipt::Request));
client->registerStanzaExtension(new gloox::DelayedDelivery(gloox::JID(), std::string("")));
client->registerStanzaExtension(new gloox::Forward());
client->registerStanzaExtension(new gloox::Carbons());
client->registerStanzaExtension(new gloox::VCardUpdate());
client->registerStanzaExtension(new gloox::HttpUploadRequest());
client->registerStanzaExtension(new gloox::HttpUploadSlot());
client->registerStanzaExtension(new gloox::Reference(gloox::Reference::Data));
// connect slots
connect(this, &ClientThread::sendMessageRequested,
messageSessionHandler->getMessageHandler(), &MessageHandler::sendMessage);
connect(this, &ClientThread::chatPartnerChanged,
messageSessionHandler->getMessageHandler(), &MessageHandler::setChatPartner);
connect(this, &ClientThread::sendFileRequested,
uploadHandler, &UploadHandler::uploadFile);
connect(this, &ClientThread::addContactRequested,
rosterManager, &RosterManager::addContact);
connect(uploadHandler, &UploadHandler::uploadProgressMade,
kaidan, &Kaidan::uploadProgressMade);
connect(uploadHandler, &UploadHandler::uploadServiceFound,
kaidan, &Kaidan::enableHttpUpload);
connect(this, &ClientThread::removeContactRequested,
rosterManager, &RosterManager::removeContact);
connect(kaidan, &Kaidan::vCardRequested, [=](QString jid) {
vCardManager->fetchVCard(jid);
});
// timed fetching of packages
connect(&workTimer, &QTimer::timeout, worker, &ClientWorker::updateClient);
workTimer.start(KAIDAN_CLIENT_LOOP_INTERVAL);
// enter event loop
qDebug() << "[client] Entering main loop...";
exec();
}
void ClientThread::setCredentials(Credentials creds)
{
QMutexLocker locker(&mutex); // => locking mutex in this function
this->creds = creds;
client->setUsername(gloox::JID(creds.jid.toStdString()).username());
client->setServer(gloox::JID(creds.jid.toStdString()).serverRaw());
client->setJidServer(gloox::JID(creds.jid.toStdString()).server());
client->setPassword(creds.password.toStdString());
client->unbindResource(client->resource());
client->bindResource(creds.jidResource.toStdString());
emit messageModel->ownJidChanged(creds.jid);
}
void ClientThread::setConnectionState(ConnectionState state)
{
connState = state;
emit connectionStateChanged(state);
}
void ClientThread::setConnectionError(gloox::ConnectionError error)
{
connError = error;
emit disconnReasonChanged((DisconnReason) error);
}
QString ClientThread::generateRandomString(unsigned int length) const
{
const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm"
"nopqrstuvwxyz0123456789");
const int numOfChars = possibleCharacters.length();
QString randomString;
for (int i = 0; i < length; ++i) {
int index = qrand() % numOfChars;
QChar nextChar = possibleCharacters.at(index);
randomString.append(nextChar);
}
return randomString;
}
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2018 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CLIENTTHREAD_H
#define CLIENTTHREAD_H
// gloox
#include <gloox/client.h>
// Qt
#include <QMutex>
#include <QThread>
#include <QTimer>
// Kaidan
#include "Enums.h"
namespace gloox {
class Client;
}
class Kaidan;
class ClientWorker;
class RosterManager;
class RosterModel;
class MessageSessionHandler;
class MessageModel;
class AvatarFileStorage;
class PresenceCache;
class PresenceHandler;
class UploadHandler;
class ServiceDiscoveryManager;
class VCardManager;
class XmlLogHandler;
class QSettings;
class QGuiApplication;
using namespace Enums;
/**
* @class KaidanClient Needed to replace server after first connection
*/
class GlooxClient : public gloox::Client
{
public:
GlooxClient(gloox::JID jid, std::string password, int port = -1)
: gloox::Client(jid, password, port)
{
}
void setJidServer(const std::string &server)
{
m_jid.setServer(server);
}
};
/**
* @class ClientThread A class controlling the thread used for the XMPP
* connection.
*
* @see ClientWorker
*/
class ClientThread : public QThread
{
Q_OBJECT
friend class ClientWorker; // can access private vars
public:
struct Credentials {
QString jid;
QString jidResource;
QString password;
// if never connected successfully before with these credentials
bool isFirstTry;
};
/**
* @param rosterModel The roster model connected to the database
* @param messageModel The message model connected to the database
* @param avatarStorage The storage class for caching user avatars
* @param creds The used credentials for connection
* @param settings Settings used for saving creds. on successful connect
* @param parent Optional QObject-based parent
*/
ClientThread(RosterModel *rosterModel, MessageModel *messageModel,
AvatarFileStorage *avatarStorage, PresenceCache *presenceCache,
Credentials creds, QSettings *settings, Kaidan *kaidan,
QGuiApplication *app, bool enableLogging = true, QObject *parent = nullptr);
/*
* Will exit the event loop and waits until thread finishes and then
* destroys the object
*/
~ClientThread();
/**
* Sets the new credentials for next connect.
*
* @param jid The users's JID
* @param jidResource The resource/device name used for connecting
* @param password The user's password
*/
void setCredentials(Credentials creds);
/**
* Returns if connection state is connected.
*/
bool isConnected()
{
return connState == ConnectionState::StateConnected;
}
/**
* Returns the current connection state
*
* @see Enums::ConnectionState
*/
ConnectionState getConnectionState()
{
return connState;
}
/**
* Returns the last error that caused a disconnection.
*/
DisconnReason getConnectionError()
{
return (DisconnReason) connError;
}
protected:
/**
* The thread's main function which will initialize the client and it's
* subcomponents.
*/
void run() override;
signals:
/**
* Emitted when the connection state has changed.
*/
void connectionStateChanged(ConnectionState state);
/**
* Emitted when the client failed to connect giving the reason of it.
*/
void disconnReasonChanged(DisconnReason reason);
/**
* Emit to start connecting on the client's thread.
*/
void connectRequested();
/**
* Emit to start the disconnection on the client's thread.
*/
void disconnectRequested();
/**
* Emit to send a message to a chat partner.
*/
void sendMessageRequested(QString toJid, QString message);
/**
* Emit to start uploading and sending a file
*/
void sendFileRequested(QString jid, QString filePath, QString message);
/**
* Emit to add a new contact to the roster.
*/
void addContactRequested(QString jid, QString name, QString msg);
/**
* Emit to remove a contact from the roster.
*/
void removeContactRequested(QString jid);
/**
* Emit to stop the work timer (needed because it has to be killed on
* its thread).
*/
void stopWorkTimerRequested();
/**
* Emit to get new credentials from QML
*/
void newCredentialsNeeded();
/**
* Emitted, when logging in with new credentials worked
*/
void logInWorked();
/**
* Emitted, when a different chat was opened on the GUI
*/
void chatPartnerChanged(QString chatPartner);
private:
/**
* Emits the signal for a given connection state.
*
* @param state The new connection state
*/
void setConnectionState(ConnectionState state);
/**
* Sets the new error as Enums::DisconnReason and emits the according
* signal
*
* @param error The last connection error
*/
void setConnectionError(gloox::ConnectionError error);
/**
* Generates a random alphanumeric string
*
* @param length The length of the generated string
*/
QString generateRandomString(unsigned int length = 4) const;
RosterModel *rosterModel;
MessageModel *messageModel;
AvatarFileStorage *avatarStorage;
Credentials creds;
Kaidan *kaidan;
GlooxClient *client;
ClientWorker *worker;
RosterManager *rosterManager;
MessageSessionHandler *messageSessionHandler;
PresenceHandler *presenceHandler;
PresenceCache *presenceCache;
UploadHandler *uploadHandler;
ServiceDiscoveryManager *serviceDiscoveryManager;
VCardManager *vCardManager;
XmlLogHandler *xmlLogHandler;
bool enableLogging;
QSettings *settings;
QMutex mutex;
QTimer workTimer;
ConnectionState connState = ConnectionState::StateNone;
gloox::ConnectionError connError = gloox::ConnNoError;
};
#endif // CLIENTTHREAD_H
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2017-2018 Kaidan developers and contributors
* Copyright (C) 2016-2018 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
......@@ -29,132 +29,107 @@
*/
#include "ClientWorker.h"
// gloox
#include <gloox/client.h>
// Qt
#include <QDebug>
#include <QSettings>
#include <QGuiApplication>
// QXmpp
#include <QXmppClient.h>
#include <QXmppConfiguration.h>
#include <QXmppPresence.h>
// Kaidan
#include "ClientThread.h"
#include "Kaidan.h"
#include "RosterManager.h"
// interval in seconds in which a new connection will be tryed
static const unsigned int RECONNECT_INTERVAL = 5000;
ClientWorker::ClientWorker(GlooxClient* client, ClientThread *controller,
QGuiApplication *app, QObject* parent)
: QObject(parent), client(client), controller(controller)
ClientWorker::ClientWorker(Caches *caches, Kaidan *kaidan, bool enableLogging, QGuiApplication *app,
QObject* parent)
: QObject(parent), caches(caches), kaidan(kaidan), enableLogging(enableLogging), app(app)
{
client->registerConnectionListener(this);
// reconnect timer
reconnectTimer.moveToThread(controller);
reconnectTimer.setInterval(RECONNECT_INTERVAL);
reconnectTimer.setSingleShot(true); // only call once
connect(&reconnectTimer, &QTimer::timeout, this, &ClientWorker::xmppConnect);
connect(this, &ClientWorker::stopReconnectTimerRequested,
&reconnectTimer, &QTimer::stop);
connect(app, &QGuiApplication::applicationStateChanged,
this, &ClientWorker::setApplicationState);
client = new QXmppClient();
client->moveToThread(thread);
rosterManager = new RosterManager(kaidan, client, caches->rosterModel);
rosterManager->moveToThread(thread);
connect(this, &ClientWorker::credentialsUpdated, this, &ClientWorker::setCredentials);
}
ClientWorker::~ClientWorker()
{
emit stopReconnectTimerRequested();
}
void ClientWorker::updateClient()
void ClientWorker::main()
{
if (controller->connState != ConnectionState::StateNone && controller->connState !=
ConnectionState::StateDisconnected) {
controller->mutex.lock();
client->recv(0);
controller->mutex.unlock();
}
}
// initialize random generator
qsrand(time(NULL));
void ClientWorker::xmppConnect()
{
qDebug() << "[client] Connecting...";
controller->setConnectionState(ConnectionState::StateConnecting);
controller->mutex.lock();
client->connect(false);
controller->mutex.unlock();
connect(client, &QXmppClient::stateChanged, kaidan, &Kaidan::setConnectionState);
connect(client, &QXmppClient::connected, this, &ClientWorker::onConnect);
connect(client, &QXmppClient::error, this, &ClientWorker::onConnectionError);
connect(this, &ClientWorker::connectRequested, this, &ClientWorker::xmppConnect);
connect(this, &ClientWorker::disconnectRequested, client, &QXmppClient::disconnectFromServer);
}
void ClientWorker::xmppDisconnect()
void ClientWorker::xmppConnect()
{
qDebug() << "[client] Disconnecting...";
if (controller->connState != ConnectionState::StateDisconnecting) {
controller->setConnectionState(ConnectionState::StateDisconnecting);
controller->mutex.lock();
client->disconnect();
controller->mutex.unlock();
}
QXmppConfiguration config;
config.setJid(creds.jid);
config.setResource(creds.jidResource.append(".").append(generateRandomString()));
config.setPassword(creds.password);
config.setAutoAcceptSubscriptions(false);
config.setStreamSecurityMode(QXmppConfiguration::TLSRequired);
config.setAutoReconnectionEnabled(true); // will automatically reconnect
// on first try we must be sure that we connect successfully
// otherwise this could end in a reconnection loop
if (creds.isFirstTry)
config.setAutoReconnectionEnabled(false);
client->connectToServer(config, QXmppPresence(QXmppPresence::Available));
}
void ClientWorker::onConnect()
{
// no mutex needed, because this is called from updateClient()
qDebug() << "[client] Connected successfully to server";
controller->setConnectionState(ConnectionState::StateConnected);