Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions webaccess/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ project(webaccess)

add_subdirectory(src)
add_subdirectory(res)
if(NOT ANDROID AND NOT IOS)
add_subdirectory(test)
endif()
2 changes: 2 additions & 0 deletions webaccess/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ else()
endif()

set(WEBACCESS_SOURCES
commonjscss.cpp
commonjscss.h
qhttpserver/http_parser.c qhttpserver/http_parser.h
qhttpserver/qhttpconnection.cpp qhttpserver/qhttpconnection.h
Expand All @@ -33,6 +34,7 @@ set(WEBACCESS_SOURCES
webaccessauth.cpp webaccessauth.h
webaccessconfiguration.cpp webaccessconfiguration.h
webaccesssimpledesk.cpp webaccesssimpledesk.h
webaccessupload.cpp webaccessupload.h
${QM_FILES}
)

Expand Down
75 changes: 75 additions & 0 deletions webaccess/src/commonjscss.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
Q Light Controller Plus
commonjscss.cpp

Copyright (c) Q Light Controller Plus

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0.txt

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "commonjscss.h"

QString webAccessJsStringEscaped(const QString &text)
{
QString escaped;
escaped.reserve(text.length() * 2);

for (const QChar &ch : text)
{
const char16_t code = ch.unicode();

switch (code)
{
case '\\':
escaped += "\\\\";
break;
case '\'':
escaped += "\\'";
break;
case '"':
escaped += "\\\"";
break;
case '\b':
escaped += "\\b";
break;
case '\f':
escaped += "\\f";
break;
case '\n':
escaped += "\\n";
break;
case '\r':
escaped += "\\r";
break;
case '\t':
escaped += "\\t";
break;
case '<':
escaped += "\\x3C";
break;
default:
if (code < 0x20 || code == 0x2028 || code == 0x2029)
{
escaped += "\\u";
escaped += QString::number(code, 16).rightJustified(4, '0');
}
else
{
escaped += ch;
}
break;
}
}

return escaped;
}
4 changes: 4 additions & 0 deletions webaccess/src/commonjscss.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
#ifndef COMMONJSCSS_H
#define COMMONJSCSS_H

#include <QString>

QString webAccessJsStringEscaped(const QString &text);

#define HTML_HEADER \
"<!DOCTYPE html>\n" \
"<head>\n" \
Expand Down
6 changes: 0 additions & 6 deletions webaccess/src/webaccess-qml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -774,12 +774,6 @@ void WebAccessQml::slotHandleWebSocketRequest(QHttpConnection *conn, QString dat
m_sd->setAbsoluteChannelValue(absAddress, uchar(value));
return;
}
else if (cmdList[0] == "GM_VALUE")
{
uchar value = cmdList[1].toInt();
m_doc->inputOutputMap()->setGrandMasterValue(value);
return;
}
else if (cmdList[0] == "POLL")
return;

Expand Down
60 changes: 34 additions & 26 deletions webaccess/src/webaccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,9 @@ void WebAccess::slotHandleWebSocketRequest(QHttpConnection *conn, QString data)

quint32 wID = cmdList[2].toUInt();
VCWidget *widget = m_vc->widget(wID);
if (widget == nullptr)
return;

switch(widget->type())
{
case VCWidget::AnimationWidget:
Expand Down Expand Up @@ -449,12 +452,6 @@ void WebAccess::slotHandleWebSocketRequest(QHttpConnection *conn, QString data)

return;
}
else if (cmdList[0] == "GM_VALUE")
{
uchar value = cmdList[1].toInt();
m_doc->inputOutputMap()->setGrandMasterValue(value);
return;
}
else if (cmdList[0] == "POLL")
return;

Expand Down Expand Up @@ -514,30 +511,36 @@ void WebAccess::slotHandleWebSocketRequest(QHttpConnection *conn, QString data)
cue->slotPreviousCue();
else if (cmdList[1] == "NEXT")
cue->slotNextCue();
else if (cmdList[1] == "STEP")
else if (cmdList[1] == "STEP" && cmdList.count() > 2)
cue->slotCurrentStepChanged(cmdList[2].toInt());
else if (cmdList[1] == "CUE_STEP_NOTE")
else if (cmdList[1] == "CUE_STEP_NOTE" && cmdList.count() > 3)
cue->slotStepNoteChanged(cmdList[2].toInt(), cmdList[3]);
else if (cmdList[1] == "CUE_SHOWPANEL")
else if (cmdList[1] == "CUE_SHOWPANEL" && cmdList.count() > 2)
cue->slotSideFaderButtonChecked(cmdList[2] == "1" ? false : true);
else if (cmdList[1] == "CUE_SIDECHANGE")
else if (cmdList[1] == "CUE_SIDECHANGE" && cmdList.count() > 2)
cue->slotSetSideFaderValue((cmdList[2]).toInt());
}
break;
case VCWidget::FrameWidget:
case VCWidget::SoloFrameWidget:
{
if (cmdList.count() < 2)
return;

VCFrame *frame = qobject_cast<VCFrame*>(widget);
if (cmdList[1] == "NEXT_PG")
frame->slotNextPage();
else if (cmdList[1] == "PREV_PG")
frame->slotPreviousPage();
else if (cmdList[1] == "FRAME_DISABLE")
else if (cmdList[1] == "FRAME_DISABLE" && cmdList.count() > 2)
frame->setDisableState(cmdList[2] == "1" ? false : true);
}
break;
case VCWidget::ClockWidget:
{
if (cmdList.count() < 2)
return;

VCClock *clock = qobject_cast<VCClock*>(widget);
if (cmdList[1] == "S")
clock->playPauseTimer();
Expand All @@ -547,24 +550,27 @@ void WebAccess::slotHandleWebSocketRequest(QHttpConnection *conn, QString data)
break;
case VCWidget::AnimationWidget:
{
if (cmdList.count() < 2)
return;

VCMatrix *matrix = qobject_cast<VCMatrix*>(widget);
if (cmdList[1] == "MATRIX_SLIDER_CHANGE")
if (cmdList[1] == "MATRIX_SLIDER_CHANGE" && cmdList.count() > 2)
matrix->slotSetSliderValue(cmdList[2].toInt());
if (cmdList[1] == "MATRIX_COMBO_CHANGE")
if (cmdList[1] == "MATRIX_COMBO_CHANGE" && cmdList.count() > 2)
matrix->slotSetAnimationValue(cmdList[2]);
if (cmdList[1] == "MATRIX_COLOR_CHANGE" && cmdList[2] == "COLOR_1")
if (cmdList[1] == "MATRIX_COLOR_CHANGE" && cmdList.count() > 3 && cmdList[2] == "COLOR_1")
matrix->slotColor1Changed(cmdList[3].toInt());
if (cmdList[1] == "MATRIX_COLOR_CHANGE" && cmdList[2] == "COLOR_2")
if (cmdList[1] == "MATRIX_COLOR_CHANGE" && cmdList.count() > 3 && cmdList[2] == "COLOR_2")
matrix->slotColor2Changed(cmdList[3].toInt());
if (cmdList[1] == "MATRIX_COLOR_CHANGE" && cmdList[2] == "COLOR_3")
if (cmdList[1] == "MATRIX_COLOR_CHANGE" && cmdList.count() > 3 && cmdList[2] == "COLOR_3")
matrix->slotColor3Changed(cmdList[3].toInt());
if (cmdList[1] == "MATRIX_COLOR_CHANGE" && cmdList[2] == "COLOR_4")
if (cmdList[1] == "MATRIX_COLOR_CHANGE" && cmdList.count() > 3 && cmdList[2] == "COLOR_4")
matrix->slotColor4Changed(cmdList[3].toInt());
if (cmdList[1] == "MATRIX_COLOR_CHANGE" && cmdList[2] == "COLOR_5")
if (cmdList[1] == "MATRIX_COLOR_CHANGE" && cmdList.count() > 3 && cmdList[2] == "COLOR_5")
matrix->slotColor5Changed(cmdList[3].toInt());
if (cmdList[1] == "MATRIX_KNOB")
if (cmdList[1] == "MATRIX_KNOB" && cmdList.count() > 3)
matrix->slotMatrixControlKnobValueChanged(cmdList[2].toInt(), cmdList[3].toInt());
if (cmdList[1] == "MATRIX_PUSHBUTTON")
if (cmdList[1] == "MATRIX_PUSHBUTTON" && cmdList.count() > 2)
matrix->slotMatrixControlPushButtonClicked(cmdList[2].toInt());
}
break;
Expand Down Expand Up @@ -704,7 +710,7 @@ QString WebAccess::getFrameHTML(const VCFrame *frame)
for (const VCFramePageShortcut* shortcut : shortcuts)
{
m_JScode += "framesPageNames[" + QString::number(frame->id()) + "][" + QString::number(index) + "] = \"" +
QString(shortcut->name()).replace("\\", "\\\\").replace("\"", "\\\"") + "\";\n";
webAccessJsStringEscaped(shortcut->name()) + "\";\n";
index++;
}
currentPageName = QString(shortcuts[frame->currentPage()]->name());
Expand All @@ -728,7 +734,7 @@ QString WebAccess::getFrameHTML(const VCFrame *frame)
str += "</div>\n";

m_JScode += "frameCaption[" + QString::number(frame->id()) + "] = \"" +
QString(frame->caption()).replace("\\", "\\\\").replace("\"", "\\\"") + "\";\n";
webAccessJsStringEscaped(frame->caption()) + "\";\n";

if (frame->isEnableButtonVisible()) {
str += "<a class=\"vcframeButton\" id=\"frEnBtn" + QString::number(frame->id()) + "\" " +
Expand All @@ -752,7 +758,8 @@ QString WebAccess::getFrameHTML(const VCFrame *frame)
"<img src=\"back.png\" title=\"" + tr("Back") + "\" width=\"27\"></a>";

str += "<div class=\"vcframePageLabel\" id=\"frPglbl" + QString::number(frame->id()) + "\" style=\"width: " + QString::number(frame->isCollapsed() ? 60 : 100)+"px;\">" +
"<div class=\"vcFrameText\" id=\"fr" + QString::number(frame->id()) + "Page\">" + currentPageName + "</div></div>\n";
"<div class=\"vcFrameText\" id=\"fr" + QString::number(frame->id()) + "Page\">" +
currentPageName.toHtmlEscaped() + "</div></div>\n";

str += "<a class=\"vcframeButton\" id=\"frMpHdrNext" + QString::number(frame->id()) + "\" href=\"javascript:frameNextPage(" +
QString::number(frame->id()) + ");\" style=\"display: " + QString(!frame->isCollapsed() ? "block" : "none") + ";\">" +
Expand Down Expand Up @@ -813,7 +820,7 @@ QString WebAccess::getSoloFrameHTML(const VCSoloFrame *frame)
for (const VCFramePageShortcut* shortcut : shortcuts)
{
m_JScode += "framesPageNames[" + QString::number(frame->id()) + "][" + QString::number(index) + "] = \"" +
QString(shortcut->name()).replace("\\", "\\\\").replace("\"", "\\\"") + "\";\n";
webAccessJsStringEscaped(shortcut->name()) + "\";\n";
index++;
}
currentPageName = QString(shortcuts[frame->currentPage()]->name());
Expand All @@ -837,7 +844,7 @@ QString WebAccess::getSoloFrameHTML(const VCSoloFrame *frame)
str += "</div>\n";

m_JScode += "frameCaption[" + QString::number(frame->id()) + "] = \"" +
QString(frame->caption()).replace("\\", "\\\\").replace("\"", "\\\"") + "\";\n";
webAccessJsStringEscaped(frame->caption()) + "\";\n";

if (frame->isEnableButtonVisible()) {
str += "<a class=\"vcframeButton\" id=\"frEnBtn" + QString::number(frame->id()) + "\" " +
Expand All @@ -861,7 +868,8 @@ QString WebAccess::getSoloFrameHTML(const VCSoloFrame *frame)
"<img src=\"back.png\" title=\"" + tr("Back") + "\" width=\"27\"></a>";

str += "<div class=\"vcframePageLabel\" id=\"frPglbl" + QString::number(frame->id()) + "\" style=\"width: " + QString::number(frame->isCollapsed() ? 60 : 100) + "px;\">" +
"<div class=\"vcFrameText\" id=\"fr" + QString::number(frame->id()) + "Page\">" + currentPageName + "</div></div>\n";
"<div class=\"vcFrameText\" id=\"fr" + QString::number(frame->id()) + "Page\">" +
currentPageName.toHtmlEscaped() + "</div></div>\n";

str += "<a class=\"vcframeButton\" id=\"frMpHdrNext" + QString::number(frame->id()) + "\" href=\"javascript:frameNextPage(" +
QString::number(frame->id()) + ");\" style=\"display: " + QString(!frame->isCollapsed() ? "block" : "none") + ";\">" +
Expand Down
51 changes: 41 additions & 10 deletions webaccess/src/webaccessbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QHostAddress>
#include <QProcess>
#include <QRegularExpression>
Expand All @@ -31,6 +30,7 @@
#include "webaccessconfiguration.h"
#include "webaccesssimpledesk.h"
#include "webaccessnetwork.h"
#include "webaccessupload.h"
#include "commonjscss.h"
#include "qlcconfig.h"
#include "qlcfile.h"
Expand All @@ -49,13 +49,6 @@

namespace
{
QString sanitizeUploadedFileName(const QString &rawName)
{
QString normalizedName = rawName;
normalizedName.replace('\\', '/');
return QFileInfo(normalizedName).fileName();
}

bool extractMultipartFilePayload(const QHttpRequest *req, QByteArray &payload, QString *fileName = nullptr)
{
payload.clear();
Expand Down Expand Up @@ -117,7 +110,16 @@ bool extractMultipartFilePayload(const QHttpRequest *req, QByteArray &payload, Q
QRegularExpression re("filename=\"([^\"]*)\"");
QRegularExpressionMatch match = re.match(QString::fromUtf8(partHeaders));
if (match.hasMatch())
*fileName = sanitizeUploadedFileName(match.captured(1));
{
const QString rawName = match.captured(1);
if (!isPlainUploadedFileName(rawName))
{
qWarning() << Q_FUNC_INFO << "Rejected fixture upload filename" << rawName;
return false;
}

*fileName = rawName;
}
}

const int payloadStart = headersEnd + payloadSeparatorSize;
Expand Down Expand Up @@ -629,6 +631,18 @@ bool WebAccessBase::handleCommonWebSocketCommand(QHttpConnection *conn, const We

return true;
}
else if (cmdList[0] == "GM_VALUE")
{
if (m_auth && user && user->level < SIMPLE_DESK_AND_VC_LEVEL)
return true;

if (cmdList.count() < 2)
return true;

uchar value = cmdList[1].toInt();
m_doc->inputOutputMap()->setGrandMasterValue(value);
return true;
}
else if (cmdList[0] == "QLC+AUTH")
{
if (!m_auth)
Expand All @@ -642,6 +656,9 @@ bool WebAccessBase::handleCommonWebSocketCommand(QHttpConnection *conn, const We

if (cmdList.at(1) == "ADD_USER")
{
if (cmdList.count() < 5)
return true;

QString username = cmdList.at(2);
QString password = cmdList.at(3);
int level = cmdList.at(4).toInt();
Expand All @@ -664,12 +681,18 @@ bool WebAccessBase::handleCommonWebSocketCommand(QHttpConnection *conn, const We
}
else if (cmdList.at(1) == "DEL_USER")
{
if (cmdList.count() < 3)
return true;

QString username = cmdList.at(2);
if (!username.isEmpty())
m_auth->deleteUser(username);
}
else if (cmdList.at(1) == "SET_USER_LEVEL")
{
if (cmdList.count() < 4)
return true;

QString username = cmdList.at(2);
int level = cmdList.at(3).toInt();
if (username.isEmpty())
Expand Down Expand Up @@ -716,7 +739,15 @@ bool WebAccessBase::handleCommonWebSocketCommand(QHttpConnection *conn, const We
if (cmdList.at(1) == "NETWORK" && m_netConfig != nullptr)
{
QString wsMessage;
if (m_netConfig->updateNetworkSettings(cmdList))
// QLC+SYS|NETWORK|dev|mode|ip|netmask|gateway|ssid|wpapsk
if (cmdList.count() < 9)
{
wsMessage = QString("ALERT|" + tr("Invalid network configuration request."));
if (conn)
conn->webSocketWrite(wsMessage);
return true;
}
else if (m_netConfig->updateNetworkSettings(cmdList))
wsMessage = QString("ALERT|" + tr("Network configuration changed. Reboot to apply the changes."));
else
wsMessage = QString("ALERT|" + tr("An error occurred while updating the network configuration."));
Expand Down
Loading
Loading