mirror of
https://github.com/QuasarApp/Heart.git
synced 2025-04-30 03:34:40 +00:00
521 lines
14 KiB
C++
521 lines
14 KiB
C++
/*
|
|
* Copyright (C) 2018-2021 QuasarApp.
|
|
* Distributed under the lgplv3 software license, see the accompanying
|
|
* Everyone is permitted to copy and distribute verbatim copies
|
|
* of this license document, but changing it is not allowed.
|
|
*/
|
|
|
|
#include "singleclient.h"
|
|
#include "quasarapp.h"
|
|
#include "authrequest.h"
|
|
#include "dberrorcodes.h"
|
|
|
|
#include <deleteobject.h>
|
|
#include <websocket.h>
|
|
#include <websocketsubscriptions.h>
|
|
#include <qaglobalutils.h>
|
|
|
|
namespace QH {
|
|
|
|
SingleClient::SingleClient() {
|
|
|
|
qRegisterMetaType<QH::ClientStatus>();
|
|
|
|
setMaxPendingConnections(1);
|
|
|
|
registerPackageType<PKG::AuthRequest>();
|
|
|
|
connect(this, &SingleClient::requestError,
|
|
this, &SingleClient::handleError);
|
|
}
|
|
|
|
ParserResult SingleClient::parsePackage(const QSharedPointer<PKG::AbstractData> &pkg,
|
|
const Header& pkgHeader,
|
|
const AbstractNodeInfo* sender) {
|
|
|
|
auto parentResult = DataBaseNode::parsePackage(pkg, pkgHeader, sender);
|
|
if (parentResult != QH::ParserResult::NotProcessed) {
|
|
return parentResult;
|
|
}
|
|
|
|
if (QH::PKG::UserMember::command() == pkg->cmd()) {
|
|
QH::PKG::UserMember *obj = static_cast<QH::PKG::UserMember*>(pkg.data());
|
|
|
|
if (!(obj->isValid() && obj->getSignToken().isValid())) {
|
|
return ParserResult::Error;
|
|
}
|
|
|
|
setMember(*obj);
|
|
setStatus(ClientStatus::Logined);
|
|
|
|
emit currentUserChanged();
|
|
|
|
return QH::ParserResult::Processed;
|
|
|
|
} else if (PKG::WebSocketSubscriptions::command() == pkg->cmd()) {
|
|
PKG::WebSocketSubscriptions *obj = static_cast<PKG::WebSocketSubscriptions*>(pkg.data());
|
|
if (!obj->isValid()) {
|
|
return ParserResult::Error;
|
|
}
|
|
|
|
setSubscribersList(obj->addresses());
|
|
return ParserResult::Processed;
|
|
}
|
|
|
|
handleRestRequest(pkg, pkgHeader);
|
|
|
|
return QH::ParserResult::NotProcessed;
|
|
}
|
|
|
|
ClientStatus SingleClient::getStatus() const {
|
|
return _status;
|
|
}
|
|
|
|
bool SingleClient::login(const QString &userId, const QString &rawPassword) {
|
|
if (getStatus() < ClientStatus::Connected) {
|
|
QuasarAppUtils::Params::log("You try make login on the offline client."
|
|
" Please wait of ClientStatus::Connected status.",
|
|
QuasarAppUtils::Error);
|
|
return false;
|
|
}
|
|
|
|
if (!p_login(userId, hashgenerator(rawPassword.toLatin1()))) {
|
|
return false;
|
|
};
|
|
|
|
if (getStatus() < ClientStatus::Loginning) {
|
|
setStatus(ClientStatus::Loginning);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SingleClient::login(const PKG::UserMember &memberData) {
|
|
|
|
if (memberData.getSignToken() == getMember().getSignToken()) {
|
|
if (isLogined()) {
|
|
return true;
|
|
}
|
|
|
|
return login();
|
|
}
|
|
|
|
setMember(memberData);
|
|
return login();
|
|
}
|
|
|
|
bool SingleClient::logout() {
|
|
if (getStatus() < ClientStatus::Logined) {
|
|
QuasarAppUtils::Params::log("You try logout on the not Loggined client."
|
|
" Please wait of ClientStatus::Logined status.",
|
|
QuasarAppUtils::Error);
|
|
return false;
|
|
}
|
|
|
|
|
|
QH::PKG::AuthRequest request;
|
|
request.copyFrom(&getMember());
|
|
request.setRequest(PKG::UserRequestType::LogOut);
|
|
|
|
if (!sendData(&request, realServerAddress())) {
|
|
return false;
|
|
};
|
|
|
|
resetUser();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SingleClient::signup(const QString &userId, const QString &rawPassword) {
|
|
if (getStatus() < ClientStatus::Connected) {
|
|
QuasarAppUtils::Params::log("You try make signup on the offline client."
|
|
" Please wait of ClientStatus::Connected status.",
|
|
QuasarAppUtils::Error);
|
|
return false;
|
|
}
|
|
|
|
if (!p_signup(userId, hashgenerator(rawPassword.toLatin1()))) {
|
|
return false;
|
|
};
|
|
|
|
if (getStatus() < ClientStatus::Loginning) {
|
|
setStatus(ClientStatus::Loginning);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SingleClient::removeUser() {
|
|
if (getStatus() < ClientStatus::Logined) {
|
|
QuasarAppUtils::Params::log("You try make remove on the not Loggined client."
|
|
" Please wait of ClientStatus::Logined status.",
|
|
QuasarAppUtils::Error);
|
|
return false;
|
|
}
|
|
|
|
|
|
QH::PKG::DeleteObject request;
|
|
request.copyFrom(&getMember());
|
|
if (!sendData(&request, realServerAddress())) {
|
|
return false;
|
|
};
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SingleClient::connectToServer() {
|
|
if (getStatus() >= ClientStatus::Connected) {
|
|
QuasarAppUtils::Params::log(" This client alredy connected to server.",
|
|
QuasarAppUtils::Warning);
|
|
return true;
|
|
}
|
|
|
|
if (getStatus() < ClientStatus::Connecting) {
|
|
setStatus(ClientStatus::Connecting);
|
|
}
|
|
auto address = serverAddress();
|
|
DataBaseNode::addNode(address.first, address.second);
|
|
|
|
return true;
|
|
}
|
|
|
|
void SingleClient::disconnectFromServer() {
|
|
if (getStatus() == ClientStatus::Dissconnected) {
|
|
return;
|
|
}
|
|
|
|
DataBaseNode::removeNode(realServerAddress());
|
|
}
|
|
|
|
void SingleClient::setStatus(const ClientStatus &status) {
|
|
if (status == _status)
|
|
return;
|
|
|
|
auto oldStatus = _status;
|
|
|
|
if (status == ClientStatus::Connecting || status == ClientStatus::Loginning) {
|
|
QTimer::singleShot(WAIT_RESPOCE_TIME, this, [this, oldStatus]() {
|
|
if (_status == ClientStatus::Connecting || _status == ClientStatus::Loginning) {
|
|
setStatus(oldStatus);
|
|
emit requestError(static_cast<unsigned char >(ErrorCodes::TimeOutError),
|
|
ErrorCodes::DBErrorCodesHelper::toString(ErrorCodes::TimeOutError));
|
|
}
|
|
});
|
|
}
|
|
|
|
_status = status;
|
|
emit statusChanged(_status);
|
|
}
|
|
|
|
void SingleClient::handleError(unsigned char code, QString error) {
|
|
QuasarAppUtils::Params::log(error, QuasarAppUtils::Error);
|
|
|
|
if (code != ErrorCodes::NoError && getStatus() == ClientStatus::Loginning) {
|
|
setStatus(ClientStatus::Connected);
|
|
}
|
|
|
|
_lastError = code;
|
|
}
|
|
|
|
bool SingleClient::p_login(const QString &userId, const QByteArray &hashPassword) {
|
|
const auto &userMember = getMember();
|
|
if ((userId.isEmpty() || hashPassword.isEmpty()) && !userMember.isValid()) {
|
|
return false;
|
|
}
|
|
|
|
QH::PKG::AuthRequest request;
|
|
if (userId.isEmpty())
|
|
request.setId(userMember.getId());
|
|
else
|
|
request.setId(userId);
|
|
|
|
request.setRequest(QH::PKG::UserRequestType::LogIn);
|
|
|
|
if (hashPassword.isEmpty()) {
|
|
if (!userMember.getSignToken().isValid()) {
|
|
return false;
|
|
}
|
|
|
|
request.setSignToken(userMember.getSignToken());
|
|
} else {
|
|
request.setAuthenticationData(hashPassword);
|
|
}
|
|
|
|
return sendData(&request, realServerAddress());
|
|
}
|
|
|
|
bool SingleClient::p_signup(const QString &userId, const QByteArray &hashPassword) {
|
|
QH::PKG::AuthRequest request;
|
|
request.setId(userId);
|
|
request.setAuthenticationData(hashPassword);
|
|
request.setRequest(QH::PKG::UserRequestType::SignUp);
|
|
|
|
return sendData(&request, realServerAddress());
|
|
}
|
|
|
|
void SingleClient::handleRestRequest(const QSharedPointer<PKG::AbstractData> &pkg,
|
|
const Header& pkgHeader) {
|
|
QMutexLocker lock(&_handlerCacheMutex);
|
|
|
|
if (_handlersCache.contains(pkgHeader.triggerHash)) {
|
|
auto action = _handlersCache.take(pkgHeader.triggerHash);
|
|
action(pkg);
|
|
}
|
|
}
|
|
|
|
void SingleClient::setRealServerAddress(const HostAddress &realServerAddress) {
|
|
_realServerAddress = realServerAddress;
|
|
}
|
|
|
|
void SingleClient::addToSubscribesList(unsigned int id) {
|
|
QMutexLocker lock(&_subscribesMutex);
|
|
_subscribes.insert(id);
|
|
}
|
|
|
|
void SingleClient::removeFromSubscribesList(unsigned int id) {
|
|
QMutexLocker lock(&_subscribesMutex);
|
|
_subscribes.remove(id);
|
|
}
|
|
|
|
void SingleClient::setSubscribersList(QSet<unsigned int> ids) {
|
|
QMutexLocker lock(&_subscribesMutex);
|
|
_subscribes = ids;
|
|
}
|
|
|
|
const HostAddress &SingleClient::realServerAddress() const {
|
|
return _realServerAddress;
|
|
}
|
|
|
|
void SingleClient::nodeErrorOccured(AbstractNodeInfo *nodeInfo,
|
|
QAbstractSocket::SocketError errorCode,
|
|
QString errorString) {
|
|
|
|
setStatus(ClientStatus::Dissconnected);
|
|
SingleBase::nodeErrorOccured(nodeInfo, errorCode, errorString);
|
|
}
|
|
|
|
const PKG::UserMember &SingleClient::getMember() const {
|
|
return _member;
|
|
}
|
|
|
|
QPair<QString, unsigned short> SingleClient::serverAddress() const {
|
|
return {"localhost", DEFAULT_PORT};
|
|
}
|
|
|
|
void SingleClient::nodeConfirmend(AbstractNodeInfo *node) {
|
|
Q_UNUSED(node)
|
|
}
|
|
|
|
void QH::SingleClient::nodeConnected(AbstractNodeInfo *node) {
|
|
|
|
debug_assert(!realServerAddress().isValid(),
|
|
"Internal Error detected in "
|
|
"the QH::SingleClient::nodeConnected(AbstractNodeInfo *node) method"
|
|
" If you see this error message"
|
|
" please send your bug report to the official github page"
|
|
" https://github.com/QuasarApp/Heart/issues/new" );
|
|
|
|
|
|
setRealServerAddress(node->networkAddress());
|
|
|
|
setStatus(ClientStatus::Connected);
|
|
}
|
|
|
|
void QH::SingleClient::nodeDisconnected(AbstractNodeInfo *node) {
|
|
Q_UNUSED(node)
|
|
setRealServerAddress(HostAddress{});
|
|
|
|
setStatus(ClientStatus::Dissconnected);
|
|
}
|
|
|
|
unsigned int SingleClient::sendData(const PKG::AbstractData *resp,
|
|
const HostAddress &address,
|
|
const Header *req) {
|
|
|
|
return DataBaseNode::sendData(resp, address, req);
|
|
}
|
|
|
|
unsigned int SingleClient::sendData(const PKG::AbstractData *resp,
|
|
const AbstractNodeInfo *node,
|
|
const Header *req) {
|
|
|
|
return DataBaseNode::sendData(resp, node, req);
|
|
}
|
|
|
|
unsigned int SingleClient::sendData(const PKG::AbstractData *resp,
|
|
const QVariant &nodeId,
|
|
const Header *req) {
|
|
|
|
return DataBaseNode::sendData(resp, nodeId, req);
|
|
}
|
|
|
|
unsigned int SingleClient::sendData(PKG::AbstractData *resp,
|
|
const HostAddress &address,
|
|
const Header *req) {
|
|
if (!signPackageWithToken(resp)) {
|
|
return 0;
|
|
}
|
|
|
|
return DataBaseNode::sendData(resp, address, req);
|
|
}
|
|
|
|
unsigned int SingleClient::sendData(PKG::AbstractData *resp,
|
|
const AbstractNodeInfo *node,
|
|
const Header *req) {
|
|
if (!signPackageWithToken(resp)) {
|
|
return 0;
|
|
}
|
|
|
|
return DataBaseNode::sendData(resp, node, req);
|
|
}
|
|
|
|
unsigned int SingleClient::sendData(PKG::AbstractData *resp,
|
|
const QVariant &nodeId,
|
|
const Header *req) {
|
|
if (!signPackageWithToken(resp)) {
|
|
return 0;
|
|
}
|
|
|
|
return DataBaseNode::sendData(resp, nodeId, req);
|
|
}
|
|
|
|
void SingleClient::resetUser() {
|
|
setMember({});
|
|
if (getStatus() > ClientStatus::Connected)
|
|
setStatus(ClientStatus::Connected);
|
|
}
|
|
|
|
void SingleClient::setMember(const PKG::UserMember &member) {
|
|
QMutexLocker lock (&_handlerMemberMutex);
|
|
_member = member;
|
|
}
|
|
|
|
ErrorCodes::Code SingleClient::getLastError() const {
|
|
return _lastError;
|
|
}
|
|
|
|
ErrorCodes::Code SingleClient::takeLastError() {
|
|
auto result = _lastError;
|
|
setLastError(ErrorCodes::NoError);
|
|
|
|
return result;
|
|
}
|
|
|
|
QString SingleClient::getLastErrorString() const {
|
|
return ErrorCodes::DBErrorCodesHelper::toString(_lastError);
|
|
}
|
|
|
|
bool SingleClient::isConnected() const {
|
|
return getStatus() >= ClientStatus::Connected;
|
|
}
|
|
|
|
bool SingleClient::isLogined() const {
|
|
return getStatus() >= ClientStatus::Logined;
|
|
}
|
|
|
|
bool SingleClient::subscribe(unsigned int id) {
|
|
if (getStatus() < ClientStatus::Logined) {
|
|
return false;
|
|
}
|
|
|
|
PKG::WebSocket request;
|
|
request.setSubscribeId(id);
|
|
request.setRequestCommnad(PKG::WebSocketRequest::Subscribe);
|
|
|
|
if (!sendData(&request, realServerAddress())) {
|
|
return false;
|
|
}
|
|
|
|
addToSubscribesList(id);
|
|
return true;
|
|
}
|
|
|
|
bool SingleClient::unsubscribe(unsigned int id) {
|
|
if (getStatus() < ClientStatus::Logined) {
|
|
return false;
|
|
}
|
|
|
|
PKG::WebSocket request;
|
|
request.setSubscribeId(id);
|
|
request.setRequestCommnad(PKG::WebSocketRequest::Unsubscribe);
|
|
|
|
if (!sendData(&request, realServerAddress())) {
|
|
return false;
|
|
}
|
|
|
|
removeFromSubscribesList(id);
|
|
return true;
|
|
}
|
|
|
|
QSet<unsigned int> SingleClient::subscribesList() const {
|
|
QMutexLocker lock(&_subscribesMutex);
|
|
return _subscribes;
|
|
}
|
|
|
|
bool SingleClient::isSubscribed(unsigned int subscribeId) const {
|
|
QMutexLocker lock(&_subscribesMutex);
|
|
return _subscribes.contains(subscribeId);
|
|
}
|
|
|
|
bool SingleClient::syncSybscribeListWithServer(const std::function<void(const QSet<unsigned int> &)> &cb) {
|
|
QMutexLocker lock(&_subscribesMutex);
|
|
|
|
PKG::WebSocket request;
|
|
request.setRequestCommnad(PKG::WebSocketRequest::SubscribeList);
|
|
|
|
auto handler = [cb, this](const QSharedPointer<const PKG::AbstractData> & result){
|
|
auto subscribes = static_cast<const PKG::WebSocketSubscriptions*>(result.data());
|
|
setSubscribersList(subscribes->addresses());
|
|
cb(_subscribes);
|
|
};
|
|
|
|
return restRequest(&request, handler);
|
|
}
|
|
|
|
bool SingleClient::signPackageWithToken(PKG::AbstractData *pkg) const {
|
|
auto tokenObject = dynamic_cast<IToken*>(pkg);
|
|
if (!tokenObject) {
|
|
return true;
|
|
}
|
|
|
|
auto token = getMember().getSignToken();
|
|
|
|
if (!token.isValid())
|
|
return false;
|
|
|
|
tokenObject->setSignToken(token);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SingleClient::restRequest(PKG::AbstractData *req,
|
|
const HandlerMethod &handler) {
|
|
|
|
|
|
if (!signPackageWithToken(req)) {
|
|
QuasarAppUtils::Params::log("Fail to sign package with token", QuasarAppUtils::Error);
|
|
return false;
|
|
}
|
|
|
|
unsigned int pkgHash = sendData(req, realServerAddress());
|
|
|
|
if (!pkgHash)
|
|
return false;
|
|
|
|
QMutexLocker lock(&_handlerCacheMutex);
|
|
_handlersCache[pkgHash] = handler;
|
|
|
|
QTimer::singleShot(WAIT_RESPOCE_TIME, this, [this, pkgHash]() {
|
|
QMutexLocker lock(&_handlerCacheMutex);
|
|
_handlersCache.remove(pkgHash);
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
void SingleClient::setLastError(const ErrorCodes::Code &lastError) {
|
|
_lastError = lastError;
|
|
}
|
|
|
|
}
|