source: ogBrowser-Git/src/mainwindow.cpp @ cc939f2

jenkinsmain
Last change on this file since cc939f2 was 068539c, checked in by Vadim Troshchinskiy Shmelev <vtroshchinskiy@…>, 2 months ago

Add ability to ignore SSL errors

  • Property mode set to 100644
File size: 17.9 KB
Line 
1#include "mainwindow.h"
2#include <QtWebEngineWidgets>
3#include <QStringList>
4#include <QWebEngineView>
5#include <QDockWidget>
6#include <QtDebug>
7#include <QWebEnginePage>
8#include <QProcess>
9#include <QTextEdit>
10#include <QMessageBox>
11#include <QPushButton>
12#include <QDateTime>
13#include <QProgressBar>
14#include <QTabWidget>
15#include <QLineEdit>
16#include <QNetworkReply>
17#include <QSslError>
18#include <QTimer>
19#include <QRegularExpression>
20#include <QStringList>
21#include <QString>
22
23#include "qtermwidget.h"
24#include "KeyboardTranslator.h"
25
26#include "digitalclock.h"
27#include "ogurlhandler.h"
28#include "ogwebpage.h"
29#include "desktopparser.h"
30
31#define BUFFERSIZE 2048
32#define REGEXP_STRING "^\\[(\\d+)\\]"
33
34#define CURRENT_TIME() QDateTime::currentDateTime().toString("dd/MM/yyyy hh:mm:ss")
35
36MainWindow::MainWindow(QWidget *parent)
37    : QMainWindow(parent),m_web(new QWebEngineView()),m_output(new QTextEdit()),
38      m_logfile(0),m_logstream(0),m_numberTerminal(0)
39{
40    // Graphic
41    setWindowTitle(tr("OpenGnsys Browser"));
42    setCentralWidget(m_web);
43    readEnvironmentValues();
44
45    m_is_admin = qgetenv("ogactiveadmin") == "true";
46    m_kiosk_mode = qgetenv("OGKIOSKMODE") == "true";
47
48    // Open the log file for append
49    if(m_env.contains("OGLOGFILE") && m_env["OGLOGFILE"]!="")
50    {
51        QFile* m_logfile=new QFile(m_env["OGLOGFILE"]);
52        if(!m_logfile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append))
53        {
54            delete m_logfile;
55            print(tr("El fichero de log no ha podido ser abierto: %1.").arg(m_env["OGLOGFILE"]));
56        }
57        else
58        {
59            m_logstream=new QTextStream(m_logfile);
60        }
61    }
62
63    // Output
64    m_output->setReadOnly(true);
65    m_output->setFontPointSize(16);
66
67    // Button Dock
68    QDockWidget* dock=new QDockWidget();
69    dock->setAllowedAreas(Qt::BottomDockWidgetArea);
70    QWidget* dummy=new QWidget();
71    dummy->setMaximumHeight(0);
72    dock->setTitleBarWidget(dummy);
73
74    // TabWidget
75    m_tabs=new QTabWidget(dock);
76    QPushButton *button=new QPushButton(tr("&Nueva Terminal"));
77    button->setFocusPolicy(Qt::TabFocus);
78    m_tabs->setCornerWidget(button);
79    m_tabs->setFocusPolicy(Qt::NoFocus);
80    m_tabs->addTab(m_output,tr("Salida"));
81    slotCreateTerminal();
82    // Assign tabs to dock
83    dock->setWidget(m_tabs);
84    // Assign tabs dock to the mainwindow if admin mode is active
85    if(isAdmin())
86        addDockWidget(Qt::BottomDockWidgetArea,dock);
87
88    // Top Dock
89    dock=new QDockWidget();
90    dock->setAllowedAreas(Qt::TopDockWidgetArea);
91    QWidget* dummy2=new QWidget();
92    dummy2->setMaximumHeight(0);
93    dock->setTitleBarWidget(dummy2);
94    // WebBar
95    m_webBar=new QLineEdit(dock);
96    // WebBar to dock
97    dock->setWidget(m_webBar);
98    // Assign top dock to the mainwindow if admin mode is active
99    if(isAdmin())
100        addDockWidget(Qt::TopDockWidgetArea,dock);
101
102    // Status bar
103    QStatusBar* st=statusBar();
104    st->setSizeGripEnabled(false);
105    // OpenGnsys logo (or alternate text)
106    m_logo=new QLabel();
107    QPixmap logo;
108    if(logo.load("/opt/opengnsys/lib/pictures/oglogo.png"))
109        m_logo->setPixmap(logo);
110    else
111        m_logo->setText("OG");
112    m_logo->setToolTip(tr("Proyecto OpenGnsys\nhttps://opengnsys.es"));
113    // Progress bar
114    m_progressBar=new QProgressBar(this);
115    m_progressBar->setRange(0,100);
116    // Connection speed
117    QString speed=readSpeed();
118    m_speedInfo=new QLabel(speed);
119    m_speedInfo->setAlignment(Qt::AlignCenter);
120    if(m_env.contains("DEFAULTSPEED") && m_env["DEFAULTSPEED"]!="")
121        if(speed.compare(m_env["DEFAULTSPEED"])!=0)
122            m_speedInfo->setStyleSheet("background-color: darkred; color: white; font-weight: bold;");
123    // Clock
124    m_clock=new DigitalClock(this);
125
126    connect(m_web,SIGNAL(loadStarted()),this,SLOT(slotWebLoadStarted()));
127    connect(m_web,SIGNAL(loadFinished(bool)),this,SLOT(slotWebLoadFinished(bool)));
128    connect(m_web,SIGNAL(loadProgress(int)),this,SLOT(slotWebLoadProgress(int)));
129    connect(m_web,SIGNAL(urlChanged(const QUrl&)),this,SLOT(slotUrlChanged(const QUrl&)));
130
131    // Ignore SSL errors.
132
133
134    // Qindel:
135    //connect(m_web->page()->networkAccessManager(),
136//            SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> &)), this,
137//            SLOT(slotSslErrors(QNetworkReply*)));
138
139
140    // Dock signals
141    connect(button,SIGNAL(clicked()),this,SLOT(slotCreateTerminal()));
142
143    // All schemes need registering first, then their handlers.
144    registerScheme("command");
145    registerScheme("command+output");
146    registerScheme("command+confirm");
147    registerScheme("command+confirm+output");
148    registerScheme("command+output+confirm");
149
150
151    auto &desktopParser = DesktopParser::getInstance();
152    QMap<QString, QString> schemes = desktopParser.getSchemes();
153
154    for(const QString scheme : schemes.keys()) {
155        if ( scheme == "http" || scheme == "https")
156            continue;
157
158        registerScheme(scheme);
159    }
160
161    /////////////////////////////////////////////////
162    // Register all handlers after the schemes
163    /////////////////////////////////////////////////
164
165    for(const QString scheme : schemes.keys()) {
166        if ( scheme == "http" || scheme == "https")
167            continue;
168
169        registerHandler(scheme, false, false, schemes[scheme]);
170    }
171
172
173
174    registerHandler("command", false, false);
175    registerHandler("command+output", false, true);
176    registerHandler("command+confirm", true, false);
177    registerHandler("command+confirm+output", true, true);
178    registerHandler("command+output+confirm", true, true);
179
180
181    //QStringList arguments=QCoreApplication::arguments();
182
183    m_web->setPage( new OGWebPage(this));
184    connect(m_web->page(), &QWebEnginePage::certificateError, this, &MainWindow::slotCertificateErrors);
185
186
187
188    OGCommandLineOptions &options = OGCommandLineOptions::getInstance();
189    QUrl url = QUrl(options.getUrl());
190
191    m_webBar->setText(url.toString());
192
193    qInfo() << "Page set to" << url;
194    m_web->load(url);
195
196
197
198
199
200
201
202
203   // auto ktm = Konsole::KeyboardTranslatorManager::instance();
204
205
206
207    showMaximized();
208    showFullScreen();
209}
210
211void MainWindow::closeEvent(QCloseEvent *event) {
212    if (isKioskMode()) {
213        qInfo() << "Modo quiosco activado, ignorando intento de cerrar ventana";
214        event->ignore();
215    }
216}
217
218
219MainWindow::~MainWindow()
220{
221    if(m_logfile)
222    {
223        m_logfile->close();
224        delete m_logfile;
225    }
226    if(m_logstream)
227        delete m_logstream;
228}
229
230void MainWindow::registerScheme(const QString &name) {
231    QWebEngineUrlScheme scheme(name.toLatin1());
232    scheme.setSyntax(QWebEngineUrlScheme::Syntax::Path);
233    scheme.setDefaultPort(0);
234    scheme.setFlags(QWebEngineUrlScheme::LocalScheme);
235    QWebEngineUrlScheme::registerScheme(scheme);
236}
237
238
239void MainWindow::registerHandler(const QString &commandName, bool confirm, bool returnOutput, const QString baseCommand) {
240    OGBrowserUrlHandlerCommand *handler = new OGBrowserUrlHandlerCommand(this);
241    connect(handler, &OGBrowserUrlHandlerCommand::command, this, &MainWindow::commandQueued);
242    handler->setAskConfirmation(confirm);
243    handler->setReturnOutput(returnOutput);
244    handler->setBaseCommand(baseCommand);
245    handler->setScheme(commandName);
246    QWebEngineProfile::defaultProfile()->installUrlSchemeHandler(commandName.toLatin1(), handler);
247}
248
249void MainWindow::commandQueued(const QString &command, bool confirm, bool returnOutput) {
250    //PendingCommand cmd;
251
252    qInfo() << "Queued command:" << command;
253
254    if (confirm) {
255        QMessageBox msgBox;
256        msgBox.setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
257        msgBox.setWindowTitle(tr("AVISO"));
258        msgBox.setIcon(QMessageBox::Question);
259        msgBox.setTextFormat(Qt::RichText);
260        msgBox.setText(tr("La siguiente acción puede modificar datos o tardar varios minutos. El equipo no podrá ser utilizado durante su ejecución."));
261        QPushButton *execButton = msgBox.addButton(tr("Ejecutar"), QMessageBox::ActionRole);
262        msgBox.addButton(tr("Cancelar"), QMessageBox::RejectRole);
263        msgBox.setDefaultButton(execButton);
264        msgBox.exec();
265
266        if (msgBox.clickedButton() != execButton) {
267            qInfo() << "User rejected running the command";
268            return;
269        }
270    }
271
272    if (returnOutput && !isAdmin()) {
273        int w=MainWindow::width(), h=MainWindow::height();
274        m_output->setWindowFlags(Qt::Window);
275        m_output->move(100, 100);
276        m_output->setFixedSize(w*0.8-100, h*0.8-100);
277        m_output->show();
278    }
279
280
281    m_command.command = command;
282    m_command.confirm = confirm;
283    m_command.returnOutput = returnOutput;
284
285
286
287
288    QStringList list=command.split(" ",Qt::SkipEmptyParts);
289    QString program=list.takeFirst();
290
291    m_command.process = new QProcess(this);
292    m_command.process->setReadChannel(QProcess::StandardOutput);
293    m_command.process->setEnvironment(QProcess::systemEnvironment());
294
295    // Process signals
296    connect(m_command.process, &QProcess::started,this, &MainWindow::slotProcessStarted);
297    connect(m_command.process, &QProcess::finished,this,&MainWindow::slotProcessFinished);
298    connect(m_command.process, &QProcess::errorOccurred, this,&MainWindow::slotProcessError);
299    connect(m_command.process, &QProcess::readyReadStandardOutput,this,&MainWindow::slotProcessOutput);
300    connect(m_command.process, &QProcess::readyReadStandardError,this,&MainWindow::slotProcessErrorOutput);
301
302
303    if(isAdmin()) {
304        m_output->setTextColor(QColor(Qt::darkGreen));
305        print(tr("Lanzando el comando: %1").arg(command));
306        m_output->setTextColor(QColor(Qt::black));
307    } else {
308        write(tr("Lanzando el comando: %1").arg(command));
309    }
310
311    m_command.process->start(program,list);
312    startProgressBar();
313
314}
315
316void MainWindow::slotWebLoadStarted()
317{
318    startProgressBar();
319    m_progressBar->setFormat(tr("%p% Cargando"));
320}
321
322void MainWindow::slotWebLoadProgress(int progress)
323{
324    m_progressBar->setValue(progress);
325}
326
327void MainWindow::slotWebLoadFinished(bool ok)
328{
329    // If any error ocurred, show a pop up
330    // Sometimes when the url hasn't got a dot, i.e /var/www/pageweb,
331    // the return value is always true so we check the bytes received too
332    qWarning() << "Load finished. URL: " << m_web->url() << "; ok = " << ok;
333
334    finishProgressBar();
335
336}
337
338void MainWindow::slotUrlChanged(const QUrl &url)
339{
340    m_webBar->setText(url.toString());
341}
342
343void MainWindow::slotSslErrors(QNetworkReply* reply)
344{
345    reply->ignoreSslErrors();
346}
347
348void MainWindow::slotCertificateErrors(const QWebEngineCertificateError &certificateError) {
349    qWarning() << "SSL error:" << certificateError.description();
350
351    OGCommandLineOptions &options = OGCommandLineOptions::getInstance();
352    if (options.getIgnoreSslErrors()) {
353        qInfo() << "Ignoring SSL errors";
354
355        auto mutableError = const_cast<QWebEngineCertificateError&>(certificateError);
356        mutableError.acceptCertificate();
357    }
358}
359
360void MainWindow::slotProcessStarted()
361{
362    startProgressBar();
363}
364
365void MainWindow::slotProcessOutput()
366{
367    m_command.process->setReadChannel(QProcess::StandardOutput);
368    char buf[BUFFERSIZE];
369    while((m_command.process->readLine(buf,BUFFERSIZE) > 0))
370    {
371        QString s(buf);
372        qInfo() << "OUT: " << buf;
373
374        if(isAdmin())
375        {
376            m_output->insertPlainText(tr("Proc. stdout: "));
377        }
378        print(s);
379        captureOutputForStatusBar(s);
380    }
381}
382
383void MainWindow::slotProcessErrorOutput()
384{
385
386   // QProcess *process = qobject_cast<QProcess*>(sender());
387   // QVector<PendingCommand>::iterator it=std::find(m_commands.begin(), m_commands.end(), []())
388
389    m_command.process->setReadChannel(QProcess::StandardError);
390    char buf[BUFFERSIZE];
391    while((m_command.process->readLine(buf,BUFFERSIZE) > 0))
392    {
393        QString s(buf);
394        qInfo() << "ERR: " << buf;
395
396        if(isAdmin())
397        {
398            m_output->insertPlainText(tr("Proc. stderr: "));
399        }
400        m_output->setTextColor(QColor(Qt::darkBlue));
401        print(s);
402        m_output->setTextColor(QColor(Qt::black));
403    }
404}
405
406void MainWindow::slotProcessFinished(int code, QProcess::ExitStatus status)
407{
408
409    qInfo() << "Finished: " << m_command.command << "with status" << status;
410
411    if(isAdmin())
412    {
413        // Admin user: show process status
414        if(status==QProcess::NormalExit)
415        {
416            if(code > 0)
417            {
418                m_output->setTextColor(QColor(Qt::darkRed));
419            }
420            print("\n"+tr("Fin del proceso. Valor de retorno: %1").arg(code));
421        }
422        else
423        {
424            m_output->setTextColor(QColor(Qt::darkRed));
425            print("\n"+tr("El proceso ha fallado inesperadamente. Salida: %1").arg(code));
426        }
427        m_output->setTextColor(QColor(Qt::black));
428    }
429    else
430    {
431        // Non-admin user: show instruction to close the popup window
432        write(tr("Fin del proceso. Valor de retorno: %1").arg(code));
433        m_output->setFontUnderline(true);
434        print("\n\n"+tr("AVISO: Pulsar el botón superior derecho para cerrar (✖)"));
435        m_output->setFontUnderline(false);
436    }
437    // On error, show a message box
438    if(code > 0 && ! m_output->isActiveWindow())
439    {
440        showErrorMessage(tr("Código de salida: %1").arg(code));
441    }
442    finishProgressBar();
443}
444
445void MainWindow::slotProcessError(QProcess::ProcessError error)
446{
447    qCritical() << "Error: " << m_command.command << "with status" << error;
448
449    QString errorMsg;
450    switch(error)
451    {
452        case QProcess::FailedToStart:
453            errorMsg=tr("Imposible lanzar el proceso.");
454            break;
455        case QProcess::WriteError:
456            errorMsg=tr("Error de escritura en el proceso.");
457            break;
458        case QProcess::ReadError:
459            errorMsg=tr("Error de lectura del proceso.");
460            break;
461        // No capturo crashed porque la pillo por finished
462        case QProcess::Crashed:
463        case QProcess::Timedout:
464            break;
465        case QProcess::UnknownError:
466        default:
467            errorMsg=tr("Error desconocido.");
468            break;
469    }
470    // Print error and show message box with timeout.
471    if(!errorMsg.isNull()) {
472        m_output->setTextColor(QColor(Qt::darkRed));
473        print(errorMsg);
474        m_output->setTextColor(QColor(Qt::black));
475        showErrorMessage(errorMsg);
476    }
477    finishProgressBar();
478}
479
480void MainWindow::slotCreateTerminal()
481{
482    QTermWidget* console = new QTermWidget(1,this);
483    QFont font = QApplication::font();
484    font.setFamily("DejaVu Sans Mono");
485    font.setPointSize(12);
486
487    console->setTerminalFont(font);
488    console->setFocusPolicy(Qt::StrongFocus);
489    console->setScrollBarPosition(QTermWidget::ScrollBarRight);
490
491    ++m_numberTerminal;
492
493    connect(console,SIGNAL(finished()),this,SLOT(slotDeleteTerminal()));
494
495    QString name=tr("Term %1").arg(m_numberTerminal);
496    m_tabs->addTab(console,name);
497}
498
499void MainWindow::slotDeleteTerminal()
500{
501    QWidget *widget = qobject_cast<QWidget *>(sender());
502    Q_ASSERT(widget);
503    m_tabs->removeTab(m_tabs->indexOf(widget));
504    delete widget;
505}
506
507int MainWindow::readEnvironmentValues()
508{
509    // The return value
510    int ret=true;
511
512    // Get all environment variables
513    QStringList environmentlist=QProcess::systemEnvironment();
514    // This is the list of the important variables
515    QStringList variablelist=QString(ENVIRONMENT).split(",");
516
517    // This is an auxiliar variable
518    QStringList stringlist;
519
520    foreach (QString str,variablelist)
521    {
522        // Look for the variable in the environment
523        stringlist=environmentlist.filter(str+"=");
524
525        if(stringlist.isEmpty())
526        {
527            m_env[str]="";
528            ret=false;
529        }
530        else
531        {
532            // Get the first element and get the value part
533            m_env[str]=(stringlist.first().split("="))[1];
534        }
535    }
536
537    return ret;
538}
539
540// Write a string to the log file
541void MainWindow::write(QString s)
542{
543    if(! s.endsWith("\n"))
544        s+="\n";
545    if(m_logstream)
546    {
547        *m_logstream<<CURRENT_TIME()<<": browser: "<<s;
548        m_logstream->flush();
549    }
550}
551
552// Print and log a string
553void MainWindow::print(QString s)
554{
555    if(! s.endsWith("\n"))
556        s+="\n";
557    write(s);
558    if(m_output)
559        m_output->insertPlainText(s);
560}
561
562// Show message in status bar
563void MainWindow::captureOutputForStatusBar(QString output)
564{
565    // Modify the status bar
566    output=output.trimmed();
567    // Get percentage (string starts with "[Number]")
568    QRegularExpression regexp(REGEXP_STRING);
569    QRegularExpressionMatch match = regexp.match(output);
570
571    if(match.hasMatch())
572    {
573        int pass=match.captured(1).toInt();
574        output.replace(regexp,"");
575        m_progressBar->setValue(pass);
576        m_progressBar->setFormat("%p%"+output);
577    }
578}
579
580// Init status bar
581void MainWindow::startProgressBar()
582{
583    QStatusBar* st=statusBar();
584    st->clearMessage();
585    st->addWidget(m_logo);
586    st->addWidget(m_progressBar,90);
587    st->addWidget(m_speedInfo,5);
588    st->addWidget(m_clock,5);
589    m_progressBar->show();
590    m_clock->show();
591    m_web->setEnabled(false);
592}
593
594// Reset status bar
595void MainWindow::finishProgressBar()
596{
597    m_progressBar->reset();
598    m_web->setEnabled(true);
599}
600
601
602// Returns communication speed
603QString MainWindow::readSpeed() {
604    if(m_env.contains("OGLOGFILE"))
605    {
606        QString infoFile=m_env["OGLOGFILE"].replace(".log", ".info.html");
607        //QString command="grep -hoe \"[0-9]*Mb/s\" "+infoFile+" 2>/dev/null";
608        QProcess process;
609        process.start("grep", QStringList({"-hoe", "[0-9]*Mb/s", infoFile}));
610        process.waitForFinished();
611        QString speed(process.readAllStandardOutput());
612        return speed.simplified();
613    }
614    else
615    {
616        return QString("");
617    }
618}
619
620// Show an error box with timeout
621void MainWindow::showErrorMessage(QString text)
622{
623    QMessageBox* msgBox=new QMessageBox();
624    msgBox->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
625    msgBox->setWindowTitle("ERROR");
626    msgBox->setIcon(QMessageBox::Warning);
627    msgBox->setText(text);
628    msgBox->show();
629    QTimer::singleShot(5000, msgBox, SLOT(close()));
630}
Note: See TracBrowser for help on using the repository browser.