1 | // Copyright (C) 2017 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "qmqttclient.h" |
5 | #include "qmqttclient_p.h" |
6 | |
7 | #include <QtCore/QLoggingCategory> |
8 | #include <QtCore/QUuid> |
9 | #include <QtCore/QtEndian> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | Q_LOGGING_CATEGORY(lcMqttClient, "qt.mqtt.client" ) |
14 | |
15 | /*! |
16 | \class QMqttClient |
17 | |
18 | \inmodule QtMqtt |
19 | \brief The QMqttClient class represents the central access communicating |
20 | with an MQTT broker. |
21 | |
22 | An MQTT client is a program or device that uses MQTT to create a network |
23 | connection to an MQTT server, also called a \e broker. The connection |
24 | request must contain a unique client identifier. Optionally, it can contain |
25 | a Will Topic, Will Message, user name, and password. |
26 | |
27 | Once a connection is created, a client can send messages that other clients |
28 | might be interested in receiving, subscribe to request notifications on |
29 | topics, unsubscribe to remove a request for notifications, and disconnect |
30 | from the broker. |
31 | */ |
32 | |
33 | /*! |
34 | \property QMqttClient::clientId |
35 | \brief This property holds the client's identifier value. |
36 | |
37 | Each client needs to have a unique ID to be able to connect to an MQTT |
38 | broker. If no client ID is specified by the user, one will be generated |
39 | automatically when a connection is established. |
40 | */ |
41 | |
42 | /*! |
43 | \property QMqttClient::hostname |
44 | \brief This property holds the hostname of the MQTT broker to connect to. |
45 | |
46 | If no transport is specified via setTransport(), the client will instantiate |
47 | a socket connection to the specified hostname itself. |
48 | */ |
49 | |
50 | /*! |
51 | \property QMqttClient::port |
52 | \brief This property holds the port to connect to the MQTT broker. |
53 | |
54 | If no transport is specified via setTransport(), the client will instantiate |
55 | a socket connection to a host with this port number. |
56 | */ |
57 | |
58 | /*! |
59 | \property QMqttClient::keepAlive |
60 | \brief This property holds the interval at which regular ping messages are |
61 | sent to the broker. |
62 | |
63 | Once a connection to a broker is established, the client needs to send |
64 | frequent updates to propagate it can still be reached. The interval between |
65 | those updates is specified by this property. |
66 | |
67 | The interval is specified in seconds. |
68 | |
69 | If the broker does not respond within a grace period the connection will be |
70 | closed. |
71 | |
72 | \sa autoKeepAlive(), requestPing(), pingResponseReceived() |
73 | */ |
74 | |
75 | /*! |
76 | \property QMqttClient::protocolVersion |
77 | \brief This property holds the MQTT standard version to use for connections. |
78 | |
79 | Specifies the version of the standard the client uses for connecting to a |
80 | broker. Valid values are: |
81 | |
82 | \list |
83 | \li 3: MQTT standard version 3.1. |
84 | \li 4: MQTT standard version 3.1.1, often referred to MQTT 4. |
85 | \endlist |
86 | */ |
87 | |
88 | /*! |
89 | \property QMqttClient::state |
90 | \brief This property holds the current state of the client. |
91 | */ |
92 | |
93 | /*! |
94 | \property QMqttClient::error |
95 | \brief Specifies the current error of the client. |
96 | */ |
97 | |
98 | /*! |
99 | \property QMqttClient::username |
100 | \brief This property holds the user name for connecting to a broker. |
101 | */ |
102 | |
103 | /*! |
104 | \property QMqttClient::password |
105 | \brief This property holds the password for connecting to a broker. |
106 | */ |
107 | |
108 | /*! |
109 | \property QMqttClient::cleanSession |
110 | \brief This property holds the state after connecting to a broker. |
111 | */ |
112 | |
113 | /*! |
114 | \property QMqttClient::willTopic |
115 | \brief This property holds the Will Topic. |
116 | */ |
117 | |
118 | /*! |
119 | \property QMqttClient::willMessage |
120 | \brief This property holds the payload of a Will Message. |
121 | */ |
122 | |
123 | /*! |
124 | \property QMqttClient::willQoS |
125 | \brief This property holds the level of QoS for sending and storing the |
126 | Will Message. |
127 | */ |
128 | |
129 | /*! |
130 | \property QMqttClient::willRetain |
131 | \brief This property holds whether the Will Message should be retained on |
132 | the broker for future subscribers to receive. |
133 | */ |
134 | |
135 | /*! |
136 | \property QMqttClient::autoKeepAlive |
137 | \since 5.14 |
138 | \brief This property holds whether the client will automatically manage |
139 | keep alive messages to the server. |
140 | |
141 | If this property is \c true, then the client will automatically send a |
142 | ping message to the server at the keepAlive interval. |
143 | |
144 | Otherwise, a user will have to manually invoke requestPing |
145 | within the specified interval of the connection. If no ping has been |
146 | sent within the interval, the server will disconnect. |
147 | |
148 | The default of this property is \c true. |
149 | |
150 | \sa keepAlive(), requestPing(), serverConnectionProperties(), pingResponseReceived() |
151 | */ |
152 | |
153 | /*! |
154 | \enum QMqttClient::TransportType |
155 | |
156 | This enum type specifies the connection method to be used to instantiate a |
157 | connection to a broker. |
158 | |
159 | \value IODevice |
160 | The transport uses a class based on a QIODevice. |
161 | \value AbstractSocket |
162 | The transport uses a class based on a QAbstractSocket. |
163 | \value SecureSocket |
164 | The transport uses a class based on a QSslSocket. |
165 | */ |
166 | |
167 | /*! |
168 | \enum QMqttClient::ClientState |
169 | |
170 | This enum type specifies the states a client can enter. |
171 | |
172 | \value Disconnected |
173 | The client is disconnected from the broker. |
174 | \value Connecting |
175 | A connection request has been made, but the broker has not approved |
176 | the connection yet. |
177 | \value Connected |
178 | The client is connected to the broker. |
179 | */ |
180 | |
181 | /*! |
182 | \enum QMqttClient::ClientError |
183 | |
184 | This enum type specifies the error state of a client. |
185 | |
186 | \value NoError |
187 | No error occurred. |
188 | \value InvalidProtocolVersion |
189 | The broker does not accept a connection using the specified protocol |
190 | version. |
191 | \value IdRejected |
192 | The client ID is malformed. This might be related to its length. |
193 | \value ServerUnavailable |
194 | The network connection has been established, but the service is |
195 | unavailable on the broker side. |
196 | \value BadUsernameOrPassword |
197 | The data in the username or password is malformed. |
198 | \value NotAuthorized |
199 | The client is not authorized to connect. |
200 | \value TransportInvalid |
201 | The underlying transport caused an error. For example, the connection |
202 | might have been interrupted unexpectedly. |
203 | \value ProtocolViolation |
204 | The client encountered a protocol violation, and therefore closed the |
205 | connection. |
206 | \value UnknownError |
207 | An unknown error occurred. |
208 | \value Mqtt5SpecificError |
209 | The error is related to MQTT protocol level 5. A reason code might |
210 | provide more details. |
211 | */ |
212 | |
213 | /*! |
214 | \enum QMqttClient::ProtocolVersion |
215 | |
216 | This enum specifies the protocol version of the MQTT standard to use during |
217 | communication with a broker. |
218 | |
219 | \value MQTT_3_1 |
220 | MQTT Standard 3.1 |
221 | \value MQTT_3_1_1 |
222 | MQTT Standard 3.1.1, publicly referred to as version 4 |
223 | \value MQTT_5_0 |
224 | MQTT Standard 5.0 |
225 | */ |
226 | |
227 | /*! |
228 | \fn QMqttClient::connected() |
229 | |
230 | This signal is emitted when a connection has been established. |
231 | */ |
232 | |
233 | /*! |
234 | \fn QMqttClient::disconnected() |
235 | |
236 | This signal is emitted when a connection has been closed. A connection may |
237 | be closed when disconnectFromHost() is called or when the broker |
238 | disconnects. |
239 | */ |
240 | |
241 | /*! |
242 | \fn QMqttClient::messageReceived(const QByteArray &message, const QMqttTopicName &topic) |
243 | |
244 | This signal is emitted when a new message has been received. The category of |
245 | the message is specified by \a topic with the content being \a message. |
246 | */ |
247 | |
248 | /*! |
249 | \fn QMqttClient::messageStatusChanged(qint32 id, QMqtt::MessageStatus s, const QMqttMessageStatusProperties &properties); |
250 | \since 5.12 |
251 | |
252 | This signal is emitted when the status for the message identified by \a id |
253 | changes. \a s specifies the new status of the message, and |
254 | \a properties specify additional properties provided by the server. |
255 | */ |
256 | |
257 | /*! |
258 | \fn QMqttClient::messageSent(qint32 id) |
259 | |
260 | Indicates that a message that was sent via the publish() function has been |
261 | received by the broker. The \a id is the same as returned by \c publish() to |
262 | help tracking the status of the message. |
263 | */ |
264 | |
265 | /*! |
266 | \fn QMqttClient::pingResponseReceived() |
267 | |
268 | This signal is emitted after the broker responds to a requestPing() call or |
269 | a keepAlive() ping message, and the connection is still valid. |
270 | */ |
271 | |
272 | /*! |
273 | \fn QMqttClient::brokerSessionRestored() |
274 | |
275 | This signal is emitted after a client has successfully connected to a broker |
276 | with the cleanSession property set to \c false, and the broker has restored |
277 | the session. |
278 | |
279 | Sessions can be restored if a client has connected previously using the same |
280 | clientId. |
281 | */ |
282 | |
283 | /*! |
284 | \since 5.12 |
285 | \fn QMqttClient::authenticationRequested(const QMqttAuthenticationProperties &p) |
286 | |
287 | This signal is emitted after a client invoked QMqttClient::connectToHost or |
288 | QMqttClient::connectToHostEncrypted and before the connection is |
289 | established. In extended authentication, a broker might request additional |
290 | details which need to be provided by invoking QMqttClient::authenticate. |
291 | \a p specifies properties provided by the broker. |
292 | |
293 | \note Extended authentication is part of the MQTT 5.0 standard and can |
294 | only be used when the client specifies MQTT_5_0 as ProtocolVersion. |
295 | |
296 | \sa authenticationFinished(), authenticate() |
297 | */ |
298 | |
299 | /*! |
300 | \since 5.12 |
301 | \fn QMqttClient::authenticationFinished(const QMqttAuthenticationProperties &p) |
302 | |
303 | This signal is emitted after extended authentication has finished. \a p |
304 | specifies available details on the authentication process. |
305 | |
306 | After successful authentication QMqttClient::connected is emitted. |
307 | |
308 | \note Extended authentication is part of the MQTT 5.0 standard and can |
309 | only be used when the client specifies MQTT_5_0 as ProtocolVersion. |
310 | |
311 | \sa authenticationRequested(), authenticate() |
312 | */ |
313 | |
314 | /*! |
315 | Creates a new MQTT client instance with the specified \a parent. |
316 | */ |
317 | QMqttClient::QMqttClient(QObject *parent) : QObject(*(new QMqttClientPrivate(this)), parent) |
318 | { |
319 | Q_D(QMqttClient); |
320 | d->m_connection.setClientPrivate(d); |
321 | } |
322 | |
323 | /*! |
324 | Deletes a MQTT client. If the MQTT client was not already disconnected from the MQTT broker, |
325 | it will be disconnected from automatically. |
326 | */ |
327 | QMqttClient::~QMqttClient() |
328 | { |
329 | Q_D(QMqttClient); |
330 | if (d->m_connection.internalState() == QMqttConnection::BrokerConnected) { |
331 | d->m_connection.setClientDestruction(); |
332 | disconnectFromHost(); |
333 | } |
334 | } |
335 | |
336 | /*! |
337 | Sets the transport to \a device. A transport can be either a socket type |
338 | or derived from QIODevice and is specified by \a transport. |
339 | |
340 | \note The transport can only be exchanged if the MQTT client is in the |
341 | \l Disconnected state. |
342 | |
343 | \note Setting a custom transport for a client does not pass over responsibility |
344 | on connection management. The transport has to be opened for QIODevice based |
345 | transports or connected for socket type transports before calling QMqttClient::connectToHost(). |
346 | */ |
347 | void QMqttClient::setTransport(QIODevice *device, QMqttClient::TransportType transport) |
348 | { |
349 | Q_D(QMqttClient); |
350 | |
351 | if (d->m_state != Disconnected) { |
352 | qCDebug(lcMqttClient) << "Changing transport layer while connected is not possible." ; |
353 | return; |
354 | } |
355 | d->m_connection.setTransport(device, transport); |
356 | } |
357 | |
358 | /*! |
359 | Returns the transport used for communication with the broker. |
360 | */ |
361 | QIODevice *QMqttClient::transport() const |
362 | { |
363 | Q_D(const QMqttClient); |
364 | return d->m_connection.transport(); |
365 | } |
366 | |
367 | /*! |
368 | Adds a new subscription to receive notifications on \a topic. The parameter |
369 | \a qos specifies the level at which security messages are received. For more |
370 | information about the available QoS levels, see \l {Quality of Service}. |
371 | |
372 | This function returns a pointer to a \l QMqttSubscription. If the same topic |
373 | is subscribed twice, the return value points to the same subscription |
374 | instance. The MQTT client is the owner of the subscription. |
375 | */ |
376 | QMqttSubscription *QMqttClient::subscribe(const QMqttTopicFilter &topic, quint8 qos) |
377 | { |
378 | return subscribe(topic, properties: QMqttSubscriptionProperties(), qos); |
379 | } |
380 | |
381 | /*! |
382 | \since 5.12 |
383 | |
384 | Adds a new subscription to receive notifications on \a topic. The parameter |
385 | \a properties specifies additional subscription properties to be validated |
386 | by the broker. The parameter \a qos specifies the level at which security |
387 | messages are received. For more information about the available QoS levels, |
388 | see \l {Quality of Service}. |
389 | |
390 | This function returns a pointer to a \l QMqttSubscription. If the same topic |
391 | is subscribed twice, the return value points to the same subscription |
392 | instance. The MQTT client is the owner of the subscription. |
393 | |
394 | \note \a properties will only be passed to the broker when the client |
395 | specifies MQTT_5_0 as ProtocolVersion. |
396 | */ |
397 | QMqttSubscription *QMqttClient::subscribe(const QMqttTopicFilter &topic, const QMqttSubscriptionProperties &properties, quint8 qos) |
398 | { |
399 | Q_D(QMqttClient); |
400 | |
401 | if (d->m_state != QMqttClient::Connected) |
402 | return nullptr; |
403 | |
404 | return d->m_connection.sendControlSubscribe(topic, qos, properties); |
405 | } |
406 | |
407 | /*! |
408 | Unsubscribes from \a topic. No notifications will be sent to any of the |
409 | subscriptions made by calling subscribe(). |
410 | |
411 | \note If a client disconnects from a broker without unsubscribing, the |
412 | broker will store all messages and publish them on the next reconnect. |
413 | */ |
414 | void QMqttClient::unsubscribe(const QMqttTopicFilter &topic) |
415 | { |
416 | unsubscribe(topic, properties: QMqttUnsubscriptionProperties()); |
417 | } |
418 | |
419 | /*! |
420 | \since 5.12 |
421 | |
422 | Unsubscribes from \a topic. No notifications will be sent to any of the |
423 | subscriptions made by calling subscribe(). \a properties specifies |
424 | additional user properties to be passed to the broker. |
425 | |
426 | \note If a client disconnects from a broker without unsubscribing, the |
427 | broker will store all messages and publish them on the next reconnect. |
428 | |
429 | \note \a properties will only be passed to the broker when the client |
430 | specifies MQTT_5_0 as ProtocolVersion. |
431 | */ |
432 | void QMqttClient::unsubscribe(const QMqttTopicFilter &topic, const QMqttUnsubscriptionProperties &properties) |
433 | { |
434 | Q_D(QMqttClient); |
435 | d->m_connection.sendControlUnsubscribe(topic, properties); |
436 | } |
437 | |
438 | /*! |
439 | Publishes a \a message to the broker with the specified \a topic. \a qos |
440 | specifies the QoS level required for transferring the message. |
441 | |
442 | If \a retain is set to \c true, the message will stay on the broker for |
443 | other clients to connect and receive the message. |
444 | |
445 | Returns an ID that is used internally to identify the message. |
446 | */ |
447 | qint32 QMqttClient::publish(const QMqttTopicName &topic, const QByteArray &message, quint8 qos, bool retain) |
448 | { |
449 | return publish(topic, properties: QMqttPublishProperties(), message, qos, retain); |
450 | } |
451 | |
452 | /*! |
453 | \since 5.12 |
454 | |
455 | Publishes a \a message to the broker with the specified \a properties and |
456 | \a topic. \a qos specifies the QoS level required for transferring |
457 | the message. |
458 | |
459 | If \a retain is set to \c true, the message will stay on the broker for |
460 | other clients to connect and receive the message. |
461 | |
462 | Returns an ID that is used internally to identify the message. |
463 | |
464 | \note \a properties will only be passed to the broker when the client |
465 | specifies MQTT_5_0 as ProtocolVersion. |
466 | */ |
467 | qint32 QMqttClient::publish(const QMqttTopicName &topic, const QMqttPublishProperties &properties, |
468 | const QByteArray &message, quint8 qos, bool retain) |
469 | { |
470 | Q_D(QMqttClient); |
471 | if (qos > 2) |
472 | return -1; |
473 | |
474 | if (d->m_state != QMqttClient::Connected) |
475 | return -1; |
476 | |
477 | return d->m_connection.sendControlPublish(topic, message, qos, retain, properties); |
478 | } |
479 | |
480 | /*! |
481 | Sends a ping message to the broker and expects a reply. |
482 | |
483 | If the connection is active and \l autoKeepAlive is \c true, then calling this |
484 | function will fail as the client is responsible for managing this process. |
485 | |
486 | Using \c requestPing() manually requires a call every time within the \l keepAlive |
487 | interval as long as the connection is active. |
488 | |
489 | To check whether the ping is successful, connect to the |
490 | \l pingResponseReceived() signal. |
491 | |
492 | Returns \c true if the ping request could be sent. |
493 | |
494 | \sa pingResponseReceived(), autoKeepAlive(), keepAlive() |
495 | */ |
496 | bool QMqttClient::requestPing() |
497 | { |
498 | Q_D(QMqttClient); |
499 | return d->m_connection.sendControlPingRequest(isAuto: false); |
500 | } |
501 | |
502 | QString QMqttClient::hostname() const |
503 | { |
504 | Q_D(const QMqttClient); |
505 | return d->m_hostname; |
506 | } |
507 | |
508 | quint16 QMqttClient::port() const |
509 | { |
510 | Q_D(const QMqttClient); |
511 | return d->m_port; |
512 | } |
513 | |
514 | /*! |
515 | Initiates a connection to the MQTT broker. |
516 | */ |
517 | void QMqttClient::connectToHost() |
518 | { |
519 | connectToHost(encrypted: false, sslPeerName: QString()); |
520 | } |
521 | |
522 | #ifndef QT_NO_SSL |
523 | /*! |
524 | \since 5.14 |
525 | Initiates an encrypted connection to the MQTT broker. |
526 | |
527 | \a conf specifies the SSL configuration to be used for the connection |
528 | */ |
529 | void QMqttClient::connectToHostEncrypted(const QSslConfiguration &conf) |
530 | { |
531 | Q_D(QMqttClient); |
532 | d->m_connection.m_sslConfiguration = conf; |
533 | connectToHost(encrypted: true, sslPeerName: QString()); |
534 | } |
535 | #endif |
536 | |
537 | void QMqttClient::connectToHost(bool encrypted, const QString &sslPeerName) |
538 | { |
539 | Q_D(QMqttClient); |
540 | |
541 | if (state() == QMqttClient::Connecting) { |
542 | qCDebug(lcMqttClient) << "Connection request currently ongoing." ; |
543 | return; |
544 | } |
545 | |
546 | if (state() == QMqttClient::Connected) { |
547 | qCDebug(lcMqttClient) << "Already connected to a broker. Rejecting connection request." ; |
548 | return; |
549 | } |
550 | |
551 | if (!d->m_connection.ensureTransport(createSecureIfNeeded: encrypted)) { |
552 | qCDebug(lcMqttClient) << "Could not ensure connection." ; |
553 | d->setStateAndError(s: Disconnected, e: TransportInvalid); |
554 | return; |
555 | } |
556 | d->m_error = QMqttClient::NoError; // Fresh reconnect, unset error |
557 | d->setStateAndError(s: Connecting); |
558 | |
559 | if (d->m_cleanSession) |
560 | d->m_connection.cleanSubscriptions(); |
561 | |
562 | if (!d->m_connection.ensureTransportOpen(sslPeerName)) { |
563 | qCDebug(lcMqttClient) << "Could not ensure that connection is open." ; |
564 | d->setStateAndError(s: Disconnected, e: TransportInvalid); |
565 | return; |
566 | } |
567 | |
568 | // Once transport has connected, it will invoke |
569 | // QMqttConnection::sendControlConnect to |
570 | // handshake with the broker |
571 | } |
572 | |
573 | /*! |
574 | Disconnects from the MQTT broker. |
575 | */ |
576 | void QMqttClient::disconnectFromHost() |
577 | { |
578 | Q_D(QMqttClient); |
579 | |
580 | switch (d->m_connection.internalState()) { |
581 | case QMqttConnection::BrokerConnected: |
582 | case QMqttConnection::ClientDestruction: |
583 | d->m_connection.sendControlDisconnect(); |
584 | break; |
585 | case QMqttConnection::BrokerDisconnected: |
586 | break; |
587 | case QMqttConnection::BrokerConnecting: |
588 | case QMqttConnection::BrokerWaitForConnectAck: |
589 | d->m_connection.m_transport->close(); |
590 | break; |
591 | } |
592 | } |
593 | |
594 | QMqttClient::ClientState QMqttClient::state() const |
595 | { |
596 | Q_D(const QMqttClient); |
597 | return d->m_state; |
598 | } |
599 | |
600 | QString QMqttClient::username() const |
601 | { |
602 | Q_D(const QMqttClient); |
603 | return d->m_username; |
604 | } |
605 | |
606 | QString QMqttClient::password() const |
607 | { |
608 | Q_D(const QMqttClient); |
609 | return d->m_password; |
610 | } |
611 | |
612 | bool QMqttClient::cleanSession() const |
613 | { |
614 | Q_D(const QMqttClient); |
615 | return d->m_cleanSession; |
616 | } |
617 | |
618 | QString QMqttClient::willTopic() const |
619 | { |
620 | Q_D(const QMqttClient); |
621 | return d->m_willTopic; |
622 | } |
623 | |
624 | quint8 QMqttClient::willQoS() const |
625 | { |
626 | Q_D(const QMqttClient); |
627 | return d->m_willQoS; |
628 | } |
629 | |
630 | QByteArray QMqttClient::willMessage() const |
631 | { |
632 | Q_D(const QMqttClient); |
633 | return d->m_willMessage; |
634 | } |
635 | |
636 | bool QMqttClient::willRetain() const |
637 | { |
638 | Q_D(const QMqttClient); |
639 | return d->m_willRetain; |
640 | } |
641 | |
642 | bool QMqttClient::autoKeepAlive() const |
643 | { |
644 | Q_D(const QMqttClient); |
645 | return d->m_autoKeepAlive; |
646 | } |
647 | |
648 | /*! |
649 | \since 5.12 |
650 | |
651 | Sets the connection properties to \a prop. \l QMqttConnectionProperties |
652 | can be used to ask the server to use a specific feature set. After a |
653 | connection request the server response can be obtained by calling |
654 | \l QMqttClient::serverConnectionProperties. |
655 | |
656 | \note The connection properties can only be set if the MQTT client is in the |
657 | \l Disconnected state. |
658 | |
659 | \note QMqttConnectionProperties can only be used when the client specifies |
660 | MQTT_5_0 as ProtocolVersion. |
661 | */ |
662 | void QMqttClient::setConnectionProperties(const QMqttConnectionProperties &prop) |
663 | { |
664 | Q_D(QMqttClient); |
665 | d->m_connectionProperties = prop; |
666 | } |
667 | |
668 | /*! |
669 | \since 5.12 |
670 | |
671 | Returns the connection properties the client requests to the broker. |
672 | |
673 | \note QMqttConnectionProperties can only be used when the client specifies |
674 | MQTT_5_0 as ProtocolVersion. |
675 | */ |
676 | QMqttConnectionProperties QMqttClient::connectionProperties() const |
677 | { |
678 | Q_D(const QMqttClient); |
679 | return d->m_connectionProperties; |
680 | } |
681 | |
682 | /*! |
683 | \since 5.12 |
684 | |
685 | Sets the last will properties to \a prop. QMqttLastWillProperties allows |
686 | to set additional features for the last will message stored at the broker. |
687 | |
688 | \note The connection properties can only be set if the MQTT client is in the |
689 | \l Disconnected state. |
690 | |
691 | \note QMqttLastWillProperties can only be used when the client specifies |
692 | MQTT_5_0 as ProtocolVersion. |
693 | */ |
694 | void QMqttClient::setLastWillProperties(const QMqttLastWillProperties &prop) |
695 | { |
696 | Q_D(QMqttClient); |
697 | d->m_lastWillProperties = prop; |
698 | } |
699 | |
700 | /*! |
701 | \since 5.12 |
702 | |
703 | Returns the last will properties. |
704 | |
705 | \note QMqttLastWillProperties can only be used when the client specifies |
706 | MQTT_5_0 as ProtocolVersion. |
707 | */ |
708 | QMqttLastWillProperties QMqttClient::lastWillProperties() const |
709 | { |
710 | Q_D(const QMqttClient); |
711 | return d->m_lastWillProperties; |
712 | } |
713 | |
714 | /*! |
715 | \since 5.12 |
716 | |
717 | Returns the QMqttServerConnectionProperties the broker returned after a |
718 | connection attempt. |
719 | |
720 | This can be used to verify that client side connection properties set by |
721 | QMqttClient::setConnectionProperties have been accepted by the broker. Also, |
722 | in case of a failed connection attempt, it can be used for connection |
723 | diagnostics. |
724 | |
725 | \note QMqttServerConnectionProperties can only be used when the client |
726 | specifies MQTT_5_0 as ProtocolVersion. |
727 | |
728 | \sa connectionProperties() |
729 | */ |
730 | QMqttServerConnectionProperties QMqttClient::serverConnectionProperties() const |
731 | { |
732 | Q_D(const QMqttClient); |
733 | return d->m_serverConnectionProperties; |
734 | } |
735 | |
736 | /*! |
737 | \since 5.12 |
738 | |
739 | Sends an authentication request to the broker. \a prop specifies |
740 | the required information to fulfill the authentication request. |
741 | |
742 | This function should only be called after a |
743 | QMqttClient::authenticationRequested signal has been emitted. |
744 | |
745 | \note Extended authentication is part of the MQTT 5.0 standard and can |
746 | only be used when the client specifies MQTT_5_0 as ProtocolVersion. |
747 | |
748 | \sa authenticationRequested(), authenticationFinished() |
749 | */ |
750 | void QMqttClient::authenticate(const QMqttAuthenticationProperties &prop) |
751 | { |
752 | Q_D(QMqttClient); |
753 | if (protocolVersion() != QMqttClient::MQTT_5_0) { |
754 | qCDebug(lcMqttClient) << "Authentication is only supported on protocol level 5." ; |
755 | return; |
756 | } |
757 | if (state() == QMqttClient::Disconnected) { |
758 | qCDebug(lcMqttClient) << "Cannot send authentication request while disconnected." ; |
759 | return; |
760 | } |
761 | d->m_connection.sendControlAuthenticate(properties: prop); |
762 | } |
763 | |
764 | QMqttClient::ClientError QMqttClient::error() const |
765 | { |
766 | Q_D(const QMqttClient); |
767 | return d->m_error; |
768 | } |
769 | |
770 | QMqttClient::ProtocolVersion QMqttClient::protocolVersion() const |
771 | { |
772 | Q_D(const QMqttClient); |
773 | return d->m_protocolVersion; |
774 | } |
775 | |
776 | QString QMqttClient::clientId() const |
777 | { |
778 | Q_D(const QMqttClient); |
779 | return d->m_clientId; |
780 | } |
781 | |
782 | quint16 QMqttClient::keepAlive() const |
783 | { |
784 | Q_D(const QMqttClient); |
785 | return d->m_keepAlive; |
786 | } |
787 | |
788 | void QMqttClient::setHostname(const QString &hostname) |
789 | { |
790 | Q_D(QMqttClient); |
791 | |
792 | if (state() != QMqttClient::Disconnected) { |
793 | qCDebug(lcMqttClient) << "Changing hostname while connected is not possible." ; |
794 | return; |
795 | } |
796 | |
797 | if (d->m_hostname == hostname) |
798 | return; |
799 | |
800 | d->m_hostname = hostname; |
801 | emit hostnameChanged(hostname); |
802 | } |
803 | |
804 | void QMqttClient::setPort(quint16 port) |
805 | { |
806 | Q_D(QMqttClient); |
807 | |
808 | if (state() != QMqttClient::Disconnected) { |
809 | qCDebug(lcMqttClient) << "Changing port while connected is not possible." ; |
810 | return; |
811 | } |
812 | |
813 | if (d->m_port == port) |
814 | return; |
815 | |
816 | d->m_port = port; |
817 | emit portChanged(port); |
818 | } |
819 | |
820 | void QMqttClient::setClientId(const QString &clientId) |
821 | { |
822 | Q_D(QMqttClient); |
823 | |
824 | if (state() != QMqttClient::Disconnected) { |
825 | qCDebug(lcMqttClient) << "Changing client ID while connected is not possible." ; |
826 | return; |
827 | } |
828 | d->setClientId(clientId); |
829 | } |
830 | |
831 | void QMqttClient::setKeepAlive(quint16 keepAlive) |
832 | { |
833 | Q_D(QMqttClient); |
834 | if (d->m_keepAlive == keepAlive) |
835 | return; |
836 | |
837 | if (state() != QMqttClient::Disconnected) { |
838 | qCDebug(lcMqttClient) << "Changing keepAlive while connected is not possible." ; |
839 | return; |
840 | } |
841 | |
842 | d->m_keepAlive = keepAlive; |
843 | emit keepAliveChanged(keepAlive); |
844 | } |
845 | |
846 | void QMqttClient::setProtocolVersion(ProtocolVersion protocolVersion) |
847 | { |
848 | Q_D(QMqttClient); |
849 | |
850 | if (state() != QMqttClient::Disconnected) { |
851 | qCDebug(lcMqttClient) << "Changing protocol version while connected is not possible." ; |
852 | return; |
853 | } |
854 | |
855 | if (d->m_protocolVersion == protocolVersion) |
856 | return; |
857 | |
858 | if (protocolVersion < 3 || protocolVersion > 5) |
859 | return; |
860 | |
861 | d->m_protocolVersion = protocolVersion; |
862 | emit protocolVersionChanged(protocolVersion); |
863 | } |
864 | |
865 | void QMqttClient::setState(ClientState state) |
866 | { |
867 | Q_D(QMqttClient); |
868 | if (d->m_state == state) |
869 | return; |
870 | |
871 | d->m_state = state; |
872 | emit stateChanged(state); |
873 | if (d->m_state == QMqttClient::Disconnected) |
874 | emit disconnected(); |
875 | else if (d->m_state == QMqttClient::Connected) |
876 | emit connected(); |
877 | } |
878 | |
879 | void QMqttClient::setUsername(const QString &username) |
880 | { |
881 | Q_D(QMqttClient); |
882 | |
883 | if (state() != QMqttClient::Disconnected) { |
884 | qCDebug(lcMqttClient) << "Changing username while connected is not possible." ; |
885 | return; |
886 | } |
887 | |
888 | if (d->m_username == username) |
889 | return; |
890 | |
891 | d->m_username = username; |
892 | emit usernameChanged(username); |
893 | } |
894 | |
895 | void QMqttClient::setPassword(const QString &password) |
896 | { |
897 | Q_D(QMqttClient); |
898 | |
899 | if (state() != QMqttClient::Disconnected) { |
900 | qCDebug(lcMqttClient) << "Changing password while connected is not possible." ; |
901 | return; |
902 | } |
903 | |
904 | if (d->m_password == password) |
905 | return; |
906 | |
907 | d->m_password = password; |
908 | emit passwordChanged(password); |
909 | } |
910 | |
911 | void QMqttClient::setCleanSession(bool cleanSession) |
912 | { |
913 | Q_D(QMqttClient); |
914 | |
915 | if (state() != QMqttClient::Disconnected) { |
916 | qCDebug(lcMqttClient) << "Changing clean session while connected is not possible." ; |
917 | return; |
918 | } |
919 | |
920 | if (d->m_cleanSession == cleanSession) |
921 | return; |
922 | |
923 | d->m_cleanSession = cleanSession; |
924 | emit cleanSessionChanged(cleanSession); |
925 | } |
926 | |
927 | void QMqttClient::setWillTopic(const QString &willTopic) |
928 | { |
929 | Q_D(QMqttClient); |
930 | |
931 | if (state() != QMqttClient::Disconnected) { |
932 | qCDebug(lcMqttClient) << "Changing will topic while connected is not possible." ; |
933 | return; |
934 | } |
935 | |
936 | if (d->m_willTopic == willTopic) |
937 | return; |
938 | |
939 | d->m_willTopic = willTopic; |
940 | emit willTopicChanged(willTopic); |
941 | } |
942 | |
943 | void QMqttClient::setWillQoS(quint8 willQoS) |
944 | { |
945 | Q_D(QMqttClient); |
946 | |
947 | if (state() != QMqttClient::Disconnected) { |
948 | qCDebug(lcMqttClient) << "Changing will qos while connected is not possible." ; |
949 | return; |
950 | } |
951 | |
952 | if (d->m_willQoS == willQoS) |
953 | return; |
954 | |
955 | d->m_willQoS = willQoS; |
956 | emit willQoSChanged(willQoS); |
957 | } |
958 | |
959 | void QMqttClient::setWillMessage(const QByteArray &willMessage) |
960 | { |
961 | Q_D(QMqttClient); |
962 | |
963 | if (state() != QMqttClient::Disconnected) { |
964 | qCDebug(lcMqttClient) << "Changing will message while connected is not possible." ; |
965 | return; |
966 | } |
967 | |
968 | if (d->m_willMessage == willMessage) |
969 | return; |
970 | |
971 | d->m_willMessage = willMessage; |
972 | emit willMessageChanged(willMessage); |
973 | } |
974 | |
975 | void QMqttClient::setWillRetain(bool willRetain) |
976 | { |
977 | Q_D(QMqttClient); |
978 | |
979 | if (state() != QMqttClient::Disconnected) { |
980 | qCDebug(lcMqttClient) << "Changing will retain while connected is not possible." ; |
981 | return; |
982 | } |
983 | |
984 | if (d->m_willRetain == willRetain) |
985 | return; |
986 | |
987 | d->m_willRetain = willRetain; |
988 | emit willRetainChanged(willRetain); |
989 | } |
990 | |
991 | void QMqttClient::setAutoKeepAlive(bool autoKeepAlive) |
992 | { |
993 | Q_D(QMqttClient); |
994 | |
995 | if (state() != QMqttClient::Disconnected) { |
996 | qCDebug(lcMqttClient) << "Changing autoKeepAlive while connected is not possible." ; |
997 | return; |
998 | } |
999 | |
1000 | if (d->m_autoKeepAlive == autoKeepAlive) |
1001 | return; |
1002 | |
1003 | d->m_autoKeepAlive = autoKeepAlive; |
1004 | emit autoKeepAliveChanged(autoKeepAlive: d->m_autoKeepAlive); |
1005 | } |
1006 | |
1007 | void QMqttClient::setError(ClientError e) |
1008 | { |
1009 | Q_D(QMqttClient); |
1010 | if (d->m_error == e) |
1011 | return; |
1012 | |
1013 | d->m_error = e; |
1014 | emit errorChanged(error: d->m_error); |
1015 | } |
1016 | |
1017 | QMqttClientPrivate::QMqttClientPrivate(QMqttClient *c) |
1018 | : QObjectPrivate() |
1019 | { |
1020 | m_client = c; |
1021 | m_clientId = QUuid::createUuid().toString(); |
1022 | m_clientId.remove(c: QLatin1Char('{')); |
1023 | m_clientId.remove(c: QLatin1Char('}')); |
1024 | m_clientId.remove(c: QLatin1Char('-')); |
1025 | m_clientId.resize(size: 23); |
1026 | #ifdef QT_BUILD_INTERNAL |
1027 | // Some test servers require a username token |
1028 | if (qEnvironmentVariableIsSet(varName: "QT_MQTT_TEST_USERNAME" )) |
1029 | m_username = qEnvironmentVariable(varName: "QT_MQTT_TEST_USERNAME" ); |
1030 | if (qEnvironmentVariableIsSet(varName: "QT_MQTT_TEST_PASSWORD" )) |
1031 | m_password = qEnvironmentVariable(varName: "QT_MQTT_TEST_PASSWORD" ); |
1032 | if (qEnvironmentVariableIsSet(varName: "QT_MQTT_TEST_CLIENTID" )) |
1033 | m_clientId = qEnvironmentVariable(varName: "QT_MQTT_TEST_CLIENTID" ); |
1034 | #endif |
1035 | } |
1036 | |
1037 | QMqttClientPrivate::~QMqttClientPrivate() |
1038 | { |
1039 | } |
1040 | |
1041 | void QMqttClientPrivate::setStateAndError(QMqttClient::ClientState s, QMqttClient::ClientError e) |
1042 | { |
1043 | Q_Q(QMqttClient); |
1044 | |
1045 | if (e != QMqttClient::NoError) |
1046 | q->setError(e); |
1047 | q->setState(s); |
1048 | } |
1049 | |
1050 | void QMqttClientPrivate::setClientId(const QString &id) |
1051 | { |
1052 | Q_Q(QMqttClient); |
1053 | |
1054 | if (m_clientId == id) |
1055 | return; |
1056 | |
1057 | m_clientId = id; |
1058 | emit q->clientIdChanged(clientId: id); |
1059 | } |
1060 | |
1061 | QT_END_NAMESPACE |
1062 | |