summaryrefslogblamecommitdiffstats
path: root/src/webview.cpp
blob: 55443b39ff9b3f842e3e2a3ef92e180726cd475c (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
                    


                   









                             

                             
                    
 

                                                                   











                                                                        


                                 




                                             
 
                               
                                                                     






                                                                                                            


                                                                                            



                                                                                                                                                            
                                                   

                                                                                       



                                                                                                                  

                                                          
                                                            



                                                                               

                                          

                                                                                                                  



                                                                                                     















                                                                                                                  
























                                              























                                                                                                                
                                     


                                          
                              


                                                                                             
                                                                                         
                                                                                                    







                                                                                                                         




                                                                   
                                                                                 

                                                                             
                 
                                   

                                                          
                                                     


                                          




                                                 








                                                   

                                                                                              
                                                                                      



                                                                              
                      
                      

                               


                                       
 
































                                                                                                                               
#include "webview.h"
#include "nam.h"
#include "global.h"

#include <QWebFrame>
#include <QNetworkReply>
#include <QMessageBox>
#include <QTimer>
#include <QUrlQuery>
#include <QCryptographicHash>
#include <QCursor>
#include <QWebHistory>
#include <QNetworkCookieJar>
#include <QWebElement>
#include <QRegularExpression>
#include <QWebPage>
#include <QWebFrame>

static QRegularExpression urlListToRegExp(const QStringList &list);

// Override user-agent to make it appear mobile
class UaWebPage : public QWebPage
{
public:
    static QRegularExpression re;

    QString userAgentForUrl(const QUrl &url) const override {
        return QWebPage::userAgentForUrl(url).replace(re, "Mobile \\1");
    }
};

QRegularExpression UaWebPage::re("(\\S+)$");

WebView::WebView(QWidget* parent)
	: QWebView(parent),
	_timerAbortMessage(new QTimer(this)),
	_abortedDownload(false),
	_inErrorState(false),
	_timerReset(new QTimer(this)),
	_firstLoad(false)
{
	auto p = new UaWebPage;
	if (!Global::getCombinedIdpWhitelist().trimmed().isEmpty()) {
		QObject::connect(p, &UaWebPage::frameCreated, [this](QWebFrame *frame) {
			QObject::connect(frame, &QWebFrame::javaScriptWindowObjectCleared, [this, frame]() {
				this->jsInjector(frame);
			});
		});
	}
	this->setPage(p);
	_timerAbortMessage->setSingleShot(true);
	_timerReset->setSingleShot(true);
	connect(page(), SIGNAL(windowCloseRequested()), this, SLOT(windowCloseRequested()));
	auto bl = Global::urlBlacklist();
	auto wl = Global::urlWhitelist();
	page()->setNetworkAccessManager(new SlxNetworkAccessManager(urlListToRegExp(bl),
																urlListToRegExp(wl), this));
	page()->setForwardUnsupportedContent(true);
	page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
	//page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
	connect(page(), SIGNAL(unsupportedContent(QNetworkReply*)),this,SLOT(unsupportedContent(QNetworkReply*)));
	connect(page(), SIGNAL(downloadRequested(QNetworkRequest)),this,SLOT(downloadRequest(QNetworkRequest)));
	connect(_timerAbortMessage, &QTimer::timeout, this, &WebView::downloadDeniedMessage);
	connect(_timerReset, &QTimer::timeout, this, [this]() {
		this->stop();
		this->page()->mainFrame()->setContent("");
		emit triggerReset(tr("Inactivity Timeout"));
	});
	connect(this, &QWebView::loadFinished, this, &WebView::onLoadFinished);
}

void WebView::jsInjector(QWebFrame *frame)
{
	QString str = Global::getCombinedIdpWhitelist().replace(
				QRegularExpression("[^\\w. /:-]", QRegularExpression::UseUnicodePropertiesOption),
				QStringLiteral(""));
	frame->evaluateJavaScript(QStringLiteral("var slxIdpFilter ='") + str + QStringLiteral("'"));
}

void WebView::windowCloseRequested()
{
	// If we have an old URL stored on the stack, navigate back to it, otherwise we return and nothing happens
	if (_urls.empty())
		return;
	QUrl url = _urls.pop();
	page()->mainFrame()->load(url);
}

QWebView* WebView::createWindow(QWebPage::WebWindowType)
{
	// Remember current URL, then return the current Web View so no new window opens
	_urls.push(this->url());
	return this;
}

void WebView::mousePressEvent(QMouseEvent* ev)
{
	QWebView::mousePressEvent(ev);
	resetTimeout();
}

void WebView::keyPressEvent(QKeyEvent* ev)
{
	QWebView::keyPressEvent(ev);
	resetTimeout();
}

void WebView::wheelEvent(QWheelEvent* ev)
{
	QWebView::wheelEvent(ev);
	resetTimeout();
}

void WebView::resetTimeout()
{
	if (!_inErrorState) {
		_timerReset->start(60000);
	}
}

void WebView::unsupportedContent(QNetworkReply* rep)
{
	_abortedDownload = true;
	rep->abort();
	rep->deleteLater();
	_timerAbortMessage->start(1);
}

void WebView::downloadRequest(QNetworkRequest)
{
	_timerAbortMessage->start(1);
}

void WebView::downloadDeniedMessage()
{
	QMessageBox::warning(this->parentWidget(), QString::fromUtf8("Denied"),
			QString::fromUtf8("The requested action triggered a download, which is not allowed.\n\n"
					"Diese Aktion löst einen Download aus, was nicht erlaubt ist."));
}

void WebView::onLoadFinished(bool ok)
{
	if (_abortedDownload || !ok) {
		_abortedDownload = false;
		_inErrorState = true;
		_timerReset->start(10000);
		return;
	}
	_inErrorState = false;
	auto user = this->page()->mainFrame()->documentElement().findFirst("#bwlp-username");
	auto pass = this->page()->mainFrame()->documentElement().findFirst("#bwlp-password");
	auto err = this->page()->mainFrame()->documentElement().findFirst("#bwlp-error");
	auto hash = this->page()->mainFrame()->documentElement().findFirst("#bwlp-hash");
	auto adminToken = this->page()->mainFrame()->documentElement().findFirst("#bwlp-cow-token");
	if (!user.isNull() && !pass.isNull() && !hash.isNull()) {
		if (hash.toPlainText() != QCryptographicHash::hash(_token.toLatin1(), QCryptographicHash::Md5).toHex()) {
			qDebug() << " *** Invalid security hash ***";
			emit triggerReset("Invalid Hash");
			return;
		}
		auto ustr = user.toPlainText();
		auto upass = pass.toPlainText();
		if (Global::isValidShibCreds(ustr, upass)) {
			QString token = adminToken.toPlainText();
			if (!token.isEmpty()) {
				Global::writeCowToken(ustr, token);
			}
			emit startAuthentication(ustr, "shib=" + _token + upass);
		} else {
			emit triggerReset("Invalid user or passhash format");
		}
	} else if (!err.isNull()) {
		this->stop();
		this->page()->mainFrame()->setContent("");
		emit triggerReset(err.toPlainText());
	} else {
		_timerReset->start(60000);
	}
	if (_firstLoad) {
		_firstLoad = false;
		this->page()->history()->clear();
		this->history()->clear();
	}
}

void WebView::reset(const QString baseUrl)
{
    QUrl url(baseUrl);
    QUrlQuery q(url.query());
    q.addQueryItem("action", "browser");
    QByteArray input;
    input.append((const char*)this, sizeof(*this));
    input.append(QString::asprintf("%d %d", QCursor::pos().x(), QCursor::pos().y()).toUtf8());
    input.append(QString::number(QDateTime::currentMSecsSinceEpoch()).toUtf8());
    _token = QCryptographicHash::hash(input, QCryptographicHash::Md5).left(8).toHex();
    q.addQueryItem("token", _token);
    url.setQuery(q);
    _urls.clear();
    this->page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar);
    this->setUrl(url);
    _firstLoad = true;
    _timerAbortMessage->stop();
    _timerReset->stop();
    QTimer::singleShot(5000, [this]() {
        _timerReset->stop();
    });
}

static QRegularExpression urlListToRegExp(const QStringList &list)
{
	if (list.isEmpty())
		return QRegularExpression("(["); // Return an invalid regex, we use .isValid to check if the list is to be used
	// 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('|') + ")$");
}