Files
DbWebserver-wifilampplugin/wifilampapplication.cpp
T

211 lines
7.6 KiB
C++

#include "wifilampapplication.h"
#include <QJsonObject>
#include <QJsonValue>
#include <QTcpServer>
#include <QStringBuilder>
#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<QString, std::function<void(WifiLampClient*)> > 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<WifiLampClient *> &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 = "<h1>Lampen-Steuerung</h1>";
output.append("<a href=\"/refresh\">Alle aktualisieren</a>");
output.append("<table border=\"1\">");
output.append("<thead>");
output.append("<tr>");
output.append("<th>IP-Adress</th>");
output.append("<th>Name</th>");
output.append("<th>Status</th>");
output.append("<th>Actions</th>");
output.append("</tr>");
output.append("</thead>");
output.append("<tbody>");
for(auto client : m_clients)
{
output.append("<tr>");
output.append("<td>" % clientId(client, true).toHtmlEscaped() % "</td>");
output.append("<td>" % client->name().toHtmlEscaped() % "</td>");
output.append("<td>" % client->status().toHtmlEscaped() % "</td>");
output.append("<td>");
output.append("<a href=\"/devices/" % clientId(client).toHtmlEscaped() % "/toggle\">" % tr("Toggle") % "</a> ");
output.append("<a href=\"/devices/" % clientId(client).toHtmlEscaped() % "/on\">" % tr("On") % "</a> ");
output.append("<a href=\"/devices/" % clientId(client).toHtmlEscaped() % "/off\">" % tr("Off") % "</a> ");
output.append("<a href=\"/devices/" % clientId(client).toHtmlEscaped() % "/refresh\">" % tr("Refresh") % "</a> ");
output.append("<a href=\"/devices/" % clientId(client).toHtmlEscaped() % "/reboot\">" % tr("Reboot") % "</a> ");
output.append("<a href=\"/devices/" % clientId(client).toHtmlEscaped() % "/delete\">" % tr("Delete") % "</a> ");
output.append("</td>");
output.append("</tr>");
}
output.append("</tbody>");
output.append("</table>");
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, "<a href=\"/\">" % tr("Follow this link") % "</a>");
}
QString WifiLampApplication::clientId(const WifiLampClient *client, bool forceIp)
{
if(!client->name().isEmpty() && !forceIp)
return client->name();
return client->peerAddress().toString() % ':' % QString::number(client->peerPort());
}