Modernize browser for Qt6, fix command execution

pull/2/head
Vadim vtroshchinskiy 2023-10-19 13:15:46 +02:00
parent 5524b710ba
commit 07ea3f8691
6 changed files with 341 additions and 50 deletions

View File

@ -9,7 +9,7 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
find_package(QT NAMES Qt5 COMPONENTS Widgets LinguistTools Network WebEngineWidgets REQUIRED)
find_package(QT NAMES Qt6 COMPONENTS Widgets LinguistTools Network WebEngineWidgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets LinguistTools Network WebEngineWidgets REQUIRED)
@ -18,6 +18,7 @@ message(STATUS "Building browser with Qt ${QT_VERSION}")
set(SOURCES
main.cpp
mainwindow.cpp
ogurlhandler.cpp
)
@ -27,7 +28,7 @@ set_property(TARGET OGBrowser PROPERTY CXX_STANDARD 17)
set_property(TARGET OGBrowser PROPERTY CXX_STANDARD_REQUIRED ON)
target_include_directories(OGBrowser PRIVATE "digitalclock" "qtermwidget/lib")
target_link_libraries(OGBrowser PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::WebEngineWidgets DigitalClock qtermwidget5)
target_link_libraries(OGBrowser PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::WebEngineWidgets DigitalClock qtermwidget6)
message(STATUS "Looking for headers in ${PROJECT_BINARY_DIR}")
target_include_directories(OGBrowser PRIVATE ${qtermwidget_INCLUDE_DIRS} ${DigitalClock_INCLUDE_DIRS} ${qtermwidget_LIB_DIRS}/lib ${PROJECT_BINARY_DIR}/../lib)

View File

