summaryrefslogblamecommitdiffstats
path: root/src/slxbrowser.cpp
blob: 8eceea0a6901472f497d0536b2bc5d942e9cd08a (plain) (tree)
1
2
3
4
5
6
7
8
9

                       
                




                            
                    
 

                                                                   
                                                
                           
                              
                            
                              



                           
 
                                         




                                          
                                   

                                                                               

                                         
         
                                                                                              



                                                                                                   
                                                              











                                       
                                                                             
 


                                                                                   

                                          
                                   
                                                                             

                                                      

                                                    
                 


                                                                                                                              
         

                                                                                           
                                           
                                               



                         
                         


 
                              
 
                           
                      
                                           
                                                              
         





                               


















                                                                                   
                                      

                          

                                                        





                                                                                                     


                                                      



                                                                                                                      
                        
                                                     
                                                                                                                         

                                                                                                                                                                                    
                                                                                                                              
                                                      







                                                                                                                
                                                             






                                                                                         





                                                                                    
                 

                                     




                                                                                
         
 
                            
                             
                                                             

 
                                           



                                      
                                                                                
 
                                        


                                         


                                          


                                  
                                                      
 
                                                                    
                               

                                                                          
                
                                                    





                                                                                                    


         
                                
 















                                                                                          

















                                                                                     
                                      











                                                                                                 
 
#include "slxbrowser.h"
#include "webview.h"
#include "nam.h"
#include <QtWebKitWidgets>
#include <QWebPage>
#include <QNetworkReply>
#include <QSslConfiguration>
#include <QProgressBar>
#include <QDateTime>

static QRegularExpression urlListToRegExp(const QStringList &list);

SlxBrowser::SlxBrowser(BrowserSettings settings)
    : QMainWindow(nullptr),
	  _settings(settings),
     _unsupportedUri(false),
	  _blockedSite(false),
	  _lastPageLoad(0),
	  _activity(false),
	  _lastActivity(0),
	  _pageValid(false)
{
	_settings.reloadInterval *= 1000;
	if (_settings.zoom <= 0) {
		_settings.zoom = 100;
	} else if (_settings.zoom > 400) {
		_settings.zoom = 400;
	}
	if (_settings.fullscreen) {
		this->showFullScreen();
		this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
	} else if (_settings.maximized) {
		this->showMaximized();
	}
	QWebSettings::globalSettings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
	//QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
	QWidget *w = new QWidget;
	QLayout *l = new QVBoxLayout;
	_browser = new WebView;
	_browser->setZoomFactor(float(_settings.zoom) * .01f);
	_progress = new QProgressBar;
	_progress->hide();
	l->addWidget(_browser);
	l->addWidget(_progress);
	l->setMargin(0);
	l->setSpacing(0);
	l->setContentsMargins(0,0,0,0);
	w->setContentsMargins(0,0,0,0);
	w->setLayout(l);
	this->setCentralWidget(w);

	_reset.setSingleShot(true);
	connect(&_reset, &QTimer::timeout, this, &SlxBrowser::reloadInitial);

	connect(_browser, &WebView::loadStarted, this, &SlxBrowser::loadStarted);
	connect(_browser, &WebView::loadFinished, this, &SlxBrowser::loadFinished);
	connect(_browser, &WebView::loadProgress, this, &SlxBrowser::loadProgress);
	//
	QWebPage *page = _browser->page();
	QNetworkAccessManager *nam;
	if (_settings.whiteList.isEmpty() && _settings.blackList.isEmpty()) {
		nam = new QNetworkAccessManager(this);
	} else {
		if (_settings.blackList.isEmpty()) {
			_settings.blackList << "*";
		}
		// Just to be safe
		_settings.whiteList << _settings.url;
		nam = new SlxNetworkAccessManager(urlListToRegExp(_settings.blackList), urlListToRegExp(_settings.whiteList));
	}
	connect(nam, &QNetworkAccessManager::sslErrors, this, &SlxBrowser::sslErrors);
	connect(nam, &QNetworkAccessManager::finished, this, &SlxBrowser::requestFinished);
	page->setNetworkAccessManager(nam);
	page->mainFrame()->load(_settings.url);
	//
	_browser->show();
}

SlxBrowser::~SlxBrowser()
{
}

void SlxBrowser::loadStarted()
{
	_pageValid = false;
	_reset.stop();
	if (_settings.reloadInterval > 0) {
		_reset.start(_settings.reloadInterval + 5000);
	}
	_normalError.clear();
	_sslErrors.clear();
	_progress->setValue(0);
	_progress->show();
}

void SlxBrowser::maybeTriggerBack()
{
	auto elems = _browser->page()->mainFrame()->documentElement().findAll("*");
	if (elems.count() > 10)
		return;
	QStringList want{{ "HEAD", "TITLE", "H1", "HR", "A", "PRE" }};
	for (auto x : elems) {
		if (x.tagName() != want.first())
			continue;
		want.pop_front();
		if (want.isEmpty())
			break;
	}
	if (want.isEmpty()) {
		// Was probably a JS redirect, go back before displaying the error
		_browser->page()->triggerAction(QWebPage::Back);
	}
}

