source: ogBrowser-Git/src/mainwindow.cpp @ 21e8f4c

jenkinsmain
Last change on this file since 21e8f4c was 476581b, checked in by Vadim Troshchinskiy Shmelev <vtroshchinskiy@…>, 16 months ago

Add desktop file parser

  • Property mode set to 100644
File size: 17.4 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    // Ignore SSL errors.
131
132
133    // Qindel:
134    //connect(m_web->page()->networkAccessManager(),
135//            SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> &)), this,
136//            SLOT(slotSslErrors(QNetworkReply*)));
137
138
139    // Dock signals
140    connect(button,SIGNAL(clicked()),this,SLOT(slotCreateTerminal()));
141
142    // All schemes need registering first, then their handlers.
143    registerScheme("command");
144    registerScheme("command+output");
145    registerScheme("command+confirm");
146    registerScheme("command+confirm+output");
147    registerScheme("command+output+confirm");
148
149
150    auto &desktopParser = DesktopParser::getInstance();
151    QMap<QString, QString> schemes = desktopParser.getSchemes();
152
153    for(const QString scheme : schemes.keys()) {
154        if ( scheme == "http" || scheme == "https")
155            continue;
156
157        registerScheme(scheme);
158    }
159
160    /////////////////////////////////////////////////
161    // Register all handlers after the schemes
162    /////////////////////////////////////////////////
163
164    for(const QString scheme : schemes.keys()) {
165        if ( scheme == "http" || scheme == "https")
166            continue;
167
168        registerHandler(scheme, false, false, schemes[scheme]);
169    }
170
171
172
173    registerHandler("command", false, false);
174    registerHandler("command+output", false, true);
175    registerHandler("command+confirm", true, false);
176    registerHandler("command+confirm+output", true, true);
177    registerHandler("command+output+confirm", true, true);
178
179
180    //QStringList arguments=QCoreApplication::arguments();
181
182    m_web->setPage( new OGWebPage(this));
183
184    OGCommandLineOptions &options = OGCommandLineOptions::getInstance();
185    QUrl url = QUrl(options.getUrl());
186
187    m_webBar->setText(url.toString());
188
189    qInfo() << "Page set to" << url;
190    m_web->load(url);
191
192
193
194
195
196
197
198
199   // auto ktm = Konsole::KeyboardTranslatorManager::instance();
200
201
202
203    showMaximized();
204    showFullScreen();
205}
206
207void MainWindow::closeEvent(QCloseEvent *event) {
208    if (isKioskMode()) {
209        qInfo() << "Modo quiosco activado, ignorando intento de cerrar ventana";
210        event->ignore();
211    }
212}
213
214
215MainWindow::~MainWindow()
216{
217    if(m_logfile)
218    {
219        m_logfile->close();
220        delete m_logfile;
221    }
222    if(m_logstream)
223        delete m_logstream;
224}
225
226void MainWindow::registerScheme(const QString &name) {
227    QWebEngineUrlScheme scheme(name.toLatin1());
228    scheme.setSyntax(QWebEngineUrlScheme::Syntax::Path);
229    scheme.setDefaultPort(0);
230    scheme.setFlags(QWebEngineUrlScheme::LocalScheme);
231    QWebEngineUrlScheme::registerScheme(scheme);
232}
233
234
235void MainWindow::registerHandler(const QString &commandName, bool confirm, bool returnOutput, const QString baseCommand) {
236    OGBrowserUrlHandlerCommand *handler = new OGBrowserUrlHandlerCommand(this);
237    connect(handler, &OGBrowserUrlHandlerCommand::command, this, &MainWindow::commandQueued);
238    handler->setAskConfirmation(confirm);
239    handler->setReturnOutput(returnOutput);
240    handler->setBaseCommand(baseCommand);
241    handler->setScheme(commandName);
242    QWebEngineProfile::defaultProfile()->installUrlSchemeHandler(commandName.toLatin1(), handler);
243}
244
245void MainWindow::commandQueued(const QString &command, bool confirm, bool returnOutput) {
246    //PendingCommand cmd;
247
248    qInfo() << "Queued command:" << command;
249
250    if (confirm) {
251        QMessageBox msgBox;
252        msgBox.setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
253        msgBox.setWindowTitle(tr("AVISO"));
254        msgBox.setIcon(QMessageBox::Question);
255        msgBox.setTextFormat(Qt::RichText);
256        msgBox.setText(tr("La siguiente acción puede modificar datos o tardar varios minutos. El equipo no podrá ser utilizado durante su ejecución."));
257        QPushButton *execButton = msgBox.addButton(tr("Ejecutar"), QMessageBox::ActionRole);
258        msgBox.addButton(tr("Cancelar"), QMessageBox::RejectRole);
259        msgBox.setDefaultButton(execButton);
260        msgBox.exec();
261
262        if (msgBox.clickedButton() != execButton) {
263            qInfo() << "User rejected running the command";
264            return;
265        }
266    }
267
268    if (returnOutput && !isAdmin()) {
269        int w=MainWindow::width(), h=MainWindow::height();
270        m_output->setWindowFlags(Qt::Window);
271        m_output->move(100, 100);
272        m_output->setFixedSize(w*0.8-100, h*0.8-100);
273        m_output->show();
274    }
275
276
277    m_command.command = command;
278    m_command.confirm = confirm;
279    m_command.returnOutput = returnOutput;
280
281
282
283
284    QStringList list=command.split(" ",Qt::SkipEmptyParts);
285    QString program=list.takeFirst();
286
287    m_command.process = new QProcess(this);
288    m_command.process->setReadChannel(QProcess::StandardOutput);
289    m_command.process->setEnvironment(QProcess::systemEnvironment());
290
291    // Process signals
292    connect(m_command.process, &QProcess::started,this, &MainWindow::slotProcessStarted);
293    connect(m_command.process, &QProcess::finished,this,&MainWindow::slotProcessFinished);
294    connect(m_command.process, &QProcess::errorOccurred, this,&MainWindow::slotProcessError);
295    connect(m_command.process, &QProcess::readyReadStandardOutput,this,&MainWindow::slotProcessOutput);
296    connect(m_command.process, &QProcess::readyReadStandardError,this,&MainWindow::slotProcessErrorOutput);
297
298
299    if(isAdmin()) {
300        m_output->setTextColor(QColor(Qt::darkGreen));
301        print(tr("Lanzando el comando: %1").arg(command));
302        m_output->setTextColor(QColor(Qt::black));
303    } else {
304        write(tr("Lanzando el comando: %1").arg(command));
305    }
306
307    m_command.process->start(program,list);
308    startProgressBar();
309
310}
311
312void MainWindow::slotWebLoadStarted()
313{
314    startProgressBar();
315    m_progressBar->setFormat(tr("%p% Cargando"));
316}
317
318void MainWindow::slotWebLoadProgress(int progress)
319{
320    m_progressBar->setValue(progress);
321}
322
323void MainWindow::slotWebLoadFinished(bool ok)
324{
325    // If any error ocurred, show a pop up
326    // Sometimes when the url hasn't got a dot, i.e /var/www/pageweb,
327    // the return value is always true so we check the bytes received too
328    qWarning() << "Load finished. URL: " << m_web->url() << "; ok = " << ok;
329
330    finishProgressBar();
331
332}
333
334void MainWindow::slotUrlChanged(const QUrl &url)
335{
336    m_webBar->setText(url.toString());
337}
338
339void MainWindow::slotSslErrors(QNetworkReply* reply)
340{
341    reply->ignoreSslErrors();
342}
343
344void MainWindow::slotProcessStarted()
345{
346    startProgressBar();
347}
348
349void MainWindow::slotProcessOutput()
350{
351    m_command.process->setReadChannel(QProcess::StandardOutput);
352    char buf[BUFFERSIZE];
353    while((m_command.process->readLine(buf,BUFFERSIZE) > 0))
354    {
355        QString s(buf);
356        qInfo() << "OUT: " << buf;
357
358        if(isAdmin())
359        {
360            m_output->insertPlainText(tr("Proc. stdout: "));
361        }
362        print(s);
363        captureOutputForStatusBar(s);
364    }
365}
366
367void MainWindow::slotProcessErrorOutput()
368{
369
370   // QProcess *process = qobject_cast<QProcess*>(sender());
371   // QVector<PendingCommand>::iterator it=std::find(m_commands.begin(), m_commands.end(), []())
372
373    m_command.process->setReadChannel(QProcess::StandardError);
374    char buf[BUFFERSIZE];
375    while((m_command.process->readLine(buf,BUFFERSIZE) > 0))
376    {
377        QString s(buf);
378        qInfo() << "ERR: " << buf;
379
380        if(isAdmin())
381        {
382            m_output->insertPlainText(tr("Proc. stderr: "));
383        }
384        m_output->setTextColor(QColor(Qt::darkBlue));
385        print(s);
386        m_output->setTextColor(QColor(Qt::black));
387    }
388}
389
390void MainWindow::slotProcessFinished(int code, QProcess::ExitStatus status)
391{
392
393    qInfo() << "Finished: " << m_command.command << "with status" << status;
394
395    if(isAdmin())
396    {
397        // Admin user: show process status
398        if(status==QProcess::NormalExit)
399        {
400            if(code > 0)
401            {
402                m_output->setTextColor(QColor(Qt::darkRed));
403            }
404            print("\n"+tr("Fin del proceso. Valor de retorno: %1").arg(code));
405        }
406        else
407        {
408            m_output->setTextColor(QColor(Qt::darkRed));
409            print("\n"+tr("El proceso ha fallado inesperadamente. Salida: %1").arg(code));
410        }
411        m_output->setTextColor(QColor(Qt::black));
412    }
413    else
414    {
415        // Non-admin user: show instruction to close the popup window
416        write(tr("Fin del proceso. Valor de retorno: %1").arg(code));
417        m_output->setFontUnderline(true);
418        print("\n\n"+tr("AVISO: Pulsar el botón superior derecho para cerrar (✖)"));
419        m_output->setFontUnderline(false);
420    }
421    // On error, show a message box
422    if(code > 0 && ! m_output->isActiveWindow())
423    {
424        showErrorMessage(tr("Código de salida: %1").arg(code));
425    }
426    finishProgressBar();
427}
428
429void MainWindow::slotProcessError(QProcess::ProcessError error)
430{
431    qCritical() << "Error: " << m_command.command << "with status" << error;
432
433    QString errorMsg;
434    switch(error)
435    {
436        case QProcess::FailedToStart:
437            errorMsg=tr("Imposible lanzar el proceso.");
438            break;
439        case QProcess::WriteError:
440            errorMsg=tr("Error de escritura en el proceso.");
441            break;
442        case QProcess::ReadError:
443            errorMsg=tr("Error de lectura del proceso.");
444            break;
445        // No capturo crashed porque la pillo por finished
446        case QProcess::Crashed:
447        case QProcess::Timedout:
448            break;
449        case QProcess::UnknownError:
450        default:
451            errorMsg=tr("Error desconocido.");
452            break;
453    }
454    // Print error and show message box with timeout.
455    if(!errorMsg.isNull()) {
456        m_output->setTextColor(QColor(Qt::darkRed));
457        print(errorMsg);
458        m_output->setTextColor(QColor(Qt::black));
459        showErrorMessage(errorMsg);
460    }
461    finishProgressBar();
462}
463
464void MainWindow::slotCreateTerminal()
465{
466    QTermWidget* console = new QTermWidget(1,this);
467    QFont font = QApplication::font();
468    font.setFamily("DejaVu Sans Mono");
469    font.setPointSize(12);
470
471    console->setTerminalFont(font);
472    console->setFocusPolicy(Qt::StrongFocus);
473    console->setScrollBarPosition(QTermWidget::ScrollBarRight);
474
475    ++m_numberTerminal;
476
477    connect(console,SIGNAL(finished()),this,SLOT(slotDeleteTerminal()));
478
479    QString name=tr("Term %1").arg(m_numberTerminal);
480    m_tabs->addTab(console,name);
481}
482
483void MainWindow::slotDeleteTerminal()
484{
485    QWidget *widget = qobject_cast<QWidget *>(sender());
486    Q_ASSERT(widget);
487    m_tabs->removeTab(m_tabs->indexOf(widget));
488    delete widget;
489}
490
491int MainWindow::readEnvironmentValues()
492{
493    // The return value
494    int ret=true;
495
496    // Get all environment variables
497    QStringList environmentlist=QProcess::systemEnvironment();
498    // This is the list of the important variables
499    QStringList variablelist=QString(ENVIRONMENT).split(",");
500
501    // This is an auxiliar variable
502    QStringList stringlist;
503
504    foreach (QString str,variablelist)
505    {
506        // Look for the variable in the environment
507        stringlist=environmentlist.filter(str+"=");
508
509        if(stringlist.isEmpty())
510        {
511            m_env[str]="";
512            ret=false;
513        }
514        else
515        {
516            // Get the first element and get the value part
517            m_env[str]=(stringlist.first().split("="))[1];
518        }
519    }
520
521    return ret;
522}
523
524// Write a string to the log file
525void MainWindow::write(QString s)
526{
527    if(! s.endsWith("\n"))
528        s+="\n";
529    if(m_logstream)
530    {
531        *m_logstream<<CURRENT_TIME()<<": browser: "<<s;
532        m_logstream->flush();
533    }
534}
535
536// Print and log a string
537void MainWindow::print(QString s)
538{
539    if(! s.endsWith("\n"))
540        s+="\n";
541    write(s);
542    if(m_output)
543        m_output->insertPlainText(s);
544}
545
546// Show message in status bar
547void MainWindow::captureOutputForStatusBar(QString output)
548{
549    // Modify the status bar
550    output=output.trimmed();
551    // Get percentage (string starts with "[Number]")
552    QRegularExpression regexp(REGEXP_STRING);
553    QRegularExpressionMatch match = regexp.match(output);
554
555    if(match.hasMatch())
556    {
557        int pass=match.captured(1).toInt();
558        output.replace(regexp,"");
559        m_progressBar->setValue(pass);
560        m_progressBar->setFormat("%p%"+output);
561    }
562}
563
564// Init status bar
565void MainWindow::startProgressBar()
566{
567    QStatusBar* st=statusBar();
568    st->clearMessage();
569    st->addWidget(m_logo);
570    st->addWidget(m_progressBar,90);
571    st->addWidget(m_speedInfo,5);
572    st->addWidget(m_clock,5);
573    m_progressBar->show();
574    m_clock->show();
575    m_web->setEnabled(false);
576}
577
578// Reset status bar
579void MainWindow::finishProgressBar()
580{
581    m_progressBar->reset();
582    m_web->setEnabled(true);
583}
584
585
586// Returns communication speed
587QString MainWindow::readSpeed() {
588    if(m_env.contains("OGLOGFILE"))
589    {
590        QString infoFile=m_env["OGLOGFILE"].replace(".log", ".info.html");
591        //QString command="grep -hoe \"[0-9]*Mb/s\" "+infoFile+" 2>/dev/null";
592        QProcess process;
593        process.start("grep", QStringList({"-hoe", "[0-9]*Mb/s", infoFile}));
594        process.waitForFinished();
595        QString speed(process.readAllStandardOutput());
596        return speed.simplified();
597    }
598    else
599    {
600        return QString("");
601    }
602}
603
604// Show an error box with timeout
605void MainWindow::showErrorMessage(QString text)
606{
607    QMessageBox* msgBox=new QMessageBox();
608    msgBox->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
609    msgBox->setWindowTitle("ERROR");
610    msgBox->setIcon(QMessageBox::Warning);
611    msgBox->setText(text);
612    msgBox->show();
613    QTimer::singleShot(5000, msgBox, SLOT(close()));
614}
Note: See TracBrowser for help on using the repository browser.