2017-12-20 14:54:05 +01:00
|
|
|
/*!
|
2018-03-28 15:10:51 +02:00
|
|
|
* \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany
|
2017-12-20 14:54:05 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "RemoteClientImpl.h"
|
|
|
|
|
|
|
|
#include "AppSettings.h"
|
|
|
|
#include "Env.h"
|
|
|
|
#include "messages/RemoteMessageParser.h"
|
|
|
|
#include "RemoteConnectorImpl.h"
|
|
|
|
|
|
|
|
#include <QJsonDocument>
|
|
|
|
#include <QJsonObject>
|
|
|
|
#include <QLoggingCategory>
|
|
|
|
|
|
|
|
Q_DECLARE_LOGGING_CATEGORY(remote_device)
|
|
|
|
|
|
|
|
using namespace governikus;
|
|
|
|
|
|
|
|
namespace governikus
|
|
|
|
{
|
|
|
|
|
|
|
|
template<> RemoteClient* createNewObject<RemoteClient*>()
|
|
|
|
{
|
|
|
|
return new RemoteClientImpl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RemoteClientImpl::RemoteClientImpl()
|
|
|
|
: mDatagramHandler()
|
|
|
|
, mRemoteDeviceList(Env::create<RemoteDeviceList*>())
|
|
|
|
, mErrorCounter()
|
|
|
|
, mRemoteConnectorThread()
|
|
|
|
, mRemoteConnector()
|
|
|
|
, mRemoteConnectorPending()
|
|
|
|
{
|
|
|
|
connect(mRemoteDeviceList.data(), &RemoteDeviceList::fireDeviceAppeared, this, &RemoteClient::fireDeviceAppeared);
|
|
|
|
connect(mRemoteDeviceList.data(), &RemoteDeviceList::fireDeviceVanished, this, &RemoteClient::fireDeviceVanished);
|
|
|
|
|
|
|
|
bootstrapRemoteConnectorThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RemoteClientImpl::~RemoteClientImpl()
|
|
|
|
{
|
|
|
|
shutdownRemoteConnectorThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RemoteClientImpl::bootstrapRemoteConnectorThread()
|
|
|
|
{
|
|
|
|
Q_ASSERT(!mRemoteConnectorThread.isRunning());
|
|
|
|
Q_ASSERT(mRemoteConnector.isNull());
|
|
|
|
|
|
|
|
mRemoteConnectorThread.setObjectName(QStringLiteral("RemoteConnectorThread"));
|
|
|
|
|
|
|
|
mRemoteConnector = Env::create<RemoteConnector*>();
|
|
|
|
mRemoteConnector->moveToThread(&mRemoteConnectorThread);
|
|
|
|
connect(&mRemoteConnectorThread, &QThread::finished, mRemoteConnector.data(), &QObject::deleteLater);
|
|
|
|
|
|
|
|
connect(mRemoteConnector.data(), &RemoteConnector::fireRemoteDispatcherCreated, this, &RemoteClientImpl::onRemoteDispatcherCreated);
|
|
|
|
connect(mRemoteConnector.data(), &RemoteConnector::fireRemoteDispatcherError, this, &RemoteClientImpl::onRemoteDispatcherError);
|
|
|
|
|
|
|
|
mRemoteConnectorThread.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RemoteClientImpl::shutdownRemoteConnectorThread()
|
|
|
|
{
|
|
|
|
if (mRemoteConnectorThread.isRunning() && !mRemoteConnectorThread.isInterruptionRequested())
|
|
|
|
{
|
|
|
|
qCDebug(remote_device) << "Shutdown RemoteConnector...";
|
|
|
|
mRemoteConnectorThread.requestInterruption(); // do not try to stop AGAIN from dtor
|
|
|
|
mRemoteConnectorThread.quit();
|
|
|
|
mRemoteConnectorThread.wait(2500);
|
|
|
|
qCDebug(remote_device) << "RemoteConnector:" << (mRemoteConnectorThread.isRunning() ? "still running" : "stopped");
|
|
|
|
|
|
|
|
Q_ASSERT(!mRemoteConnectorThread.isRunning());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QSharedPointer<RemoteDeviceListEntry> RemoteClientImpl::mapToAndTakeRemoteConnectorPending(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor)
|
|
|
|
{
|
|
|
|
QMutableVectorIterator<QSharedPointer<RemoteDeviceListEntry> > i(mRemoteConnectorPending);
|
|
|
|
while (i.hasNext())
|
|
|
|
{
|
|
|
|
QSharedPointer<RemoteDeviceListEntry> entry = i.next();
|
|
|
|
if (entry->contains(pRemoteDeviceDescriptor))
|
|
|
|
{
|
|
|
|
i.remove();
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_ASSERT(false);
|
|
|
|
return QSharedPointer<RemoteDeviceListEntry>();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RemoteClientImpl::onNewMessage(const QJsonDocument& pData, const QHostAddress& pAddress)
|
|
|
|
{
|
|
|
|
bool isIPv4;
|
|
|
|
pAddress.toIPv4Address(&isIPv4);
|
|
|
|
if (!isIPv4)
|
|
|
|
{
|
|
|
|
static int timesLogged = 0;
|
|
|
|
if (timesLogged < 20)
|
|
|
|
{
|
|
|
|
qCCritical(remote_device) << "IPv6 not supported" << pAddress;
|
|
|
|
timesLogged++;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const QSharedPointer<const Discovery>& discovery = RemoteMessageParser().parseDiscovery(pData);
|
|
|
|
if (discovery.isNull())
|
|
|
|
{
|
|
|
|
static int timesLogged = 0;
|
|
|
|
if (timesLogged < 20)
|
|
|
|
{
|
|
|
|
qCDebug(remote_device) << "Discarding unparsable message";
|
|
|
|
timesLogged++;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const RemoteDeviceDescriptor remoteDeviceDescriptor(discovery, pAddress);
|
|
|
|
mRemoteDeviceList->update(remoteDeviceDescriptor);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RemoteClientImpl::onRemoteDispatcherCreated(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor, const QSharedPointer<RemoteDispatcher>& pDispatcher)
|
|
|
|
{
|
|
|
|
mErrorCounter[pRemoteDeviceDescriptor.getIfdId()] = 0;
|
|
|
|
const QSharedPointer<RemoteDeviceListEntry>& entry = mapToAndTakeRemoteConnectorPending(pRemoteDeviceDescriptor);
|
|
|
|
if (entry.isNull())
|
|
|
|
{
|
|
|
|
qCDebug(remote_device) << "Failed to map RemoteConnector response:" << pRemoteDeviceDescriptor;
|
|
|
|
Q_ASSERT(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mConnectedDeviceIds.append(entry->getRemoteDeviceDescriptor().getIfdId());
|
|
|
|
connect(pDispatcher.data(), &RemoteDispatcher::fireClosed, this, &RemoteClientImpl::onDispatcherDestroyed);
|
|
|
|
|
|
|
|
Q_EMIT fireEstablishConnectionDone(entry, GlobalStatus::Code::No_Error);
|
|
|
|
Q_EMIT fireNewRemoteDispatcher(pDispatcher);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RemoteClientImpl::onRemoteDispatcherError(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor, RemoteErrorCode pErrorCode)
|
|
|
|
{
|
|
|
|
const QSharedPointer<RemoteDeviceListEntry>& entry = mapToAndTakeRemoteConnectorPending(pRemoteDeviceDescriptor);
|
|
|
|
if (entry.isNull())
|
|
|
|
{
|
|
|
|
qCDebug(remote_device) << "Failed to map RemoteConnector response:" << pRemoteDeviceDescriptor;
|
|
|
|
Q_ASSERT(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pErrorCode == RemoteErrorCode::REMOTE_HOST_REFUSED_CONNECTION || pErrorCode == RemoteErrorCode::NO_SUPPORTED_API_LEVEL)
|
|
|
|
{
|
|
|
|
mErrorCounter[pRemoteDeviceDescriptor.getIfdId()] += 1;
|
2018-03-28 15:10:51 +02:00
|
|
|
if (mErrorCounter[pRemoteDeviceDescriptor.getIfdId()] >= 7)
|
2017-12-20 14:54:05 +01:00
|
|
|
{
|
2018-03-28 15:10:51 +02:00
|
|
|
qCCritical(remote_device) << "Remote device refused connection seven times, removing certificate with fingerprint:" << pRemoteDeviceDescriptor.getIfdId();
|
2017-12-20 14:54:05 +01:00
|
|
|
RemoteServiceSettings& settings = AppSettings::getInstance().getRemoteServiceSettings();
|
|
|
|
QString deviceName;
|
|
|
|
const auto& infos = settings.getRemoteInfos();
|
|
|
|
for (const auto& remoteInfo : infos)
|
|
|
|
{
|
|
|
|
if (remoteInfo.getFingerprint() == pRemoteDeviceDescriptor.getIfdId())
|
|
|
|
{
|
|
|
|
deviceName = remoteInfo.getName();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
settings.removeTrustedCertificate(pRemoteDeviceDescriptor.getIfdId());
|
|
|
|
mErrorCounter[pRemoteDeviceDescriptor.getIfdId()] = 0;
|
|
|
|
if (!deviceName.isEmpty())
|
|
|
|
{
|
|
|
|
Q_EMIT fireCertificateRemoved(deviceName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mErrorCounter[pRemoteDeviceDescriptor.getIfdId()] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_EMIT fireEstablishConnectionDone(entry, RemoteConnector::errorToGlobalStatus(pErrorCode));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RemoteClientImpl::startDetection()
|
|
|
|
{
|
|
|
|
if (!mDatagramHandler.isNull())
|
|
|
|
{
|
|
|
|
qCDebug(remote_device) << "Detection already started";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mDatagramHandler.reset(Env::create<DatagramHandler*>());
|
|
|
|
connect(mDatagramHandler.data(), &DatagramHandler::fireNewMessage, this, &RemoteClientImpl::onNewMessage);
|
|
|
|
Q_EMIT fireDetectionChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RemoteClientImpl::stopDetection()
|
|
|
|
{
|
|
|
|
mDatagramHandler.reset();
|
|
|
|
mRemoteDeviceList->clear();
|
|
|
|
Q_EMIT fireDetectionChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool RemoteClientImpl::isDetecting()
|
|
|
|
{
|
|
|
|
return !mDatagramHandler.isNull();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RemoteClientImpl::establishConnection(const QSharedPointer<RemoteDeviceListEntry>& pEntry, const QString& pPsk)
|
|
|
|
{
|
|
|
|
if (mRemoteConnector.isNull())
|
|
|
|
{
|
|
|
|
qCCritical(remote_device) << "There is no RemoteConnector!";
|
|
|
|
Q_ASSERT(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pEntry)
|
|
|
|
{
|
|
|
|
qCWarning(remote_device) << "Cannot connect to given device.";
|
|
|
|
Q_EMIT fireEstablishConnectionDone(pEntry, GlobalStatus::Code::RemoteConnector_ConnectionError);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mRemoteConnectorPending += pEntry;
|
|
|
|
|
|
|
|
QMetaObject::invokeMethod(mRemoteConnector.data(), "onConnectRequest", Qt::QueuedConnection,
|
|
|
|
Q_ARG(RemoteDeviceDescriptor, pEntry->getRemoteDeviceDescriptor()),
|
|
|
|
Q_ARG(QString, pPsk));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QVector<QSharedPointer<RemoteDeviceListEntry> > RemoteClientImpl::getRemoteDevices() const
|
|
|
|
{
|
|
|
|
return mRemoteDeviceList->getRemoteDevices();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RemoteClientImpl::requestRemoteDevices()
|
|
|
|
{
|
|
|
|
Q_EMIT fireRemoteDevicesInfo(mRemoteDeviceList->getRemoteDevices());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QVector<RemoteServiceSettings::RemoteInfo> RemoteClientImpl::getConnectedDeviceInfos()
|
|
|
|
{
|
|
|
|
RemoteServiceSettings& settings = AppSettings::getInstance().getRemoteServiceSettings();
|
|
|
|
auto remoteInfos = settings.getRemoteInfos();
|
|
|
|
QVector<RemoteServiceSettings::RemoteInfo> result;
|
|
|
|
for (const auto& info : remoteInfos)
|
|
|
|
{
|
|
|
|
for (const auto& id : qAsConst(mConnectedDeviceIds))
|
|
|
|
{
|
|
|
|
if (info.getFingerprint() == id)
|
|
|
|
{
|
|
|
|
result.append(info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RemoteClientImpl::onDispatcherDestroyed(GlobalStatus::Code pCloseCode, const QSharedPointer<RemoteDispatcher>& pRemoteDispatcher)
|
|
|
|
{
|
|
|
|
if (pRemoteDispatcher)
|
|
|
|
{
|
|
|
|
mConnectedDeviceIds.removeAll(pRemoteDispatcher->getId());
|
|
|
|
}
|
|
|
|
Q_EMIT fireDispatcherDestroyed(pCloseCode, pRemoteDispatcher);
|
|
|
|
}
|