Source code
Revision control
Copy as Markdown
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#ifdef USE_SYSTEM_OPEN62541
#include <open62541/client.h>
#include <open62541/client_config_default.h>
#else
#include "qopen62541.h"
#endif
#include "qopen62541backend.h"
#include "qopen62541client.h"
#include "qopen62541node.h"
#include "qopen62541subscription.h"
#include "qopen62541utils.h"
#include "qopen62541valueconverter.h"
#include <private/qopcuaclient_p.h>
#include <private/qopcuahistoryreadresponseimpl_p.h>
#include <private/qopcuasecuritypolicyuris_p.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qthread.h>
#include <QtCore/qurl.h>
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QT_OPCUA_PLUGINS_OPEN62541)
using namespace Qt::Literals::StringLiterals;
QOpen62541Client::QOpen62541Client(const QVariantMap &backendProperties)
: QOpcUaClientImpl()
, m_backend(new Open62541AsyncBackend(this))
{
#ifdef UA_ENABLE_ENCRYPTION
m_hasSha1SignatureSupport = Open62541Utils::checkSha1SignatureSupport();
if (!m_hasSha1SignatureSupport)
qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "SHA-1 signatures are not supported by OpenSSL"
<< "The security policies Basic128Rsa15 and Basic256 will not be available";
#endif
bool ok = false;
const quint32 clientIterateInterval = backendProperties.value(u"clientIterateIntervalMs"_s, 50)
.toUInt(&ok);
if (ok)
m_backend->m_clientIterateInterval = clientIterateInterval;
const quint32 asyncRequestTimeout = backendProperties.value(u"asyncRequestTimeoutMs"_s, 15000)
.toUInt(&ok);
if (ok)
m_backend->m_asyncRequestTimeout = asyncRequestTimeout;
m_thread = new QThread();
m_thread->setObjectName("QOpen62541Client");
connectBackendWithClient(m_backend);
m_backend->moveToThread(m_thread);
connect(m_thread, &QThread::finished, m_thread, &QObject::deleteLater);
connect(m_thread, &QThread::finished, m_backend, &QObject::deleteLater);
m_thread->start();
}
QOpen62541Client::~QOpen62541Client()
{
// The connectError() and passwordForPrivateKeyRequired() signals use a blocking queued connection.
// They must be disconnected before waiting for the thread to avoid a deadlock.
QObject::disconnect(m_backend, &Open62541AsyncBackend::connectError, this, &QOpcUaClientImpl::connectError);
QObject::disconnect(m_backend, &Open62541AsyncBackend::passwordForPrivateKeyRequired,
this, &QOpcUaClientImpl::passwordForPrivateKeyRequired);
if (m_thread->isRunning())
m_thread->quit();
m_thread->wait();
}
void QOpen62541Client::connectToEndpoint(const QOpcUaEndpointDescription &endpoint)
{
QMetaObject::invokeMethod(m_backend, &Open62541AsyncBackend::connectToEndpoint, Qt::QueuedConnection, endpoint);
}
void QOpen62541Client::disconnectFromEndpoint()
{
QMetaObject::invokeMethod(m_backend, &Open62541AsyncBackend::disconnectFromEndpoint, Qt::QueuedConnection);
}
QOpcUaNode *QOpen62541Client::node(const QString &nodeId)
{
UA_NodeId uaNodeId = Open62541Utils::nodeIdFromQString(nodeId);
if (UA_NodeId_isNull(&uaNodeId))
return nullptr;
auto tempNode = new QOpen62541Node(uaNodeId, this, nodeId);
if (!tempNode->registered()) {
qCDebug(QT_OPCUA_PLUGINS_OPEN62541) << "Failed to register node with backend, maximum number of nodes reached.";
delete tempNode;
return nullptr;
}
return new QOpcUaNode(tempNode, m_client);
}
QString QOpen62541Client::backend() const
{
return u"open62541"_s;
}
bool QOpen62541Client::requestEndpoints(const QUrl &url)
{
return QMetaObject::invokeMethod(m_backend, &Open62541AsyncBackend::requestEndpoints,
Qt::QueuedConnection, url);
}
bool QOpen62541Client::findServers(const QUrl &url, const QStringList &localeIds, const QStringList &serverUris)
{
return QMetaObject::invokeMethod(m_backend, &Open62541AsyncBackend::findServers,
Qt::QueuedConnection, url, localeIds, serverUris);
}
bool QOpen62541Client::readNodeAttributes(const QList<QOpcUaReadItem> &nodesToRead)
{
return QMetaObject::invokeMethod(m_backend, &Open62541AsyncBackend::readNodeAttributes,
Qt::QueuedConnection, nodesToRead);
}
bool QOpen62541Client::writeNodeAttributes(const QList<QOpcUaWriteItem> &nodesToWrite)
{
return QMetaObject::invokeMethod(m_backend, &Open62541AsyncBackend::writeNodeAttributes,
Qt::QueuedConnection, nodesToWrite);
}
QOpcUaHistoryReadResponse *QOpen62541Client::readHistoryData(const QOpcUaHistoryReadRawRequest &request)
{
if (!m_client)
return nullptr;
auto impl = new QOpcUaHistoryReadResponseImpl(request);
auto result = new QOpcUaHistoryReadResponse(impl);
// Connect signals
QObject::connect(m_backend, &QOpcUaBackend::historyDataAvailable, impl, &QOpcUaHistoryReadResponseImpl::handleDataAvailable);
QObject::connect(impl, &QOpcUaHistoryReadResponseImpl::historyReadRawRequested, this, &QOpen62541Client::handleHistoryReadRawRequested);
QObject::connect(this, &QOpen62541Client::historyReadRequestError, impl, &QOpcUaHistoryReadResponseImpl::handleRequestError);
auto success = handleHistoryReadRawRequested(request, {}, false, impl->handle());
if (!success) {
delete result;
return nullptr;
}
return result;
}
QOpcUaHistoryReadResponse *QOpen62541Client::readHistoryEvents(const QOpcUaHistoryReadEventRequest &request)
{
if (!m_client)
return nullptr;
auto impl = new QOpcUaHistoryReadResponseImpl(request);
const auto result = new QOpcUaHistoryReadResponse(impl);
// Connect signals
QObject::connect(m_backend, &QOpcUaBackend::historyEventsAvailable, impl, &QOpcUaHistoryReadResponseImpl::handleEventsAvailable);
QObject::connect(impl, &QOpcUaHistoryReadResponseImpl::historyReadEventsRequested, this, &QOpen62541Client::handleHistoryReadEventsRequested);
QObject::connect(this, &QOpen62541Client::historyReadRequestError, impl, &QOpcUaHistoryReadResponseImpl::handleRequestError);
const auto success = handleHistoryReadEventsRequested(request, {}, false, impl->handle());
if (!success) {
delete result;
return nullptr;
}
return result;
}
bool QOpen62541Client::addNode(const QOpcUaAddNodeItem &nodeToAdd)
{
return QMetaObject::invokeMethod(m_backend, &Open62541AsyncBackend::addNode,
Qt::QueuedConnection, nodeToAdd);
}
bool QOpen62541Client::deleteNode(const QString &nodeId, bool deleteTargetReferences)
{
return QMetaObject::invokeMethod(m_backend, &Open62541AsyncBackend::deleteNode, Qt::QueuedConnection,
nodeId, deleteTargetReferences);
}
bool QOpen62541Client::addReference(const QOpcUaAddReferenceItem &referenceToAdd)
{
return QMetaObject::invokeMethod(m_backend, &Open62541AsyncBackend::addReference,
Qt::QueuedConnection, referenceToAdd);
}
bool QOpen62541Client::deleteReference(const QOpcUaDeleteReferenceItem &referenceToDelete)
{
return QMetaObject::invokeMethod(m_backend, &Open62541AsyncBackend::deleteReference,
Qt::QueuedConnection, referenceToDelete);
}
QStringList QOpen62541Client::supportedSecurityPolicies() const
{
#ifdef UA_ENABLE_ENCRYPTION
size_t numPolicies = m_hasSha1SignatureSupport ? 6 : 4;
#else
size_t numPolicies = 1;
#endif
QStringList result;
result.reserve(numPolicies);
result.append(QOpcUa::NonePolicy);
#ifdef UA_ENABLE_ENCRYPTION
// Sort by strength
if (m_hasSha1SignatureSupport) {
result.append(QOpcUa::Basic128Rsa15Policy);
result.append(QOpcUa::Basic256Policy);
}
result.append(QOpcUa::Aes128Sha256RsaOaepPolicy);
result.append(QOpcUa::Basic256Sha256Policy);
result.append(QOpcUa::Aes256Sha256RsaPssPolicy);
#endif
return result;
}
QList<QOpcUaUserTokenPolicy::TokenType> QOpen62541Client::supportedUserTokenTypes() const
{
return QList<QOpcUaUserTokenPolicy::TokenType> {
QOpcUaUserTokenPolicy::TokenType::Anonymous,
QOpcUaUserTokenPolicy::TokenType::Username,
#ifdef UA_ENABLE_ENCRYPTION
QOpcUaUserTokenPolicy::Certificate
#endif
};
}
bool QOpen62541Client::handleHistoryReadRawRequested(const QOpcUaHistoryReadRawRequest &request, const QList<QByteArray> &continuationPoints,
bool releaseContinuationPoints, quint64 handle)
{
const auto success = QMetaObject::invokeMethod(m_backend, &Open62541AsyncBackend::readHistoryRaw,
Qt::QueuedConnection, request, continuationPoints,
releaseContinuationPoints, handle);
if (!success)
emit historyReadRequestError(handle);
return success;
}
bool QOpen62541Client::registerNodes(const QStringList &nodesToRegister)
{
return QMetaObject::invokeMethod(m_backend, &Open62541AsyncBackend::registerNodes,
Qt::QueuedConnection, nodesToRegister);
}
bool QOpen62541Client::unregisterNodes(const QStringList &nodesToUnregister)
{
return QMetaObject::invokeMethod(m_backend, &Open62541AsyncBackend::unregisterNodes,
Qt::QueuedConnection, nodesToUnregister);
}
bool QOpen62541Client::handleHistoryReadEventsRequested(const QOpcUaHistoryReadEventRequest &request, const QList<QByteArray> &continuationPoints,
bool releaseContinuationPoints, quint64 handle)
{
const auto success = QMetaObject::invokeMethod(m_backend, &Open62541AsyncBackend::readHistoryEvents,
Qt::QueuedConnection, request, continuationPoints,
releaseContinuationPoints, handle);
if (!success)
emit historyReadRequestError(handle);
return success;
}
QT_END_NAMESPACE