source: ogBrowser-Git/src/mainwindow.cpp

qndtest
Last change on this file was 59c8a0f, checked in by Ramón M. Gómez <ramongomez@…>, 5 years ago

#919: Fix bug with quotation marks in constants.

  • Property mode set to 100644
File size: 17.4 KB
Line 
1#include "mainwindow.h"
2#include <QtWebKit>
3#include <QStringList>
4#include <QWebView>
5#include <QDockWidget>
6#include <QtDebug>
7#include <QWebPage>
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 <libintl.h>
20
21#include "qtermwidget.h"
22#include "digitalclock.h"
23
24#define BUFFERSIZE 2048
25#define REGEXP_STRING "^\\[(\\d+)\\]"
26
27#define CURRENT_TIME() QDateTime::currentDateTime().toString("dd/MM/yyyy hh:mm:ss")
28
29MainWindow::MainWindow(QWidget *parent)
30    : QMainWindow(parent),m_web(new QWebView()),m_output(new QTextEdit()),
31      m_process(new QProcess(this)),
32      m_logfile(0),m_logstream(0),m_numberTerminal(0)
33{
34    // Graphic
35    showFullScreen();
36    setWindowTitle(tr("OpenGnsys Browser"));
37    setCentralWidget(m_web);
38    readEnvironmentValues();
39
40    // Open the log file for append
41    if(m_env.contains("OGLOGFILE") && m_env["OGLOGFILE"]!="")
42    {
43        QFile* m_logfile=new QFile(m_env["OGLOGFILE"]);
44        if(!m_logfile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append))
45        {
46            delete m_logfile;
47            print(tr(gettext("El fichero de log no ha podido ser abierto: "))+m_env["OGLOGFILE"]+".");
48        }
49        else
50        {
51            m_logstream=new QTextStream(m_logfile);
52        }
53    }
54
55    // Output
56    m_output->setReadOnly(true);
57    m_output->setFontPointSize(16);
58
59    // Button Dock
60    QDockWidget* dock=new QDockWidget();
61    dock->setAllowedAreas(Qt::BottomDockWidgetArea);
62    QWidget* dummy=new QWidget();
63    dummy->setMaximumHeight(0);
64    dock->setTitleBarWidget(dummy);
65
66    // TabWidget
67    m_tabs=new QTabWidget(dock);
68    QPushButton *button=new QPushButton(tr(gettext("&Nueva Terminal")));
69    button->setFocusPolicy(Qt::TabFocus);
70    m_tabs->setCornerWidget(button);
71    m_tabs->setFocusPolicy(Qt::NoFocus);
72    m_tabs->addTab(m_output,tr(gettext("Salida")));
73    slotCreateTerminal();
74    // Assign tabs to dock
75    dock->setWidget(m_tabs);
76    // Assign tabs dock to the mainwindow if admin mode is active
77    if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
78        addDockWidget(Qt::BottomDockWidgetArea,dock);
79
80    // Top Dock
81    dock=new QDockWidget();
82    dock->setAllowedAreas(Qt::TopDockWidgetArea);
83    QWidget* dummy2=new QWidget();
84    dummy2->setMaximumHeight(0);
85    dock->setTitleBarWidget(dummy2);
86    // WebBar
87    m_webBar=new QLineEdit(dock);
88    // WebBar to dock
89    dock->setWidget(m_webBar);
90    // Assign top dock to the mainwindow if admin mode is active
91    if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
92        addDockWidget(Qt::TopDockWidgetArea,dock);
93
94    // Status bar
95    QStatusBar* st=statusBar();
96    st->setSizeGripEnabled(false);
97    // OpenGnsys logo (or alternate text)
98    m_logo=new QLabel();
99    QPixmap logo;
100    if(logo.load("/opt/opengnsys/lib/pictures/oglogo.png"))
101        m_logo->setPixmap(logo);
102    else
103        m_logo->setText("OG");
104    m_logo->setToolTip(tr(gettext("Proyecto OpenGnsys"))+"\nhttps://opengnsys.es");
105    // Progress bar
106    m_progressBar=new QProgressBar(this);
107    m_progressBar->setRange(0,100);
108    // Connection speed
109    QString speed=readSpeed();
110    m_speedInfo=new QLabel(speed);
111    m_speedInfo->setAlignment(Qt::AlignCenter);
112    if(m_env.contains("DEFAULTSPEED") && m_env["DEFAULTSPEED"]!="")
113        if(speed.compare(m_env["DEFAULTSPEED"])!=0)
114            m_speedInfo->setStyleSheet("background-color: darkred; color: white; font-weight: bold;");
115    // Clock
116    m_clock=new DigitalClock(this);
117
118    m_web->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
119
120    // Web signals
121    connect(m_web,SIGNAL(linkClicked(const QUrl&)),this,
122            SLOT(slotLinkHandle(const QUrl&)));
123    connect(m_web,SIGNAL(loadStarted()),this,SLOT(slotWebLoadStarted()));
124    connect(m_web,SIGNAL(loadFinished(bool)),this,SLOT(slotWebLoadFinished(bool)));
125    connect(m_web,SIGNAL(loadProgress(int)),this,SLOT(slotWebLoadProgress(int)));
126    connect(m_web,SIGNAL(urlChanged(const QUrl&)),this,SLOT(slotUrlChanged(const QUrl&)));
127    // Ignore SSL errors.
128    connect(m_web->page()->networkAccessManager(),
129            SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> &)), this,
130            SLOT(slotSslErrors(QNetworkReply*)));
131
132    // Process signals
133    connect(m_process,SIGNAL(started()),this,SLOT(slotProcessStarted()));
134    connect(m_process,SIGNAL(finished(int,QProcess::ExitStatus)),
135            this,SLOT(slotProcessFinished(int,QProcess::ExitStatus)));
136    connect(m_process,SIGNAL(error(QProcess::ProcessError)),
137            this,SLOT(slotProcessError(QProcess::ProcessError)));
138    connect(m_process,SIGNAL(readyReadStandardOutput()),this,SLOT(slotProcessOutput()));
139    connect(m_process,SIGNAL(readyReadStandardError()),
140            this,SLOT(slotProcessErrorOutput()));
141
142    // Dock signals
143    connect(button,SIGNAL(clicked()),this,SLOT(slotCreateTerminal()));
144    connect(m_webBar,SIGNAL(returnPressed()),this,SLOT(slotWebBarReturnPressed()));
145
146    QStringList arguments=QCoreApplication::arguments();
147    m_webBar->setText(arguments[1]);
148    m_web->load(QUrl(arguments[1]));
149}
150
151MainWindow::~MainWindow()
152{
153    if(m_logfile)
154    {
155        m_logfile->close();
156        delete m_logfile;
157    }
158    if(m_logstream)
159        delete m_logstream;
160}
161
162void MainWindow::slotLinkHandle(const QUrl &url)
163{
164    // Check if it's executing another process
165    if(m_process->state() != QProcess::NotRunning)
166    {
167        print(tr(gettext("Hay otro proceso en ejecución. Por favor espere.")));
168        return;
169    }
170    QString urlString = url.toString();
171    QString urlScheme = url.scheme();
172    // Clear the output widget for a normal user
173    if(! m_env.contains("ogactiveadmin") || m_env["ogactiveadmin"] != "true")
174    {
175        m_output->clear();
176    }
177    if(urlScheme == COMMAND_CONFIRM || urlScheme == COMMAND_CONFIRM_OUTPUT ||
178       urlScheme == COMMAND_OUTPUT_CONFIRM || urlScheme == COMMAND_WITH_CONFIRMATION)
179    {
180        // For all command with confirmation links, show a popup box
181        QMessageBox msgBox;
182        msgBox.setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
183        msgBox.setWindowTitle(tr(gettext("AVISO")));
184        msgBox.setIcon(QMessageBox::Question);
185        msgBox.setTextFormat(Qt::RichText);
186        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.")));
187        QPushButton *execButton = msgBox.addButton(tr(gettext("Ejecutar")), QMessageBox::ActionRole);
188        msgBox.addButton(tr(gettext("Cancelar")), QMessageBox::RejectRole);
189        msgBox.setDefaultButton(execButton);
190        msgBox.exec();
191        // Continue if user press the execution button
192        if (msgBox.clickedButton() == execButton)
193        {
194            // For command with confirmation and output link, show an output window to non-admin user
195            if((urlScheme == COMMAND_CONFIRM_OUTPUT || urlScheme == COMMAND_OUTPUT_CONFIRM) &&
196               (! m_env.contains("ogactiveadmin") || m_env["ogactiveadmin"] != "true"))
197            {
198                int w=MainWindow::width(), h=MainWindow::height();
199                m_output->setWindowFlags(Qt::Window);
200                m_output->move(100, 100);
201                m_output->setFixedSize(w*0.8-100, h*0.8-100);
202                m_output->show();
203            }
204            // Execute the command
205            executeCommand(urlString.remove(0, urlScheme.length()+1));
206        }
207    }
208    else if(urlScheme == COMMAND || urlScheme == COMMAND_OUTPUT)
209    {
210        // For command with output link, show an output window to non-admin user
211        if(urlScheme == COMMAND_OUTPUT &&
212           (! m_env.contains("ogactiveadmin") || m_env["ogactiveadmin"] != "true"))
213        {
214            int w=MainWindow::width(), h=MainWindow::height();
215            m_output->setWindowFlags(Qt::Window);
216            m_output->move(100, 100);
217            m_output->setFixedSize(w*0.8-100, h*0.8-100);
218            m_output->show();
219        }
220        // Execute the command
221        executeCommand(urlString.remove(0, urlScheme.length()+1));
222    }
223    else
224    {
225        // For other link, load webpage
226        m_web->load(url);
227    }
228}
229
230void MainWindow::slotWebLoadStarted()
231{
232    startProgressBar();
233    m_progressBar->setFormat(gettext("%p% Cargando"));
234}
235
236void MainWindow::slotWebLoadProgress(int progress)
237{
238    m_progressBar->setValue(progress);
239}
240
241void MainWindow::slotWebLoadFinished(bool ok)
242{
243    // If any error ocurred, show a pop up
244    // Sometimes when the url hasn't got a dot, i.e /var/www/pageweb,
245    // the return value is always true so we check the bytes received too
246    if(ok == false)
247    {
248        QMessageBox msgBox;
249        msgBox.setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
250        msgBox.setWindowTitle(tr(gettext("AVISO")));
251        msgBox.setIcon(QMessageBox::Question);
252        msgBox.setTextFormat(Qt::RichText);
253        msgBox.setText(tr(gettext("La p&aacute;gina no se puede cargar.")));
254
255        QPushButton *reloadButton = msgBox.addButton(tr(gettext("Recargar")), QMessageBox::ActionRole);
256        msgBox.addButton(tr(gettext("Abortar")), QMessageBox::RejectRole);
257        msgBox.exec();
258
259        if (msgBox.clickedButton() == reloadButton)
260        {
261            m_web->reload();
262        }
263        else
264        {
265            close();
266        }
267    }
268    else
269    {
270        finishProgressBar();
271    }
272}
273
274void MainWindow::slotUrlChanged(const QUrl &url)
275{
276    m_webBar->setText(url.toString());
277}
278
279void MainWindow::slotSslErrors(QNetworkReply* reply)
280{
281    reply->ignoreSslErrors();
282}
283
284void MainWindow::slotProcessStarted()
285{
286    startProgressBar();
287}
288
289void MainWindow::slotProcessOutput()
290{
291    m_process->setReadChannel(QProcess::StandardOutput);
292    char buf[BUFFERSIZE];
293    while((m_process->readLine(buf,BUFFERSIZE) > 0))
294    {
295        QString s(buf);
296        if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
297        {
298            m_output->insertPlainText(tr("Proc. stdout: "));
299        }
300        print(s);
301        captureOutputForStatusBar(s);
302    }
303}
304
305void MainWindow::slotProcessErrorOutput()
306{
307    m_process->setReadChannel(QProcess::StandardError);
308    char buf[BUFFERSIZE];
309    while((m_process->readLine(buf,BUFFERSIZE) > 0))
310    {
311        QString s(buf);
312        if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
313        {
314            m_output->insertPlainText(tr("Proc. stderr: "));
315        }
316        m_output->setTextColor(QColor(Qt::darkBlue));
317        print(s);
318        m_output->setTextColor(QColor(Qt::black));
319    }
320}
321
322void MainWindow::slotProcessFinished(int code, QProcess::ExitStatus status)
323{
324    if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
325    {
326        // Admin user: show process status
327        if(status==QProcess::NormalExit)
328        {
329            if(code > 0)
330            {
331                m_output->setTextColor(QColor(Qt::darkRed));
332            }
333            print("\n"+tr(gettext("Fin del proceso. Valor de retorno: "))+QString::number(code));
334        }
335        else
336        {
337            m_output->setTextColor(QColor(Qt::darkRed));
338            print("\n"+tr(gettext("El proceso ha fallado inesperadamente. Salida: ")+code));
339        }
340        m_output->setTextColor(QColor(Qt::black));
341    }
342    else
343    {
344        // Non-admin user: show instruction to close the popup window
345        write(tr(gettext("Fin del proceso. Valor de retorno: "))+QString::number(code));
346        m_output->setFontUnderline(true);
347        print("\n\n"+tr(gettext("AVISO: Pulsar el botón superior derecho para cerrar"))+" [X]");
348        m_output->setFontUnderline(false);
349    }
350    // On error, show a message box
351    if(code > 0 && ! m_output->isActiveWindow())
352    {
353        showErrorMessage(gettext("Código de salida: ")+QString::number(code));
354    }
355    finishProgressBar();
356}
357
358void MainWindow::slotProcessError(QProcess::ProcessError error)
359{
360    QString errorMsg;
361    switch(error)
362    {
363        case QProcess::FailedToStart:
364            errorMsg=tr(gettext("Imposible lanzar el proceso."));
365            break;
366        case QProcess::WriteError:
367            errorMsg=tr(gettext("Error de escritura en el proceso."));
368            break;
369        case QProcess::ReadError:
370            errorMsg=tr(gettext("Error de lectura del proceso."));
371            break;
372        // No capturo crashed porque la pillo por finished
373        case QProcess::Crashed:
374        case QProcess::Timedout:
375            break;
376        case QProcess::UnknownError:
377        default:
378            errorMsg=tr(gettext("Error desconocido."));
379            break;
380    }
381    // Print error and show message box with timeout.
382    if(!errorMsg.isNull()) {
383        m_output->setTextColor(QColor(Qt::darkRed));
384        print(errorMsg);
385        m_output->setTextColor(QColor(Qt::black));
386        showErrorMessage(errorMsg);
387    }
388    finishProgressBar();
389}
390
391void MainWindow::slotCreateTerminal()
392{
393    QTermWidget* console = new QTermWidget(1,this);
394    QFont font = QApplication::font();
395    font.setFamily("DejaVu Sans Mono");
396    font.setPointSize(12);
397   
398    console->setTerminalFont(font);
399    console->setFocusPolicy(Qt::StrongFocus);
400    console->setScrollBarPosition(QTermWidget::ScrollBarRight);
401
402    ++m_numberTerminal;
403
404    connect(console,SIGNAL(finished()),this,SLOT(slotDeleteTerminal()));
405
406    QString name=tr("Term ")+QString::number(m_numberTerminal);
407    m_tabs->addTab(console,name);
408}
409
410void MainWindow::slotDeleteTerminal()
411{
412    QWidget *widget = qobject_cast<QWidget *>(sender());
413    Q_ASSERT(widget);
414    m_tabs->removeTab(m_tabs->indexOf(widget));
415    delete widget;
416}
417
418void MainWindow::slotWebBarReturnPressed()
419{
420    QUrl url(m_webBar->text());
421    if(url.isValid())
422        slotLinkHandle(url);
423}
424
425int MainWindow::readEnvironmentValues()
426{
427    // The return value
428    int ret=true;
429
430    // Get all environment variables
431    QStringList environmentlist=QProcess::systemEnvironment();
432    // This is the list of the important variables
433    QStringList variablelist=QString(ENVIRONMENT).split(",");
434
435    // This is an auxiliar variable
436    QStringList stringlist;
437
438    foreach (QString str,variablelist)
439    {
440        // Look for the variable in the environment
441        stringlist=environmentlist.filter(str+"=");
442
443        if(stringlist.isEmpty())
444        {
445            m_env[str]="";
446            ret=false;
447        }
448        else
449        {
450            // Get the first element and get the value part
451            m_env[str]=(stringlist.first().split("="))[1];
452        }
453    }
454
455    return ret;
456}
457
458// Write a string to the log file
459void MainWindow::write(QString s)
460{
461    if(! s.endsWith("\n"))
462        s+="\n";
463    if(m_logstream)
464    {
465        *m_logstream<<CURRENT_TIME()<<": browser: "<<s;
466        m_logstream->flush();
467    }
468}
469
470// Print and log a string
471void MainWindow::print(QString s)
472{
473    if(! s.endsWith("\n"))
474        s+="\n";
475    write(s);
476    if(m_output)
477        m_output->insertPlainText(s);
478}
479
480// Show message in status bar
481void MainWindow::captureOutputForStatusBar(QString output)
482{
483    // Modify the status bar
484    output=output.trimmed();
485    // Get percentage (string starts with "[Number]")
486    QRegExp regexp(REGEXP_STRING);
487    if(regexp.indexIn(output) != -1)
488    {
489        int pass=regexp.cap(1).toInt();
490        output.replace(regexp,"");
491        m_progressBar->setValue(pass);
492        m_progressBar->setFormat("%p%"+output);
493    }
494}
495
496// Init status bar
497void MainWindow::startProgressBar()
498{
499    QStatusBar* st=statusBar();
500    st->clearMessage();
501    st->addWidget(m_logo);
502    st->addWidget(m_progressBar,90);
503    st->addWidget(m_speedInfo,5);
504    st->addWidget(m_clock,5);
505    m_progressBar->show();
506    m_clock->show();
507    m_web->setEnabled(false);
508}
509
510// Reset status bar
511void MainWindow::finishProgressBar()
512{
513    m_progressBar->reset();
514    m_web->setEnabled(true);
515}
516
517// Execute a command
518void MainWindow::executeCommand(QString &string)
519{
520    QStringList list=string.split(" ",QString::SkipEmptyParts);
521    QString program=list.takeFirst();
522    m_process->setReadChannel(QProcess::StandardOutput);
523    // Assign the same Browser's environment to the process
524    m_process->setEnvironment(QProcess::systemEnvironment());
525    m_process->start(program,list);
526    // Only show the command line to admin user
527    if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
528    {
529        m_output->setTextColor(QColor(Qt::darkGreen));
530        print(tr(gettext("Lanzando el comando: "))+string);
531        m_output->setTextColor(QColor(Qt::black));
532    }
533    else
534    {
535        write(tr(gettext("Lanzando el comando: "))+string);
536    }
537    startProgressBar();
538}
539
540// Returns communication speed
541QString MainWindow::readSpeed() {
542    if(m_env.contains("OGLOGFILE"))
543    {
544        QString infoFile=m_env["OGLOGFILE"].replace(".log", ".info.html");
545        QString command="grep -hoe \"[0-9]*Mb/s\" "+infoFile+" 2>/dev/null";
546        QProcess process;
547        process.start(command);
548        process.waitForFinished();
549        QString speed(process.readAllStandardOutput());
550        return speed.simplified();
551    }
552    else
553    {
554        return QString("");
555    }
556}
557
558// Show an error box with timeout
559void MainWindow::showErrorMessage(QString text)
560{
561    QMessageBox* msgBox=new QMessageBox();
562    msgBox->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
563    msgBox->setWindowTitle(gettext("ERROR"));
564    msgBox->setIcon(QMessageBox::Warning);
565    msgBox->setText(text);
566    msgBox->show();
567    QTimer::singleShot(5000, msgBox, SLOT(close()));
568}
Note: See TracBrowser for help on using the repository browser.