/*! * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany */ #include "RemoteClientImpl.h" #include "AppSettings.h" #include "Env.h" #include "messages/RemoteMessageParser.h" #include "RemoteConnectorImpl.h" #include #include #include Q_DECLARE_LOGGING_CATEGORY(remote_device) using namespace governikus; namespace governikus { template<> RemoteClient* createNewObject() { return new RemoteClientImpl; } } RemoteClientImpl::RemoteClientImpl() : mDatagramHandler() , mRemoteDeviceList(Env::create()) , 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(); 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 RemoteClientImpl::mapToAndTakeRemoteConnectorPending(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor) { QMutableVectorIterator > i(mRemoteConnectorPending); while (i.hasNext()) { QSharedPointer entry = i.next(); if (entry->contains(pRemoteDeviceDescriptor)) { i.remove(); return entry; } } Q_ASSERT(false); return QSharedPointer(); } 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& 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& pDispatcher) { mErrorCounter[pRemoteDeviceDescriptor.getIfdId()] = 0; const QSharedPointer& 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& 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; if (mErrorCounter[pRemoteDeviceDescriptor.getIfdId()] >= 7) { qCCritical(remote_device) << "Remote device refused connection seven times, removing certificate with fingerprint:" << pRemoteDeviceDescriptor.getIfdId(); 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()); 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& 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 > RemoteClientImpl::getRemoteDevices() const { return mRemoteDeviceList->getRemoteDevices(); } void RemoteClientImpl::requestRemoteDevices() { Q_EMIT fireRemoteDevicesInfo(mRemoteDeviceList->getRemoteDevices()); } QVector RemoteClientImpl::getConnectedDeviceInfos() { RemoteServiceSettings& settings = AppSettings::getInstance().getRemoteServiceSettings(); auto remoteInfos = settings.getRemoteInfos(); QVector 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& pRemoteDispatcher) { if (pRemoteDispatcher) { mConnectedDeviceIds.removeAll(pRemoteDispatcher->getId()); } Q_EMIT fireDispatcherDestroyed(pCloseCode, pRemoteDispatcher); }