@ -16,10 +16,16 @@
#include <QNetworkReply>
#include <QSslError>
#include <QTimer>
#include <QRegularExpression>
#include <QStringList>
#include <QString>
#include <libintl.h>
#include "qtermwidget.h"
#include "digitalclock.h"
#include "ogurlhandler.h"
#define BUFFERSIZE 2048
#define REGEXP_STRING "^\\[(\\d+)\\]"
@ -28,15 +34,15 @@
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),m_web(new QWebEngineView()),m_output(new QTextEdit()),
m_process(new QProcess(this)),
m_logfile(0),m_logstream(0),m_numberTerminal(0)
{
// Graphic
showFullScreen();
setWindowTitle(tr("OpenGnsys Browser"));
setCentralWidget(m_web);
readEnvironmentValues();
m_is_admin = qgetenv("ogactiveadmin") == "true";
// Open the log file for append
if(m_env.contains("OGLOGFILE") && m_env["OGLOGFILE"]!="")
{
@ -74,7 +80,7 @@ MainWindow::MainWindow(QWidget *parent)
// Assign tabs to dock
dock->setWidget(m_tabs);
// Assign tabs dock to the mainwindow if admin mode is active
if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
if(isAdmin())
addDockWidget(Qt::BottomDockWidgetArea,dock);
// Top Dock
@ -88,7 +94,7 @@ MainWindow::MainWindow(QWidget *parent)
// WebBar to dock
dock->setWidget(m_webBar);
// Assign top dock to the mainwindow if admin mode is active
if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
if(isAdmin())
addDockWidget(Qt::TopDockWidgetArea,dock);
// Status bar
@ -139,23 +145,68 @@ MainWindow::MainWindow(QWidget *parent)
// SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> &)), this,
// SLOT(slotSslErrors(QNetworkReply*)));
// Process signals
connect(m_process,SIGNAL(started()),this,SLOT(slotProcessStarted()));
connect(m_process,SIGNAL(finished(int,QProcess::ExitStatus)),
this,SLOT(slotProcessFinished(int,QProcess::ExitStatus)));
connect(m_process,SIGNAL(error(QProcess::ProcessError)),
this,SLOT(slotProcessError(QProcess::ProcessError)));
connect(m_process,SIGNAL(readyReadStandardOutput()),this,SLOT(slotProcessOutput()));
connect(m_process,SIGNAL(readyReadStandardError()),
this,SLOT(slotProcessErrorOutput()));
// Dock signals
connect(button,SIGNAL(clicked()),this,SLOT(slotCreateTerminal()));
connect(m_webBar,SIGNAL(returnPressed()),this,SLOT(slotWebBarReturnPressed()));
QWebEngineUrlScheme schemeCommand("command");
schemeCommand.setSyntax(QWebEngineUrlScheme::Syntax::Path);
schemeCommand.setDefaultPort(0);
schemeCommand.setFlags(QWebEngineUrlScheme::LocalScheme);
QWebEngineUrlScheme::registerScheme(schemeCommand);
QWebEngineUrlScheme schemeCommandOut("command+output");
schemeCommandOut.setSyntax(QWebEngineUrlScheme::Syntax::Path);
schemeCommandOut.setDefaultPort(0);
schemeCommandOut.setFlags(QWebEngineUrlScheme::LocalScheme);
QWebEngineUrlScheme::registerScheme(schemeCommandOut);
QWebEngineUrlScheme schemeCommandConfirm("command+confirm");
schemeCommandConfirm.setSyntax(QWebEngineUrlScheme::Syntax::Path);
schemeCommandConfirm.setDefaultPort(0);
schemeCommandConfirm.setFlags(QWebEngineUrlScheme::LocalScheme);
QWebEngineUrlScheme::registerScheme(schemeCommandConfirm);
OGBrowserUrlHandlerCommand *cmdHandler = new OGBrowserUrlHandlerCommand(this);
connect(cmdHandler, &OGBrowserUrlHandlerCommand::command, this, &MainWindow::commandQueued);
registerHandler("command", false, false);
registerHandler("command+output", false, true);
registerHandler("command+confirm", true, false);
registerHandler("command+confirm+output", true, true);
registerHandler("command+output+confirm", true, true);
/*
QWebEngineProfile::defaultProfile()->installUrlSchemeHandler("command", cmdHandler);
OGBrowserUrlHandlerCommand *cmdOutHandler = new OGBrowserUrlHandlerCommand(this);
connect(cmdOutHandler, &OGBrowserUrlHandlerCommand::command, this, &MainWindow::commandQueued);
cmdOutHandler->setReturnOutput(true);
QWebEngineProfile::defaultProfile()->installUrlSchemeHandler("command+output", cmdOutHandler);
OGBrowserUrlHandlerCommand *cmdConfHandler = new OGBrowserUrlHandlerCommand(this);
connect(cmdConfHandler, &OGBrowserUrlHandlerCommand::command, this, &MainWindow::commandQueued);
cmdConfHandler->setAskConfirmation(true);
QWebEngineProfile::defaultProfile()->installUrlSchemeHandler("command+confirm", cmdConfHandler);
*/
QStringList arguments=QCoreApplication::arguments();
m_webBar->setText(arguments[1]);
m_web->load(QUrl(arguments[1]));
m_webBar->setText(arguments.at(1));
m_web->load(QUrl(arguments.at(1)));
showMaximized();
showFullScreen();
}
MainWindow::~MainWindow()
@ -169,10 +220,86 @@ MainWindow::~MainWindow()
delete m_logstream;
}
void MainWindow::registerHandler(const QString &commandName, bool confirm, bool returnOutput) {
OGBrowserUrlHandlerCommand *handler = new OGBrowserUrlHandlerCommand(this);
connect(handler, &OGBrowserUrlHandlerCommand::command, this, &MainWindow::commandQueued);
handler->setAskConfirmation(confirm);
handler->setReturnOutput(returnOutput);
QWebEngineProfile::defaultProfile()->installUrlSchemeHandler(commandName.toLatin1(), handler);
}
void MainWindow::commandQueued(const QString &command, bool confirm, bool returnOutput) {
//PendingCommand cmd;
qInfo() << "Queued command:" << command;
if (confirm) {
QMessageBox msgBox;
msgBox.setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
msgBox.setWindowTitle(tr(gettext("AVISO")));
msgBox.setIcon(QMessageBox::Question);
msgBox.setTextFormat(Qt::RichText);
msgBox.setText(tr(gettext("La siguiente acci&oacute;n puede modificar datos o tardar varios minutos. El equipo no podr&aacute; ser utilizado durante su ejecuci&oacute;n.")));
QPushButton *execButton = msgBox.addButton(tr(gettext("Ejecutar")), QMessageBox::ActionRole);
msgBox.addButton(tr(gettext("Cancelar")), QMessageBox::RejectRole);
msgBox.setDefaultButton(execButton);
msgBox.exec();
if (msgBox.clickedButton() != execButton) {
qInfo() << "User rejected running the command";
return;
}
}
if (returnOutput && !isAdmin()) {
int w=MainWindow::width(), h=MainWindow::height();
m_output->setWindowFlags(Qt::Window);
m_output->move(100, 100);
m_output->setFixedSize(w*0.8-100, h*0.8-100);
m_output->show();
}
m_command.command = command;
m_command.confirm = confirm;
m_command.returnOutput = returnOutput;
QStringList list=command.split(" ",Qt::SkipEmptyParts);
QString program=list.takeFirst();
m_command.process = new QProcess(this);
m_command.process->setReadChannel(QProcess::StandardOutput);
m_command.process->setEnvironment(QProcess::systemEnvironment());
// Process signals
connect(m_command.process, &QProcess::started,this, &MainWindow::slotProcessStarted);
connect(m_command.process, &QProcess::finished,this,&MainWindow::slotProcessFinished);
connect(m_command.process, &QProcess::errorOccurred, this,&MainWindow::slotProcessError);
connect(m_command.process, &QProcess::readyReadStandardOutput,this,&MainWindow::slotProcessOutput);
connect(m_command.process, &QProcess::readyReadStandardError,this,&MainWindow::slotProcessErrorOutput);
if(isAdmin()) {
m_output->setTextColor(QColor(Qt::darkGreen));
print(tr(gettext("Lanzando el comando: "))+command);
m_output->setTextColor(QColor(Qt::black));
} else {
write(tr(gettext("Lanzando el comando: "))+command);
}
m_command.process->start(program,list);
startProgressBar();
}
void MainWindow::slotLinkHandle(const QUrl &url)
{
// Check if it's executing another process
if(m_process->state() != QProcess::NotRunning)
if(m_command.process->state() != QProcess::NotRunning)
{
print(tr(gettext("Hay otro proceso en ejecución. Por favor espere.")));
return;
@ -180,7 +307,7 @@ void MainWindow::slotLinkHandle(const QUrl &url)
QString urlString = url.toString();
QString urlScheme = url.scheme();
// Clear the output widget for a normal user
if(! m_env.contains("ogactiveadmin") || m_env["ogactiveadmin"] != "true")
if(!isAdmin())
{
m_output->clear();
}
@ -202,8 +329,7 @@ void MainWindow::slotLinkHandle(const QUrl &url)
if (msgBox.clickedButton() == execButton)
{
// For command with confirmation and output link, show an output window to non-admin user
if((urlScheme == COMMAND_CONFIRM_OUTPUT || urlScheme == COMMAND_OUTPUT_CONFIRM) &&
(! m_env.contains("ogactiveadmin") || m_env["ogactiveadmin"] != "true"))
if((urlScheme == COMMAND_CONFIRM_OUTPUT || urlScheme == COMMAND_OUTPUT_CONFIRM) && !isAdmin())
{
int w=MainWindow::width(), h=MainWindow::height();
m_output->setWindowFlags(Qt::Window);
@ -218,8 +344,7 @@ void MainWindow::slotLinkHandle(const QUrl &url)
else if(urlScheme == COMMAND || urlScheme == COMMAND_OUTPUT)
{
// For command with output link, show an output window to non-admin user
if(urlScheme == COMMAND_OUTPUT &&
(! m_env.contains("ogactiveadmin") || m_env["ogactiveadmin"] != "true"))
if(urlScheme == COMMAND_OUTPUT && !isAdmin())
{
int w=MainWindow::width(), h=MainWindow::height();
m_output->setWindowFlags(Qt::Window);
@ -253,7 +378,10 @@ void MainWindow::slotWebLoadFinished(bool ok)
// If any error ocurred, show a pop up
// Sometimes when the url hasn't got a dot, i.e /var/www/pageweb,
// the return value is always true so we check the bytes received too
if(ok == false)
qWarning() << "Load finished. URL: " << m_web->url() << "; ok = " << ok;
#if 0
if(!ok && !m_web->url().isEmpty())
{
QMessageBox msgBox;
msgBox.setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
@ -279,6 +407,9 @@ void MainWindow::slotWebLoadFinished(bool ok)
{
finishProgressBar();
}
#endif
finishProgressBar();
}
void MainWindow::slotUrlChanged(const QUrl &url)
@ -298,12 +429,14 @@ void MainWindow::slotProcessStarted()
void MainWindow::slotProcessOutput()
{
m_process->setReadChannel(QProcess::StandardOutput);
m_command.process->setReadChannel(QProcess::StandardOutput);
char buf[BUFFERSIZE];
while((m_process->readLine(buf,BUFFERSIZE) > 0))
while((m_command.process->readLine(buf,BUFFERSIZE) > 0))
{
QString s(buf);
if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
qInfo() << "OUT: " << buf;
if(isAdmin())
{
m_output->insertPlainText(tr("Proc. stdout: "));
}
@ -314,12 +447,18 @@ void MainWindow::slotProcessOutput()
void MainWindow::slotProcessErrorOutput()
{
m_process->setReadChannel(QProcess::StandardError);
// QProcess *process = qobject_cast<QProcess*>(sender());
// QVector<PendingCommand>::iterator it=std::find(m_commands.begin(), m_commands.end(), []())
m_command.process->setReadChannel(QProcess::StandardError);
char buf[BUFFERSIZE];
while((m_process->readLine(buf,BUFFERSIZE) > 0))
while((m_command.process->readLine(buf,BUFFERSIZE) > 0))
{
QString s(buf);
if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
qInfo() << "ERR: " << buf;
if(isAdmin())
{
m_output->insertPlainText(tr("Proc. stderr: "));
}
@ -331,7 +470,10 @@ void MainWindow::slotProcessErrorOutput()
void MainWindow::slotProcessFinished(int code, QProcess::ExitStatus status)
{
if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
qInfo() << "Finished: " << m_command.command << "with status" << status;
if(isAdmin())
{
// Admin user: show process status
if(status==QProcess::NormalExit)
@ -367,6 +509,8 @@ void MainWindow::slotProcessFinished(int code, QProcess::ExitStatus status)
void MainWindow::slotProcessError(QProcess::ProcessError error)
{
qCritical() << "Error: " << m_command.command << "with status" << error;
QString errorMsg;
switch(error)
{
@ -493,10 +637,12 @@ void MainWindow::captureOutputForStatusBar(QString output)
// Modify the status bar
output=output.trimmed();
// Get percentage (string starts with "[Number]")
QRegExp regexp(REGEXP_STRING);
if(regexp.indexIn(output) != -1)
QRegularExpression regexp(REGEXP_STRING);
QRegularExpressionMatch match = regexp.match(output);
if(match.hasMatch())
{
int pass=regexp.cap(1).toInt();
int pass=match.captured(1).toInt();
output.replace(regexp,"");
m_progressBar->setValue(pass);
m_progressBar->setFormat("%p%"+output);
@ -527,7 +673,7 @@ void MainWindow::finishProgressBar()
// Execute a command
void MainWindow::executeCommand(QString &string)
{
QStringList list=string.split(" ",Qt::SkipEmptyParts);
/* QStringList list=string.split(" ",Qt::SkipEmptyParts);
QString program=list.takeFirst();
m_process->setReadChannel(QProcess::StandardOutput);
// Assign the same Browser's environment to the process
@ -544,7 +690,7 @@ void MainWindow::executeCommand(QString &string)
{
write(tr(gettext("Lanzando el comando: "))+string);
}
startProgressBar();
startProgressBar(); */
}
// Returns communication speed

View File

@ -16,21 +16,31 @@
#include <QMainWindow>
#include <QNetworkReply>
#include <QSslError>
#include <QProgressBar>
#include <QLabel>
#include <QLineEdit>
#include <QTextEdit>
#include <QFile>
#include <QWebEngineView>
#include <QVector>
#include "digitalclock.h"
class QWebEngineView;
class QTextEdit;
class QVBoxLayout;
class QProcess;
class QStringList;
class QString;
class QUrl;
class QFile;
class QTextStream;
class QTermWidget;
class QProgressBar;
class QLineEdit;
class QLabel;
struct PendingCommand {
QString command = "";
bool confirm = false;
bool returnOutput = false;
QProcess *process = nullptr;
bool operator==(const PendingCommand &other) {
return other.process == process;
}
};
class MainWindow : public QMainWindow
{
@ -63,6 +73,16 @@ class MainWindow : public QMainWindow
void slotWebBarReturnPressed();
void slotUrlChanged(const QUrl &url);
void commandQueued(const QString &command, bool confirm, bool returnOutput);
private:
bool isAdmin() const { return m_is_admin; }
void registerHandler(const QString &name, bool confirm, bool output);
bool m_is_admin{false};
//Functions
protected:
int readEnvironmentValues();
@ -85,12 +105,16 @@ class MainWindow : public QMainWindow
QTabWidget *m_tabs;
QLineEdit *m_webBar;
QProcess *m_process;
//QProcess *m_process;
QMap<QString,QString> m_env;
QFile *m_logfile;
QTextStream *m_logstream;
PendingCommand m_command;
//QVector<PendingCommand> m_commands;
int m_numberTerminal;
};

4
src/ogbrowser.h 100644
View File

@ -0,0 +1,4 @@
#pragma once
#include <

View File

@ -0,0 +1,72 @@
#include "ogurlhandler.h"
#include <QWebEngineUrlRequestJob>
#include <QMessageBox>
#include <QDebug>
void OGBrowserUrlHandlerCommand::requestStarted(QWebEngineUrlRequestJob *job) {
qInfo() << "Command request: " << job->requestUrl();
emit command(job->requestUrl().path(), askConfirmation(), returnOuput());
//job->redirect(QUrl());
job->fail(QWebEngineUrlRequestJob::NoError);
return;
/*
// For all command with confirmation links, show a popup box
if (askConfirmation()) {
QMessageBox msgBox;
msgBox.setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
msgBox.setWindowTitle(tr(gettext("AVISO")));
msgBox.setIcon(QMessageBox::Question);
msgBox.setTextFormat(Qt::RichText);
msgBox.setText(tr(gettext("La siguiente acci&oacute;n puede modificar datos o tardar varios minutos. El equipo no podr&aacute; ser utilizado durante su ejecuci&oacute;n.")));
QPushButton *execButton = msgBox.addButton(tr(gettext("Ejecutar")), QMessageBox::ActionRole);
msgBox.addButton(tr(gettext("Cancelar")), QMessageBox::RejectRole);
msgBox.setDefaultButton(execButton);
msgBox.exec();
// Continue if user press the execution button
if (msgBox.clickedButton() == execButton)
{
// For command with confirmation and output link, show an output window to non-admin user
if((urlScheme == COMMAND_CONFIRM_OUTPUT || urlScheme == COMMAND_OUTPUT_CONFIRM) &&
(! m_env.contains("ogactiveadmin") || m_env["ogactiveadmin"] != "true"))
{
int w=MainWindow::width(), h=MainWindow::height();
m_output->setWindowFlags(Qt::Window);
m_output->move(100, 100);
m_output->setFixedSize(w*0.8-100, h*0.8-100);
m_output->show();
}
} else {
job->fail(QWebEngineUrlRequestJob::RequestDenied);
return;
}
}
// Execute the command
executeCommand(urlString.remove(0, urlScheme.length()+1));
else if(urlScheme == COMMAND || urlScheme == COMMAND_OUTPUT)
{
// For command with output link, show an output window to non-admin user
if(urlScheme == COMMAND_OUTPUT &&
(! m_env.contains("ogactiveadmin") || m_env["ogactiveadmin"] != "true"))
{
int w=MainWindow::width(), h=MainWindow::height();
m_output->setWindowFlags(Qt::Window);
m_output->move(100, 100);
m_output->setFixedSize(w*0.8-100, h*0.8-100);
m_output->show();
}
// Execute the command
executeCommand(urlString.remove(0, urlScheme.length()+1));
} */
}

44
src/ogurlhandler.h 100644
View File

@ -0,0 +1,44 @@
#pragma once
#include <QWebEngineUrlSchemeHandler>
#include <QObject>
#define COMMAND "command"
#define COMMAND_CONFIRM "command+confirm"
#define COMMAND_WITH_CONFIRMATION "commandwithconfirmation" // Backwards compatibility
#define COMMAND_OUTPUT "command+output"
#define COMMAND_CONFIRM_OUTPUT "command+confirm+output"
#define COMMAND_OUTPUT_CONFIRM "command+output+confirm"
#define ENVIRONMENT "OGLOGFILE,ogactiveadmin,DEFAULTSPEED"
class OGBrowserUrlHandlerCommand : public QWebEngineUrlSchemeHandler {
Q_OBJECT
public:
OGBrowserUrlHandlerCommand(QObject *parent = nullptr) : QWebEngineUrlSchemeHandler(parent) {
};
virtual ~OGBrowserUrlHandlerCommand() {
};
virtual void requestStarted(QWebEngineUrlRequestJob *job);
bool askConfirmation() const { return m_ask_confirmation; }
bool returnOuput() const { return m_return_output; }
void setAskConfirmation(const bool ask) { m_ask_confirmation = ask; }
void setReturnOutput(const bool retOutput) { m_return_output = retOutput; }
signals:
void command(const QString &command, bool confirm, bool returnOutput);
private:
bool m_ask_confirmation = false;
bool m_return_output = false;
};