void SlxBrowser::loadFinished(bool ok)
{
	_progress->hide();
	bool abortedDl = _browser->wasAbortedDownload();
	if (!abortedDl && !ok) {
		if (_unsupportedUri) {
			QMessageBox::warning(this, QString::fromUtf8("Denied"),
					QString::fromUtf8("This URL type is not supported.\n\n"
							"Diese Art Link wird nicht unterstützt.\n\n"
							"(z.B. Mail)"));
		} else if (_blockedSite) {
			maybeTriggerBack();
			QTimer::singleShot(10, [=]() {
				maybeTriggerBack();
				QMessageBox::warning(this, QString::fromUtf8("Denied"),
						QString::fromUtf8("Target URL not allowed.\n\n"
								"Dieser Link führt auf eine nicht erlaubte Seite."));
			});
		} else {
			_browser->blockSignals(true);
			_browser->page()->mainFrame()->setHtml("<html><body style='background:blue;color:white'><br><br>"
												   "<center><h1>Page Load Error</h1><div id='content'></div><br>"
												   "<p><a style='color:white' href='#' onclick='window.history.back()'>Back</a></p>"
												   "</center></body></html>");
			_browser->blockSignals(false);
			QWebElement el = _browser->page()->mainFrame()->documentElement().findFirst("#content");
			QString str;
			if (!_sslErrors.empty()) {
				str.append("SSL Errors:\n");
				for (QSslError err : _sslErrors) {
					str.append(err.errorString());
					str.append('\n');
				}
			} else if (!_normalError.isEmpty()) {
				str.append("Load Error:\n");
				str.append(_normalError);
			} else {
				str.append("Unknown Error");
			}
			str.append("\n\n\n\n" + QDateTime::currentDateTime().toString());
			el.setPlainText(str);
			_pageValid = false;
			if (_settings.reloadInterval > 0) {
				_reset.start(qMin(30000, _settings.reloadInterval));
			} else {
				_reset.start(30000);
			}
		}
		_sslErrors.clear();
		_normalError.clear();
	} else {
		_pageValid = true;
		if (_settings.reloadInterval > 0) {
			_reset.start(qMax(_settings.reloadInterval / 20, 1000));
		}
	}

    _unsupportedUri = false;
	_blockedSite = false;
	_lastPageLoad = QDateTime::currentMSecsSinceEpoch();;
}

void SlxBrowser::loadProgress(int progress)
{
	_progress->setValue(progress);
}

void SlxBrowser::sslErrors(QNetworkReply* reply, const QList<QSslError>& errors)
{
	if (_settings.ignoreSslErrors) {
		reply->ignoreSslErrors();
		return;
	}
	for (const auto& err : errors) {
		qDebug() << "SSL:" << err;
	}
	_sslErrors.append(errors);
}

void SlxBrowser::requestFinished(QNetworkReply *reply)
{
	if (reply->error() == QNetworkReply::ProtocolUnknownError) {
        _unsupportedUri = true;
	} else if (reply->error() == QNetworkReply::UnknownNetworkError) {
		_blockedSite = true;
	} else {
		_normalError = reply->errorString();
		if (_normalError.isEmpty()) {
			int ec = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
			if (ec >= 400) {
				_normalError = QString::asprintf("HTTP ERROR %d", ec);
			}
		}
	}
}

void SlxBrowser::reloadInitial()
{
	if (_pageValid) {
		if (_activity) {
			_lastActivity = QDateTime::currentMSecsSinceEpoch();
			_activity = false;
			_reset.start(qMax(_settings.reloadInterval / 20, 1000));
			return;
		}
		qint64 now = QDateTime::currentMSecsSinceEpoch();
		if (now - qMax(_lastActivity, _lastPageLoad) > _settings.reloadInterval) {
			_browser->page()->mainFrame()->load(_settings.url);
		} else {
			_reset.start(qMax(_settings.reloadInterval / 20, 1000));
		}
	} else {
		_browser->page()->mainFrame()->load(_settings.url);
	}
}

static QRegularExpression urlListToRegExp(const QStringList &list)
{
	// We search in the escaped string, so actually look for \*\* and \*
	// Capture char before that because it must not be another backslash, as that
	// means the star was already escaped in the list.
	// Since these are C strings there are some additional backslashes here.
	static const QRegularExpression STARSTAR("(^|[^\\\\])\\\\\\*\\\\\\*");
	static const QRegularExpression STAR("(^|[^\\\\])\\\\\\*");
	static const QRegularExpression QUEST("(^|[^\\\\])\\\\\\?");
	static const QString STARSTAR_REP("\\1.*");
	static const QString STAR_REP("\\1[^/]*");
	static const QString QUEST_REP("\\1.?");
	QStringList regexes; // All my regex's live in Regtexas
	for (const QString &str : list) {
		QString mangled;
		if (str.contains(QLatin1String("//"))) {
			mangled = str;
		} else if (str.contains(QLatin1Char('/')) || str.contains(QLatin1String("**"))) {
			mangled = "*//" + str;
		} else {
			mangled = "*//" + str + "/**";
		}
		mangled = QRegularExpression::escape(mangled);
		mangled = mangled.replace(STARSTAR, STARSTAR_REP).replace(STAR, STAR_REP)
				.replace(QUEST, QUEST_REP);
		regexes << mangled;
	}
	qDebug() << regexes;
	return QRegularExpression("^(" + regexes.join('|') + ")$");
}