574 lines
16 KiB
C++
574 lines
16 KiB
C++
#include "mainwindow.h"
|
|
#include <QtWebEngineWidgets>
|
|
#include <QStringList>
|
|
#include <QWebEngineView>
|
|
#include <QDockWidget>
|
|
#include <QtDebug>
|
|
#include <QWebEnginePage>
|
|
#include <QProcess>
|
|
#include <QTextEdit>
|
|
#include <QMessageBox>
|
|
#include <QPushButton>
|
|
#include <QDateTime>
|
|
#include <QProgressBar>
|
|
#include <QTabWidget>
|
|
#include <QLineEdit>
|
|
#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+)\\]"
|
|
|
|
#define CURRENT_TIME() QDateTime::currentDateTime().toString("dd/MM/yyyy hh:mm:ss")
|
|
|
|
MainWindow::MainWindow(QWidget *parent)
|
|
: QMainWindow(parent),m_web(new QWebEngineView()),m_output(new QTextEdit()),
|
|
m_logfile(0),m_logstream(0),m_numberTerminal(0)
|
|
{
|
|
// Graphic
|
|
setWindowTitle(tr("OpenGnsys Browser"));
|
|
setCentralWidget(m_web);
|
|
readEnvironmentValues();
|
|
|
|
m_is_admin = qgetenv("ogactiveadmin") == "true";
|
|
m_kiosk_mode = qgetenv("OGKIOSKMODE") == "true";
|
|
|
|
// Open the log file for append
|
|
if(m_env.contains("OGLOGFILE") && m_env["OGLOGFILE"]!="")
|
|
{
|
|
QFile* m_logfile=new QFile(m_env["OGLOGFILE"]);
|
|
if(!m_logfile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append))
|
|
{
|
|
delete m_logfile;
|
|
print(tr(gettext("El fichero de log no ha podido ser abierto: "))+m_env["OGLOGFILE"]+".");
|
|
}
|
|
else
|
|
{
|
|
m_logstream=new QTextStream(m_logfile);
|
|
}
|
|
}
|
|
|
|
// Output
|
|
m_output->setReadOnly(true);
|
|
m_output->setFontPointSize(16);
|
|
|
|
// Button Dock
|
|
QDockWidget* dock=new QDockWidget();
|
|
dock->setAllowedAreas(Qt::BottomDockWidgetArea);
|
|
QWidget* dummy=new QWidget();
|
|
dummy->setMaximumHeight(0);
|
|
dock->setTitleBarWidget(dummy);
|
|
|
|
// TabWidget
|
|
m_tabs=new QTabWidget(dock);
|
|
QPushButton *button=new QPushButton(tr(gettext("&Nueva Terminal")));
|
|
button->setFocusPolicy(Qt::TabFocus);
|
|
m_tabs->setCornerWidget(button);
|
|
m_tabs->setFocusPolicy(Qt::NoFocus);
|
|
m_tabs->addTab(m_output,tr(gettext("Salida")));
|
|
slotCreateTerminal();
|
|
// Assign tabs to dock
|
|
dock->setWidget(m_tabs);
|
|
// Assign tabs dock to the mainwindow if admin mode is active
|
|
if(isAdmin())
|
|
addDockWidget(Qt::BottomDockWidgetArea,dock);
|
|
|
|
// Top Dock
|
|
dock=new QDockWidget();
|
|
dock->setAllowedAreas(Qt::TopDockWidgetArea);
|
|
QWidget* dummy2=new QWidget();
|
|
dummy2->setMaximumHeight(0);
|
|
dock->setTitleBarWidget(dummy2);
|
|
// WebBar
|
|
m_webBar=new QLineEdit(dock);
|
|
// WebBar to dock
|
|
dock->setWidget(m_webBar);
|
|
// Assign top dock to the mainwindow if admin mode is active
|
|
if(isAdmin())
|
|
addDockWidget(Qt::TopDockWidgetArea,dock);
|
|
|
|
// Status bar
|
|
QStatusBar* st=statusBar();
|
|
st->setSizeGripEnabled(false);
|
|
// OpenGnsys logo (or alternate text)
|
|
m_logo=new QLabel();
|
|
QPixmap logo;
|
|
if(logo.load("/opt/opengnsys/lib/pictures/oglogo.png"))
|
|
m_logo->setPixmap(logo);
|
|
else
|
|
m_logo->setText("OG");
|
|
m_logo->setToolTip(tr(gettext("Proyecto OpenGnsys"))+"\nhttps://opengnsys.es");
|
|
// Progress bar
|
|
m_progressBar=new QProgressBar(this);
|
|
m_progressBar->setRange(0,100);
|
|
// Connection speed
|
|
QString speed=readSpeed();
|
|
m_speedInfo=new QLabel(speed);
|
|
m_speedInfo->setAlignment(Qt::AlignCenter);
|
|
if(m_env.contains("DEFAULTSPEED") && m_env["DEFAULTSPEED"]!="")
|
|
if(speed.compare(m_env["DEFAULTSPEED"])!=0)
|
|
m_speedInfo->setStyleSheet("background-color: darkred; color: white; font-weight: bold;");
|
|
// Clock
|
|
m_clock=new DigitalClock(this);
|
|
|
|
connect(m_web,SIGNAL(loadStarted()),this,SLOT(slotWebLoadStarted()));
|
|
connect(m_web,SIGNAL(loadFinished(bool)),this,SLOT(slotWebLoadFinished(bool)));
|
|
connect(m_web,SIGNAL(loadProgress(int)),this,SLOT(slotWebLoadProgress(int)));
|
|
connect(m_web,SIGNAL(urlChanged(const QUrl&)),this,SLOT(slotUrlChanged(const QUrl&)));
|
|
// Ignore SSL errors.
|
|
|
|
|
|
// Qindel:
|
|
//connect(m_web->page()->networkAccessManager(),
|
|
// SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> &)), this,
|
|
// SLOT(slotSslErrors(QNetworkReply*)));
|
|
|
|
|
|
// Dock signals
|
|
connect(button,SIGNAL(clicked()),this,SLOT(slotCreateTerminal()));
|
|
|
|
// All schemes need registering first, then their handlers.
|
|
registerScheme("command");
|
|
registerScheme("command+output");
|
|
registerScheme("command+confirm");
|
|
registerScheme("command+confirm+output");
|
|
registerScheme("command+output+confirm");
|
|
|
|
|
|
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);
|
|
|
|
|
|
QStringList arguments=QCoreApplication::arguments();
|
|
m_webBar->setText(arguments.at(1));
|
|
m_web->load(QUrl(arguments.at(1)));
|
|
|
|
|
|
|
|
|
|
|
|
showMaximized();
|
|
showFullScreen();
|
|
}
|
|
|
|
void MainWindow::closeEvent(QCloseEvent *event) {
|
|
if (isKioskMode()) {
|
|
qInfo() << "Modo quiosco activado, ignorando intento de cerrar ventana";
|
|
event->ignore();
|
|
}
|
|
}
|
|
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
if(m_logfile)
|
|
{
|
|
m_logfile->close();
|
|
delete m_logfile;
|
|
}
|
|
if(m_logstream)
|
|
delete m_logstream;
|
|
}
|
|
|
|
void MainWindow::registerScheme(const QString &name) {
|
|
QWebEngineUrlScheme scheme(name.toLatin1());
|
|
scheme.setSyntax(QWebEngineUrlScheme::Syntax::Path);
|
|
scheme.setDefaultPort(0);
|
|
scheme.setFlags(QWebEngineUrlScheme::LocalScheme);
|
|
QWebEngineUrlScheme::registerScheme(scheme);
|
|
}
|
|
|
|
|
|
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ón puede modificar datos o tardar varios minutos. El equipo no podrá ser utilizado durante su ejecució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::slotWebLoadStarted()
|
|
{
|
|
startProgressBar();
|
|
m_progressBar->setFormat(gettext("%p% Cargando"));
|
|
}
|
|
|
|
void MainWindow::slotWebLoadProgress(int progress)
|
|
{
|
|
m_progressBar->setValue(progress);
|
|
}
|
|
|
|
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
|
|
qWarning() << "Load finished. URL: " << m_web->url() << "; ok = " << ok;
|
|
|
|
finishProgressBar();
|
|
|
|
}
|
|
|
|
void MainWindow::slotUrlChanged(const QUrl &url)
|
|
{
|
|
m_webBar->setText(url.toString());
|
|
}
|
|
|
|
void MainWindow::slotSslErrors(QNetworkReply* reply)
|
|
{
|
|
reply->ignoreSslErrors();
|
|
}
|
|
|
|
void MainWindow::slotProcessStarted()
|
|
{
|
|
startProgressBar();
|
|
}
|
|
|
|
void MainWindow::slotProcessOutput()
|
|
{
|
|
m_command.process->setReadChannel(QProcess::StandardOutput);
|
|
char buf[BUFFERSIZE];
|
|
while((m_command.process->readLine(buf,BUFFERSIZE) > 0))
|
|
{
|
|
QString s(buf);
|
|
qInfo() << "OUT: " << buf;
|
|
|
|
if(isAdmin())
|
|
{
|
|
m_output->insertPlainText(tr("Proc. stdout: "));
|
|
}
|
|
print(s);
|
|
captureOutputForStatusBar(s);
|
|
}
|
|
}
|
|
|
|
void MainWindow::slotProcessErrorOutput()
|
|
{
|
|
|
|
// 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_command.process->readLine(buf,BUFFERSIZE) > 0))
|
|
{
|
|
QString s(buf);
|
|
qInfo() << "ERR: " << buf;
|
|
|
|
if(isAdmin())
|
|
{
|
|
m_output->insertPlainText(tr("Proc. stderr: "));
|
|
}
|
|
m_output->setTextColor(QColor(Qt::darkBlue));
|
|
print(s);
|
|
m_output->setTextColor(QColor(Qt::black));
|
|
}
|
|
}
|
|
|
|
void MainWindow::slotProcessFinished(int code, QProcess::ExitStatus status)
|
|
{
|
|
|
|
qInfo() << "Finished: " << m_command.command << "with status" << status;
|
|
|
|
if(isAdmin())
|
|
{
|
|
// Admin user: show process status
|
|
if(status==QProcess::NormalExit)
|
|
{
|
|
if(code > 0)
|
|
{
|
|
m_output->setTextColor(QColor(Qt::darkRed));
|
|
}
|
|
print("\n"+tr(gettext("Fin del proceso. Valor de retorno: "))+QString::number(code));
|
|
}
|
|
else
|
|
{
|
|
m_output->setTextColor(QColor(Qt::darkRed));
|
|
print("\n"+tr(gettext("El proceso ha fallado inesperadamente. Salida: ")+code));
|
|
}
|
|
m_output->setTextColor(QColor(Qt::black));
|
|
}
|
|
else
|
|
{
|
|
// Non-admin user: show instruction to close the popup window
|
|
write(tr(gettext("Fin del proceso. Valor de retorno: "))+QString::number(code));
|
|
m_output->setFontUnderline(true);
|
|
print("\n\n"+tr(gettext("AVISO: Pulsar el botón superior derecho para cerrar"))+" [X]");
|
|
m_output->setFontUnderline(false);
|
|
}
|
|
// On error, show a message box
|
|
if(code > 0 && ! m_output->isActiveWindow())
|
|
{
|
|
showErrorMessage(gettext("Código de salida: ")+QString::number(code));
|
|
}
|
|
finishProgressBar();
|
|
}
|
|
|
|
void MainWindow::slotProcessError(QProcess::ProcessError error)
|
|
{
|
|
qCritical() << "Error: " << m_command.command << "with status" << error;
|
|
|
|
QString errorMsg;
|
|
switch(error)
|
|
{
|
|
case QProcess::FailedToStart:
|
|
errorMsg=tr(gettext("Imposible lanzar el proceso."));
|
|
break;
|
|
case QProcess::WriteError:
|
|
errorMsg=tr(gettext("Error de escritura en el proceso."));
|
|
break;
|
|
case QProcess::ReadError:
|
|
errorMsg=tr(gettext("Error de lectura del proceso."));
|
|
break;
|
|
// No capturo crashed porque la pillo por finished
|
|
case QProcess::Crashed:
|
|
case QProcess::Timedout:
|
|
break;
|
|
case QProcess::UnknownError:
|
|
default:
|
|
errorMsg=tr(gettext("Error desconocido."));
|
|
break;
|
|
}
|
|
// Print error and show message box with timeout.
|
|
if(!errorMsg.isNull()) {
|
|
m_output->setTextColor(QColor(Qt::darkRed));
|
|
print(errorMsg);
|
|
m_output->setTextColor(QColor(Qt::black));
|
|
showErrorMessage(errorMsg);
|
|
}
|
|
finishProgressBar();
|
|
}
|
|
|
|
void MainWindow::slotCreateTerminal()
|
|
{
|
|
QTermWidget* console = new QTermWidget(1,this);
|
|
QFont font = QApplication::font();
|
|
font.setFamily("DejaVu Sans Mono");
|
|
font.setPointSize(12);
|
|
|
|
console->setTerminalFont(font);
|
|
console->setFocusPolicy(Qt::StrongFocus);
|
|
console->setScrollBarPosition(QTermWidget::ScrollBarRight);
|
|
|
|
++m_numberTerminal;
|
|
|
|
connect(console,SIGNAL(finished()),this,SLOT(slotDeleteTerminal()));
|
|
|
|
QString name=tr("Term ")+QString::number(m_numberTerminal);
|
|
m_tabs->addTab(console,name);
|
|
}
|
|
|
|
void MainWindow::slotDeleteTerminal()
|
|
{
|
|
QWidget *widget = qobject_cast<QWidget *>(sender());
|
|
Q_ASSERT(widget);
|
|
m_tabs->removeTab(m_tabs->indexOf(widget));
|
|
delete widget;
|
|
}
|
|
|
|
int MainWindow::readEnvironmentValues()
|
|
{
|
|
// The return value
|
|
int ret=true;
|
|
|
|
// Get all environment variables
|
|
QStringList environmentlist=QProcess::systemEnvironment();
|
|
// This is the list of the important variables
|
|
QStringList variablelist=QString(ENVIRONMENT).split(",");
|
|
|
|
// This is an auxiliar variable
|
|
QStringList stringlist;
|
|
|
|
foreach (QString str,variablelist)
|
|
{
|
|
// Look for the variable in the environment
|
|
stringlist=environmentlist.filter(str+"=");
|
|
|
|
if(stringlist.isEmpty())
|
|
{
|
|
m_env[str]="";
|
|
ret=false;
|
|
}
|
|
else
|
|
{
|
|
// Get the first element and get the value part
|
|
m_env[str]=(stringlist.first().split("="))[1];
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Write a string to the log file
|
|
void MainWindow::write(QString s)
|
|
{
|
|
if(! s.endsWith("\n"))
|
|
s+="\n";
|
|
if(m_logstream)
|
|
{
|
|
*m_logstream<<CURRENT_TIME()<<": browser: "<<s;
|
|
m_logstream->flush();
|
|
}
|
|
}
|
|
|
|
// Print and log a string
|
|
void MainWindow::print(QString s)
|
|
{
|
|
if(! s.endsWith("\n"))
|
|
s+="\n";
|
|
write(s);
|
|
if(m_output)
|
|
m_output->insertPlainText(s);
|
|
}
|
|
|
|
// Show message in status bar
|
|
void MainWindow::captureOutputForStatusBar(QString output)
|
|
{
|
|
// Modify the status bar
|
|
output=output.trimmed();
|
|
// Get percentage (string starts with "[Number]")
|
|
QRegularExpression regexp(REGEXP_STRING);
|
|
QRegularExpressionMatch match = regexp.match(output);
|
|
|
|
if(match.hasMatch())
|
|
{
|
|
int pass=match.captured(1).toInt();
|
|
output.replace(regexp,"");
|
|
m_progressBar->setValue(pass);
|
|
m_progressBar->setFormat("%p%"+output);
|
|
}
|
|
}
|
|
|
|
// Init status bar
|
|
void MainWindow::startProgressBar()
|
|
{
|
|
QStatusBar* st=statusBar();
|
|
st->clearMessage();
|
|
st->addWidget(m_logo);
|
|
st->addWidget(m_progressBar,90);
|
|
st->addWidget(m_speedInfo,5);
|
|
st->addWidget(m_clock,5);
|
|
m_progressBar->show();
|
|
m_clock->show();
|
|
m_web->setEnabled(false);
|
|
}
|
|
|
|
// Reset status bar
|
|
void MainWindow::finishProgressBar()
|
|
{
|
|
m_progressBar->reset();
|
|
m_web->setEnabled(true);
|
|
}
|
|
|
|
|
|
// Returns communication speed
|
|
QString MainWindow::readSpeed() {
|
|
if(m_env.contains("OGLOGFILE"))
|
|
{
|
|
QString infoFile=m_env["OGLOGFILE"].replace(".log", ".info.html");
|
|
//QString command="grep -hoe \"[0-9]*Mb/s\" "+infoFile+" 2>/dev/null";
|
|
QProcess process;
|
|
process.start("grep", QStringList({"-hoe", "[0-9]*Mb/s", infoFile}));
|
|
process.waitForFinished();
|
|
QString speed(process.readAllStandardOutput());
|
|
return speed.simplified();
|
|
}
|
|
else
|
|
{
|
|
return QString("");
|
|
}
|
|
}
|
|
|
|
// Show an error box with timeout
|
|
void MainWindow::showErrorMessage(QString text)
|
|
{
|
|
QMessageBox* msgBox=new QMessageBox();
|
|
msgBox->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
|
|
msgBox->setWindowTitle(gettext("ERROR"));
|
|
msgBox->setIcon(QMessageBox::Warning);
|
|
msgBox->setText(text);
|
|
msgBox->show();
|
|
QTimer::singleShot(5000, msgBox, SLOT(close()));
|
|
}
|