#include "mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "qtermwidget.h" #include "KeyboardTranslator.h" #include "digitalclock.h" #include "ogurlhandler.h" #include "ogwebpage.h" #include "desktopparser.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("El fichero de log no ha podido ser abierto: %1.").arg(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("&Nueva Terminal")); button->setFocusPolicy(Qt::TabFocus); m_tabs->setCornerWidget(button); m_tabs->setFocusPolicy(Qt::NoFocus); m_tabs->addTab(m_output,tr("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("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 &)), 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"); auto &desktopParser = DesktopParser::getInstance(); QMap schemes = desktopParser.getSchemes(); for(const QString scheme : schemes.keys()) { if ( scheme == "http" || scheme == "https") continue; registerScheme(scheme); } ///////////////////////////////////////////////// // Register all handlers after the schemes ///////////////////////////////////////////////// for(const QString scheme : schemes.keys()) { if ( scheme == "http" || scheme == "https") continue; registerHandler(scheme, false, false, schemes[scheme]); } 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_web->setPage( new OGWebPage(this)); OGCommandLineOptions &options = OGCommandLineOptions::getInstance(); QUrl url = QUrl(options.getUrl()); m_webBar->setText(url.toString()); qInfo() << "Page set to" << url; m_web->load(url); // auto ktm = Konsole::KeyboardTranslatorManager::instance(); 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, const QString baseCommand) { OGBrowserUrlHandlerCommand *handler = new OGBrowserUrlHandlerCommand(this); connect(handler, &OGBrowserUrlHandlerCommand::command, this, &MainWindow::commandQueued); handler->setAskConfirmation(confirm); handler->setReturnOutput(returnOutput); handler->setBaseCommand(baseCommand); handler->setScheme(commandName); 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("AVISO")); msgBox.setIcon(QMessageBox::Question); msgBox.setTextFormat(Qt::RichText); msgBox.setText(tr("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("Ejecutar"), QMessageBox::ActionRole); msgBox.addButton(tr("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("Lanzando el comando: %1").arg(command)); m_output->setTextColor(QColor(Qt::black)); } else { write(tr("Lanzando el comando: %1").arg(command)); } m_command.process->start(program,list); startProgressBar(); } void MainWindow::slotWebLoadStarted() { startProgressBar(); m_progressBar->setFormat(tr("%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(sender()); // QVector::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("Fin del proceso. Valor de retorno: %1").arg(code)); } else { m_output->setTextColor(QColor(Qt::darkRed)); print("\n"+tr("El proceso ha fallado inesperadamente. Salida: %1").arg(code)); } m_output->setTextColor(QColor(Qt::black)); } else { // Non-admin user: show instruction to close the popup window write(tr("Fin del proceso. Valor de retorno: %1").arg(code)); m_output->setFontUnderline(true); print("\n\n"+tr("AVISO: Pulsar el botón superior derecho para cerrar (✖)")); m_output->setFontUnderline(false); } // On error, show a message box if(code > 0 && ! m_output->isActiveWindow()) { showErrorMessage(tr("Código de salida: %1").arg(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("Imposible lanzar el proceso."); break; case QProcess::WriteError: errorMsg=tr("Error de escritura en el proceso."); break; case QProcess::ReadError: errorMsg=tr("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("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 %1").arg(m_numberTerminal); m_tabs->addTab(console,name); } void MainWindow::slotDeleteTerminal() { QWidget *widget = qobject_cast(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<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("ERROR"); msgBox->setIcon(QMessageBox::Warning); msgBox->setText(text); msgBox->show(); QTimer::singleShot(5000, msgBox, SLOT(close())); }