#include "wifilampapplication.h" #include #include #include #include #include "utils/netutils.h" #include "webserver.h" #include "httprequest.h" #include "httpresponse.h" #include "httpclientconnection.h" #include "httpnotfoundexception.h" #include "wifilampclient.h" const QString WifiLampApplication::SERVER_NAME(QStringLiteral("WifiLamp Server 1.0")); WifiLampApplication::WifiLampApplication(const QJsonObject &config, WebServer &webServer) : WebApplication(&webServer), m_webServer(webServer) { if(!config.contains(QStringLiteral("controlHostAddress"))) throw std::runtime_error("WifiLamp application does not contain controlHostAddress"); const auto hostAddressVal = config.value(QStringLiteral("controlHostAddress")); if(!hostAddressVal.isString()) throw std::runtime_error("WifiLamp application controlHostAddress is not a string"); m_hostAddress = parseHostAddress(hostAddressVal.toString()); if(!config.contains(QStringLiteral("controlPort"))) throw std::runtime_error("WifiLamp application does not contain controlPort"); const auto portVal = config.value(QStringLiteral("controlPort")); if(!portVal.isDouble()) throw std::runtime_error("WifiLamp application controlPort is not a number"); m_port = portVal.toInt(); m_tcpServer = new QTcpServer(this); } void WifiLampApplication::start() { if(!m_tcpServer->listen(m_hostAddress, m_port)) throw std::runtime_error(QString("Could not start listening on %0:%1 because %2") .arg(m_hostAddress.toString()).arg(m_port).arg(m_tcpServer->errorString()).toStdString()); connect(m_tcpServer, &QTcpServer::acceptError, this, &WifiLampApplication::acceptError); connect(m_tcpServer, &QTcpServer::newConnection, this, &WifiLampApplication::newConnection); } void WifiLampApplication::handleRequest(HttpClientConnection *connection, const HttpRequest &request) { if(!request.path.startsWith('/')) { HttpResponse response; response.protocol = request.protocol; response.statusCode = HttpResponse::StatusCode::BadRequest; response.headers.insert(HttpResponse::HEADER_SERVER, SERVER_NAME); response.headers.insert(HttpResponse::HEADER_CONTENTTYPE, QStringLiteral("text/html")); connection->sendResponse(response, tr("Path does not start with /")); return; } if(request.path == QStringLiteral("/")) { handleRoot(connection, request); } else if(request.path == QStringLiteral("/refresh")) { for(auto client : m_clients) client->requestStatus(); redirectRoot(connection, request); } else if(request.path.startsWith("/devices/")) { auto parts = request.path.split('/'); if(parts.count() != 4) throw HttpNotFoundException(request); WifiLampClient *client; { auto iter = std::find_if(m_clients.constBegin(), m_clients.constEnd(), [&parts](auto client){ return clientId(client) == parts.at(2); }); if(iter == m_clients.constEnd()) throw HttpNotFoundException(request); client = *iter; } static const QHash > actions { { QStringLiteral("toggle"), [](auto client){ client->toggle(); } }, { QStringLiteral("on"), [](auto client){ client->on(); } }, { QStringLiteral("off"), [](auto client){ client->off(); } }, { QStringLiteral("refresh"), [](auto client){ client->requestStatus(); } }, { QStringLiteral("reboot"), [](auto client){ client->reboot(); } }, { QStringLiteral("delete"), [](auto client){ client->deleteLater(); } } }; { auto iter = actions.find(parts.at(3)); if(iter == actions.constEnd()) throw HttpNotFoundException(request); else { (*iter)(client); redirectRoot(connection, request); } } } else throw HttpNotFoundException(request); } const QSet &WifiLampApplication::clients() const { return m_clients; } void WifiLampApplication::acceptError(QAbstractSocket::SocketError socketError) { qCritical() << socketError; } void WifiLampApplication::newConnection() { auto socket = m_tcpServer->nextPendingConnection(); if(!socket) { qWarning() << "null socket received"; return; } auto client = new WifiLampClient(*socket, *this); m_clients.insert(client); connect(client, &QObject::destroyed, this, [this, client](){ m_clients.remove(client); }); } void WifiLampApplication::handleRoot(HttpClientConnection *connection, const HttpRequest &request) { QString output = "

Lampen-Steuerung

"; output.append("Alle aktualisieren"); output.append(""); output.append(""); output.append(""); output.append(""); output.append(""); output.append(""); output.append(""); output.append(""); output.append(""); output.append(""); for(auto client : m_clients) { output.append(""); output.append(""); output.append(""); output.append(""); output.append(""); output.append(""); } output.append(""); output.append("
IP-AdressNameStatusActions
" % clientId(client, true).toHtmlEscaped() % "" % client->name().toHtmlEscaped() % "" % client->status().toHtmlEscaped() % ""); output.append("" % tr("Toggle") % " "); output.append("" % tr("On") % " "); output.append("" % tr("Off") % " "); output.append("" % tr("Refresh") % " "); output.append("" % tr("Reboot") % " "); output.append("" % tr("Delete") % " "); output.append("
"); HttpResponse response; response.protocol = request.protocol; response.statusCode = HttpResponse::StatusCode::OK; response.headers.insert(HttpResponse::HEADER_SERVER, SERVER_NAME); response.headers.insert(HttpResponse::HEADER_CONTENTTYPE, QStringLiteral("text/html")); connection->sendResponse(response, output); } void WifiLampApplication::redirectRoot(HttpClientConnection *connection, const HttpRequest &request) { HttpResponse response; response.protocol = request.protocol; response.statusCode = HttpResponse::StatusCode::Found; response.headers.insert(HttpResponse::HEADER_SERVER, SERVER_NAME); response.headers.insert(HttpResponse::HEADER_CONTENTTYPE, QStringLiteral("text/html")); response.headers.insert(HttpResponse::HEADER_LOCATION, QStringLiteral("/")); connection->sendResponse(response, "" % tr("Follow this link") % ""); } QString WifiLampApplication::clientId(const WifiLampClient *client, bool forceIp) { if(!client->name().isEmpty() && !forceIp) return client->name(); return client->peerAddress().toString() % ':' % QString::number(client->peerPort()); }