From a25e54ac48ba7541fc04667ecb2262c1f9ce2b83 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 <0xFEEDC0DE64@gmail.com> Date: Fri, 20 Apr 2018 21:26:45 +0200 Subject: [PATCH] Added WifiLamp server code --- .gitignore | 73 ++++++++++++++++++++ WifiLamp.pro | 17 +++++ main.cpp | 26 +++++++ relaisclient.cpp | 93 +++++++++++++++++++++++++ relaisclient.h | 43 ++++++++++++ relaisserver.cpp | 113 +++++++++++++++++++++++++++++++ relaisserver.h | 61 +++++++++++++++++ relaiswebserver.cpp | 161 ++++++++++++++++++++++++++++++++++++++++++++ relaiswebserver.h | 27 ++++++++ 9 files changed, 614 insertions(+) create mode 100644 .gitignore create mode 100644 WifiLamp.pro create mode 100644 main.cpp create mode 100644 relaisclient.cpp create mode 100644 relaisclient.h create mode 100644 relaisserver.cpp create mode 100644 relaisserver.h create mode 100644 relaiswebserver.cpp create mode 100644 relaiswebserver.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/WifiLamp.pro b/WifiLamp.pro new file mode 100644 index 0000000..db1e91c --- /dev/null +++ b/WifiLamp.pro @@ -0,0 +1,17 @@ +QT = core network + +CONFIG += c++11 +CONFIG -= app_bundle + +DEFINES += QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000 + +HEADERS += relaisclient.h \ + relaisserver.h \ + relaiswebserver.h + +SOURCES += main.cpp \ + relaisclient.cpp \ + relaisserver.cpp \ + relaiswebserver.cpp + +include(QtWebserver/QtWebserver.pri) diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..24f763f --- /dev/null +++ b/main.cpp @@ -0,0 +1,26 @@ +#include +#include + +#include "relaisserver.h" +#include "relaiswebserver.h" + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + RelaisServer server(&app); + if(!server.listen(QHostAddress::Any, 1234)) + { + qCritical() << "server could not start listening:" << server.errorString(); + return -1; + } + + RelaisWebserver webServer(&server, &app); + if(!webServer.listen(QHostAddress::Any, 8080)) + { + qCritical() << "webserver could not start listening:" << webServer.errorString(); + return -2; + } + + return app.exec(); +} diff --git a/relaisclient.cpp b/relaisclient.cpp new file mode 100644 index 0000000..5f9dea4 --- /dev/null +++ b/relaisclient.cpp @@ -0,0 +1,93 @@ +#include "relaisclient.h" + +#include + +#include "relaisserver.h" + +RelaisClient::RelaisClient(QTcpSocket *socket, RelaisServer *server) : + QObject(server), + m_socket(socket), + m_server(server), + m_state(Name) +{ + m_socket->setParent(this); + connect(m_socket, &QIODevice::readyRead, this, &RelaisClient::readyRead); + connect(m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater); +} + +quint16 RelaisClient::localPort() const +{ + return m_socket->localPort(); +} + +QHostAddress RelaisClient::localAddress() const +{ + return m_socket->localAddress(); +} + +quint16 RelaisClient::peerPort() const +{ + return m_socket->peerPort(); +} + +QHostAddress RelaisClient::peerAddress() const +{ + return m_socket->peerAddress(); +} + +QString RelaisClient::peerName() const +{ + return m_socket->peerName(); +} + +const QString &RelaisClient::name() const +{ + return m_name; +} + +const QString &RelaisClient::status() const +{ + return m_status; +} + +void RelaisClient::on() +{ + m_socket->write(QByteArrayLiteral("1")); + m_status = QString(); +} + +void RelaisClient::off() +{ + m_socket->write(QByteArrayLiteral("0")); + m_status = QString(); +} + +void RelaisClient::toggle() +{ + m_socket->write(QByteArrayLiteral("t")); + m_status = QString(); +} + +void RelaisClient::requestStatus() +{ + m_socket->write(QByteArrayLiteral("s")); + m_status = QString(); +} + +void RelaisClient::readyRead() +{ + m_buffer.append(m_socket->readAll()); + + int index; + while((index = m_buffer.indexOf(QByteArrayLiteral("\r\n"))) != -1) + { + QString line(m_buffer.left(index)); + m_buffer.remove(0, index + 2); + + switch(m_state) + { + case Name: m_name = line; m_state = Status; break; + case Status: m_status = line; break; + } + } +} diff --git a/relaisclient.h b/relaisclient.h new file mode 100644 index 0000000..fef9544 --- /dev/null +++ b/relaisclient.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +class QTcpSocket; + +class RelaisServer; + +class RelaisClient : public QObject +{ + Q_OBJECT + +public: + explicit RelaisClient(QTcpSocket *socket, RelaisServer *server); + + quint16 localPort() const; + QHostAddress localAddress() const; + quint16 peerPort() const; + QHostAddress peerAddress() const; + QString peerName() const; + + const QString &name() const; + const QString &status() const; + +public Q_SLOTS: + void on(); + void off(); + void toggle(); + void requestStatus(); + +private Q_SLOTS: + void readyRead(); + +private: + QTcpSocket *m_socket; + RelaisServer *m_server; + QByteArray m_buffer; + enum { Name, Status } m_state; + + QString m_name; + QString m_status; +}; diff --git a/relaisserver.cpp b/relaisserver.cpp new file mode 100644 index 0000000..effebfc --- /dev/null +++ b/relaisserver.cpp @@ -0,0 +1,113 @@ +#include "relaisserver.h" + +#include + +#include "relaisclient.h" + +RelaisServer::RelaisServer(QObject *parent) : + QObject(parent), + m_server(new QTcpServer(this)) +{ + connect(m_server, &QTcpServer::acceptError, this, &RelaisServer::acceptError); + connect(m_server, &QTcpServer::newConnection, this, &RelaisServer::newConnection); +} + +bool RelaisServer::listen(const QHostAddress &address, quint16 port) +{ + return m_server->listen(address, port); +} + +void RelaisServer::close() +{ + m_server->close(); +} + +bool RelaisServer::isListening() const +{ + return m_server->isListening(); +} + +void RelaisServer::setMaxPendingConnections(int numConnections) +{ + m_server->setMaxPendingConnections(numConnections); +} + +int RelaisServer::maxPendingConnections() const +{ + return m_server->maxPendingConnections(); +} + +quint16 RelaisServer::serverPort() const +{ + return m_server->serverPort(); +} + +QHostAddress RelaisServer::serverAddress() const +{ + return m_server->serverAddress(); +} + +qintptr RelaisServer::socketDescriptor() const +{ + return m_server->socketDescriptor(); +} + +bool RelaisServer::setSocketDescriptor(qintptr socketDescriptor) +{ + return m_server->setSocketDescriptor(socketDescriptor); +} + +bool RelaisServer::hasPendingConnections() const +{ + return m_server->hasPendingConnections(); +} + +QAbstractSocket::SocketError RelaisServer::serverError() const +{ + return m_server->serverError(); +} + +QString RelaisServer::errorString() const +{ + return m_server->errorString(); +} + +void RelaisServer::pauseAccepting() +{ + m_server->pauseAccepting(); +} + +void RelaisServer::resumeAccepting() +{ + m_server->resumeAccepting(); +} + +#ifndef QT_NO_NETWORKPROXY +void RelaisServer::setProxy(const QNetworkProxy &networkProxy) +{ + m_server->setProxy(networkProxy); +} + +QNetworkProxy RelaisServer::proxy() const +{ + return m_server->proxy(); +} +#endif + +const QSet &RelaisServer::clients() const +{ + return m_clients; +} + +void RelaisServer::newConnection() +{ + auto connection = m_server->nextPendingConnection(); + if(!connection) + return; + + auto client = new RelaisClient(connection, this); + m_clients.insert(client); + connect(client, &QObject::destroyed, this, [=](){ + m_clients.remove(client); + }); +} diff --git a/relaisserver.h b/relaisserver.h new file mode 100644 index 0000000..45cd2a3 --- /dev/null +++ b/relaisserver.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include +#ifndef QT_NO_NETWORKPROXY +#include +#endif + +class QTcpServer; + +class RelaisClient; + +class RelaisServer : public QObject +{ + Q_OBJECT + +public: + explicit RelaisServer(QObject *parent = Q_NULLPTR); + + bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0); + void close(); + + bool isListening() const; + + void setMaxPendingConnections(int numConnections); + int maxPendingConnections() const; + + quint16 serverPort() const; + QHostAddress serverAddress() const; + + qintptr socketDescriptor() const; + bool setSocketDescriptor(qintptr socketDescriptor); + + bool hasPendingConnections() const; + + QAbstractSocket::SocketError serverError() const; + QString errorString() const; + + void pauseAccepting(); + void resumeAccepting(); + +#ifndef QT_NO_NETWORKPROXY + void setProxy(const QNetworkProxy &networkProxy); + QNetworkProxy proxy() const; +#endif + + const QSet &clients() const; + +Q_SIGNALS: + void acceptError(QAbstractSocket::SocketError socketError); + +protected: + QSet m_clients; + +private Q_SLOTS: + void newConnection(); + +private: + QTcpServer *m_server; +}; diff --git a/relaiswebserver.cpp b/relaiswebserver.cpp new file mode 100644 index 0000000..9cab43c --- /dev/null +++ b/relaiswebserver.cpp @@ -0,0 +1,161 @@ +#include "relaiswebserver.h" + +#include + +#include "httpclientconnection.h" +#include "httpcontainers.h" +#include "relaisserver.h" +#include "relaisclient.h" + +RelaisWebserver::RelaisWebserver(RelaisServer *relaisServer, QObject *parent) : + HttpServer(parent), + m_relaisServer(relaisServer) +{ +} + +void RelaisWebserver::handleRequest(HttpClientConnection *connection, const HttpRequest &request) +{ + qDebug() << request.path; + + if(!request.path.startsWith('/')) + { + HttpResponse response; + response.protocol = request.protocol; + response.statusCode = HttpResponse::StatusCode::BadRequest; + response.body = "Path does not start with /"; + response.headers.insert(QStringLiteral("Server"), QStringLiteral("Hatschi Server 1.0")); + response.headers.insert(QStringLiteral("Content-Type"), QStringLiteral("text/html")); + + connection->sendResponse(response); + return; + } + + if(request.path == QStringLiteral("/")) + { + handleRoot(connection, request); + } + else if(request.path.startsWith("/devices/")) + { + auto parts = request.path.split('/'); + if(parts.count() != 4) + { + handle404(connection, request); + return; + } + + RelaisClient *client = Q_NULLPTR; + + for(auto _client : m_relaisServer->clients()) + { + if(!_client->name().isEmpty() && + _client->name() == parts.at(2)) + { + client = _client; + break; + } + } + + if(!client) + { + handle404(connection, request); + return; + } + + if(parts.at(3) == "on") + { + client->on(); + redirectRoot(connection, request); + return; + } + else if(parts.at(3) == "off") + { + client->off(); + redirectRoot(connection, request); + return; + } + else if(parts.at(3) == "toggle") + { + client->toggle(); + redirectRoot(connection, request); + return; + } + else + { + handle404(connection, request); + return; + } + } + else + { + handle404(connection, request); + } +} + +void RelaisWebserver::handleRoot(HttpClientConnection *connection, const HttpRequest &request) +{ + QString output = "

