diff --git a/SavvyCAN.pro b/SavvyCAN.pro index a42ad47..b946b25 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -4,11 +4,12 @@ # #------------------------------------------------- -QT = core gui printsupport qml serialbus serialport widgets help +QT = core gui printsupport qml serialbus serialport widgets help network CONFIG(release, debug|release):DEFINES += QT_NO_DEBUG_OUTPUT CONFIG += c++11 +CONFIG += NO_UNIT_TESTS DEFINES += QCUSTOMPLOT_USE_OPENGL @@ -16,9 +17,22 @@ TARGET = SavvyCAN TEMPLATE = app SOURCES += main.cpp\ + mqtt/qmqtt_client.cpp \ + mqtt/qmqtt_client_p.cpp \ + mqtt/qmqtt_frame.cpp \ + mqtt/qmqtt_message.cpp \ + mqtt/qmqtt_network.cpp \ + mqtt/qmqtt_router.cpp \ + mqtt/qmqtt_routesubscription.cpp \ + mqtt/qmqtt_socket.cpp \ + mqtt/qmqtt_ssl_socket.cpp \ + mqtt/qmqtt_timer.cpp \ + mqtt/qmqtt_websocket.cpp \ + mqtt/qmqtt_websocketiodevice.cpp \ re/dbccomparatorwindow.cpp \ mainwindow.cpp \ canframemodel.cpp \ + simplecrypt.cpp \ utility.cpp \ qcustomplot.cpp \ frameplaybackwindow.cpp \ @@ -77,7 +91,27 @@ SOURCES += main.cpp\ HEADERS += mainwindow.h \ can_structs.h \ canframemodel.h \ \ + mqtt/qmqtt.h \ + mqtt/qmqtt_client.h \ + mqtt/qmqtt_client_p.h \ + mqtt/qmqtt_frame.h \ + mqtt/qmqtt_global.h \ + mqtt/qmqtt_message.h \ + mqtt/qmqtt_message_p.h \ + mqtt/qmqtt_network_p.h \ + mqtt/qmqtt_networkinterface.h \ + mqtt/qmqtt_routedmessage.h \ + mqtt/qmqtt_router.h \ + mqtt/qmqtt_routesubscription.h \ + mqtt/qmqtt_socket_p.h \ + mqtt/qmqtt_socketinterface.h \ + mqtt/qmqtt_ssl_socket_p.h \ + mqtt/qmqtt_timer_p.h \ + mqtt/qmqtt_timerinterface.h \ + mqtt/qmqtt_websocket_p.h \ + mqtt/qmqtt_websocketiodevice_p.h \ re/dbccomparatorwindow.h \ + simplecrypt.h \ utility.h \ qcustomplot.h \ frameplaybackwindow.h \ diff --git a/connections/canconconst.h b/connections/canconconst.h index f70ebd9..71dc3fd 100644 --- a/connections/canconconst.h +++ b/connections/canconconst.h @@ -18,6 +18,7 @@ namespace CANCon { KVASER, SERIALBUS, REMOTE, + MQTT, NONE }; } diff --git a/connections/newconnectiondialog.cpp b/connections/newconnectiondialog.cpp index 1f03eba..d2bd9e7 100644 --- a/connections/newconnectiondialog.cpp +++ b/connections/newconnectiondialog.cpp @@ -25,6 +25,8 @@ NewConnectionDialog::NewConnectionDialog(QVector* ips, QWidget *parent) connect(ui->rbGVRET, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged); connect(ui->rbSocketCAN, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged); connect(ui->rbRemote, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged); + connect(ui->rbMQTT, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged); + connect(ui->cbDeviceType, QOverload::of(&QComboBox::currentIndexChanged), this, &NewConnectionDialog::handleDeviceTypeChanged); connect(ui->btnOK, &QPushButton::clicked, this, &NewConnectionDialog::handleCreateButton); @@ -50,6 +52,7 @@ void NewConnectionDialog::handleConnTypeChanged() if (ui->rbGVRET->isChecked()) selectSerial(); if (ui->rbSocketCAN->isChecked()) selectSocketCan(); if (ui->rbRemote->isChecked()) selectRemote(); + } void NewConnectionDialog::handleDeviceTypeChanged() @@ -102,6 +105,14 @@ void NewConnectionDialog::selectRemote() } } +void NewConnectionDialog::selectMQTT() +{ + ui->lPort->setText("Topic Name:"); + ui->lblDeviceType->setHidden(true); + ui->cbDeviceType->setHidden(true); + ui->cbPort->clear(); +} + void NewConnectionDialog::setPortName(CANCon::type pType, QString pPortName, QString pDriver) { @@ -156,6 +167,7 @@ QString NewConnectionDialog::getPortName() case CANCon::GVRET_SERIAL: case CANCon::SERIALBUS: case CANCon::REMOTE: + case CANCon::MQTT: return ui->cbPort->currentText(); default: qDebug() << "getPortName: can't get port"; @@ -178,6 +190,7 @@ CANCon::type NewConnectionDialog::getConnectionType() if (ui->rbGVRET->isChecked()) return CANCon::GVRET_SERIAL; if (ui->rbSocketCAN->isChecked()) return CANCon::SERIALBUS; if (ui->rbRemote->isChecked()) return CANCon::REMOTE; + if (ui->rbMQTT->isChecked()) return CANCon::MQTT; qDebug() << "getConnectionType: error"; return CANCon::NONE; diff --git a/connections/newconnectiondialog.h b/connections/newconnectiondialog.h index f826b70..52b43e5 100644 --- a/connections/newconnectiondialog.h +++ b/connections/newconnectiondialog.h @@ -40,6 +40,7 @@ private: void selectKvaser(); void selectSocketCan(); void selectRemote(); + void selectMQTT(); bool isSerialBusAvailable(); void setPortName(CANCon::type pType, QString pPortName, QString pDriver); }; diff --git a/mainsettingsdialog.cpp b/mainsettingsdialog.cpp index 149fef0..dd1764f 100644 --- a/mainsettingsdialog.cpp +++ b/mainsettingsdialog.cpp @@ -1,8 +1,13 @@ #include "mainsettingsdialog.h" #include "ui_mainsettingsdialog.h" #include "helpwindow.h" - #include +#include "simplecrypt.h" + +//using this simple encryption library to obfuscate stored password a bit. It's not super secure but better than +//storing a password in straight plaintext. You have the source to this application anyway, whatever algorithm used, +//whatever key, you'd see it. Just behave yourselves +SimpleCrypt crypto(Q_UINT64_C(0xdeadbeefface6285)); MainSettingsDialog::MainSettingsDialog(QWidget *parent) : QDialog(parent), @@ -11,6 +16,7 @@ MainSettingsDialog::MainSettingsDialog(QWidget *parent) : QSettings settings; ui->setupUi(this); + //TODO: This is still hard coded to support only two buses. Sometimes there is none, sometimes 1, sometimes much more than 2. Fix this. ui->comboSendingBus->addItem(tr("None")); ui->comboSendingBus->addItem(tr("0")); ui->comboSendingBus->addItem(tr("1")); @@ -30,8 +36,12 @@ MainSettingsDialog::MainSettingsDialog(QWidget *parent) : ui->spinPlaybackSpeed->setValue(settings.value("Playback/DefSpeed", 5).toInt()); ui->lineClockFormat->setText(settings.value("Main/TimeFormat", "MMM-dd HH:mm:ss.zzz").toString()); ui->lineRemoteHost->setText(settings.value("Remote/Host", "api.savvycan.com").toString()); - ui->lineRemotePort->setText(settings.value("Remote/Port", "21315").toString()); // = 0x5343 = SC. Yep, really creative port number - ui->cbAutoStartRemote->setChecked(settings.value("Remote/AutoStart", false).toBool()); + ui->lineRemotePort->setText(settings.value("Remote/Port", "8883").toString()); //default port for SSL enabled MQTT + ui->lineRemoteUser->setText(settings.value("Remote/User", "Anonymous").toString()); + QByteArray encPass = settings.value("Remote/Pass", "").toByteArray(); + QString decPass = crypto.decryptToString(encPass); + ui->lineRemotePassword->setText(decPass); + ui->cbLoadConnections->setChecked(settings.value("Main/SaveRestoreConnections", false).toBool()); ui->spinFontSize->setValue(settings.value("Main/FontSize", ui->cbDisplayHex->font().pointSize()).toUInt()); @@ -82,9 +92,10 @@ MainSettingsDialog::MainSettingsDialog(QWidget *parent) : connect(ui->cbUseFiltered, SIGNAL(toggled(bool)), this, SLOT(updateSettings())); connect(ui->lineClockFormat, SIGNAL(editingFinished()), this, SLOT(updateSettings())); connect(ui->cbUseOpenGL, SIGNAL(toggled(bool)), this, SLOT(updateSettings())); - connect(ui->cbAutoStartRemote, SIGNAL(toggled(bool)), this, SLOT(updateSettings())); connect(ui->lineRemoteHost, SIGNAL(editingFinished()), this, SLOT(updateSettings())); connect(ui->lineRemotePort, SIGNAL(editingFinished()), this, SLOT(updateSettings())); + connect(ui->lineRemoteUser, SIGNAL(editingFinished()), this, SLOT(updateSettings())); + connect(ui->lineRemotePassword, SIGNAL(editingFinished()), this, SLOT(updateSettings())); connect(ui->cbLoadConnections, SIGNAL(toggled(bool)), this, SLOT(updateSettings())); connect(ui->cbFilterLabeling, SIGNAL(toggled(bool)), this, SLOT(updateSettings())); installEventFilter(this); @@ -144,7 +155,9 @@ void MainSettingsDialog::updateSettings() settings.setValue("Main/FontSize", ui->spinFontSize->value()); settings.setValue("Remote/Host", ui->lineRemoteHost->text()); settings.setValue("Remote/Port", ui->lineRemotePort->text()); - settings.setValue("Remote/AutoStart", ui->cbAutoStartRemote->isChecked()); + settings.setValue("Remote/User", ui->lineRemoteUser->text()); + QByteArray encPass = crypto.encryptToByteArray(ui->lineRemotePassword->text()); + settings.setValue("Remote/Pass", encPass); settings.setValue("Main/FilterLabeling", ui->cbFilterLabeling->isChecked()); settings.sync(); diff --git a/mqtt/LICENSE b/mqtt/LICENSE new file mode 100644 index 0000000..56bef4e --- /dev/null +++ b/mqtt/LICENSE @@ -0,0 +1,2 @@ +This project is dual licensed under the Eclipse Public License 1.0 and the +Eclipse Distribution License 1.0 as described in the epl-v10 and edl-v10 files. diff --git a/mqtt/README.md b/mqtt/README.md new file mode 100644 index 0000000..ba0068b --- /dev/null +++ b/mqtt/README.md @@ -0,0 +1,139 @@ +QMQTT +===== + +mqtt client for Qt + +**Please compile the library with Qt >= 5.3 version. On Windows you need to specify `CONFIG += NO_UNIT_TESTS`, since gtest is not supported.** + +SSL is enabled by default, if the version of OpenSSL < 1.0.2, SSL may not be supported. + +Disable the SSL in CMakeLists.txt (cmake): + + option( ${PROJECT_NAME}_SSL "Enable SSL support for MQTT" OFF ) + +Disable the SSL with src/mqtt/qmqtt.pro (qmake): + + CONFIG+=QMQTT_NO_SSL + +To add websocket support, compile the library with Qt >= 5.7, and specify 'CONFIG += QMQTT_WEBSOCKETS'. +This also works when compiling qmqtt for WebAssembly. + +Usage +===== + +In your QMake project, add: + + QT += qmqtt + +Connect using TCP: + + #include "qmqtt.h" + + QMQTT::Client *client = new QMQTT::Client(QHostAddress::LocalHost, 1883); + client->setClientId("clientId"); + client->setUsername("user"); + client->setPassword("password"); + client->connectToHost(); + +Connect using SSL: + + QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration(); + // Add custom SSL options here (for example extra certificates) + QMQTT::Client *client = new QMQTT::Client("example.com", 8883, sslConfig); + client->setClientId("clientId"); + client->setUsername("user"); + client->setPassword("password"); + // Optionally, set ssl errors you want to ignore. Be careful, because this may weaken security. + // See QSslSocket::ignoreSslErrors(const QList &) for more information. + client->ignoreSslErrors() + client->connectToHost(); + // Here's another option to suppress SSL errors (again, be careful) + QObject::connect(client, &QMQTT::Client::sslErrors, [&](const QList &errors) { + // Investigate the errors here, if you find no serious problems, call ignoreSslErrors() + // to continue connecting. + client->ignoreSslErrors(); + }); + +Connect using WebSockets: + + QMQTT::Client *client = new QMQTT::Client("ws://www.example.com/mqtt", "", QWebSocketProtocol::VersionLatest); + client->setClientId("clientId"); + client->setUsername("user"); + client->setPassword("password"); + client->connectToHost(); + +Slots +===== + + void setHost(const QHostAddress& host); + void setPort(const quint16 port); + void setClientId(const QString& clientId); + void setUsername(const QString& username); + void setPassword(const QString& password); + void setKeepAlive(const int keepAlive); + void setCleanSession(const bool cleansess); + void setAutoReconnect(const bool value); + void setAutoReconnectInterval(const int autoReconnectInterval); + void setWillTopic(const QString& willTopic); + void setWillQos(const quint8 willQos); + void setWillRetain(const bool willRetain); + void setWillMessage(const QString& willMessage); + + void connectToHost(); + void disconnectFromHost(); + + quint16 subscribe(const QString& topic, const quint8 qos); + void unsubscribe(const QString& topic); + + quint16 publish(const Message& message); + +Signals +======= + + void connected(); + void disconnected(); + void error(const QMQTT::ClientError error); + + void subscribed(const QString& topic, const quint8 qos); + void unsubscribed(const QString& topic); + void published(const quint16 msgid, const quint8 qos); + void pingresp(); + void received(const QMQTT::Message& message); + + +License +======= + +New BSD License + + +Contributors +============= + +[@Kampfgnom](https://github.com/Kampfgnom) + +[@rafaeldelucena](https://github.com/rafaeldelucena) + +[@Vortex375](https://github.com/Vortex375) + +[@mwallnoefer](https://github.com/mwallnoefer) + +[@KonstantinRitt](https://github.com/KonstantinRitt) + +[@cncap](https://github.com/cncap) + +[@Psy-Kai](https://github.com/Psy-Kai) + +[@ejvr](https://github.com/ejvr) + +[@keytee](https://github.com/keytee) + + +Authors +======= + +[@emqplus](https://github.com/emqplus) Feng Lee + +[@wguynes](https://github.com/wguynes) William Guynes + +[@wuming123057](https://github.com/wuming123057) Xuan diff --git a/mqtt/edl-v10 b/mqtt/edl-v10 new file mode 100644 index 0000000..d36ee54 --- /dev/null +++ b/mqtt/edl-v10 @@ -0,0 +1,30 @@ +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + Neither the name of the Eclipse Foundation, Inc. nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/mqtt/epl-v10 b/mqtt/epl-v10 new file mode 100644 index 0000000..4ec0f61 --- /dev/null +++ b/mqtt/epl-v10 @@ -0,0 +1,220 @@ +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM +CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + + +1. DEFINITIONS + +"Contribution" means: + a) in the case of the initial Contributor, the initial code and + documentation distributed under this Agreement, and + + b) in the case of each subsequent Contributor: + + i) changes to the Program, and + ii) additions to the Program; + +where such changes and/or additions to the Program originate from and are +distributed by that particular Contributor. A Contribution 'originates' from a +Contributor if it was added to the Program by such Contributor itself or anyone +acting on such Contributor's behalf. Contributions do not include additions to +the Program which: (i) are separate modules of software distributed in +conjunction with the Program under their own license agreement, and (ii) are +not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents " mean patent claims licensable by a Contributor which are +necessarily infringed by the use or sale of its Contribution alone or when +combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, +including all Contributors. + + +2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free copyright license to + reproduce, prepare derivative works of, publicly display, publicly + perform, distribute and sublicense the Contribution of such Contributor, + if any, and such derivative works, in source code and object code form. + + b) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free patent license under + Licensed Patents to make, use, sell, offer to sell, import and otherwise + transfer the Contribution of such Contributor, if any, in source code and + object code form. This patent license shall apply to the combination of the + Contribution and the Program if, at the time the Contribution is added by the + Contributor, such addition of the Contribution causes such combination to be + covered by the Licensed Patents. The patent license shall not apply to any + other combinations which include the Contribution. No hardware per se is + licensed hereunder. + + c) Recipient understands that although each Contributor grants the licenses + to its Contributions set forth herein, no assurances are provided by any + Contributor that the Program does not infringe the patent or other + intellectual property rights of any other entity. Each Contributor disclaims + any liability to Recipient for claims brought by any other entity based on + infringement of intellectual property rights or otherwise. As a condition to + exercising the rights and licenses granted hereunder, each Recipient hereby + assumes sole responsibility to secure any other intellectual property rights + needed, if any. For example, if a third party patent license is required to + allow Recipient to distribute the Program, it is Recipient's responsibility + to acquire that license before distributing the Program. + + d) Each Contributor represents that to its knowledge it has sufficient + copyright rights in its Contribution, if any, to grant the copyright license + set forth in this Agreement. + + +3. REQUIREMENTS + + A Contributor may choose to distribute the Program in object code form under + its own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + + b) its license agreement: + + i) effectively disclaims on behalf of all Contributors all warranties and + conditions, express and implied, including warranties or conditions of + title and non-infringement, and implied warranties or conditions of + merchantability and fitness for a particular purpose; + + ii) effectively excludes on behalf of all Contributors all liability for + damages, including direct, indirect, special, incidental and consequential + damages, such as lost profits; + + iii) states that any provisions which differ from this Agreement are offered + by that Contributor alone and not by any other party; and + + iv) states that source code for the Program is available from such + Contributor, and informs licensees how to obtain it in a reasonable manner + on or through a medium customarily used for software exchange. + + When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + + b) a copy of this Agreement must be included with each copy of the Program. + + Contributors may not remove or alter any copyright notices contained within + the Program. + + Each Contributor must identify itself as the originator of its Contribution, + if any, in a manner that reasonably allows subsequent Recipients to identify + the originator of the Contribution. + + +4. COMMERCIAL DISTRIBUTION + + Commercial distributors of software may accept certain responsibilities with + respect to end users, business partners and the like. While this license is + intended to facilitate the commercial use of the Program, the Contributor who + includes the Program in a commercial product offering should do so in a + manner which does not create potential liability for other Contributors. + Therefore, if a Contributor includes the Program in a commercial product + offering, such Contributor ("Commercial Contributor") hereby agrees to defend + and indemnify every other Contributor ("Indemnified Contributor") against any + losses, damages and costs (collectively "Losses") arising from claims, + lawsuits and other legal actions brought by a third party against the + Indemnified Contributor to the extent caused by the acts or omissions of such + Commercial Contributor in connection with its distribution of the Program in + a commercial product offering. The obligations in this section do not apply + to any claims or Losses relating to any actual or alleged intellectual + property infringement. In order to qualify, an Indemnified Contributor must: + a) promptly notify the Commercial Contributor in writing of such claim, and + b) allow the Commercial Contributor to control, and cooperate with the + Commercial Contributor in, the defense and any related settlement + negotiations. The Indemnified Contributor may participate in any such claim + at its own expense. + + For example, a Contributor might include the Program in a commercial product + offering, Product X. That Contributor is then a Commercial Contributor. If + that Commercial Contributor then makes performance claims, or offers + warranties related to Product X, those performance claims and warranties are + such Commercial Contributor's responsibility alone. Under this section, the + Commercial Contributor would have to defend claims against the other + Contributors related to those performance claims and warranties, and if a + court requires any other Contributor to pay any damages as a result, the + Commercial Contributor must pay those damages. + + +5. NO WARRANTY + + EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON + AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER + EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR + CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A + PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the + appropriateness of using and distributing the Program and assumes all risks + associated with its exercise of rights under this Agreement , including but + not limited to the risks and costs of program errors, compliance with + applicable laws, damage to or loss of data, programs or equipment, and + unavailability or interruption of operations. + + +6. DISCLAIMER OF LIABILITY + + EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY + CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION + LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE + EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH DAMAGES. + + +7. GENERAL + + If any provision of this Agreement is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of the + remainder of the terms of this Agreement, and without further action by the + parties hereto, such provision shall be reformed to the minimum extent + necessary to make such provision valid and enforceable. + + If Recipient institutes patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Program itself + (excluding combinations of the Program with other software or hardware) + infringes such Recipient's patent(s), then such Recipient's rights granted + under Section 2(b) shall terminate as of the date such litigation is filed. + + All Recipient's rights under this Agreement shall terminate if it fails to + comply with any of the material terms or conditions of this Agreement and + does not cure such failure in a reasonable period of time after becoming + aware of such noncompliance. If all Recipient's rights under this Agreement + terminate, Recipient agrees to cease use and distribution of the Program as + soon as reasonably practicable. However, Recipient's obligations under this + Agreement and any licenses granted by Recipient relating to the Program shall + continue and survive. + + Everyone is permitted to copy and distribute copies of this Agreement, but in + order to avoid inconsistency the Agreement is copyrighted and may only be + modified in the following manner. The Agreement Steward reserves the right to + publish new versions (including revisions) of this Agreement from time to + time. No one other than the Agreement Steward has the right to modify this + Agreement. The Eclipse Foundation is the initial Agreement Steward. The + Eclipse Foundation may assign the responsibility to serve as the Agreement + Steward to a suitable separate entity. Each new version of the Agreement will + be given a distinguishing version number. The Program (including + Contributions) may always be distributed subject to the version of the + Agreement under which it was received. In addition, after a new version of + the Agreement is published, Contributor may elect to distribute the Program + (including its Contributions) under the new version. Except as expressly + stated in Sections 2(a) and 2(b) above, Recipient receives no rights or + licenses to the intellectual property of any Contributor under this + Agreement, whether expressly, by implication, estoppel or otherwise. All + rights in the Program not expressly granted under this Agreement are + reserved. + + This Agreement is governed by the laws of the State of New York and the + intellectual property laws of the United States of America. No party to this + Agreement will bring a legal action under this Agreement more than one year + after the cause of action arose. Each party waives its rights to a jury trial + in any resulting litigation. \ No newline at end of file diff --git a/mqtt/qmqtt.h b/mqtt/qmqtt.h new file mode 100644 index 0000000..6e607dd --- /dev/null +++ b/mqtt/qmqtt.h @@ -0,0 +1,38 @@ +/* + * qmqtt.h - qmqtt library heaer + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_H +#define QMQTT_H + +#include +#include + +#endif // QMQTT_H diff --git a/mqtt/qmqtt.pri b/mqtt/qmqtt.pri new file mode 100644 index 0000000..c5790bf --- /dev/null +++ b/mqtt/qmqtt.pri @@ -0,0 +1,48 @@ +INCLUDEPATH += $$PWD + +PUBLIC_HEADERS += \ + $$PWD/qmqtt_client.h \ + $$PWD/qmqtt_frame.h \ + $$PWD/qmqtt_message.h \ + $$PWD/qmqtt_routesubscription.h \ + $$PWD/qmqtt_routedmessage.h \ + $$PWD/qmqtt_router.h \ + $$PWD/qmqtt_networkinterface.h \ + $$PWD/qmqtt_socketinterface.h \ + $$PWD/qmqtt_timerinterface.h + +PRIVATE_HEADERS += \ + $$PWD/qmqtt_client_p.h \ + $$PWD/qmqtt_message_p.h \ + $$PWD/qmqtt_network_p.h \ + $$PWD/qmqtt_socket_p.h \ + $$PWD/qmqtt_timer_p.h + +SOURCES += \ + $$PWD/qmqtt_client_p.cpp \ + $$PWD/qmqtt_client.cpp \ + $$PWD/qmqtt_frame.cpp \ + $$PWD/qmqtt_message.cpp \ + $$PWD/qmqtt_network.cpp \ + $$PWD/qmqtt_routesubscription.cpp \ + $$PWD/qmqtt_router.cpp \ + $$PWD/qmqtt_socket.cpp \ + $$PWD/qmqtt_timer.cpp + +QMQTT_WEBSOCKETS { + PRIVATE_HEADERS += \ + $$PWD/qmqtt_websocket_p.h \ + $$PWD/qmqtt_websocketiodevice_p.h + + SOURCES += \ + $$PWD/qmqtt_websocket.cpp \ + $$PWD/qmqtt_websocketiodevice.cpp +} + +!contains(CONFIG, QMQTT_NO_SSL) { + PRIVATE_HEADERS += \ + $$PWD/qmqtt_ssl_socket_p.h + + SOURCES += \ + $$PWD/qmqtt_ssl_socket.cpp +} diff --git a/mqtt/qmqtt.pro b/mqtt/qmqtt.pro new file mode 100644 index 0000000..4862266 --- /dev/null +++ b/mqtt/qmqtt.pro @@ -0,0 +1,17 @@ +TARGET = QtQmqtt +QT = core network +qtHaveModule(websockets): QMQTT_WEBSOCKETS: QT += websockets + +DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII + +# CONFIG += QMQTT_NO_SSL + +HEADERS += \ + $$PWD/qmqtt_global.h \ + $$PWD/qmqtt.h + +include(qmqtt.pri) + +HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS + +load(qt_module) diff --git a/mqtt/qmqtt.qbs b/mqtt/qmqtt.qbs new file mode 100644 index 0000000..b3d0f62 --- /dev/null +++ b/mqtt/qmqtt.qbs @@ -0,0 +1,134 @@ +import qbs +import qbs.TextFile +import qbs.FileInfo + +Product { + name: "qmqtt" + type: [libraryType] + property bool webSocketSupport: false + property string libraryType: "dynamiclibrary" + targetName: "qmqtt" + version: "1.0.0" + + cpp.defines: [ + "QT_BUILD_QMQTT_LIB", + "QT_NO_CAST_TO_ASCII", + "QT_NO_CAST_FROM_ASCII", + ] + + cpp.includePaths: sourceDirectory + + files: [ + "*.cpp" + ] + + Group { + name: "Public Headers" + fileTags: [ + "hpp", + "public_headers" + ] + files: [ + "*.h" + ] + excludeFiles: privateHeaders.files + } + + Group { + id: privateHeaders + name: "Private Headers" + fileTags: [ + "hpp", + "private_headers", + ] + files: [ + "*_p.h" + ] + } + + Group { + fileTagsFilter: libraryType + qbs.install: true + qbs.installDir: "lib" + } + + Group { + fileTagsFilter: "public_headers" + qbs.install: true + qbs.installDir: FileInfo.joinPaths("include", product.name) + } + + Group { + fileTagsFilter: "private_headers" + qbs.install: true + qbs.installDir: FileInfo.joinPaths("include", product.name, version, product.name) + } + + Group { + fileTagsFilter: "Exporter.qbs.module" + qbs.installDir: FileInfo.joinPaths("qbs", "modules", product.name, "private") + qbs.install: true + } + + Group { + fileTagsFilter: "Exporter.pkgconfig.pc" + qbs.installDir: FileInfo.joinPaths("lib" ,"pkgconfig") + qbs.install: true + } + + Depends { + name: "cpp" + } + + Depends { + name: "Qt" + property var baseModules: ["core", "network"] + + Properties { + condition: webSocketSupport + submodules: baseModules.concat(["websockets"]) + } + Properties { + condition: !webSocketSupport + submodules: baseModules + } + } + + Depends { + name: "Exporter.qbs" + } + Depends { + name: "Exporter.pkgconfig" + } + + Export { + Depends { + name: "cpp" + } + + Depends { + name: "Qt" + property var baseModules: ["core", "network"] + Properties { + condition: product.webSocketSupport + submodules: baseModules.concat(["websockets"]) + } + Properties { + condition: !product.webSocketSupport + submodules: baseModules + } + } + + cpp.includePaths: [ + product.sourceDirectory, + FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix, "include"), + ] + prefixMapping: [ + { + prefix: product.sourceDirectory, + replacement: FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix, "include", + product.name) + } + ] + } +} diff --git a/mqtt/qmqtt_client.cpp b/mqtt/qmqtt_client.cpp new file mode 100644 index 0000000..78542e4 --- /dev/null +++ b/mqtt/qmqtt_client.cpp @@ -0,0 +1,412 @@ +/* + * qmqtt_client.cpp - qmqtt client + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "qmqtt_client.h" +#include "qmqtt_client_p.h" + +QMQTT::Client::Client(const QHostAddress& host, + const quint16 port, + QObject* parent) + : QObject(parent) + , d_ptr(new ClientPrivate(this)) +{ + Q_D(Client); + d->init(host, port); +} + +#ifndef QT_NO_SSL +QMQTT::Client::Client(const QString &hostName, + const quint16 port, + const QSslConfiguration &config, + const bool ignoreSelfSigned, QObject *parent) + : QObject(parent) + , d_ptr(new ClientPrivate(this)) +{ + Q_D(Client); + d->init(hostName, port, config, ignoreSelfSigned); +} +#endif // QT_NO_SSL + +QMQTT::Client::Client(const QString &hostName, + const quint16 port, + const bool ssl, + const bool ignoreSelfSigned, + QObject* parent) + : QObject(parent) + , d_ptr(new ClientPrivate(this)) +{ + Q_D(Client); + d->init(hostName, port, ssl, ignoreSelfSigned); +} + +#ifdef QT_WEBSOCKETS_LIB +QMQTT::Client::Client(const QString& url, + const QString& origin, + QWebSocketProtocol::Version version, + bool ignoreSelfSigned, + QObject* parent) + : QObject(parent) + , d_ptr(new ClientPrivate(this)) +{ + Q_D(Client); +#ifndef QT_NO_SSL + d->init(url, origin, version, NULL, ignoreSelfSigned); +#else + Q_UNUSED(ignoreSelfSigned) + d->init(url, origin, version); +#endif // QT_NO_SSL +} + +#ifndef QT_NO_SSL +QMQTT::Client::Client(const QString& url, + const QString& origin, + QWebSocketProtocol::Version version, + const QSslConfiguration& sslConfig, + const bool ignoreSelfSigned, + QObject* parent) + : QObject(parent) + , d_ptr(new ClientPrivate(this)) +{ + Q_D(Client); + d->init(url, origin, version, &sslConfig, ignoreSelfSigned); +} +#endif // QT_NO_SSL +#endif // QT_WEBSOCKETS_LIB + +QMQTT::Client::Client(NetworkInterface* network, + const QHostAddress& host, + const quint16 port, + QObject* parent) + : QObject(parent) + , d_ptr(new ClientPrivate(this)) +{ + Q_D(Client); + d->init(host, port, network); +} + +QMQTT::Client::~Client() +{ +} + +QHostAddress QMQTT::Client::host() const +{ + Q_D(const Client); + return d->host(); +} + +void QMQTT::Client::setHost(const QHostAddress& host) +{ + Q_D(Client); + d->setHost(host); +} + +QString QMQTT::Client::hostName() const +{ + Q_D(const Client); + return d->hostName(); +} + +void QMQTT::Client::setHostName(const QString &hostName) +{ + Q_D(Client); + d->setHostName(hostName); +} + +quint16 QMQTT::Client::port() const +{ + Q_D(const Client); + return d->port(); +} + +void QMQTT::Client::setPort(const quint16 port) +{ + Q_D(Client); + d->setPort(port); +} + +QString QMQTT::Client::clientId() const +{ + Q_D(const Client); + return d->clientId(); +} + +void QMQTT::Client::setClientId(const QString& clientId) +{ + Q_D(Client); + d->setClientId(clientId); +} + +QString QMQTT::Client::username() const +{ + Q_D(const Client); + return d->username(); +} + +void QMQTT::Client::setUsername(const QString& username) +{ + Q_D(Client); + d->setUsername(username); +} + +QMQTT::MQTTVersion QMQTT::Client::version() const +{ + Q_D(const Client); + return d->version(); +} + +void QMQTT::Client::setVersion(const QMQTT::MQTTVersion version) +{ + Q_D(Client); + d->setVersion(version); +} + +QByteArray QMQTT::Client::password() const +{ + Q_D(const Client); + return d->password(); +} + +void QMQTT::Client::setPassword(const QByteArray &password) +{ + Q_D(Client); + d->setPassword(password); +} + +quint16 QMQTT::Client::keepAlive() const +{ + Q_D(const Client); + return d->keepAlive(); +} + +void QMQTT::Client::setKeepAlive(const quint16 keepAlive) +{ + Q_D(Client); + d->setKeepAlive(keepAlive); +} + +bool QMQTT::Client::cleanSession() const +{ + Q_D(const Client); + return d->cleanSession(); +} + +void QMQTT::Client::setCleanSession(const bool cleanSession) +{ + Q_D(Client); + d->setCleanSession(cleanSession); +} + +bool QMQTT::Client::autoReconnect() const +{ + Q_D(const Client); + return d->autoReconnect(); +} + +void QMQTT::Client::setAutoReconnect(const bool value) +{ + Q_D(Client); + d->setAutoReconnect(value); +} + +int QMQTT::Client::autoReconnectInterval() const +{ + Q_D(const Client); + return d->autoReconnectInterval(); +} + +void QMQTT::Client::setAutoReconnectInterval(const int autoReconnectInterval) +{ + Q_D(Client); + d->setAutoReconnectInterval(autoReconnectInterval); +} + +QString QMQTT::Client::willTopic() const +{ + Q_D(const Client); + return d->willTopic(); +} + +void QMQTT::Client::setWillTopic(const QString& willTopic) +{ + Q_D(Client); + d->setWillTopic(willTopic); +} + +quint8 QMQTT::Client::willQos() const +{ + Q_D(const Client); + return d->willQos(); +} + +void QMQTT::Client::setWillQos(const quint8 willQos) +{ + Q_D(Client); + d->setWillQos(willQos); +} + +bool QMQTT::Client::willRetain() const +{ + Q_D(const Client); + return d->willRetain(); +} + +void QMQTT::Client::setWillRetain(const bool willRetain) +{ + Q_D(Client); + d->setWillRetain(willRetain); +} + +QByteArray QMQTT::Client::willMessage() const +{ + Q_D(const Client); + return d->willMessage(); +} + +void QMQTT::Client::setWillMessage(const QByteArray &willMessage) +{ + Q_D(Client); + d->setWillMessage(willMessage); +} + +QMQTT::ConnectionState QMQTT::Client::connectionState() const +{ + Q_D(const Client); + return d->connectionState(); +} + +bool QMQTT::Client::isConnectedToHost() const +{ + Q_D(const Client); + return d->isConnectedToHost(); +} + +#ifndef QT_NO_SSL +QSslConfiguration QMQTT::Client::sslConfiguration() const +{ + Q_D(const Client); + return d->sslConfiguration(); +} + +void QMQTT::Client::setSslConfiguration(const QSslConfiguration& config) +{ + Q_D(Client); + d->setSslConfiguration(config); +} +#endif // QT_NO_SSL + +void QMQTT::Client::connectToHost() +{ + Q_D(Client); + d->connectToHost(); +} + +void QMQTT::Client::onNetworkConnected() +{ + Q_D(Client); + d->onNetworkConnected(); +} + +quint16 QMQTT::Client::publish(const Message& message) +{ + Q_D(Client); + return d->publish(message); +} + +void QMQTT::Client::subscribe(const QString& topic, const quint8 qos) +{ + Q_D(Client); + d->subscribe(topic, qos); +} + +void QMQTT::Client::unsubscribe(const QString& topic) +{ + Q_D(Client); + d->unsubscribe(topic); +} + +void QMQTT::Client::onTimerPingReq() +{ + Q_D(Client); + d->onTimerPingReq(); +} + +void QMQTT::Client::onPingTimeout() +{ + Q_D(Client); + d->onPingTimeout(); +} + +void QMQTT::Client::disconnectFromHost() +{ + Q_D(Client); + d->disconnectFromHost(); +} + +void QMQTT::Client::onNetworkReceived(const QMQTT::Frame& frame) +{ + Q_D(Client); + d->onNetworkReceived(frame); +} + +void QMQTT::Client::onNetworkDisconnected() +{ + Q_D(Client); + d->onNetworkDisconnected(); +} + +void QMQTT::Client::onNetworkError(QAbstractSocket::SocketError error) +{ + Q_D(Client); + d->onNetworkError(error); +} + +#ifndef QT_NO_SSL +void QMQTT::Client::onSslErrors(const QList& errors) +{ + Q_D(Client); + d->onSslErrors(errors); +} +#endif // QT_NO_SSL + +#ifndef QT_NO_SSL +void QMQTT::Client::ignoreSslErrors() +{ + Q_D(Client); + d->ignoreSslErrors(); +} + +void QMQTT::Client::ignoreSslErrors(const QList& errors) +{ + Q_D(Client); + d->ignoreSslErrors(errors); +} +#endif // QT_NO_SSL diff --git a/mqtt/qmqtt_client.h b/mqtt/qmqtt_client.h new file mode 100644 index 0000000..da89f01 --- /dev/null +++ b/mqtt/qmqtt_client.h @@ -0,0 +1,276 @@ +/* + * qmqtt_client.h - qmqtt client header + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_CLIENT_H +#define QMQTT_CLIENT_H + +#include "qmqtt_global.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef QT_WEBSOCKETS_LIB +#include +#endif // QT_WEBSOCKETS_LIB + +#ifndef QT_NO_SSL +#include +QT_FORWARD_DECLARE_CLASS(QSslError) +#endif // QT_NO_SSL + +namespace QMQTT { + +static const quint8 LIBRARY_VERSION_MAJOR = 0; +static const quint8 LIBRARY_VERSION_MINOR = 3; +static const quint8 LIBRARY_VERSION_REVISION = 1; +//static const char* LIBRARY_VERSION = "0.3.1"; + +enum MQTTVersion +{ + V3_1_0 = 3, + V3_1_1 = 4 +}; + +enum ConnectionState +{ + STATE_INIT = 0, + STATE_CONNECTING, + STATE_CONNECTED, + STATE_DISCONNECTED +}; + +enum ClientError +{ + UnknownError = 0, + SocketConnectionRefusedError, + SocketRemoteHostClosedError, + SocketHostNotFoundError, + SocketAccessError, + SocketResourceError, + SocketTimeoutError, + SocketDatagramTooLargeError, + SocketNetworkError, + SocketAddressInUseError, + SocketAddressNotAvailableError, + SocketUnsupportedSocketOperationError, + SocketUnfinishedSocketOperationError, + SocketProxyAuthenticationRequiredError, + SocketSslHandshakeFailedError, + SocketProxyConnectionRefusedError, + SocketProxyConnectionClosedError, + SocketProxyConnectionTimeoutError, + SocketProxyNotFoundError, + SocketProxyProtocolError, + SocketOperationError, + SocketSslInternalError, + SocketSslInvalidUserDataError, + SocketTemporaryError, + MqttUnacceptableProtocolVersionError=1<<16, + MqttIdentifierRejectedError, + MqttServerUnavailableError, + MqttBadUserNameOrPasswordError, + MqttNotAuthorizedError, + MqttNoPingResponse +}; + +class ClientPrivate; +class Message; +class Frame; +class NetworkInterface; + +class Q_MQTT_EXPORT Client : public QObject +{ + Q_OBJECT + Q_PROPERTY(quint16 _port READ port WRITE setPort) + Q_PROPERTY(QHostAddress _host READ host WRITE setHost) + Q_PROPERTY(QString _hostName READ hostName WRITE setHostName) + Q_PROPERTY(QString _clientId READ clientId WRITE setClientId) + Q_PROPERTY(QString _username READ username WRITE setUsername) + Q_PROPERTY(QByteArray _password READ password WRITE setPassword) + Q_PROPERTY(quint16 _keepAlive READ keepAlive WRITE setKeepAlive) + Q_PROPERTY(MQTTVersion _version READ version WRITE setVersion) + Q_PROPERTY(bool _autoReconnect READ autoReconnect WRITE setAutoReconnect) + Q_PROPERTY(int _autoReconnectInterval READ autoReconnectInterval WRITE setAutoReconnectInterval) + Q_PROPERTY(bool _cleanSession READ cleanSession WRITE setCleanSession) + Q_PROPERTY(QString _willTopic READ willTopic WRITE setWillTopic) + Q_PROPERTY(quint8 _willQos READ willQos WRITE setWillQos) + Q_PROPERTY(bool _willRetain READ willRetain WRITE setWillRetain) + Q_PROPERTY(QByteArray _willMessage READ willMessage WRITE setWillMessage) + Q_PROPERTY(QString _connectionState READ connectionState) +#ifndef QT_NO_SSL + Q_PROPERTY(QSslConfiguration _sslConfiguration READ sslConfiguration WRITE setSslConfiguration) +#endif // QT_NO_SSL + +public: + Client(const QHostAddress& host = QHostAddress::LocalHost, + const quint16 port = 1883, + QObject* parent = NULL); + +#ifndef QT_NO_SSL + Client(const QString& hostName, + const quint16 port, + const QSslConfiguration& config, + const bool ignoreSelfSigned=false, + QObject* parent = NULL); +#endif // QT_NO_SSL + + // This function is provided for backward compatibility with older versions of QMQTT. + // If the ssl parameter is true, this function will load a private key ('cert.key') and a local + // certificate ('cert.crt') from the current working directory. It will also set PeerVerifyMode + // to None. This may not be the safest way to set up an SSL connection. + Client(const QString& hostName, + const quint16 port, + const bool ssl, + const bool ignoreSelfSigned, + QObject* parent = NULL); + +#ifdef QT_WEBSOCKETS_LIB + // Create a connection over websockets + Client(const QString& url, + const QString& origin, + QWebSocketProtocol::Version version, + bool ignoreSelfSigned = false, + QObject* parent = NULL); + +#ifndef QT_NO_SSL + Client(const QString& url, + const QString& origin, + QWebSocketProtocol::Version version, + const QSslConfiguration& config, + const bool ignoreSelfSigned = false, + QObject* parent = NULL); +#endif // QT_NO_SSL +#endif // QT_WEBSOCKETS_LIB + + // for testing purposes only + Client(NetworkInterface* network, + const QHostAddress& host = QHostAddress::LocalHost, + const quint16 port = 1883, + QObject* parent = NULL); + + virtual ~Client(); + + QHostAddress host() const; + QString hostName() const; + quint16 port() const; + QString clientId() const; + QString username() const; + QByteArray password() const; + QMQTT::MQTTVersion version() const; + quint16 keepAlive() const; + bool cleanSession() const; + bool autoReconnect() const; + int autoReconnectInterval() const; + ConnectionState connectionState() const; + QString willTopic() const; + quint8 willQos() const; + bool willRetain() const; + QByteArray willMessage() const; + + bool isConnectedToHost() const; +#ifndef QT_NO_SSL + QSslConfiguration sslConfiguration() const; + void setSslConfiguration(const QSslConfiguration& config); +#endif // QT_NO_SSL + +public slots: + void setHost(const QHostAddress& host); + void setHostName(const QString& hostName); + void setPort(const quint16 port); + void setClientId(const QString& clientId); + void setUsername(const QString& username); + void setPassword(const QByteArray& password); + void setVersion(const MQTTVersion version); + void setKeepAlive(const quint16 keepAlive); + void setCleanSession(const bool cleanSession); + void setAutoReconnect(const bool value); + void setAutoReconnectInterval(const int autoReconnectInterval); + void setWillTopic(const QString& willTopic); + void setWillQos(const quint8 willQos); + void setWillRetain(const bool willRetain); + void setWillMessage(const QByteArray& willMessage); + + void connectToHost(); + void disconnectFromHost(); + + void subscribe(const QString& topic, const quint8 qos = 0); + void unsubscribe(const QString& topic); + + quint16 publish(const QMQTT::Message& message); + +#ifndef QT_NO_SSL + void ignoreSslErrors(); + void ignoreSslErrors(const QList& errors); +#endif // QT_NO_SSL + +signals: + void connected(); + void disconnected(); + void error(const QMQTT::ClientError error); + + void subscribed(const QString& topic, const quint8 qos = 0); + void unsubscribed(const QString& topic); + void published(const QMQTT::Message& message, quint16 msgid = 0); + void received(const QMQTT::Message& message); + void pingresp(); +#ifndef QT_NO_SSL + void sslErrors(const QList& errors); +#endif // QT_NO_SSL + +protected slots: + void onNetworkConnected(); + void onNetworkDisconnected(); + void onNetworkReceived(const QMQTT::Frame& frame); + void onTimerPingReq(); + void onPingTimeout(); + void onNetworkError(QAbstractSocket::SocketError error); +#ifndef QT_NO_SSL + void onSslErrors(const QList& errors); +#endif // QT_NO_SSL + +protected: + QScopedPointer d_ptr; + +private: + Q_DISABLE_COPY(Client) + Q_DECLARE_PRIVATE(Client) +}; + +} // namespace QMQTT + +Q_DECLARE_METATYPE(QMQTT::ClientError) + +#endif // QMQTT_CLIENT_H diff --git a/mqtt/qmqtt_client_p.cpp b/mqtt/qmqtt_client_p.cpp new file mode 100644 index 0000000..5f53297 --- /dev/null +++ b/mqtt/qmqtt_client_p.cpp @@ -0,0 +1,811 @@ +/* + * qmqtt_client_p.cpp - qmqtt client private + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "qmqtt_client_p.h" +#include "qmqtt_network_p.h" +#include "qmqtt_frame.h" +#include "qmqtt_message.h" + +#include +#include +#ifndef QT_NO_SSL +#include +#include +#include +#endif // QT_NO_SSL + +Q_LOGGING_CATEGORY(client, "qmqtt.client") + +static const quint8 QOS0 = 0x00; +static const quint8 QOS1 = 0x01; +static const quint8 QOS2 = 0x02; + +QMQTT::ClientPrivate::ClientPrivate(Client* qq_ptr) + : _host(QHostAddress::LocalHost) + , _port(1883) + , _gmid(1) + , _version(MQTTVersion::V3_1_0) + , _clientId(QUuid::createUuid().toString()) + , _cleanSession(false) + , _connectionState(STATE_INIT) + , _willQos(0) + , _willRetain(false) + , q_ptr(qq_ptr) +{ + setKeepAlive(300); +} + +QMQTT::ClientPrivate::~ClientPrivate() +{ +} + +void QMQTT::ClientPrivate::init(const QHostAddress& host, const quint16 port, NetworkInterface* network) +{ + Q_Q(Client); + _host = host; + _port = port; + if(network == NULL) + { + init(new Network(q)); + } + else + { + init(network); + } +} + +#ifndef QT_NO_SSL +void QMQTT::ClientPrivate::init(const QString& hostName, const quint16 port, + const QSslConfiguration &config, const bool ignoreSelfSigned) +{ + Q_Q(Client); + _hostName = hostName; + _port = port; + _ignoreSelfSigned = ignoreSelfSigned; + init(new Network(config, q)); + QObject::connect(_network.data(), &QMQTT::Network::sslErrors, q, &QMQTT::Client::onSslErrors); +} +#endif // QT_NO_SSL + +void QMQTT::ClientPrivate::init(const QString& hostName, const quint16 port, const bool ssl, + const bool ignoreSelfSigned) +{ + Q_Q(Client); + _hostName = hostName; + _port = port; + if (ssl) + { +#ifndef QT_NO_SSL + QSslConfiguration sslConf = QSslConfiguration::defaultConfiguration(); + QList certs = QSslCertificate::fromPath(QStringLiteral("./cert.crt")); + if (!certs.isEmpty()) + sslConf.setLocalCertificate(certs.first()); + QFile file(QStringLiteral("./cert.key")); + if (file.open(QIODevice::ReadOnly)) { + sslConf.setPrivateKey(QSslKey(file.readAll(), QSsl::Rsa)); + } + sslConf.setPeerVerifyMode(QSslSocket::VerifyNone); + init(hostName, port, sslConf, ignoreSelfSigned); +#else + Q_UNUSED(ignoreSelfSigned) + qCritical() << "SSL not supported in this QT build"; +#endif // QT_NO_SSL + } + else + { + init(new Network(q)); + } +} + +#ifdef QT_WEBSOCKETS_LIB +#ifndef QT_NO_SSL +void QMQTT::ClientPrivate::init(const QString& url, + const QString& origin, + QWebSocketProtocol::Version version, + const QSslConfiguration* sslConfig, + bool ignoreSelfSigned) +{ + Q_Q(Client); + _hostName = url; + _ignoreSelfSigned = ignoreSelfSigned; + init(new Network(origin, version, sslConfig, q)); +} +#endif // QT_NO_SSL + +void QMQTT::ClientPrivate::init(const QString& url, + const QString& origin, + QWebSocketProtocol::Version version) +{ + Q_Q(Client); + _hostName = url; + init(new Network(origin, version, q)); +} +#endif // QT_WEBSOCKETS_LIB + +void QMQTT::ClientPrivate::init(NetworkInterface* network) +{ + Q_Q(Client); + + _network.reset(network); + _timer.setSingleShot(true); + _pingResponseTimer.setSingleShot(true); + + QObject::connect(&_timer, &QTimer::timeout, q, &Client::onTimerPingReq); + QObject::connect(&_pingResponseTimer, &QTimer::timeout, q, &Client::onPingTimeout); + QObject::connect(_network.data(), &Network::connected, + q, &Client::onNetworkConnected); + QObject::connect(_network.data(), &Network::disconnected, + q, &Client::onNetworkDisconnected); + QObject::connect(_network.data(), &Network::received, + q, &Client::onNetworkReceived); + QObject::connect(_network.data(), &Network::error, + q, &Client::onNetworkError); +} + +void QMQTT::ClientPrivate::connectToHost() +{ + _connectionState = ConnectionState::STATE_CONNECTING; + if (_hostName.isEmpty()) + { + _network->connectToHost(_host, _port); + } + else + { + _network->connectToHost(_hostName, _port); + } +} + +void QMQTT::ClientPrivate::onNetworkConnected() +{ + sendConnect(); +} + +void QMQTT::ClientPrivate::sendConnect() +{ + quint8 header = CONNECT; + quint8 flags = 0; + + //header + Frame frame(header); + + //flags + flags = FLAG_CLEANSESS(flags, _cleanSession ? 1 : 0 ); + flags = FLAG_WILL(flags, willTopic().isEmpty() ? 0 : 1); + if (!willTopic().isEmpty()) + { + flags = FLAG_WILLQOS(flags, willQos()); + flags = FLAG_WILLRETAIN(flags, willRetain() ? 1 : 0); + } + if (!username().isEmpty()) + { + flags = FLAG_USERNAME(flags, 1); + flags = FLAG_PASSWD(flags, !password().isEmpty() ? 1 : 0); + } + + //payload + if(_version == V3_1_1) + { + frame.writeString(QStringLiteral(PROTOCOL_MAGIC_3_1_1)); + } + else + { + frame.writeString(QStringLiteral(PROTOCOL_MAGIC_3_1_0)); + } + frame.writeChar(_version); + frame.writeChar(flags); + frame.writeInt(keepAlive()); + frame.writeString(_clientId); + if(!willTopic().isEmpty()) + { + frame.writeString(willTopic()); + // According to the specs (http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718031) + // the will message gets always sent together with the topic, also when it is empty which is perfectly valid. + frame.writeByteArray(_willMessage); + } + if (!_username.isEmpty()) + { + frame.writeString(_username); + if (!_password.isEmpty()) + { + frame.writeByteArray(_password); + } + } + sendFrame(frame); +} + +quint16 QMQTT::ClientPrivate::sendPublish(const Message &message) +{ + quint16 msgid = message.id(); + + quint8 header = PUBLISH; + header = SETRETAIN(header, message.retain() ? 1 : 0); + header = SETQOS(header, message.qos()); + header = SETDUP(header, message.dup() ? 1 : 0); + Frame frame(header); + frame.writeString(message.topic()); + if(message.qos() > QOS0) { + if (msgid == 0) + msgid = nextmid(); + frame.writeInt(msgid); + } + if(!message.payload().isEmpty()) { + frame.writeRawData(message.payload()); + } + sendFrame(frame); + return msgid; +} + +void QMQTT::ClientPrivate::sendPuback(const quint8 type, const quint16 mid) +{ + Frame frame(type); + frame.writeInt(mid); + sendFrame(frame); +} + +quint16 QMQTT::ClientPrivate::sendSubscribe(const QString & topic, const quint8 qos) +{ + quint16 mid = nextmid(); + Frame frame(SETQOS(SUBSCRIBE, QOS1)); + frame.writeInt(mid); + frame.writeString(topic); + frame.writeChar(qos); + sendFrame(frame); + return mid; +} + +quint16 QMQTT::ClientPrivate::sendUnsubscribe(const QString &topic) +{ + quint16 mid = nextmid(); + Frame frame(SETQOS(UNSUBSCRIBE, QOS1)); + frame.writeInt(mid); + frame.writeString(topic); + sendFrame(frame); + return mid; +} + +void QMQTT::ClientPrivate::onTimerPingReq() +{ + if (!isConnectedToHost()) + return; + Frame frame(PINGREQ); + sendFrame(frame); + _pingResponseTimer.start(); +} + +void QMQTT::ClientPrivate::onPingTimeout() +{ + Q_Q(Client); + emit q->error(MqttNoPingResponse); + disconnectFromHost(); +} + +void QMQTT::ClientPrivate::disconnectFromHost() +{ + _connectionState = ConnectionState::STATE_DISCONNECTED; + sendDisconnect(); + _network->disconnectFromHost(); +} + +void QMQTT::ClientPrivate::sendDisconnect() +{ + Frame frame(DISCONNECT); + sendFrame(frame); +} + +void QMQTT::ClientPrivate::sendFrame(const Frame &frame) +{ + _network->sendFrame(frame); + _timer.start(); +} + +void QMQTT::ClientPrivate::stopKeepAlive() +{ + _timer.stop(); + _pingResponseTimer.stop(); +} + +quint16 QMQTT::ClientPrivate::nextmid() +{ + return _gmid++; +} + +quint16 QMQTT::ClientPrivate::publish(const Message& message) +{ + Q_Q(Client); + quint16 msgid = sendPublish(message); + + // Emit published only at QOS0 + if (message.qos() == QOS0) + emit q->published(message, msgid); + else + _midToMessage[msgid] = message; + + return msgid; +} + +void QMQTT::ClientPrivate::puback(const quint8 type, const quint16 msgid) +{ + sendPuback(type, msgid); +} + +void QMQTT::ClientPrivate::subscribe(const QString& topic, const quint8 qos) +{ + quint16 msgid = sendSubscribe(topic, qos); + _midToTopic[msgid] = topic; +} + +void QMQTT::ClientPrivate::unsubscribe(const QString& topic) +{ + quint16 msgid = sendUnsubscribe(topic); + _midToTopic[msgid] = topic; +} + +void QMQTT::ClientPrivate::onNetworkDisconnected() +{ + Q_Q(Client); + + stopKeepAlive(); + _midToTopic.clear(); + _midToMessage.clear(); + emit q->disconnected(); +} + +void QMQTT::ClientPrivate::onNetworkReceived(const QMQTT::Frame& frm) +{ + QMQTT::Frame frame(frm); + quint8 qos = 0; + bool retain, dup; + QString topic; + quint16 mid = 0; + quint8 header = frame.header(); + quint8 type = GETTYPE(header); + + switch(type) + { + case CONNACK: + frame.readChar(); + handleConnack(frame.readChar()); + break; + case PUBLISH: + qos = GETQOS(header); + retain = GETRETAIN(header); + dup = GETDUP(header); + topic = frame.readString(); + if( qos > QOS0) { + mid = frame.readInt(); + } + handlePublish(Message(mid, topic, frame.data(), qos, retain, dup)); + break; + case PUBACK: + case PUBREC: + case PUBREL: + case PUBCOMP: + mid = frame.readInt(); + handlePuback(type, mid); + break; + case SUBACK: + mid = frame.readInt(); + topic = _midToTopic.take(mid); + qos = frame.readChar(); + handleSuback(topic, qos); + break; + case UNSUBACK: + mid = frame.readInt(); + topic = _midToTopic.take(mid); + handleUnsuback(topic); + break; + case PINGRESP: + handlePingresp(); + break; + default: + break; + } +} + +void QMQTT::ClientPrivate::handleConnack(const quint8 ack) +{ + Q_Q(Client); + + switch (ack) + { + case 0: + _connectionState = ConnectionState::STATE_CONNECTED; + emit q->connected(); + break; + case 1: + emit q->error(MqttUnacceptableProtocolVersionError); + break; + case 2: + emit q->error(MqttIdentifierRejectedError); + break; + case 3: + emit q->error(MqttServerUnavailableError); + break; + case 4: + emit q->error(MqttBadUserNameOrPasswordError); + break; + case 5: + emit q->error(MqttNotAuthorizedError); + break; + default: + emit q->error(UnknownError); + break; + } +} + +void QMQTT::ClientPrivate::handlePublish(const Message& message) +{ + Q_Q(Client); + + if(message.qos() == QOS1) + { + sendPuback(PUBACK, message.id()); + } + else if(message.qos() == QOS2) + { + sendPuback(PUBREC, message.id()); + } + emit q->received(message); +} + +void QMQTT::ClientPrivate::handlePuback(const quint8 type, const quint16 msgid) +{ + Q_Q(Client); + + switch (type) + { + case PUBREC: + sendPuback(SETQOS(PUBREL, QOS1), msgid); + break; + case PUBREL: + sendPuback(PUBCOMP, msgid); + break; + case PUBACK: + case PUBCOMP: + // Emit published on PUBACK at QOS1 and on PUBCOMP at QOS2 + emit q->published(_midToMessage.take(msgid), msgid); + break; + } +} + +void QMQTT::ClientPrivate::handlePingresp() +{ + // Stop the ping response timer to prevent disconnection. It will be restarted when the next + // ping request has been sent. + _pingResponseTimer.stop(); + Q_Q(Client); + emit q->pingresp(); +} + +void QMQTT::ClientPrivate::handleSuback(const QString &topic, const quint8 qos) +{ + Q_Q(Client); + emit q->subscribed(topic, qos); +} + +void QMQTT::ClientPrivate::handleUnsuback(const QString &topic) { + Q_Q(Client); + emit q->unsubscribed(topic); +} + +bool QMQTT::ClientPrivate::autoReconnect() const +{ + return _network->autoReconnect(); +} + +void QMQTT::ClientPrivate::setAutoReconnect(const bool autoReconnect) +{ + _network->setAutoReconnect(autoReconnect); +} + +int QMQTT::ClientPrivate::autoReconnectInterval() const +{ + return _network->autoReconnectInterval(); +} + +void QMQTT::ClientPrivate::setAutoReconnectInterval(const int autoReconnectInterval) +{ + _network->setAutoReconnectInterval(autoReconnectInterval); +} + +bool QMQTT::ClientPrivate::isConnectedToHost() const +{ + return _network->isConnectedToHost(); +} + +QMQTT::ConnectionState QMQTT::ClientPrivate::connectionState() const +{ + return _connectionState; +} + +void QMQTT::ClientPrivate::setCleanSession(const bool cleanSession) +{ + _cleanSession = cleanSession; +} + +bool QMQTT::ClientPrivate::cleanSession() const +{ + return _cleanSession; +} + +void QMQTT::ClientPrivate::setKeepAlive(const quint16 keepAlive) +{ + // _timer will be started when a message is sent. + _timer.setInterval(keepAlive*1000); + // The MQTT specification does not mention a timeout value in this case, so we use 10% of the + // keep alive interval. + _pingResponseTimer.setInterval(qBound(keepAlive * 100, 10000, keepAlive * 1000)); +} + +quint16 QMQTT::ClientPrivate::keepAlive() const +{ + return _timer.interval() / 1000; +} + +void QMQTT::ClientPrivate::setPassword(const QByteArray& password) +{ + _password = password; +} + +QByteArray QMQTT::ClientPrivate::password() const +{ + return _password; +} + +void QMQTT::ClientPrivate::setUsername(const QString& username) +{ + _username = username; +} + +QString QMQTT::ClientPrivate::username() const +{ + return _username; +} + +void QMQTT::ClientPrivate::setVersion(const MQTTVersion version) +{ + _version = version; +} + +QMQTT::MQTTVersion QMQTT::ClientPrivate::version() const +{ + return _version; +} + +void QMQTT::ClientPrivate::setClientId(const QString& clientId) +{ + if(clientId.isEmpty()) + { + _clientId = QUuid::createUuid().toString(); + } + else + { + _clientId = clientId; + } +} + +QString QMQTT::ClientPrivate::clientId() const +{ + return _clientId; +} + +void QMQTT::ClientPrivate::setPort(const quint16 port) +{ + _port = port; +} + +quint16 QMQTT::ClientPrivate::port() const +{ + return _port; +} + +void QMQTT::ClientPrivate::setHost(const QHostAddress& host) +{ + _host = host; +} + +QHostAddress QMQTT::ClientPrivate::host() const +{ + return _host; +} + +void QMQTT::ClientPrivate::setHostName(const QString& hostName) +{ + _hostName = hostName; +} + +QString QMQTT::ClientPrivate::hostName() const +{ + return _hostName; +} + +QString QMQTT::ClientPrivate::willTopic() const +{ + return _willTopic; +} + +void QMQTT::ClientPrivate::setWillTopic(const QString& willTopic) +{ + _willTopic = willTopic; +} + +quint8 QMQTT::ClientPrivate::willQos() const +{ + return _willQos; +} + +void QMQTT::ClientPrivate::setWillQos(const quint8 willQos) +{ + _willQos = willQos; +} + +bool QMQTT::ClientPrivate::willRetain() const +{ + return _willRetain; +} + +void QMQTT::ClientPrivate::setWillRetain(const bool willRetain) +{ + _willRetain = willRetain; +} + +QByteArray QMQTT::ClientPrivate::willMessage() const +{ + return _willMessage; +} + +void QMQTT::ClientPrivate::setWillMessage(const QByteArray& willMessage) +{ + _willMessage = willMessage; +} + +void QMQTT::ClientPrivate::onNetworkError(QAbstractSocket::SocketError socketError) +{ + Q_Q(Client); + + switch (socketError) + { + case QAbstractSocket::ConnectionRefusedError: + emit q->error(SocketConnectionRefusedError); + break; + case QAbstractSocket::RemoteHostClosedError: + emit q->error(SocketRemoteHostClosedError); + break; + case QAbstractSocket::HostNotFoundError: + emit q->error(SocketHostNotFoundError); + break; + case QAbstractSocket::SocketAccessError: + emit q->error(SocketAccessError); + break; + case QAbstractSocket::SocketResourceError: + emit q->error(SocketResourceError); + break; + case QAbstractSocket::SocketTimeoutError: + emit q->error(SocketTimeoutError); + break; + case QAbstractSocket::DatagramTooLargeError: + emit q->error(SocketDatagramTooLargeError); + break; + case QAbstractSocket::NetworkError: + emit q->error(SocketNetworkError); + break; + case QAbstractSocket::AddressInUseError: + emit q->error(SocketAddressInUseError); + break; + case QAbstractSocket::SocketAddressNotAvailableError: + emit q->error(SocketAddressNotAvailableError); + break; + case QAbstractSocket::UnsupportedSocketOperationError: + emit q->error(SocketUnsupportedSocketOperationError); + break; + case QAbstractSocket::UnfinishedSocketOperationError: + emit q->error(SocketUnfinishedSocketOperationError); + break; + case QAbstractSocket::ProxyAuthenticationRequiredError: + emit q->error(SocketProxyAuthenticationRequiredError); + break; + case QAbstractSocket::SslHandshakeFailedError: + emit q->error(SocketSslHandshakeFailedError); + break; + case QAbstractSocket::ProxyConnectionRefusedError: + emit q->error(SocketProxyConnectionRefusedError); + break; + case QAbstractSocket::ProxyConnectionClosedError: + emit q->error(SocketProxyConnectionClosedError); + break; + case QAbstractSocket::ProxyConnectionTimeoutError: + emit q->error(SocketProxyConnectionTimeoutError); + break; + case QAbstractSocket::ProxyNotFoundError: + emit q->error(SocketProxyNotFoundError); + break; + case QAbstractSocket::ProxyProtocolError: + emit q->error(SocketProxyProtocolError); + break; + case QAbstractSocket::OperationError: + emit q->error(SocketOperationError); + break; + case QAbstractSocket::SslInternalError: + emit q->error(SocketSslInternalError); + break; + case QAbstractSocket::SslInvalidUserDataError: + emit q->error(SocketSslInvalidUserDataError); + break; + case QAbstractSocket::TemporaryError: + emit q->error(SocketTemporaryError); + break; + default: + emit q->error(UnknownError); + break; + } +} + +#ifndef QT_NO_SSL +void QMQTT::ClientPrivate::ignoreSslErrors() +{ + _network->ignoreSslErrors(); +} + +void QMQTT::ClientPrivate::ignoreSslErrors(const QList& errors) +{ + _network->ignoreSslErrors(errors); +} + +QSslConfiguration QMQTT::ClientPrivate::sslConfiguration() const +{ + return _network->sslConfiguration(); +} + +void QMQTT::ClientPrivate::setSslConfiguration(const QSslConfiguration& config) +{ + _network->setSslConfiguration(config); +} + +void QMQTT::ClientPrivate::onSslErrors(const QList& errors) +{ + Q_Q(Client); + + emit q->sslErrors(errors); + + if (!_ignoreSelfSigned) + return; + foreach (QSslError error, errors) + { + if (error.error() != QSslError::SelfSignedCertificate && + error.error() != QSslError::SelfSignedCertificateInChain) + { + return; + } + } + ignoreSslErrors(); +} +#endif // QT_NO_SSL diff --git a/mqtt/qmqtt_client_p.h b/mqtt/qmqtt_client_p.h new file mode 100644 index 0000000..03ec994 --- /dev/null +++ b/mqtt/qmqtt_client_p.h @@ -0,0 +1,183 @@ +/* + * qmqtt_client_p.h - qmqtt client private header + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_CLIENT_P_H +#define QMQTT_CLIENT_P_H + +#include "qmqtt_client.h" + +#include +#include +#include +#include +#include +#include + +#ifdef QT_WEBSOCKETS_LIB +#include +#endif // QT_WEBSOCKETS_LIB + +#ifndef QT_NO_SSL +#include +QT_FORWARD_DECLARE_CLASS(QSslError) +#endif // QT_NO_SSL + +namespace QMQTT { + +class NetworkInterface; + +class ClientPrivate +{ +public: + ClientPrivate(Client* qq_ptr); + ~ClientPrivate(); + + void init(const QHostAddress& host, const quint16 port, NetworkInterface* network = NULL); +#ifndef QT_NO_SSL + void init(const QString& hostName, const quint16 port, const QSslConfiguration& config, + const bool ignoreSelfSigned=false); +#endif // QT_NO_SSL + void init(const QString& hostName, const quint16 port, const bool ssl, const bool ignoreSelfSigned); +#ifdef QT_WEBSOCKETS_LIB +#ifndef QT_NO_SSL + void init(const QString& url, + const QString& origin, + QWebSocketProtocol::Version version, + const QSslConfiguration* sslConfig, + bool ignoreSelfSigned); +#endif // QT_NO_SSL + void init(const QString& url, + const QString& origin, + QWebSocketProtocol::Version version); +#endif // QT_WEBSOCKETS_LIB + void init(NetworkInterface* network); + + QHostAddress _host; + QString _hostName; + quint16 _port; +#ifdef QT_WEBSOCKETS_LIB + QWebSocketProtocol::Version _webSocketVersion; +#endif // QT_WEBSOCKETS_LIB +#ifndef QT_NO_SSL + bool _ignoreSelfSigned; +#endif // QT_NO_SSL + quint16 _gmid; + MQTTVersion _version; + QString _clientId; + QString _username; + QByteArray _password; + bool _cleanSession; + ConnectionState _connectionState; + QScopedPointer _network; + QTimer _timer; + QTimer _pingResponseTimer; + QString _willTopic; + quint8 _willQos; + bool _willRetain; + QByteArray _willMessage; + QHash _midToTopic; + QHash _midToMessage; + + Client* const q_ptr; + + quint16 nextmid(); + void connectToHost(); + void sendConnect(); + void onTimerPingReq(); + void onPingTimeout(); + quint16 sendUnsubscribe(const QString &topic); + quint16 sendSubscribe(const QString &topic, const quint8 qos); + quint16 sendPublish(const Message &message); + void sendPuback(const quint8 type, const quint16 mid); + void sendDisconnect(); + void sendFrame(const Frame &frame); + void disconnectFromHost(); + void stopKeepAlive(); + void onNetworkConnected(); + void onNetworkDisconnected(); + quint16 publish(const Message& message); + void puback(const quint8 type, const quint16 msgid); + void subscribe(const QString& topic, const quint8 qos); + void unsubscribe(const QString& topic); + void onNetworkReceived(const QMQTT::Frame& frame); + void handleConnack(const quint8 ack); + void handlePublish(const Message& message); + void handlePuback(const quint8 type, const quint16 msgid); + void handleSuback(const QString& topic, const quint8 qos); + void handleUnsuback(const QString& topic); + void handlePingresp(); + bool autoReconnect() const; + void setAutoReconnect(const bool autoReconnect); + int autoReconnectInterval() const; + void setAutoReconnectInterval(const int autoReconnectInterval); + bool isConnectedToHost() const; + QMQTT::ConnectionState connectionState() const; + void setCleanSession(const bool cleanSession); + bool cleanSession() const; + void setKeepAlive(const quint16 keepAlive); + quint16 keepAlive() const; + void setPassword(const QByteArray& password); + QByteArray password() const; + void setUsername(const QString& username); + QString username() const; + void setVersion(const MQTTVersion); + MQTTVersion version() const; + void setClientId(const QString& clientId); + QString clientId() const; + void setPort(const quint16 port); + quint16 port() const; + void setHost(const QHostAddress& host); + QHostAddress host() const; + void setHostName(const QString& hostName); + QString hostName() const; + void setWillTopic(const QString& willTopic); + void setWillQos(const quint8 willQos); + void setWillRetain(const bool willRetain); + void setWillMessage(const QByteArray& willMessage); + QString willTopic() const; + quint8 willQos() const; + bool willRetain() const; + QByteArray willMessage() const; + void onNetworkError(QAbstractSocket::SocketError error); +#ifndef QT_NO_SSL + void ignoreSslErrors(); + void ignoreSslErrors(const QList& errors); + QSslConfiguration sslConfiguration() const; + void setSslConfiguration(const QSslConfiguration& config); + void onSslErrors(const QList& errors); +#endif // QT_NO_SSL + + Q_DECLARE_PUBLIC(Client) +}; + +} // namespace QMQTT + +#endif // QMQTT_CLIENT_P_H diff --git a/mqtt/qmqtt_frame.cpp b/mqtt/qmqtt_frame.cpp new file mode 100644 index 0000000..7f7e152 --- /dev/null +++ b/mqtt/qmqtt_frame.cpp @@ -0,0 +1,209 @@ +/* + * qmqtt_frame.cpp - qmqtt frame + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "qmqtt_frame.h" + +#include +#include + +namespace QMQTT { + +Q_LOGGING_CATEGORY(frame, "qmqtt.frame") + +Frame::Frame() + : _header(0) + , _data(QByteArray()) +{ +} + +Frame::Frame(const quint8 header) + : _header(header) + , _data(QByteArray()) +{ +} + +Frame::Frame(const quint8 header, const QByteArray &data) + : _header(header) + , _data(data) +{ +} + +Frame::Frame(const Frame& other) +{ + _header = other._header; + _data = other._data; +} + +Frame& Frame::operator=(const Frame& other) +{ + _header = other._header; + _data = other._data; + return *this; +} + +bool Frame::operator==(const Frame& other) const +{ + return _header == other._header + && _data == other._data; +} + + +Frame::~Frame() +{ +} + +quint8 Frame::header() const +{ + return _header; +} + +QByteArray Frame::data() const +{ + return _data; +} + +quint8 Frame::readChar() +{ + char c = _data.at(0); + _data.remove(0, 1); + return c; +} + +quint16 Frame::readInt() +{ + quint8 msb = static_cast(_data.at(0)); + quint8 lsb = static_cast(_data.at(1)); + _data.remove(0, 2); + return (msb << 8) | lsb; +} + +QByteArray Frame::readByteArray() +{ + quint16 len = readInt(); + QByteArray data = _data.left(len); + _data.remove(0, len); + return data; +} + +QString Frame::readString() +{ + quint16 len = readInt(); + QString s = QString::fromUtf8(_data.left(len)); + _data.remove(0, len); + return s; +} + +void Frame::writeInt(const quint16 i) +{ + _data.append(MSB(i)); + _data.append(LSB(i)); +} + +void Frame::writeByteArray(const QByteArray &data) +{ + if (data.size() > (int)USHRT_MAX) + { + qCritical("qmqtt: Binary data size bigger than %u bytes, truncate it!", USHRT_MAX); + writeInt(USHRT_MAX); + _data.append(data.left(USHRT_MAX)); + return; + } + + writeInt(data.size()); + _data.append(data); +} + +void Frame::writeString(const QString &string) +{ + QByteArray data = string.toUtf8(); + if (data.size() > (int)USHRT_MAX) + { + qCritical("qmqtt: String size bigger than %u bytes, truncate it!", USHRT_MAX); + data.resize(USHRT_MAX); + } + writeInt(data.size()); + _data.append(data); +} + +void Frame::writeChar(const quint8 c) +{ + _data.append(c); +} + +void Frame::writeRawData(const QByteArray &data) +{ + _data.append(data); +} + +void Frame::write(QDataStream &stream) const +{ + QByteArray lenbuf; + + if (!encodeLength(lenbuf, _data.size())) + { + qCritical("qmqtt: Control packet bigger than 256 MB, dropped!"); + return; + } + + stream << (quint8)_header; + if(_data.size() == 0) { + stream << (quint8)0; + return; + } + if (stream.writeRawData(lenbuf.data(), lenbuf.size()) != lenbuf.size()) + { + qCritical("qmqtt: Control packet write error!"); + return; + } + if (stream.writeRawData(_data.data(), _data.size()) != _data.size()) + { + qCritical("qmqtt: Control packet write error!"); + } +} + +bool Frame::encodeLength(QByteArray &lenbuf, int length) const +{ + lenbuf.clear(); + quint8 d; + do { + d = length % 128; + length /= 128; + if (length > 0) { + d |= 0x80; + } + lenbuf.append(d); + } while (length > 0); + + return lenbuf.size() <= 4; +} + +} // namespace QMQTT diff --git a/mqtt/qmqtt_frame.h b/mqtt/qmqtt_frame.h new file mode 100644 index 0000000..0005b82 --- /dev/null +++ b/mqtt/qmqtt_frame.h @@ -0,0 +1,137 @@ +/* + * qmqtt_frame.h - qmqtt frame heaer + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_FRAME_H +#define QMQTT_FRAME_H + +#include "qmqtt_global.h" + +#include +#include +#include + +QT_FORWARD_DECLARE_CLASS(QDataStream) + +#define PROTOCOL_MAGIC_3_1_0 "MQIsdp" +#define PROTOCOL_MAGIC_3_1_1 "MQTT" + +#define RANDOM_CLIENT_PREFIX "QMQTT-" + +#define CONNECT 0x10 +#define CONNACK 0x20 +#define PUBLISH 0x30 +#define PUBACK 0x40 +#define PUBREC 0x50 +#define PUBREL 0x60 +#define PUBCOMP 0x70 +#define SUBSCRIBE 0x80 +#define SUBACK 0x90 +#define UNSUBSCRIBE 0xA0 +#define UNSUBACK 0xB0 +#define PINGREQ 0xC0 +#define PINGRESP 0xD0 +#define DISCONNECT 0xE0 + +#define LSB(A) quint8(A & 0x00FF) +#define MSB(A) quint8((A & 0xFF00) >> 8) + +/* +|-------------------------------------- +| 7 6 5 4 | 3 | 2 1 | 0 | +| Type | DUP flag | QoS | RETAIN | +|-------------------------------------- +*/ +#define GETTYPE(HDR) (HDR & 0xF0) +#define SETQOS(HDR, Q) (HDR | ((Q) << 1)) +#define GETQOS(HDR) ((HDR & 0x06) >> 1) +#define SETDUP(HDR, D) (HDR | ((D) << 3)) +#define GETDUP(HDR) ((HDR & 0x08) >> 3) +#define SETRETAIN(HDR, R) (HDR | (R)) +#define GETRETAIN(HDR) (HDR & 0x01) + +/* +|---------------------------------------------------------------------------------- +| 7 | 6 | 5 | 4 3 | 2 | 1 | 0 | +| username | password | willretain | willqos | willflag | cleansession | reserved | +|---------------------------------------------------------------------------------- +*/ +#define FLAG_CLEANSESS(F, C) (F | ((C) << 1)) +#define FLAG_WILL(F, W) (F | ((W) << 2)) +#define FLAG_WILLQOS(F, Q) (F | ((Q) << 3)) +#define FLAG_WILLRETAIN(F, R) (F | ((R) << 5)) +#define FLAG_PASSWD(F, P) (F | ((P) << 6)) +#define FLAG_USERNAME(F, U) (F | ((U) << 7)) + +namespace QMQTT { + +class Q_MQTT_EXPORT Frame +{ +public: + explicit Frame(); + explicit Frame(const quint8 header); + explicit Frame(const quint8 header, const QByteArray &data); + virtual ~Frame(); + + Frame(const Frame& other); + Frame& operator=(const Frame& other); + + bool operator==(const Frame& other) const; + inline bool operator!=(const Frame& other) const + { return !operator==(other); } + + quint8 header() const; + QByteArray data() const; + + quint16 readInt(); + quint8 readChar(); + QByteArray readByteArray(); + QString readString(); + + void writeInt(const quint16 i); + void writeChar(const quint8 c); + void writeByteArray(const QByteArray &data); + void writeString(const QString &string); + void writeRawData(const QByteArray &data); + + //TODO: FIXME LATER + void write(QDataStream &stream) const; + bool encodeLength(QByteArray &lenbuf, int length) const; + +private: + quint8 _header; + QByteArray _data; +}; + +} // namespace QMQTT + +Q_DECLARE_METATYPE(QMQTT::Frame) + +#endif // QMQTT_FRAME_H diff --git a/mqtt/qmqtt_global.h b/mqtt/qmqtt_global.h new file mode 100644 index 0000000..d633168 --- /dev/null +++ b/mqtt/qmqtt_global.h @@ -0,0 +1,48 @@ +/* + * qmqtt_global.h - qmqtt libray global + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_GLOBAL_H +#define QMQTT_GLOBAL_H + +#include + +#ifndef QT_STATIC +# if defined(QT_BUILD_QMQTT_LIB) +# define Q_MQTT_EXPORT Q_DECL_EXPORT +# else +# define Q_MQTT_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_MQTT_EXPORT +#endif + +#endif // QMQTT_GLOBAL_H + diff --git a/mqtt/qmqtt_message.cpp b/mqtt/qmqtt_message.cpp new file mode 100644 index 0000000..7beec31 --- /dev/null +++ b/mqtt/qmqtt_message.cpp @@ -0,0 +1,136 @@ +/* + * qmqtt_message.cpp - qmqtt message + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "qmqtt_message.h" +#include "qmqtt_message_p.h" + +namespace QMQTT { + +Message::Message() + : d(new MessagePrivate) +{ +} + +Message::Message(const quint16 id, const QString &topic, const QByteArray &payload, + const quint8 qos, const bool retain, const bool dup) + : d(new MessagePrivate(id, topic, payload, qos, retain, dup)) +{ +} + +Message::Message(const Message &other) + : d(other.d) +{ +} + +Message::~Message() +{ +} + +Message &Message::operator=(const Message &other) +{ + d = other.d; + return *this; +} + +bool Message::operator==(const Message &other) const +{ + if (d == other.d) + return true; + return d->id == other.d->id + && d->qos == other.d->qos + && d->retain == other.d->retain + && d->dup == other.d->dup + && d->topic == other.d->topic + && d->payload == other.d->payload; +} + +quint16 Message::id() const +{ + return d->id; +} + +void Message::setId(const quint16 id) +{ + d->id = id; +} + +quint8 Message::qos() const +{ + return d->qos; +} + +void Message::setQos(const quint8 qos) +{ + d->qos = qos; +} + +bool Message::retain() const +{ + return d->retain; +} + +void Message::setRetain(const bool retain) +{ + d->retain = retain; +} + +bool Message::dup() const +{ + return d->dup; +} + +void Message::setDup(const bool dup) +{ + d->dup = dup; +} + +QString Message::topic() const +{ + return d->topic; +} + +void Message::setTopic(const QString &topic) +{ + d->topic = topic; +} + +QByteArray Message::payload() const +{ + return d->payload; +} + +void Message::setPayload(const QByteArray &payload) +{ + d->payload = payload; +} + +} // namespace QMQTT diff --git a/mqtt/qmqtt_message.h b/mqtt/qmqtt_message.h new file mode 100644 index 0000000..3dbaa02 --- /dev/null +++ b/mqtt/qmqtt_message.h @@ -0,0 +1,96 @@ +/* + * qmqtt_message.h - qmqtt message header + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_MESSAGE_H +#define QMQTT_MESSAGE_H + +#include "qmqtt_global.h" + +#include +#include +#include +#include + +namespace QMQTT { + +class MessagePrivate; + +class Q_MQTT_EXPORT Message +{ +public: + Message(); + explicit Message(const quint16 id, const QString &topic, const QByteArray &payload, + const quint8 qos = 0, const bool retain = false, const bool dup = false); + Message(const Message &other); + ~Message(); + + Message &operator=(const Message &other); +#ifdef Q_COMPILER_RVALUE_REFS + inline Message &operator=(Message &&other) Q_DECL_NOTHROW + { swap(other); return *this; } +#endif + + bool operator==(const Message &other) const; + inline bool operator!=(const Message &other) const + { return !operator==(other); } + + inline void swap(Message &other) Q_DECL_NOTHROW + { qSwap(d, other.d); } + + quint16 id() const; + void setId(const quint16 id); + + quint8 qos() const; + void setQos(const quint8 qos); + + bool retain() const; + void setRetain(const bool retain); + + bool dup() const; + void setDup(const bool dup); + + QString topic() const; + void setTopic(const QString &topic); + + QByteArray payload() const; + void setPayload(const QByteArray &payload); + +private: + QSharedDataPointer d; +}; + +} // namespace QMQTT + +Q_DECLARE_SHARED(QMQTT::Message) + +Q_DECLARE_METATYPE(QMQTT::Message) + +#endif // QMQTT_MESSAGE_H diff --git a/mqtt/qmqtt_message_p.h b/mqtt/qmqtt_message_p.h new file mode 100644 index 0000000..148019c --- /dev/null +++ b/mqtt/qmqtt_message_p.h @@ -0,0 +1,83 @@ +/* + * qmqtt_message.h - qmqtt message private header + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_MESSAGE_P_H +#define QMQTT_MESSAGE_P_H + +#include +#include +#include + +namespace QMQTT { + +class MessagePrivate : public QSharedData +{ +public: + inline MessagePrivate() + : QSharedData(), + id(0), + qos(0), + retain(false), + dup(false) + {} + + inline MessagePrivate(const MessagePrivate &other) + : QSharedData(other), + id(other.id), + qos(other.qos), + retain(other.retain), + dup(other.dup), + topic(other.topic), + payload(other.payload) + {} + + inline MessagePrivate(quint16 id, const QString &topic, const QByteArray &payload, + quint8 qos, bool retain, bool dup) + : QSharedData(), + id(id), + qos(qos), + retain(retain), + dup(dup), + topic(topic), + payload(payload) + {} + + quint16 id; + quint8 qos : 2; + quint8 retain: 1; + quint8 dup: 1; + QString topic; + QByteArray payload; +}; + +} // namespace QMQTT + +#endif // QMQTT_MESSAGE_P_H diff --git a/mqtt/qmqtt_network.cpp b/mqtt/qmqtt_network.cpp new file mode 100644 index 0000000..b4f55ab --- /dev/null +++ b/mqtt/qmqtt_network.cpp @@ -0,0 +1,295 @@ +/* + * qmqtt_network.cpp - qmqtt network + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "qmqtt_network_p.h" +#include "qmqtt_socket_p.h" +#include "qmqtt_ssl_socket_p.h" +#include "qmqtt_timer_p.h" +#include "qmqtt_websocket_p.h" +#include "qmqtt_frame.h" + +#include + +const quint16 DEFAULT_PORT = 1883; +const quint16 DEFAULT_SSL_PORT = 8883; +const bool DEFAULT_AUTORECONNECT = false; +const int DEFAULT_AUTORECONNECT_INTERVAL_MS = 5000; + +QMQTT::Network::Network(QObject* parent) + : NetworkInterface(parent) + , _port(DEFAULT_PORT) + , _autoReconnect(DEFAULT_AUTORECONNECT) + , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS) + , _socket(new QMQTT::Socket) + , _autoReconnectTimer(new QMQTT::Timer) + , _readState(Header) +{ + initialize(); +} + +#ifndef QT_NO_SSL +QMQTT::Network::Network(const QSslConfiguration& config, QObject *parent) + : NetworkInterface(parent) + , _port(DEFAULT_SSL_PORT) + , _autoReconnect(DEFAULT_AUTORECONNECT) + , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS) + , _socket(new QMQTT::SslSocket(config)) + , _autoReconnectTimer(new QMQTT::Timer) + , _readState(Header) +{ + initialize(); + connect(_socket, &QMQTT::SslSocket::sslErrors, this, &QMQTT::Network::sslErrors); +} +#endif // QT_NO_SSL + +#ifdef QT_WEBSOCKETS_LIB +#ifndef QT_NO_SSL +QMQTT::Network::Network(const QString& origin, + QWebSocketProtocol::Version version, + const QSslConfiguration* sslConfig, + QObject* parent) + : NetworkInterface(parent) + , _port(DEFAULT_SSL_PORT) + , _autoReconnect(DEFAULT_AUTORECONNECT) + , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS) + , _socket(new QMQTT::WebSocket(origin, version, sslConfig)) + , _autoReconnectTimer(new QMQTT::Timer) + , _readState(Header) +{ + initialize(); +} +#endif // QT_NO_SSL + +QMQTT::Network::Network(const QString& origin, + QWebSocketProtocol::Version version, + QObject* parent) + : NetworkInterface(parent) + , _port(DEFAULT_PORT) + , _autoReconnect(DEFAULT_AUTORECONNECT) + , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS) + , _socket(new QMQTT::WebSocket(origin, version)) + , _autoReconnectTimer(new QMQTT::Timer) + , _readState(Header) +{ + initialize(); +} +#endif // QT_WEBSOCKETS_LIB + +QMQTT::Network::Network(SocketInterface* socketInterface, TimerInterface* timerInterface, + QObject* parent) + : NetworkInterface(parent) + , _port(DEFAULT_PORT) + , _autoReconnect(DEFAULT_AUTORECONNECT) + , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS) + , _socket(socketInterface) + , _autoReconnectTimer(timerInterface) + , _readState(Header) +{ + initialize(); +} + +void QMQTT::Network::initialize() +{ + _socket->setParent(this); + _autoReconnectTimer->setParent(this); + _autoReconnectTimer->setSingleShot(true); + _autoReconnectTimer->setInterval(_autoReconnectInterval); + + QObject::connect(_socket, &SocketInterface::connected, this, &Network::connected); + QObject::connect(_socket, &SocketInterface::disconnected, this, &Network::onDisconnected); + QObject::connect(_socket->ioDevice(), &QIODevice::readyRead, this, &Network::onSocketReadReady); + QObject::connect( + _autoReconnectTimer, &TimerInterface::timeout, + this, static_cast(&Network::connectToHost)); + QObject::connect(_socket, + static_cast(&SocketInterface::error), + this, &Network::onSocketError); +} + +QMQTT::Network::~Network() +{ +} + +bool QMQTT::Network::isConnectedToHost() const +{ + return _socket->state() == QAbstractSocket::ConnectedState; +} + +void QMQTT::Network::connectToHost(const QHostAddress& host, const quint16 port) +{ + // Reset the hostname, because if it is not empty connectToHost() will use it instead of _host. + _hostName.clear(); + _host = host; + _port = port; + connectToHost(); +} + +void QMQTT::Network::connectToHost(const QString& hostName, const quint16 port) +{ + _hostName = hostName; + _port = port; + connectToHost(); +} + +void QMQTT::Network::connectToHost() +{ + _readState = Header; + if (_hostName.isEmpty()) + { + _socket->connectToHost(_host, _port); + } + else + { + _socket->connectToHost(_hostName, _port); + } +} + +void QMQTT::Network::onSocketError(QAbstractSocket::SocketError socketError) +{ + emit error(socketError); + if(_autoReconnect) + { + _autoReconnectTimer->start(); + } +} + +void QMQTT::Network::sendFrame(const Frame& frame) +{ + if(_socket->state() == QAbstractSocket::ConnectedState) + { + QDataStream out(_socket->ioDevice()); + frame.write(out); + } +} + +void QMQTT::Network::disconnectFromHost() +{ + _socket->disconnectFromHost(); +} + +QAbstractSocket::SocketState QMQTT::Network::state() const +{ + return _socket->state(); +} + +bool QMQTT::Network::autoReconnect() const +{ + return _autoReconnect; +} + +void QMQTT::Network::setAutoReconnect(const bool autoReconnect) +{ + _autoReconnect = autoReconnect; +} + +int QMQTT::Network::autoReconnectInterval() const +{ + return _autoReconnectInterval; +} + +void QMQTT::Network::setAutoReconnectInterval(const int autoReconnectInterval) +{ + _autoReconnectInterval = autoReconnectInterval; + _autoReconnectTimer->setInterval(_autoReconnectInterval); +} + +void QMQTT::Network::onSocketReadReady() +{ + QIODevice *ioDevice = _socket->ioDevice(); + // Only read the available (cached) bytes, so the read will never block. + QByteArray data = ioDevice->read(ioDevice->bytesAvailable()); + foreach(char byte, data) { + switch (_readState) { + case Header: + _header = static_cast(byte); + _readState = Length; + _length = 0; + _shift = 0; + _data.resize(0); // keep allocated buffer + break; + case Length: + _length |= (byte & 0x7F) << _shift; + _shift += 7; + if ((byte & 0x80) != 0) + break; + if (_length == 0) { + _readState = Header; + Frame frame(_header, _data); + emit received(frame); + break; + } + _readState = PayLoad; + _data.reserve(_length); + break; + case PayLoad: + _data.append(byte); + --_length; + if (_length > 0) + break; + _readState = Header; + Frame frame(_header, _data); + emit received(frame); + break; + } + } +} + +void QMQTT::Network::onDisconnected() +{ + emit disconnected(); + if(_autoReconnect) + { + _autoReconnectTimer->start(); + } +} + +#ifndef QT_NO_SSL +void QMQTT::Network::ignoreSslErrors(const QList& errors) +{ + _socket->ignoreSslErrors(errors); +} + +void QMQTT::Network::ignoreSslErrors() +{ + _socket->ignoreSslErrors(); +} + +QSslConfiguration QMQTT::Network::sslConfiguration() const +{ + return _socket->sslConfiguration(); +} + +void QMQTT::Network::setSslConfiguration(const QSslConfiguration& config) +{ + _socket->setSslConfiguration(config); +} +#endif // QT_NO_SSL diff --git a/mqtt/qmqtt_network_p.h b/mqtt/qmqtt_network_p.h new file mode 100644 index 0000000..8dc77fc --- /dev/null +++ b/mqtt/qmqtt_network_p.h @@ -0,0 +1,139 @@ +/* + * qmqtt_network_p.h - qmqtt network private header + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_NETWORK_P_H +#define QMQTT_NETWORK_P_H + +#include "qmqtt_networkinterface.h" + +#include +#include +#include +#include + +#ifdef QT_WEBSOCKETS_LIB +#include +#endif // QT_WEBSOCKETS_LIB + +#ifndef QT_NO_SSL +#include +QT_FORWARD_DECLARE_CLASS(QSslError) +#endif // QT_NO_SSL + +namespace QMQTT { + +class SocketInterface; +class TimerInterface; +class Frame; + +class Network : public NetworkInterface +{ + Q_OBJECT + +public: + Network(QObject* parent = NULL); +#ifndef QT_NO_SSL + Network(const QSslConfiguration& config, QObject* parent = NULL); +#endif // QT_NO_SSL +#ifdef QT_WEBSOCKETS_LIB +#ifndef QT_NO_SSL + Network(const QString& origin, + QWebSocketProtocol::Version version, + const QSslConfiguration* sslConfig, + QObject* parent = NULL); +#endif // QT_NO_SSL + Network(const QString& origin, + QWebSocketProtocol::Version version, + QObject* parent = NULL); +#endif // QT_WEBSOCKETS_LIB + Network(SocketInterface* socketInterface, TimerInterface* timerInterface, + QObject* parent = NULL); + ~Network(); + + void sendFrame(const Frame &frame); + bool isConnectedToHost() const; + bool autoReconnect() const; + void setAutoReconnect(const bool autoReconnect); + QAbstractSocket::SocketState state() const; + int autoReconnectInterval() const; + void setAutoReconnectInterval(const int autoReconnectInterval); +#ifndef QT_NO_SSL + void ignoreSslErrors(const QList& errors); + QSslConfiguration sslConfiguration() const; + void setSslConfiguration(const QSslConfiguration& config); +#endif // QT_NO_SSL + +public slots: + void connectToHost(const QHostAddress& host, const quint16 port); + void connectToHost(const QString& hostName, const quint16 port); + void disconnectFromHost(); +#ifndef QT_NO_SSL + void ignoreSslErrors(); +#endif // QT_NO_SSL + +protected slots: + void onSocketError(QAbstractSocket::SocketError socketError); + +protected: + void initialize(); + + quint16 _port; + QHostAddress _host; + QString _hostName; + bool _autoReconnect; + int _autoReconnectInterval; + SocketInterface* _socket; + TimerInterface* _autoReconnectTimer; + + enum ReadState { + Header, + Length, + PayLoad + }; + + ReadState _readState; + quint8 _header; + int _length; + int _shift; + QByteArray _data; + +protected slots: + void onSocketReadReady(); + void onDisconnected(); + void connectToHost(); + +private: + Q_DISABLE_COPY(Network) +}; + +} // namespace QMQTT + +#endif // QMQTT_NETWORK_P_H diff --git a/mqtt/qmqtt_networkinterface.h b/mqtt/qmqtt_networkinterface.h new file mode 100644 index 0000000..c2d927d --- /dev/null +++ b/mqtt/qmqtt_networkinterface.h @@ -0,0 +1,92 @@ +/* + * qmqtt_networkinterface.h - qmqtt network interface header + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_NETWORK_INTERFACE_H +#define QMQTT_NETWORK_INTERFACE_H + +#include "qmqtt_global.h" + +#include +#include +#include +#include +#include + +#ifndef QT_NO_SSL +#include +QT_FORWARD_DECLARE_CLASS(QSslError) +#endif // QT_NO_SSL + +namespace QMQTT { + +class Frame; + +class Q_MQTT_EXPORT NetworkInterface : public QObject +{ + Q_OBJECT +public: + explicit NetworkInterface(QObject* parent = NULL) : QObject(parent) {} + virtual ~NetworkInterface() {} + + virtual void sendFrame(const Frame& frame) = 0; + virtual bool isConnectedToHost() const = 0; + virtual bool autoReconnect() const = 0; + virtual void setAutoReconnect(const bool autoReconnect) = 0; + virtual int autoReconnectInterval() const = 0; + virtual void setAutoReconnectInterval(const int autoReconnectInterval) = 0; + virtual QAbstractSocket::SocketState state() const = 0; +#ifndef QT_NO_SSL + virtual void ignoreSslErrors(const QList& errors) = 0; + virtual QSslConfiguration sslConfiguration() const = 0; + virtual void setSslConfiguration(const QSslConfiguration& config) = 0; +#endif // QT_NO_SSL + +public slots: + virtual void connectToHost(const QHostAddress& host, const quint16 port) = 0; + virtual void connectToHost(const QString& hostName, const quint16 port) = 0; + virtual void disconnectFromHost() = 0; +#ifndef QT_NO_SSL + virtual void ignoreSslErrors() = 0; +#endif // QT_NO_SSL + +signals: + void connected(); + void disconnected(); + void received(const QMQTT::Frame& frame); + void error(QAbstractSocket::SocketError error); +#ifndef QT_NO_SSL + void sslErrors(const QList& errors); +#endif // QT_NO_SSL +}; + +} // namespace QMQTT + +#endif // QMQTT_NETWORK_INTERFACE_H diff --git a/mqtt/qmqtt_routedmessage.h b/mqtt/qmqtt_routedmessage.h new file mode 100644 index 0000000..54af227 --- /dev/null +++ b/mqtt/qmqtt_routedmessage.h @@ -0,0 +1,71 @@ +/* + * qmqtt_router.h - qmqtt router + * + * Copyright (c) 2013 Ery Lee + * Router added by Niklas Wulf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_ROUTEDMESSAGE_H +#define QMQTT_ROUTEDMESSAGE_H + +#include "qmqtt_message.h" + +#include +#include +#include + +namespace QMQTT { + +class RouteSubscription; + +class Q_MQTT_EXPORT RoutedMessage +{ +public: + inline RoutedMessage() + {} + inline RoutedMessage(const Message &message) + : _message(message) + {} + + inline const Message &message() const + { return _message; } + inline QHash parameters() const + { return _parameters; } + +private: + friend class RouteSubscription; + + Message _message; + QHash _parameters; +}; + +} // namespace QMQTT + +Q_DECLARE_METATYPE(QMQTT::RoutedMessage) + +#endif // QMQTT_ROUTEDMESSAGE_H diff --git a/mqtt/qmqtt_router.cpp b/mqtt/qmqtt_router.cpp new file mode 100644 index 0000000..f8fe309 --- /dev/null +++ b/mqtt/qmqtt_router.cpp @@ -0,0 +1,67 @@ +/* + * qmqtt_router.cpp - qmqtt router + * + * Copyright (c) 2013 Ery Lee + * Router added by Niklas Wulf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "qmqtt_router.h" +#include "qmqtt_client.h" +#include "qmqtt_routesubscription.h" + +#include + +namespace QMQTT { + +Q_LOGGING_CATEGORY(router, "qmqtt.router") + +Router::Router(Client *parent) : QObject(parent), _client(parent) +{ +} + +RouteSubscription *Router::subscribe(const QString &route) +{ + RouteSubscription *subscription = new RouteSubscription(this); + subscription->setRoute(route); + connect(_client, &Client::connected, subscription, [this, subscription]() { + _client->subscribe(subscription->_topic, 0); + }); + if (_client->isConnectedToHost()) + _client->subscribe(subscription->_topic, 0); + connect(_client, &Client::received, subscription, &RouteSubscription::routeMessage); + return subscription; +} + +Client *Router::client() const +{ + return _client; +} + +} // namespace QMQTT + diff --git a/mqtt/qmqtt_router.h b/mqtt/qmqtt_router.h new file mode 100644 index 0000000..84988b0 --- /dev/null +++ b/mqtt/qmqtt_router.h @@ -0,0 +1,60 @@ +/* + * qmqtt_router.h - qmqtt router + * + * Copyright (c) 2013 Ery Lee + * Router added by Niklas Wulf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_ROUTER_H +#define QMQTT_ROUTER_H + +#include "qmqtt_global.h" + +#include + +namespace QMQTT { + +class Client; +class RouteSubscription; + +class Q_MQTT_EXPORT Router : public QObject +{ + Q_OBJECT +public: + explicit Router(Client *parent = 0); + + RouteSubscription *subscribe(const QString &route); + Client *client() const; + +private: + Client *_client; +}; + +} // namespace QMQTT + +#endif // QMQTT_ROUTER_H diff --git a/mqtt/qmqtt_routesubscription.cpp b/mqtt/qmqtt_routesubscription.cpp new file mode 100644 index 0000000..81c4f93 --- /dev/null +++ b/mqtt/qmqtt_routesubscription.cpp @@ -0,0 +1,117 @@ +/* + * qmqtt_router.cpp - qmqtt router + * + * Copyright (c) 2013 Ery Lee + * Router added by Niklas Wulf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "qmqtt_routesubscription.h" +#include "qmqtt_router.h" +#include "qmqtt_client.h" +#include "qmqtt_routedmessage.h" + +#include +#include +#include +#include +#include + +namespace QMQTT { + +Q_LOGGING_CATEGORY(routerSubscription, "qmqtt.routersubscription") + +RouteSubscription::RouteSubscription(Router *parent) + : QObject(parent), + _client(parent->client()) +{ + Q_ASSERT(!_client.isNull()); +} + +RouteSubscription::~RouteSubscription() +{ + if (Q_LIKELY(!_client.isNull() && _client->isConnectedToHost())) + _client->unsubscribe(_topic); +} + +QString RouteSubscription::route() const +{ + return _topic; +} + +void RouteSubscription::setRoute(const QString &route) +{ + QRegularExpression parameterNamesRegExp(QStringLiteral("\\:([a-zA-Z0-9]+)")); // note how names must not contain dashes or underscores + + // Remove paramter names to get the actual topic "route" + QString topic = route; + topic.remove(parameterNamesRegExp); + + // Remove the MQTT wildcards to get a regular expression, which matches the parameters + QString parameterRegExp = route; + parameterRegExp + .remove(QLatin1Char('+')) + .replace(parameterNamesRegExp, QStringLiteral("([a-zA-Z0-9_-]+)")) // note how parameter values may contain dashes or underscores + .remove(QLatin1Char('#')) + .replace(QLatin1String("$"), QLatin1String("\\$")); + + // Extract the parameter names + QRegularExpressionMatchIterator it = parameterNamesRegExp.globalMatch(route); + QStringList names; + while(it.hasNext()) { + QRegularExpressionMatch match = it.next(); + QString parameterName = match.captured(1); + names << parameterName; + } + + _topic = topic; + _parameterNames = names; + _regularExpression = QRegularExpression(parameterRegExp); +} + +void RouteSubscription::routeMessage(const Message &message) +{ + QString topic = message.topic(); + QRegularExpressionMatch match = _regularExpression.match(topic); + if(!match.hasMatch()) { + return; + } + + RoutedMessage routedMessage(message); + + for(int i = 0, c = _parameterNames.size(); i < c; ++i) { + QString name = _parameterNames.at(i); + QString value = match.captured(i + 1); + + routedMessage._parameters.insert(name, value); + } + + emit received(routedMessage); +} + +} // namespace QMQTT diff --git a/mqtt/qmqtt_routesubscription.h b/mqtt/qmqtt_routesubscription.h new file mode 100644 index 0000000..bd0608f --- /dev/null +++ b/mqtt/qmqtt_routesubscription.h @@ -0,0 +1,79 @@ +/* + * qmqtt_router.h - qmqtt router + * + * Copyright (c) 2013 Ery Lee + * Router added by Niklas Wulf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_ROUTESUBSCRIPTION_H +#define QMQTT_ROUTESUBSCRIPTION_H + +#include "qmqtt_global.h" + +#include +#include +#include +#include +#include + +namespace QMQTT { + +class Client; +class Message; +class RoutedMessage; +class Router; + +class Q_MQTT_EXPORT RouteSubscription : public QObject +{ + Q_OBJECT +public: + ~RouteSubscription(); + + QString route() const; + +signals: + void received(const RoutedMessage &message); + +private slots: + void routeMessage(const Message &message); + +private: + friend class Router; + + explicit RouteSubscription(Router *parent = 0); + void setRoute(const QString &route); + + QPointer _client; + QString _topic; + QRegularExpression _regularExpression; + QStringList _parameterNames; +}; + +} // namespace QMQTT + +#endif // QMQTT_ROUTESUBSCRIPTION_H diff --git a/mqtt/qmqtt_socket.cpp b/mqtt/qmqtt_socket.cpp new file mode 100644 index 0000000..f0628f0 --- /dev/null +++ b/mqtt/qmqtt_socket.cpp @@ -0,0 +1,81 @@ +/* + * qmqtt_socket.cpp - qmqtt socket + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "qmqtt_socket_p.h" + +#include + +QMQTT::Socket::Socket(QObject* parent) + : SocketInterface(parent) + , _socket(new QTcpSocket(this)) +{ + connect(_socket.data(), &QTcpSocket::connected, this, &SocketInterface::connected); + connect(_socket.data(), &QTcpSocket::disconnected, this, &SocketInterface::disconnected); + connect(_socket.data(), + static_cast(&QTcpSocket::error), + this, + static_cast(&SocketInterface::error)); +} + +QMQTT::Socket::~Socket() +{ +} + +QIODevice *QMQTT::Socket::ioDevice() +{ + return _socket.data(); +} + +void QMQTT::Socket::connectToHost(const QHostAddress& address, quint16 port) +{ + _socket->connectToHost(address, port); +} + +void QMQTT::Socket::connectToHost(const QString& hostName, quint16 port) +{ + _socket->connectToHost(hostName, port); +} + +void QMQTT::Socket::disconnectFromHost() +{ + _socket->disconnectFromHost(); +} + +QAbstractSocket::SocketState QMQTT::Socket::state() const +{ + return _socket->state(); +} + +QAbstractSocket::SocketError QMQTT::Socket::error() const +{ + return _socket->error(); +} diff --git a/mqtt/qmqtt_socket_p.h b/mqtt/qmqtt_socket_p.h new file mode 100644 index 0000000..248a42d --- /dev/null +++ b/mqtt/qmqtt_socket_p.h @@ -0,0 +1,69 @@ +/* + * qmqtt_socket_p.h - qmqtt socket private header + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_SOCKET_P_H +#define QMQTT_SOCKET_P_H + +#include "qmqtt_socketinterface.h" + +#include +#include +#include +#include +#include + +QT_FORWARD_DECLARE_CLASS(QIODevice) +QT_FORWARD_DECLARE_CLASS(QTcpSocket) + +namespace QMQTT +{ + +class Socket : public SocketInterface +{ + Q_OBJECT +public: + explicit Socket(QObject* parent = NULL); + virtual ~Socket(); + + virtual QIODevice *ioDevice(); + void connectToHost(const QHostAddress& address, quint16 port); + void connectToHost(const QString& hostName, quint16 port); + void disconnectFromHost(); + QAbstractSocket::SocketState state() const; + QAbstractSocket::SocketError error() const; + +protected: + QScopedPointer _socket; +}; + +} + +#endif // QMQTT_SOCKET_P_H diff --git a/mqtt/qmqtt_socketinterface.h b/mqtt/qmqtt_socketinterface.h new file mode 100644 index 0000000..97965b1 --- /dev/null +++ b/mqtt/qmqtt_socketinterface.h @@ -0,0 +1,84 @@ +/* + * qmqtt_socketinterface.h - qmqtt socket interface header + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_SOCKET_INTERFACE_H +#define QMQTT_SOCKET_INTERFACE_H + +#include "qmqtt_global.h" + +#include +#include +#include +#include +#include + +#ifndef QT_NO_SSL +#include +QT_FORWARD_DECLARE_CLASS(QSslError) +#endif // QT_NO_SSL + +QT_FORWARD_DECLARE_CLASS(QIODevice) + +namespace QMQTT +{ + +class Q_MQTT_EXPORT SocketInterface : public QObject +{ + Q_OBJECT +public: + explicit SocketInterface(QObject* parent = NULL) : QObject(parent) {} + virtual ~SocketInterface() {} + + virtual QIODevice* ioDevice() = 0; + virtual void connectToHost(const QHostAddress& address, quint16 port) = 0; + virtual void connectToHost(const QString& hostName, quint16 port) = 0; + virtual void disconnectFromHost() = 0; + virtual QAbstractSocket::SocketState state() const = 0; + virtual QAbstractSocket::SocketError error() const = 0; +#ifndef QT_NO_SSL + virtual void ignoreSslErrors(const QList& errors) { Q_UNUSED(errors); } + virtual void ignoreSslErrors() {} + virtual QSslConfiguration sslConfiguration() const { return QSslConfiguration(); } + virtual void setSslConfiguration(const QSslConfiguration& config) { Q_UNUSED(config); } +#endif // QT_NO_SSL + +signals: + void connected(); + void disconnected(); + void error(QAbstractSocket::SocketError socketError); +#ifndef QT_NO_SSL + void sslErrors(const QList& errors); +#endif // QT_NO_SSL +}; + +} + +#endif // QMQTT_SOCKET_INTERFACE_H diff --git a/mqtt/qmqtt_ssl_socket.cpp b/mqtt/qmqtt_ssl_socket.cpp new file mode 100644 index 0000000..2d3a808 --- /dev/null +++ b/mqtt/qmqtt_ssl_socket.cpp @@ -0,0 +1,112 @@ +/* + * qmqtt_ssl_socket.cpp - qmqtt SSL socket + * + * Copyright (c) 2013 Ery Lee + * Copyright (c) 2016 Matthias Dieter Wallnöfer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "qmqtt_ssl_socket_p.h" + +#ifndef QT_NO_SSL + +#include +#include + +QMQTT::SslSocket::SslSocket(const QSslConfiguration& config, QObject* parent) + : SocketInterface(parent) + , _socket(new QSslSocket(this)) +{ + _socket->setSslConfiguration(config); + connect(_socket.data(), &QSslSocket::encrypted, this, &SocketInterface::connected); + connect(_socket.data(), &QSslSocket::disconnected, this, &SocketInterface::disconnected); + connect(_socket.data(), + static_cast(&QSslSocket::error), + this, + static_cast(&SocketInterface::error)); + connect(_socket.data(), + static_cast&)>(&QSslSocket::sslErrors), + this, + &SslSocket::sslErrors); +} + +QMQTT::SslSocket::~SslSocket() +{ +} + +QIODevice *QMQTT::SslSocket::ioDevice() +{ + return _socket.data(); +} + +void QMQTT::SslSocket::connectToHost(const QHostAddress& address, quint16 port) +{ + _socket->connectToHostEncrypted(address.toString(), port); +} + +void QMQTT::SslSocket::connectToHost(const QString& hostName, quint16 port) +{ + _socket->connectToHostEncrypted(hostName, port); +} + +void QMQTT::SslSocket::disconnectFromHost() +{ + _socket->disconnectFromHost(); +} + +QAbstractSocket::SocketState QMQTT::SslSocket::state() const +{ + return _socket->state(); +} + +QAbstractSocket::SocketError QMQTT::SslSocket::error() const +{ + return _socket->error(); +} + +void QMQTT::SslSocket::ignoreSslErrors(const QList& errors) +{ + _socket->ignoreSslErrors(errors); +} + +void QMQTT::SslSocket::ignoreSslErrors() +{ + _socket->ignoreSslErrors(); +} + +QSslConfiguration QMQTT::SslSocket::sslConfiguration() const +{ + return _socket->sslConfiguration(); +} + +void QMQTT::SslSocket::setSslConfiguration(const QSslConfiguration& config) +{ + _socket->setSslConfiguration(config); +} + +#endif // QT_NO_SSL diff --git a/mqtt/qmqtt_ssl_socket_p.h b/mqtt/qmqtt_ssl_socket_p.h new file mode 100644 index 0000000..46ddc33 --- /dev/null +++ b/mqtt/qmqtt_ssl_socket_p.h @@ -0,0 +1,79 @@ +/* + * qmqtt_ssl_socket_p.h - qmqtt SSL socket private header + * + * Copyright (c) 2013 Ery Lee + * Copyright (c) 2016 Matthias Dieter Wallnöfer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_SSL_SOCKET_P_H +#define QMQTT_SSL_SOCKET_P_H + +#ifndef QT_NO_SSL + +#include "qmqtt_socketinterface.h" + +#include +#include +#include +#include +#include + +QT_FORWARD_DECLARE_CLASS(QSslSocket) +QT_FORWARD_DECLARE_CLASS(QSslError) +#include + +namespace QMQTT +{ + +class SslSocket : public SocketInterface +{ + Q_OBJECT +public: + explicit SslSocket(const QSslConfiguration& config, QObject* parent = NULL); + virtual ~SslSocket(); + + virtual QIODevice *ioDevice(); + void connectToHost(const QHostAddress& address, quint16 port); + void connectToHost(const QString& hostName, quint16 port); + void disconnectFromHost(); + QAbstractSocket::SocketState state() const; + QAbstractSocket::SocketError error() const; + void ignoreSslErrors(const QList& errors); + void ignoreSslErrors(); + QSslConfiguration sslConfiguration() const; + void setSslConfiguration(const QSslConfiguration& config); + +protected: + QScopedPointer _socket; +}; + +} + +#endif // QT_NO_SSL + +#endif // QMQTT_SSL_SOCKET_P_H diff --git a/mqtt/qmqtt_timer.cpp b/mqtt/qmqtt_timer.cpp new file mode 100644 index 0000000..a971d83 --- /dev/null +++ b/mqtt/qmqtt_timer.cpp @@ -0,0 +1,73 @@ +/* + * qmqtt_timer.cpp - qmqtt timer + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "qmqtt_timer_p.h" + +QMQTT::Timer::Timer(QObject* parent) + : TimerInterface(parent) +{ + connect(&_timer, &QTimer::timeout, this, &TimerInterface::timeout); +} + +QMQTT::Timer::~Timer() +{ +} + +bool QMQTT::Timer::isSingleShot() const +{ + return _timer.isSingleShot(); +} + +void QMQTT::Timer::setSingleShot(bool singleShot) +{ + _timer.setSingleShot(singleShot); +} + +int QMQTT::Timer::interval() const +{ + return _timer.interval(); +} + +void QMQTT::Timer::setInterval(int msec) +{ + _timer.setInterval(msec); +} + +void QMQTT::Timer::start() +{ + _timer.start(); +} + +void QMQTT::Timer::stop() +{ + _timer.stop(); +} diff --git a/mqtt/qmqtt_timer_p.h b/mqtt/qmqtt_timer_p.h new file mode 100644 index 0000000..093a141 --- /dev/null +++ b/mqtt/qmqtt_timer_p.h @@ -0,0 +1,62 @@ +/* + * qmqtt_timer_p.h - qmqtt timer private header + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_TIMER_P_H +#define QMQTT_TIMER_P_H + +#include "qmqtt_timerinterface.h" + +#include +#include + +namespace QMQTT { + +class Timer : public TimerInterface +{ + Q_OBJECT +public: + explicit Timer(QObject *parent = 0); + virtual ~Timer(); + + bool isSingleShot() const; + void setSingleShot(bool singleShot); + int interval() const; + void setInterval(int msec); + void start(); + void stop(); + +protected: + QTimer _timer; +}; + +} + +#endif // QMQTT_TIMER_P_H diff --git a/mqtt/qmqtt_timerinterface.h b/mqtt/qmqtt_timerinterface.h new file mode 100644 index 0000000..4f2cfcb --- /dev/null +++ b/mqtt/qmqtt_timerinterface.h @@ -0,0 +1,62 @@ +/* + * qmqtt_timerinterface.h - qmqtt timer interface header + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_TIMER_INTERFACE_H +#define QMQTT_TIMER_INTERFACE_H + +#include "qmqtt_global.h" + +#include + +namespace QMQTT { + +class Q_MQTT_EXPORT TimerInterface : public QObject +{ + Q_OBJECT +public: + explicit TimerInterface(QObject* parent = NULL) : QObject(parent) {} + virtual ~TimerInterface() {} + + virtual bool isSingleShot() const = 0; + virtual void setSingleShot(bool singleShot) = 0; + virtual int interval() const = 0; + virtual void setInterval(int msec) = 0; + virtual void start() = 0; + virtual void stop() = 0; + +signals: + void timeout(); +}; + +} + +#endif // QMQTT_TIMER_INTERFACE_H + diff --git a/mqtt/qmqtt_websocket.cpp b/mqtt/qmqtt_websocket.cpp new file mode 100644 index 0000000..c824786 --- /dev/null +++ b/mqtt/qmqtt_websocket.cpp @@ -0,0 +1,103 @@ +#ifdef QT_WEBSOCKETS_LIB + +#include "qmqtt_websocket_p.h" + +#include +#include +#include + +#ifndef QT_NO_SSL +QMQTT::WebSocket::WebSocket(const QString& origin, + QWebSocketProtocol::Version version, + const QSslConfiguration* sslConfig, + QObject* parent) + : SocketInterface(parent) + , _socket(new QWebSocket(origin, version, this)) + , _ioDevice(new WebSocketIODevice(_socket, this)) +{ + initialize(); + if (sslConfig != NULL) + _socket->setSslConfiguration(*sslConfig); + connect(_socket, &QWebSocket::sslErrors, this, &WebSocket::sslErrors); +} +#endif // QT_NO_SSL + +QMQTT::WebSocket::WebSocket(const QString& origin, + QWebSocketProtocol::Version version, + QObject* parent) + : SocketInterface(parent) + , _socket(new QWebSocket(origin, version, this)) + , _ioDevice(new WebSocketIODevice(_socket, this)) +{ + initialize(); +} + +void QMQTT::WebSocket::initialize() +{ + connect(_socket, &QWebSocket::connected, this, &WebSocket::connected); + connect(_socket, &QWebSocket::disconnected, this, &WebSocket::disconnected); + connect(_socket, + static_cast(&QWebSocket::error), + this, + static_cast(&SocketInterface::error)); +} + +QMQTT::WebSocket::~WebSocket() +{ +} + +void QMQTT::WebSocket::connectToHost(const QHostAddress& address, quint16 port) +{ + Q_UNUSED(address) + Q_UNUSED(port) + qFatal("No supported"); +} + +void QMQTT::WebSocket::connectToHost(const QString& hostName, quint16 port) +{ + Q_UNUSED(port) + QUrl url(hostName); + QNetworkRequest request(url); + request.setRawHeader("Sec-WebSocket-Protocol", "mqtt"); + _ioDevice->connectToHost(request); +} + +void QMQTT::WebSocket::disconnectFromHost() +{ + _socket->close(); +} + +QAbstractSocket::SocketState QMQTT::WebSocket::state() const +{ + return _socket->state(); +} + +QAbstractSocket::SocketError QMQTT::WebSocket::error() const +{ + return _socket->error(); +} + +#ifndef QT_NO_SSL +void QMQTT::WebSocket::ignoreSslErrors(const QList& errors) +{ + _socket->ignoreSslErrors(errors); +} + +void QMQTT::WebSocket::ignoreSslErrors() +{ + _socket->ignoreSslErrors(); +} + +QSslConfiguration QMQTT::WebSocket::sslConfiguration() const +{ + return _socket->sslConfiguration(); +} + +void QMQTT::WebSocket::setSslConfiguration(const QSslConfiguration& config) +{ + _socket->setSslConfiguration(config); +} + +#endif // QT_NO_SSL + +#endif // QT_WEBSOCKETS_LIB diff --git a/mqtt/qmqtt_websocket_p.h b/mqtt/qmqtt_websocket_p.h new file mode 100644 index 0000000..6d159fa --- /dev/null +++ b/mqtt/qmqtt_websocket_p.h @@ -0,0 +1,102 @@ +/* + * qmqtt_websocket_p.h - qmqtt socket private header + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef QMQTT_WEBSOCKET_H +#define QMQTT_WEBSOCKET_H + +#ifdef QT_WEBSOCKETS_LIB + +#include +#include + +#include +#include +#include +#include +#include +#include + +QT_FORWARD_DECLARE_CLASS(QIODevice) + +#ifndef QT_NO_SSL +#include +QT_FORWARD_DECLARE_CLASS(QSslError) +#endif // QT_NO_SSL + +namespace QMQTT +{ + +class WebSocket : public SocketInterface +{ + Q_OBJECT +public: +#ifndef QT_NO_SSL + WebSocket(const QString& origin, + QWebSocketProtocol::Version version, + const QSslConfiguration* sslConfig, + QObject* parent = NULL); +#endif // QT_NO_SSL + + WebSocket(const QString& origin, + QWebSocketProtocol::Version version, + QObject* parent = NULL); + + virtual ~WebSocket(); + + QIODevice *ioDevice() + { + return _ioDevice; + } + + void connectToHost(const QHostAddress& address, quint16 port); + void connectToHost(const QString& hostName, quint16 port); + void disconnectFromHost(); + QAbstractSocket::SocketState state() const; + QAbstractSocket::SocketError error() const; +#ifndef QT_NO_SSL + void ignoreSslErrors(const QList& errors); + void ignoreSslErrors(); + QSslConfiguration sslConfiguration() const; + void setSslConfiguration(const QSslConfiguration& config); +#endif // QT_NO_SSL + +private: + void initialize(); + + QWebSocket *_socket; + WebSocketIODevice *_ioDevice; +}; + +} + +#endif // QT_WEBSOCKETS_LIB + +#endif // QMQTT_WEBSOCKET_H diff --git a/mqtt/qmqtt_websocketiodevice.cpp b/mqtt/qmqtt_websocketiodevice.cpp new file mode 100644 index 0000000..478d3c3 --- /dev/null +++ b/mqtt/qmqtt_websocketiodevice.cpp @@ -0,0 +1,48 @@ +#ifdef QT_WEBSOCKETS_LIB + +#include "qmqtt_websocketiodevice_p.h" + +#include + +QMQTT::WebSocketIODevice::WebSocketIODevice(QWebSocket *socket, QObject *parent) + : QIODevice(parent) + , _webSocket(socket) +{ + connect(_webSocket, &QWebSocket::bytesWritten, this, &WebSocketIODevice::bytesWritten); + connect(_webSocket, &QWebSocket::binaryMessageReceived, + this, &WebSocketIODevice::binaryMessageReceived); +} + +bool QMQTT::WebSocketIODevice::connectToHost(const QNetworkRequest &request) +{ + _webSocket->open(request); + return QIODevice::open(QIODevice::ReadWrite); +} + +qint64 QMQTT::WebSocketIODevice::bytesAvailable() const +{ + return _buffer.count(); +} + +void QMQTT::WebSocketIODevice::binaryMessageReceived(const QByteArray &frame) +{ + _buffer.append(frame); + emit readyRead(); +} + +qint64 QMQTT::WebSocketIODevice::readData(char *data, qint64 maxSize) +{ + int size = qMin(static_cast(maxSize), _buffer.count()); + for (int i=0; i(maxSize)); + return _webSocket->sendBinaryMessage(d); +} + +#endif // QT_WEBSOCKETS_LIB diff --git a/mqtt/qmqtt_websocketiodevice_p.h b/mqtt/qmqtt_websocketiodevice_p.h new file mode 100644 index 0000000..e936220 --- /dev/null +++ b/mqtt/qmqtt_websocketiodevice_p.h @@ -0,0 +1,86 @@ +/* + * qmqtt_socketinterface.h - qmqtt socket interface header + * + * Copyright (c) 2013 Ery Lee + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of mqttc nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef QMQTT_WEBSOCKETIODEVICE_H +#define QMQTT_WEBSOCKETIODEVICE_H + +#ifdef QT_WEBSOCKETS_LIB + +#include +#include +#include +#include + +QT_FORWARD_DECLARE_CLASS(QWebSocket) +QT_FORWARD_DECLARE_CLASS(QNetworkRequest) +QT_FORWARD_DECLARE_CLASS(QSslError) + +namespace QMQTT +{ + +class WebSocketIODevice : public QIODevice +{ + Q_OBJECT +public: + explicit WebSocketIODevice(QWebSocket *socket, QObject *parent = NULL); + + bool connectToHost(const QNetworkRequest &request); + + virtual qint64 bytesAvailable() const; + +signals: + void connected(); + + void disconnected(); + + void error(QAbstractSocket::SocketError error); + + void sslErrors(const QList &errors); + +protected: + virtual qint64 readData(char *data, qint64 maxSize); + + virtual qint64 writeData(const char *data, qint64 maxSize); + +private slots: + void binaryMessageReceived(const QByteArray &frame); + +private: + QByteArray _buffer; + QWebSocket *_webSocket; +}; + +} + +#endif // QT_WEBSOCKETS_LIB + +#endif // QMQTT_WEBSOCKETIODEVICE_H diff --git a/simplecrypt.cpp b/simplecrypt.cpp new file mode 100644 index 0000000..79a16ba --- /dev/null +++ b/simplecrypt.cpp @@ -0,0 +1,252 @@ +/* +Copyright (c) 2011, Andre Somers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Rathenau Instituut, Andre Somers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include "simplecrypt.h" +#include +#include +#include +#include +#include +#include + +SimpleCrypt::SimpleCrypt(): + m_key(0), + m_compressionMode(CompressionAuto), + m_protectionMode(ProtectionChecksum), + m_lastError(ErrorNoError) +{ + qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); +} + +SimpleCrypt::SimpleCrypt(quint64 key): + m_key(key), + m_compressionMode(CompressionAuto), + m_protectionMode(ProtectionChecksum), + m_lastError(ErrorNoError) +{ + qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); + splitKey(); +} + +void SimpleCrypt::setKey(quint64 key) +{ + m_key = key; + splitKey(); +} + +void SimpleCrypt::splitKey() +{ + m_keyParts.clear(); + m_keyParts.resize(8); + for (int i=0;i<8;i++) { + quint64 part = m_key; + for (int j=i; j>0; j--) + part = part >> 8; + part = part & 0xff; + m_keyParts[i] = static_cast(part); + } +} + +QByteArray SimpleCrypt::encryptToByteArray(const QString& plaintext) +{ + QByteArray plaintextArray = plaintext.toUtf8(); + return encryptToByteArray(plaintextArray); +} + +QByteArray SimpleCrypt::encryptToByteArray(QByteArray plaintext) +{ + if (m_keyParts.isEmpty()) { + qWarning() << "No key set."; + m_lastError = ErrorNoKeySet; + return QByteArray(); + } + + + QByteArray ba = plaintext; + + CryptoFlags flags = CryptoFlagNone; + if (m_compressionMode == CompressionAlways) { + ba = qCompress(ba, 9); //maximum compression + flags |= CryptoFlagCompression; + } else if (m_compressionMode == CompressionAuto) { + QByteArray compressed = qCompress(ba, 9); + if (compressed.count() < ba.count()) { + ba = compressed; + flags |= CryptoFlagCompression; + } + } + + QByteArray integrityProtection; + if (m_protectionMode == ProtectionChecksum) { + flags |= CryptoFlagChecksum; + QDataStream s(&integrityProtection, QIODevice::WriteOnly); + s << qChecksum(ba.constData(), ba.length()); + } else if (m_protectionMode == ProtectionHash) { + flags |= CryptoFlagHash; + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(ba); + + integrityProtection += hash.result(); + } + + //prepend a random char to the string + char randomChar = char(qrand() & 0xFF); + ba = randomChar + integrityProtection + ba; + + int pos(0); + char lastChar(0); + + int cnt = ba.count(); + + while (pos < cnt) { + ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar; + lastChar = ba.at(pos); + ++pos; + } + + QByteArray resultArray; + resultArray.append(char(0x03)); //version for future updates to algorithm + resultArray.append(char(flags)); //encryption flags + resultArray.append(ba); + + m_lastError = ErrorNoError; + return resultArray; +} + +QString SimpleCrypt::encryptToString(const QString& plaintext) +{ + QByteArray plaintextArray = plaintext.toUtf8(); + QByteArray cypher = encryptToByteArray(plaintextArray); + QString cypherString = QString::fromLatin1(cypher.toBase64()); + return cypherString; +} + +QString SimpleCrypt::encryptToString(QByteArray plaintext) +{ + QByteArray cypher = encryptToByteArray(plaintext); + QString cypherString = QString::fromLatin1(cypher.toBase64()); + return cypherString; +} + +QString SimpleCrypt::decryptToString(const QString &cyphertext) +{ + QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); + QByteArray plaintextArray = decryptToByteArray(cyphertextArray); + QString plaintext = QString::fromUtf8(plaintextArray, plaintextArray.size()); + + return plaintext; +} + +QString SimpleCrypt::decryptToString(QByteArray cypher) +{ + QByteArray ba = decryptToByteArray(cypher); + QString plaintext = QString::fromUtf8(ba, ba.size()); + + return plaintext; +} + +QByteArray SimpleCrypt::decryptToByteArray(const QString& cyphertext) +{ + QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); + QByteArray ba = decryptToByteArray(cyphertextArray); + + return ba; +} + +QByteArray SimpleCrypt::decryptToByteArray(QByteArray cypher) +{ + if (m_keyParts.isEmpty()) { + qWarning() << "No key set."; + m_lastError = ErrorNoKeySet; + return QByteArray(); + } + + QByteArray ba = cypher; + + if( cypher.count() < 3 ) + return QByteArray(); + + char version = ba.at(0); + + if (version !=3) { //we only work with version 3 + m_lastError = ErrorUnknownVersion; + qWarning() << "Invalid version or not a cyphertext."; + return QByteArray(); + } + + CryptoFlags flags = CryptoFlags(ba.at(1)); + + ba = ba.mid(2); + int pos(0); + int cnt(ba.count()); + char lastChar = 0; + + while (pos < cnt) { + char currentChar = ba[pos]; + ba[pos] = ba.at(pos) ^ lastChar ^ m_keyParts.at(pos % 8); + lastChar = currentChar; + ++pos; + } + + ba = ba.mid(1); //chop off the random number at the start + + bool integrityOk(true); + if (flags.testFlag(CryptoFlagChecksum)) { + if (ba.length() < 2) { + m_lastError = ErrorIntegrityFailed; + return QByteArray(); + } + quint16 storedChecksum; + { + QDataStream s(&ba, QIODevice::ReadOnly); + s >> storedChecksum; + } + ba = ba.mid(2); + quint16 checksum = qChecksum(ba.constData(), ba.length()); + integrityOk = (checksum == storedChecksum); + } else if (flags.testFlag(CryptoFlagHash)) { + if (ba.length() < 20) { + m_lastError = ErrorIntegrityFailed; + return QByteArray(); + } + QByteArray storedHash = ba.left(20); + ba = ba.mid(20); + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(ba); + integrityOk = (hash.result() == storedHash); + } + + if (!integrityOk) { + m_lastError = ErrorIntegrityFailed; + return QByteArray(); + } + + if (flags.testFlag(CryptoFlagCompression)) + ba = qUncompress(ba); + + m_lastError = ErrorNoError; + return ba; +} diff --git a/simplecrypt.h b/simplecrypt.h new file mode 100644 index 0000000..e275639 --- /dev/null +++ b/simplecrypt.h @@ -0,0 +1,225 @@ +/* +Copyright (c) 2011, Andre Somers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Rathenau Instituut, Andre Somers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SIMPLECRYPT_H +#define SIMPLECRYPT_H +#include +#include +#include + +/** + @short Simple encryption and decryption of strings and byte arrays + + This class provides a simple implementation of encryption and decryption + of strings and byte arrays. + + @warning The encryption provided by this class is NOT strong encryption. It may + help to shield things from curious eyes, but it will NOT stand up to someone + determined to break the encryption. Don't say you were not warned. + + The class uses a 64 bit key. Simply create an instance of the class, set the key, + and use the encryptToString() method to calculate an encrypted version of the input string. + To decrypt that string again, use an instance of SimpleCrypt initialized with + the same key, and call the decryptToString() method with the encrypted string. If the key + matches, the decrypted version of the string will be returned again. + + If you do not provide a key, or if something else is wrong, the encryption and + decryption function will return an empty string or will return a string containing nonsense. + lastError() will return a value indicating if the method was succesful, and if not, why not. + + SimpleCrypt is prepared for the case that the encryption and decryption + algorithm is changed in a later version, by prepending a version identifier to the cypertext. + */ +class SimpleCrypt +{ +public: + /** + CompressionMode describes if compression will be applied to the data to be + encrypted. + */ + enum CompressionMode { + CompressionAuto, /*!< Only apply compression if that results in a shorter plaintext. */ + CompressionAlways, /*!< Always apply compression. Note that for short inputs, a compression may result in longer data */ + CompressionNever /*!< Never apply compression. */ + }; + /** + IntegrityProtectionMode describes measures taken to make it possible to detect problems with the data + or wrong decryption keys. + + Measures involve adding a checksum or a cryptograhpic hash to the data to be encrypted. This + increases the length of the resulting cypertext, but makes it possible to check if the plaintext + appears to be valid after decryption. + */ + enum IntegrityProtectionMode { + ProtectionNone, /*!< The integerity of the encrypted data is not protected. It is not really possible to detect a wrong key, for instance. */ + ProtectionChecksum,/*!< A simple checksum is used to verify that the data is in order. If not, an empty string is returned. */ + ProtectionHash /*!< A cryptographic hash is used to verify the integrity of the data. This method produces a much stronger, but longer check */ + }; + /** + Error describes the type of error that occured. + */ + enum Error { + ErrorNoError, /*!< No error occurred. */ + ErrorNoKeySet, /*!< No key was set. You can not encrypt or decrypt without a valid key. */ + ErrorUnknownVersion, /*!< The version of this data is unknown, or the data is otherwise not valid. */ + ErrorIntegrityFailed, /*!< The integrity check of the data failed. Perhaps the wrong key was used. */ + }; + + /** + Constructor. + + Constructs a SimpleCrypt instance without a valid key set on it. + */ + SimpleCrypt(); + /** + Constructor. + + Constructs a SimpleCrypt instance and initializes it with the given @arg key. + */ + explicit SimpleCrypt(quint64 key); + + /** + (Re-) initializes the key with the given @arg key. + */ + void setKey(quint64 key); + /** + Returns true if SimpleCrypt has been initialized with a key. + */ + bool hasKey() const {return !m_keyParts.isEmpty();} + + /** + Sets the compression mode to use when encrypting data. The default mode is Auto. + + Note that decryption is not influenced by this mode, as the decryption recognizes + what mode was used when encrypting. + */ + void setCompressionMode(CompressionMode mode) {m_compressionMode = mode;} + /** + Returns the CompressionMode that is currently in use. + */ + CompressionMode compressionMode() const {return m_compressionMode;} + + /** + Sets the integrity mode to use when encrypting data. The default mode is Checksum. + + Note that decryption is not influenced by this mode, as the decryption recognizes + what mode was used when encrypting. + */ + void setIntegrityProtectionMode(IntegrityProtectionMode mode) {m_protectionMode = mode;} + /** + Returns the IntegrityProtectionMode that is currently in use. + */ + IntegrityProtectionMode integrityProtectionMode() const {return m_protectionMode;} + + /** + Returns the last error that occurred. + */ + Error lastError() const {return m_lastError;} + + /** + Encrypts the @arg plaintext string with the key the class was initialized with, and returns + a cyphertext the result. The result is a base64 encoded version of the binary array that is the + actual result of the string, so it can be stored easily in a text format. + */ + QString encryptToString(const QString& plaintext) ; + /** + Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns + a cyphertext the result. The result is a base64 encoded version of the binary array that is the + actual result of the encryption, so it can be stored easily in a text format. + */ + QString encryptToString(QByteArray plaintext) ; + /** + Encrypts the @arg plaintext string with the key the class was initialized with, and returns + a binary cyphertext in a QByteArray the result. + + This method returns a byte array, that is useable for storing a binary format. If you need + a string you can store in a text file, use encryptToString() instead. + */ + QByteArray encryptToByteArray(const QString& plaintext) ; + /** + Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns + a binary cyphertext in a QByteArray the result. + + This method returns a byte array, that is useable for storing a binary format. If you need + a string you can store in a text file, use encryptToString() instead. + */ + QByteArray encryptToByteArray(QByteArray plaintext) ; + + /** + Decrypts a cyphertext string encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QString decryptToString(const QString& cyphertext) ; + /** + Decrypts a cyphertext string encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QByteArray decryptToByteArray(const QString& cyphertext) ; + /** + Decrypts a cyphertext binary encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QString decryptToString(QByteArray cypher) ; + /** + Decrypts a cyphertext binary encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QByteArray decryptToByteArray(QByteArray cypher) ; + + //enum to describe options that have been used for the encryption. Currently only one, but + //that only leaves room for future extensions like adding a cryptographic hash... + enum CryptoFlag{CryptoFlagNone = 0, + CryptoFlagCompression = 0x01, + CryptoFlagChecksum = 0x02, + CryptoFlagHash = 0x04 + }; + Q_DECLARE_FLAGS(CryptoFlags, CryptoFlag); +private: + + void splitKey(); + + quint64 m_key; + QVector m_keyParts; + CompressionMode m_compressionMode; + IntegrityProtectionMode m_protectionMode; + Error m_lastError; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(SimpleCrypt::CryptoFlags) + +#endif // SimpleCrypt_H diff --git a/ui/mainsettingsdialog.ui b/ui/mainsettingsdialog.ui index cce67e8..678280e 100644 --- a/ui/mainsettingsdialog.ui +++ b/ui/mainsettingsdialog.ui @@ -7,7 +7,7 @@ 0 0 965 - 591 + 668 @@ -287,10 +287,10 @@ - false + true - Remote Connection: + MQTT Remote CAN Settings: @@ -300,6 +300,9 @@ + + + @@ -307,16 +310,30 @@ - - - - - + + - Auto start remote hosting + User Name: + + + + + + + + + + Password + + + + + + + QLineEdit::Password @@ -371,7 +388,6 @@ cbInfoAutoExpand lineRemoteHost lineRemotePort - cbAutoStartRemote diff --git a/ui/newconnectiondialog.ui b/ui/newconnectiondialog.ui index de55127..26eeb74 100644 --- a/ui/newconnectiondialog.ui +++ b/ui/newconnectiondialog.ui @@ -6,8 +6,8 @@ 0 0 - 356 - 282 + 359 + 291 @@ -33,7 +33,7 @@ - + false @@ -46,7 +46,14 @@ - Network connection + Network Connection + + + + + + + MQTT Connection