Source code
Revision control
Copy as Markdown
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:critical reason:network-protocol
#include "qmqttclient.h"
#include "qmqttclient_p.h"
#ifdef QT_MQTT_WITH_WEBSOCKETS
#include "transportLayers/mqtt_websocket_io_p.h"
#include "transportLayers/mqtt_secure_websocket_io_p.h"
#endif // QT_MQTT_WITH_WEBSOCKETS
#include <QtCore/QLoggingCategory>
#include <QtCore/QUuid>
#include <QtCore/QtEndian>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcMqttClient, "qt.mqtt.client")
/*!
\class QMqttClient
\inmodule QtMqtt
\brief The QMqttClient class represents the central access communicating
with an MQTT broker.
An MQTT client is a program or device that uses MQTT to create a network
connection to an MQTT server, also called a \e broker. The connection
request must contain a unique client identifier. Optionally, it can contain
a Will Topic, Will Message, user name, and password.
Once a connection is created, a client can send messages that other clients
might be interested in receiving, subscribe to request notifications on
topics, unsubscribe to remove a request for notifications, and disconnect
from the broker.
*/
/*!
\property QMqttClient::clientId
\brief This property holds the client's identifier value.
Each client needs to have a unique ID to be able to connect to an MQTT
broker. If no client ID is specified by the user, one will be generated
automatically when a connection is established.
*/
/*!
\property QMqttClient::hostname
\brief This property holds the hostname of the MQTT broker to connect to.
If no transport is specified via setTransport(), the client will instantiate
a socket connection to the specified hostname itself.
*/
/*!
\property QMqttClient::port
\brief This property holds the port to connect to the MQTT broker.
If no transport is specified via setTransport(), the client will instantiate
a socket connection to a host with this port number.
*/
/*!
\property QMqttClient::keepAlive
\brief This property holds the interval at which regular ping messages are
sent to the broker.
Once a connection to a broker is established, the client needs to send
frequent updates to propagate it can still be reached. The interval between
those updates is specified by this property.
The interval is specified in seconds.
If the broker does not respond within a grace period the connection will be
closed.
\sa autoKeepAlive(), requestPing(), pingResponseReceived()
*/
/*!
\property QMqttClient::protocolVersion
\brief This property holds the MQTT standard version to use for connections.
Specifies the version of the standard the client uses for connecting to a
broker. Valid values are:
\list
\li 3: MQTT standard version 3.1.
\li 4: MQTT standard version 3.1.1, often referred to MQTT 4.
\li 5: MQTT Standard 5.0.
\endlist
*/
/*!
\property QMqttClient::state
\brief This property holds the current state of the client.
*/
/*!
\property QMqttClient::error
\brief Specifies the current error of the client.
*/
/*!
\property QMqttClient::username
\brief This property holds the user name for connecting to a broker.
*/
/*!
\property QMqttClient::password
\brief This property holds the password for connecting to a broker.
*/
/*!
\property QMqttClient::cleanSession
\brief This property holds the state after connecting to a broker.
*/
/*!
\property QMqttClient::willTopic
\brief This property holds the Will Topic.
*/
/*!
\property QMqttClient::willMessage
\brief This property holds the payload of a Will Message.
*/
/*!
\property QMqttClient::willQoS
\brief This property holds the level of QoS for sending and storing the
Will Message.
*/
/*!
\property QMqttClient::willRetain
\brief This property holds whether the Will Message should be retained on
the broker for future subscribers to receive.
*/
/*!
\property QMqttClient::autoKeepAlive
\since 5.14
\brief This property holds whether the client will automatically manage
keep alive messages to the server.
If this property is \c true, then the client will automatically send a
ping message to the server at the keepAlive interval.
Otherwise, a user will have to manually invoke requestPing
within the specified interval of the connection. If no ping has been
sent within the interval, the server will disconnect.
The default of this property is \c true.
\sa keepAlive(), requestPing(), serverConnectionProperties(), pingResponseReceived()
*/
/*!
\property QMqttClient::readBufferSize
\since 6.13
\brief This property holds the size of the internal read buffer.
The property limits the amount of data that the client can receive
before you call read() or readAll().
A read buffer size of 0 (the default) means that the buffer has no size
limit, ensuring that no data is lost.
\note The limit is not applicable for a plain \c TransportType::IODevice
transport type.
*/
/*!
\enum QMqttClient::TransportType
This enum type specifies the connection method to be used to instantiate a
connection to a broker.
\value IODevice
The transport uses a class based on a QIODevice.
\value AbstractSocket
The transport uses a class based on a QAbstractSocket.
\value SecureSocket
The transport uses a class based on a QSslSocket.
\value [since 6.10] WebSocket
The transport uses a class based on a QIODevice,
which uses a QWebSocket internally.
\value [since 6.10] SecureWebSocket
The transport uses a class based on a QIODevice,
which uses a secure QWebSocket internally.
*/
/*!
\enum QMqttClient::ClientState
This enum type specifies the states a client can enter.
\value Disconnected
The client is disconnected from the broker.
\value Connecting
A connection request has been made, but the broker has not approved
the connection yet.
\value Connected
The client is connected to the broker.
*/
/*!
\enum QMqttClient::ClientError
This enum type specifies the error state of a client.
\value NoError
No error occurred.
\value InvalidProtocolVersion
The broker does not accept a connection using the specified protocol
version.
\value IdRejected
The client ID is malformed. This might be related to its length.
\value ServerUnavailable
The network connection has been established, but the service is
unavailable on the broker side.
\value BadUsernameOrPassword
The data in the username or password is malformed.
\value NotAuthorized
The client is not authorized to connect.
\value TransportInvalid
The underlying transport caused an error. For example, the connection
might have been interrupted unexpectedly.
\value ProtocolViolation
The client encountered a protocol violation, and therefore closed the
connection.
\value UnknownError
An unknown error occurred.
\value Mqtt5SpecificError
The error is related to MQTT protocol level 5. A reason code might
provide more details.
*/
/*!
\enum QMqttClient::ProtocolVersion
This enum specifies the protocol version of the MQTT standard to use during
communication with a broker.
\value MQTT_3_1
MQTT Standard 3.1
\value MQTT_3_1_1
MQTT Standard 3.1.1, publicly referred to as version 4
\value MQTT_5_0
MQTT Standard 5.0
*/
/*!
\fn QMqttClient::connected()
This signal is emitted when a connection has been established.
*/
/*!
\fn QMqttClient::disconnected()
This signal is emitted when a connection has been closed. A connection may
be closed when disconnectFromHost() is called or when the broker
disconnects.
*/
/*!
\fn QMqttClient::messageReceived(const QByteArray &message, const QMqttTopicName &topic)
This signal is emitted when a new message has been received. The category of
the message is specified by \a topic with the content being \a message.
*/
/*!
\fn QMqttClient::messageStatusChanged(qint32 id, QMqtt::MessageStatus s, const QMqttMessageStatusProperties &properties);
\since 5.12
This signal is emitted when the status for the message identified by \a id
changes. \a s specifies the new status of the message, and
\a properties specify additional properties provided by the server.
*/
/*!
\fn QMqttClient::messageSent(qint32 id)
Indicates that a message that was sent via the publish() function has been
received by the broker. The \a id is the same as returned by \c publish() to
help tracking the status of the message.
*/
/*!
\fn QMqttClient::pingResponseReceived()
This signal is emitted after the broker responds to a requestPing() call or
a keepAlive() ping message, and the connection is still valid.
*/
/*!
\fn QMqttClient::brokerSessionRestored()
This signal is emitted after a client has successfully connected to a broker
with the cleanSession property set to \c false, and the broker has restored
the session.
Sessions can be restored if a client has connected previously using the same
clientId.
*/
/*!
\since 5.12
\fn QMqttClient::authenticationRequested(const QMqttAuthenticationProperties &p)
This signal is emitted after a client invoked QMqttClient::connectToHost or
QMqttClient::connectToHostEncrypted and before the connection is
established. In extended authentication, a broker might request additional
details which need to be provided by invoking QMqttClient::authenticate.
\a p specifies properties provided by the broker.
\note Extended authentication is part of the MQTT 5.0 standard and can
only be used when the client specifies MQTT_5_0 as ProtocolVersion.
\sa authenticationFinished(), authenticate()
*/
/*!
\since 5.12
\fn QMqttClient::authenticationFinished(const QMqttAuthenticationProperties &p)
This signal is emitted after extended authentication has finished. \a p
specifies available details on the authentication process.
After successful authentication QMqttClient::connected is emitted.
\note Extended authentication is part of the MQTT 5.0 standard and can
only be used when the client specifies MQTT_5_0 as ProtocolVersion.
\sa authenticationRequested(), authenticate()
*/
/*!
\since 6.13
\fn QMqttClient::readBufferSizeChanged(qint64 size)
This signal is emitted when the \a size of internal read buffer is changed.
\sa readBufferSize()
*/
/*!
Creates a new MQTT client instance with the specified \a parent.
*/
QMqttClient::QMqttClient(QObject *parent) : QObject(*(new QMqttClientPrivate(this)), parent)
{
Q_D(QMqttClient);
d->m_connection.setClientPrivate(d);
}
/*!
Deletes a MQTT client. If the MQTT client was not already disconnected from the MQTT broker,
it will be disconnected from automatically.
\note If a transport is specified with setTransport it must be valid at this point
*/
QMqttClient::~QMqttClient()
{
Q_D(QMqttClient);
if (d->m_connection.internalState() == QMqttConnection::BrokerConnected) {
d->m_connection.setClientDestruction();
disconnectFromHost();
}
d->m_connection.disconnectAndResetTransport();
}
/*!
Sets the transport to \a device. A transport can be either a socket type
or derived from QIODevice and is specified by \a transport.
\note The transport can only be exchanged if the MQTT client is in the
\l Disconnected state.
\note Setting a custom transport for a client does not pass over responsibility
on connection management or ownership. The transport has to be opened for QIODevice based
transports or connected for socket type transports before calling QMqttClient::connectToHost().
*/
void QMqttClient::setTransport(QIODevice *device, QMqttClient::TransportType transport)
{
Q_D(QMqttClient);
if (d->m_state != Disconnected) {
qCDebug(lcMqttClient) << "Changing transport layer while connected is not possible.";
return;
}
d->m_connection.setTransport(device, transport);
// Set the currently active buffer size for a new transport.
d->m_connection.setReadBufferSize(d->m_readBufferSize);
}
/*!
Returns the transport used for communication with the broker.
*/
QIODevice *QMqttClient::transport() const
{
Q_D(const QMqttClient);
return d->m_connection.transport();
}
/*!
\since 6.13
Limits the read buffer of the transport used by this client to
\a size bytes. Triggers warning message if the limit could not
be applied.
The transport does not buffer more than \a size bytes: it stops
reading from the network until the client has taken the data out of
the buffer. Use this function to protect the application against a
broker that sends more data than the application can handle, which
could make it run out of memory. A size of \c 0 means that the buffer
has no size limit, which is the default of the underlying transport.
\code
QMqttClient client;
client.setHostname(QStringLiteral("broker.example.com"));
client.setPort(1883);
client.setReadBufferSize(64 * 1024);
client.connectToHost();
\endcode
The size can be set before connecting. It is applied to the transport
when connectToHost() creates it, or when setTransport() sets it. The size
has no effect if the transport is a plain \l QMqttClient::IODevice.
Before Qt 6.13, the \c QMqttClient class provided no way to limit the
amount of incoming data. However, applications using older versions of
\c QMqttClient with certain types of transport, such as transports derived
from \c AbstractSocket, can protect themselves against memory exhaustion
caused by a broker, as shown in the example below:
\code
QMqttClient client;
client.setHostname(QStringLiteral("broker.example.com"));
client.setPort(1883);
QObject::connect(&client, &QMqttClient::connected, &client, [&client]() {
QAbstractSocket *socket = dynamic_cast<QAbstractSocket *>(client.transport());
if (socket)
socket->setReadBufferSize(64 * 1024);
});
client.connectToHost();
\endcode
\note If you use a custom QIODevice-based transport, implement
support for a read buffer limit in the device and configure it
before calling setTransport(). Some devices already support it.
For example, \l QSerialPort provides QSerialPort::setReadBufferSize().
\note This limit and the MQTT 5 maximum packet size protect against
different things. The maximum packet size limits the size of an
individual packet, not the total size of a buffer.
\sa setTransport(), QMqttConnectionProperties::setMaximumPacketSize()
*/
void QMqttClient::setReadBufferSize(qint64 size)
{
Q_D(QMqttClient);
if (d->m_readBufferSize == size)
return;
// Store the size even if there is no transport yet. setTransport() and
// connectToHost() apply it to the transport they set or create.
d->m_readBufferSize = size;
d->m_connection.setReadBufferSize(size);
emit readBufferSizeChanged(d->m_readBufferSize);
}
/*!
Adds a new subscription to receive notifications on \a topic. The parameter
\a qos specifies the level at which security messages are received. For more
information about the available QoS levels, see \l {Quality of Service}.
This function returns a pointer to a \l QMqttSubscription. If the same topic
is subscribed twice, the return value points to the same subscription
instance. The MQTT client is the owner of the subscription.
*/
QMqttSubscription *QMqttClient::subscribe(const QMqttTopicFilter &topic, quint8 qos)
{
return subscribe(topic, QMqttSubscriptionProperties(), qos);
}
/*!
\since 5.12
Adds a new subscription to receive notifications on \a topic. The parameter
\a properties specifies additional subscription properties to be validated
by the broker. The parameter \a qos specifies the level at which security
messages are received. For more information about the available QoS levels,
see \l {Quality of Service}.
This function returns a pointer to a \l QMqttSubscription. If the same topic
is subscribed twice, the return value points to the same subscription
instance. The MQTT client is the owner of the subscription.
\note \a properties will only be passed to the broker when the client
specifies MQTT_5_0 as ProtocolVersion.
*/
QMqttSubscription *QMqttClient::subscribe(const QMqttTopicFilter &topic, const QMqttSubscriptionProperties &properties, quint8 qos)
{
Q_D(QMqttClient);
if (d->m_state != QMqttClient::Connected)
return nullptr;
return d->m_connection.sendControlSubscribe(topic, qos, properties);
}
/*!
Unsubscribes from \a topic. No notifications will be sent to any of the
subscriptions made by calling subscribe().
\note If a client disconnects from a broker without unsubscribing, the
broker will store all messages and publish them on the next reconnect.
*/
void QMqttClient::unsubscribe(const QMqttTopicFilter &topic)
{
unsubscribe(topic, QMqttUnsubscriptionProperties());
}
/*!
\since 5.12
Unsubscribes from \a topic. No notifications will be sent to any of the
subscriptions made by calling subscribe(). \a properties specifies
additional user properties to be passed to the broker.
\note If a client disconnects from a broker without unsubscribing, the
broker will store all messages and publish them on the next reconnect.
\note \a properties will only be passed to the broker when the client
specifies MQTT_5_0 as ProtocolVersion.
*/
void QMqttClient::unsubscribe(const QMqttTopicFilter &topic, const QMqttUnsubscriptionProperties &properties)
{
Q_D(QMqttClient);
d->m_connection.sendControlUnsubscribe(topic, properties);
}
/*!
Publishes a \a message to the broker with the specified \a topic. \a qos
specifies the QoS level required for transferring the message.
If \a retain is set to \c true, the message will stay on the broker for
other clients to connect and receive the message.
Returns an ID that is used internally to identify the message.
*/
qint32 QMqttClient::publish(const QMqttTopicName &topic, const QByteArray &message, quint8 qos, bool retain)
{
return publish(topic, QMqttPublishProperties(), message, qos, retain);
}
/*!
\since 5.12
Publishes a \a message to the broker with the specified \a properties and
\a topic. \a qos specifies the QoS level required for transferring
the message.
If \a retain is set to \c true, the message will stay on the broker for
other clients to connect and receive the message.
Returns an ID that is used internally to identify the message.
\note \a properties will only be passed to the broker when the client
specifies MQTT_5_0 as ProtocolVersion.
*/
qint32 QMqttClient::publish(const QMqttTopicName &topic, const QMqttPublishProperties &properties,
const QByteArray &message, quint8 qos, bool retain)
{
Q_D(QMqttClient);
if (qos > 2)
return -1;
if (d->m_state != QMqttClient::Connected)
return -1;
return d->m_connection.sendControlPublish(topic, message, qos, retain, properties);
}
/*!
Sends a ping message to the broker and expects a reply.
If the connection is active and \l autoKeepAlive is \c true, then calling this
function will fail as the client is responsible for managing this process.
Using \c requestPing() manually requires a call every time within the \l keepAlive
interval as long as the connection is active.
To check whether the ping is successful, connect to the
\l pingResponseReceived() signal.
Returns \c true if the ping request could be sent.
\sa pingResponseReceived(), autoKeepAlive(), keepAlive()
*/
bool QMqttClient::requestPing()
{
Q_D(QMqttClient);
return d->m_connection.sendControlPingRequest(false);
}
QString QMqttClient::hostname() const
{
Q_D(const QMqttClient);
return d->m_hostname;
}
quint16 QMqttClient::port() const
{
Q_D(const QMqttClient);
return d->m_port;
}
/*!
\since 6.10
Set a web socket transport type and initiates a connection to the MQTT broker.
Earlier transport type is replaced.
If \a webSocket is given, hostname and port is ignored, and connection is done
with that websocket. webSocket is owned by the calling code, and must be valid
for the lifetime of the QMqttClient object.
*/
void QMqttClient::connectToHostWebSocket(QWebSocket *webSocket)
{
#ifndef QT_MQTT_WITH_WEBSOCKETS
Q_UNUSED(webSocket);
qWarning() << " Qt built without websocket support, connect skipped";
#else
Q_D(QMqttClient);
d->m_connection.connectTransport(
TransportType::WebSocket,
std::make_shared<QMqttWebSocketIO>(nullptr, webSocket));
connectToHost();
d->m_connection.m_transportIsSet = false;
#endif // QT_MQTT_WITH_WEBSOCKETS
}
/*!
\since 6.10
Set a secure websocket transport type and initiates a connection to the MQTT broker.
Earlier transport type is replaced.
If \a webSocket is given, hostname and port is ignored, and connection is done
with that websocket. webSocket is owned by the calling code, and must be valid
for the lifetime of the QMqttClient object.
*/
void QMqttClient::connectToHostWebSocketEncrypted(QWebSocket *webSocket)
{
#ifndef QT_MQTT_WITH_WEBSOCKETS
Q_UNUSED(webSocket);
qWarning() << " Qt built without websocket support, connect skipped";
#else
Q_D(QMqttClient);
d->m_connection.connectTransport(
TransportType::SecureWebSocket,
std::make_shared<QMqttSecureWebSocketIO>(nullptr, webSocket));
connectToHost();
d->m_connection.m_transportIsSet = false;
#endif // QT_MQTT_WITH_WEBSOCKETS
}
/*!
Initiates a connection to the MQTT broker.
*/
void QMqttClient::connectToHost()
{
connectToHost(false, QString());
}
#ifndef QT_NO_SSL
/*!
\since 5.14
Initiates an encrypted connection to the MQTT broker.
\a conf specifies the SSL configuration to be used for the connection
*/
void QMqttClient::connectToHostEncrypted(const QSslConfiguration &conf)
{
Q_D(QMqttClient);
d->m_connection.m_sslConfiguration = conf;
connectToHost(true, QString());
}
#endif
void QMqttClient::connectToHost(bool encrypted, const QString &sslPeerName)
{
Q_D(QMqttClient);
if (state() == QMqttClient::Connecting) {
qCDebug(lcMqttClient) << "Connection request currently ongoing.";
return;
}
if (state() == QMqttClient::Connected) {
qCDebug(lcMqttClient) << "Already connected to a broker. Rejecting connection request.";
return;
}
if (!d->m_connection.ensureTransport(encrypted)) {
qCDebug(lcMqttClient) << "Could not ensure connection.";
d->setStateAndError(Disconnected, TransportInvalid);
return;
}
d->m_error = QMqttClient::NoError; // Fresh reconnect, unset error
d->setStateAndError(Connecting);
if (d->m_cleanSession)
d->m_connection.cleanSubscriptions();
if (!d->m_connection.ensureTransportOpen(sslPeerName)) {
qCDebug(lcMqttClient) << "Could not ensure that connection is open.";
d->setStateAndError(Disconnected, TransportInvalid);
return;
}
// We can safely set the active buffer size for the new transport here,
// as the transport type has been determined by ensureTransport() or
// connectToHostWebSocket()/connectToHostWebSocketEncrypted() at this point.
d->m_connection.setReadBufferSize(d->m_readBufferSize);
// Once transport has connected, it will invoke
// QMqttConnection::sendControlConnect to
// handshake with the broker
}
/*!
Disconnects from the MQTT broker.
\note This function may block while waiting for pending data
to be written to the transport.
See \l {Threading and Blocking Operations} for recommended approaches
on handling it.
*/
void QMqttClient::disconnectFromHost()
{
Q_D(QMqttClient);
switch (d->m_connection.internalState()) {
case QMqttConnection::BrokerConnected:
case QMqttConnection::ClientDestruction:
d->m_connection.sendControlDisconnect();
break;
case QMqttConnection::BrokerDisconnected:
break;
case QMqttConnection::BrokerConnecting:
case QMqttConnection::BrokerWaitForConnectAck:
d->m_connection.m_transport->close();
break;
}
}
QMqttClient::ClientState QMqttClient::state() const
{
Q_D(const QMqttClient);
return d->m_state;
}
QString QMqttClient::username() const
{
Q_D(const QMqttClient);
return d->m_username;
}
QString QMqttClient::password() const
{
Q_D(const QMqttClient);
return d->m_password;
}
bool QMqttClient::cleanSession() const
{
Q_D(const QMqttClient);
return d->m_cleanSession;
}
QString QMqttClient::willTopic() const
{
Q_D(const QMqttClient);
return d->m_willTopic;
}
quint8 QMqttClient::willQoS() const
{
Q_D(const QMqttClient);
return d->m_willQoS;
}
QByteArray QMqttClient::willMessage() const
{
Q_D(const QMqttClient);
return d->m_willMessage;
}
bool QMqttClient::willRetain() const
{
Q_D(const QMqttClient);
return d->m_willRetain;
}
bool QMqttClient::autoKeepAlive() const
{
Q_D(const QMqttClient);
return d->m_autoKeepAlive;
}
qint64 QMqttClient::readBufferSize() const
{
Q_D(const QMqttClient);
return d->m_readBufferSize;
}
/*!
\since 5.12
Sets the connection properties to \a prop. \l QMqttConnectionProperties
can be used to ask the server to use a specific feature set. After a
connection request the server response can be obtained by calling
\l QMqttClient::serverConnectionProperties.
\note The connection properties can only be set if the MQTT client is in the
\l Disconnected state.
\note QMqttConnectionProperties can only be used when the client specifies
MQTT_5_0 as ProtocolVersion.
*/
void QMqttClient::setConnectionProperties(const QMqttConnectionProperties &prop)
{
Q_D(QMqttClient);
d->m_connectionProperties = prop;
}
/*!
\since 5.12
Returns the connection properties the client requests to the broker.
\note QMqttConnectionProperties can only be used when the client specifies
MQTT_5_0 as ProtocolVersion.
*/
QMqttConnectionProperties QMqttClient::connectionProperties() const
{
Q_D(const QMqttClient);
return d->m_connectionProperties;
}
/*!
\since 5.12
Sets the last will properties to \a prop. QMqttLastWillProperties allows
to set additional features for the last will message stored at the broker.
\note The connection properties can only be set if the MQTT client is in the
\l Disconnected state.
\note QMqttLastWillProperties can only be used when the client specifies
MQTT_5_0 as ProtocolVersion.
*/
void QMqttClient::setLastWillProperties(const QMqttLastWillProperties &prop)
{
Q_D(QMqttClient);
d->m_lastWillProperties = prop;
}
/*!
\since 5.12
Returns the last will properties.
\note QMqttLastWillProperties can only be used when the client specifies
MQTT_5_0 as ProtocolVersion.
*/
QMqttLastWillProperties QMqttClient::lastWillProperties() const
{
Q_D(const QMqttClient);
return d->m_lastWillProperties;
}
/*!
\since 5.12
Returns the QMqttServerConnectionProperties the broker returned after a
connection attempt.
This can be used to verify that client side connection properties set by
QMqttClient::setConnectionProperties have been accepted by the broker. Also,
in case of a failed connection attempt, it can be used for connection
diagnostics.
\note QMqttServerConnectionProperties can only be used when the client
specifies MQTT_5_0 as ProtocolVersion.
\sa connectionProperties()
*/
QMqttServerConnectionProperties QMqttClient::serverConnectionProperties() const
{
Q_D(const QMqttClient);
return d->m_serverConnectionProperties;
}
/*!
\since 5.12
Sends an authentication request to the broker. \a prop specifies
the required information to fulfill the authentication request.
This function should only be called after a
QMqttClient::authenticationRequested signal has been emitted.
\note Extended authentication is part of the MQTT 5.0 standard and can
only be used when the client specifies MQTT_5_0 as ProtocolVersion.
\sa authenticationRequested(), authenticationFinished()
*/
void QMqttClient::authenticate(const QMqttAuthenticationProperties &prop)
{
Q_D(QMqttClient);
if (protocolVersion() != QMqttClient::MQTT_5_0) {
qCDebug(lcMqttClient) << "Authentication is only supported on protocol level 5.";
return;
}
if (state() == QMqttClient::Disconnected) {
qCDebug(lcMqttClient) << "Cannot send authentication request while disconnected.";
return;
}
d->m_connection.sendControlAuthenticate(prop);
}
QMqttClient::ClientError QMqttClient::error() const
{
Q_D(const QMqttClient);
return d->m_error;
}
QMqttClient::ProtocolVersion QMqttClient::protocolVersion() const
{
Q_D(const QMqttClient);
return d->m_protocolVersion;
}
QString QMqttClient::clientId() const
{
Q_D(const QMqttClient);
return d->m_clientId;
}
quint16 QMqttClient::keepAlive() const
{
Q_D(const QMqttClient);
return d->m_keepAlive;
}
void QMqttClient::setHostname(const QString &hostname)
{
Q_D(QMqttClient);
if (state() != QMqttClient::Disconnected) {
qCDebug(lcMqttClient) << "Changing hostname while connected is not possible.";
return;
}
if (d->m_hostname == hostname)
return;
d->m_hostname = hostname;
emit hostnameChanged(hostname);
}
void QMqttClient::setPort(quint16 port)
{
Q_D(QMqttClient);
if (state() != QMqttClient::Disconnected) {
qCDebug(lcMqttClient) << "Changing port while connected is not possible.";
return;
}
if (d->m_port == port)
return;
d->m_port = port;
emit portChanged(port);
}
void QMqttClient::setClientId(const QString &clientId)
{
Q_D(QMqttClient);
if (state() != QMqttClient::Disconnected) {
qCDebug(lcMqttClient) << "Changing client ID while connected is not possible.";
return;
}
d->setClientId(clientId);
}
void QMqttClient::setKeepAlive(quint16 keepAlive)
{
Q_D(QMqttClient);
if (d->m_keepAlive == keepAlive)
return;
if (state() != QMqttClient::Disconnected) {
qCDebug(lcMqttClient) << "Changing keepAlive while connected is not possible.";
return;
}
d->m_keepAlive = keepAlive;
emit keepAliveChanged(keepAlive);
}
void QMqttClient::setProtocolVersion(ProtocolVersion protocolVersion)
{
Q_D(QMqttClient);
if (state() != QMqttClient::Disconnected) {
qCDebug(lcMqttClient) << "Changing protocol version while connected is not possible.";
return;
}
if (d->m_protocolVersion == protocolVersion)
return;
if (protocolVersion < 3 || protocolVersion > 5)
return;
d->m_protocolVersion = protocolVersion;
emit protocolVersionChanged(protocolVersion);
}
void QMqttClient::setState(ClientState state)
{
Q_D(QMqttClient);
if (d->m_state == state)
return;
d->m_state = state;
emit stateChanged(state);
if (d->m_state == QMqttClient::Disconnected)
emit disconnected();
else if (d->m_state == QMqttClient::Connected)
emit connected();
}
void QMqttClient::setUsername(const QString &username)
{
Q_D(QMqttClient);
if (state() != QMqttClient::Disconnected) {
qCDebug(lcMqttClient) << "Changing username while connected is not possible.";
return;
}
if (d->m_username == username)
return;
d->m_username = username;
emit usernameChanged(username);
}
void QMqttClient::setPassword(const QString &password)
{
Q_D(QMqttClient);
if (state() != QMqttClient::Disconnected) {
qCDebug(lcMqttClient) << "Changing password while connected is not possible.";
return;
}
if (d->m_password == password)
return;
d->m_password = password;
emit passwordChanged(password);
}
void QMqttClient::setCleanSession(bool cleanSession)
{
Q_D(QMqttClient);
if (state() != QMqttClient::Disconnected) {
qCDebug(lcMqttClient) << "Changing clean session while connected is not possible.";
return;
}
if (d->m_cleanSession == cleanSession)
return;
d->m_cleanSession = cleanSession;
emit cleanSessionChanged(cleanSession);
}
void QMqttClient::setWillTopic(const QString &willTopic)
{
Q_D(QMqttClient);
if (state() != QMqttClient::Disconnected) {
qCDebug(lcMqttClient) << "Changing will topic while connected is not possible.";
return;
}
if (d->m_willTopic == willTopic)
return;
d->m_willTopic = willTopic;
emit willTopicChanged(willTopic);
}
void QMqttClient::setWillQoS(quint8 willQoS)
{
Q_D(QMqttClient);
if (state() != QMqttClient::Disconnected) {
qCDebug(lcMqttClient) << "Changing will qos while connected is not possible.";
return;
}
if (d->m_willQoS == willQoS)
return;
d->m_willQoS = willQoS;
emit willQoSChanged(willQoS);
}
void QMqttClient::setWillMessage(const QByteArray &willMessage)
{
Q_D(QMqttClient);
if (state() != QMqttClient::Disconnected) {
qCDebug(lcMqttClient) << "Changing will message while connected is not possible.";
return;
}
if (d->m_willMessage == willMessage)
return;
d->m_willMessage = willMessage;
emit willMessageChanged(willMessage);
}
void QMqttClient::setWillRetain(bool willRetain)
{
Q_D(QMqttClient);
if (state() != QMqttClient::Disconnected) {
qCDebug(lcMqttClient) << "Changing will retain while connected is not possible.";
return;
}
if (d->m_willRetain == willRetain)
return;
d->m_willRetain = willRetain;
emit willRetainChanged(willRetain);
}
void QMqttClient::setAutoKeepAlive(bool autoKeepAlive)
{
Q_D(QMqttClient);
if (state() != QMqttClient::Disconnected) {
qCDebug(lcMqttClient) << "Changing autoKeepAlive while connected is not possible.";
return;
}
if (d->m_autoKeepAlive == autoKeepAlive)
return;
d->m_autoKeepAlive = autoKeepAlive;
emit autoKeepAliveChanged(d->m_autoKeepAlive);
}
void QMqttClient::setError(ClientError e)
{
Q_D(QMqttClient);
if (d->m_error == e)
return;
d->m_error = e;
emit errorChanged(d->m_error);
}
QMqttClientPrivate::QMqttClientPrivate(QMqttClient *c)
: QObjectPrivate()
{
m_client = c;
m_clientId = QUuid::createUuid().toString(QUuid::Id128);
m_clientId.resize(23);
#ifdef QT_BUILD_INTERNAL
// Some test servers require a username token
if (qEnvironmentVariableIsSet("QT_MQTT_TEST_USERNAME"))
m_username = qEnvironmentVariable("QT_MQTT_TEST_USERNAME");
if (qEnvironmentVariableIsSet("QT_MQTT_TEST_PASSWORD"))
m_password = qEnvironmentVariable("QT_MQTT_TEST_PASSWORD");
if (qEnvironmentVariableIsSet("QT_MQTT_TEST_CLIENTID"))
m_clientId = qEnvironmentVariable("QT_MQTT_TEST_CLIENTID");
#endif
}
QMqttClientPrivate::~QMqttClientPrivate()
{
}
void QMqttClientPrivate::setStateAndError(QMqttClient::ClientState s, QMqttClient::ClientError e)
{
Q_Q(QMqttClient);
if (e != QMqttClient::NoError)
q->setError(e);
q->setState(s);
}
void QMqttClientPrivate::setClientId(const QString &id)
{
Q_Q(QMqttClient);
if (m_clientId == id)
return;
m_clientId = id;
emit q->clientIdChanged(id);
}
QT_END_NAMESPACE