" % tr("%0 clients").arg(m_relaisServer->clients().count()) % "

"; + + output.append(""); + output.append(""); + output.append(""); + output.append(""); + output.append(""); + output.append(""); + output.append(""); + output.append(""); + output.append(""); + output.append(""); + for(auto client : m_relaisServer->clients()) + { + output.append(""); + output.append(""); + output.append(""); + output.append(""); + if(!client->name().isEmpty()) + { + output.append(""); + } + output.append(""); + } + output.append(""); + output.append("
IP-AdressNameStatusActions
" % client->peerAddress().toString() % ':' % QString::number(client->peerPort()) % "" % client->name() % "" % client->status() % "name().toHtmlEscaped() % "/toggle\">" % tr("toggle") % " "); + if(client->status() != QStringLiteral("on")) + output.append("name().toHtmlEscaped() % "/on\">" % tr("on") % " "); + if(client->status() != QStringLiteral("off")) + output.append("name().toHtmlEscaped() % "/off\">" % tr("off") % " "); + output.append("
"); + + HttpResponse response; + response.protocol = request.protocol; + response.statusCode = HttpResponse::StatusCode::OK; + response.body = output.toUtf8(); + response.headers.insert(QStringLiteral("Server"), QStringLiteral("Hatschi Server 1.0")); + response.headers.insert(QStringLiteral("Content-Type"), QStringLiteral("text/html")); + + connection->sendResponse(response); +} + +void RelaisWebserver::redirectRoot(HttpClientConnection *connection, const HttpRequest &request) +{ + HttpResponse response; + response.protocol = request.protocol; + response.statusCode = HttpResponse::StatusCode::Found; + response.body = QByteArrayLiteral("Follow this link"); + response.headers.insert(QStringLiteral("Server"), QStringLiteral("Hatschi Server 1.0")); + response.headers.insert(QStringLiteral("Content-Type"), QStringLiteral("text/html")); + response.headers.insert(QStringLiteral("Location"), QStringLiteral("/")); + + connection->sendResponse(response); +} + +void RelaisWebserver::handle404(HttpClientConnection *connection, const HttpRequest &request) +{ + HttpResponse response; + response.protocol = request.protocol; + response.statusCode = HttpResponse::StatusCode::NotFound; + response.body = "Not Found"; + response.headers.insert(QStringLiteral("Server"), QStringLiteral("Hatschi Server 1.0")); + response.headers.insert(QStringLiteral("Content-Type"), QStringLiteral("text/html")); + + connection->sendResponse(response); +} diff --git a/relaiswebserver.h b/relaiswebserver.h new file mode 100644 index 0000000..ca1d7da --- /dev/null +++ b/relaiswebserver.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "httpserver.h" + +class RelaisServer; +class HttpClientConnection; +class HttpRequest; + +class RelaisWebserver : public HttpServer +{ + Q_OBJECT + +public: + explicit RelaisWebserver(RelaisServer *relaisServer, QObject *parent = Q_NULLPTR); + +protected: + void handleRequest(HttpClientConnection *connection, const HttpRequest &request) Q_DECL_OVERRIDE; + +private: + void handleRoot(HttpClientConnection *connection, const HttpRequest &request); + void redirectRoot(HttpClientConnection *connection, const HttpRequest &request); + void handle404(HttpClientConnection *connection, const HttpRequest &request); + + RelaisServer *m_relaisServer; +};