aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFelix (xq) Queißner <git@mq32.de>2020-06-16 22:01:59 +0200
committerFelix (xq) Queißner <git@mq32.de>2020-06-16 22:01:59 +0200
commita3f3e3933c4a2522e233917a6795c6e9d677e65c (patch)
tree6e1bd483bbd5e8ca6fee4566e544de48bfa754a6 /src
parentbc18d9356828f1ae40d3b4ce5432b30ca13cfc15 (diff)
downloadkristall-a3f3e3933c4a2522e233917a6795c6e9d677e65c.tar.gz
Refactoring: Changes internal structure of requests and unifies a lot of code. Now all errors are handled the same.
Diffstat (limited to 'src')
-rw-r--r--src/about/updates.gemini2
-rw-r--r--src/abouthandler.cpp60
-rw-r--r--src/abouthandler.hpp23
-rw-r--r--src/browsertab.cpp470
-rw-r--r--src/browsertab.hpp55
-rw-r--r--src/filehandler.cpp43
-rw-r--r--src/filehandler.hpp23
-rw-r--r--src/fingerclient.cpp7
-rw-r--r--src/fingerclient.hpp19
-rw-r--r--src/geminiclient.cpp81
-rw-r--r--src/geminiclient.hpp64
-rw-r--r--src/gopherclient.cpp7
-rw-r--r--src/gopherclient.hpp17
-rw-r--r--src/kristall.hpp2
-rw-r--r--src/kristall.pro11
-rw-r--r--src/main.cpp3
-rw-r--r--src/mainwindow.cpp12
-rw-r--r--src/mainwindow.hpp3
-rw-r--r--src/protocolhandler.cpp17
-rw-r--r--src/protocolhandler.hpp59
-rw-r--r--src/webclient.cpp11
-rw-r--r--src/webclient.hpp21
22 files changed, 552 insertions, 458 deletions
diff --git a/src/about/updates.gemini b/src/about/updates.gemini
index ad5ae6c..5a1711a 100644
--- a/src/about/updates.gemini
+++ b/src/about/updates.gemini
@@ -7,7 +7,7 @@
* Fixed bug: Status bar label now does elide links that are too long instead of resizing the window.
* Fixed bug: Gopher end-of-file marker is now better detected.
* Adds support for server certificate handling for gemini://
-*
+* Reworked internal network structure. Makes room for future improvements
## 0.2
* Implement Ctrl+D/*Add to favourites* menu item
diff --git a/src/abouthandler.cpp b/src/abouthandler.cpp
new file mode 100644
index 0000000..a836cf2
--- /dev/null
+++ b/src/abouthandler.cpp
@@ -0,0 +1,60 @@
+#include "abouthandler.hpp"
+#include "kristall.hpp"
+
+#include <QUrl>
+#include <QFile>
+
+AboutHandler::AboutHandler()
+{
+
+}
+
+bool AboutHandler::supportsScheme(const QString &scheme) const
+{
+ return (scheme == "about");
+}
+
+bool AboutHandler::startRequest(const QUrl &url)
+{
+ if (url.path() == "blank")
+ {
+ emit this->requestComplete("", "text/gemini");
+ }
+ else if (url.path() == "favourites")
+ {
+ QByteArray document;
+
+ document.append("# Favourites\n");
+ document.append("\n");
+
+ for (auto const &fav : global_favourites.getAll())
+ {
+ document.append("=> " + fav.toString().toUtf8() + "\n");
+ }
+
+ this->requestComplete(document, "text/gemini");
+ }
+ else
+ {
+ QFile file(QString(":/about/%1.gemini").arg(url.path()));
+ if (file.open(QFile::ReadOnly))
+ {
+ emit this->requestComplete(file.readAll(), "text/gemini");
+ }
+ else
+ {
+ emit this->networkError(ResourceNotFound, "The requested resource does not exist.");
+ }
+ }
+ return true;
+}
+
+bool AboutHandler::isInProgress() const
+{
+ return false;
+}
+
+bool AboutHandler::cancelRequest()
+{
+ return true;
+}
diff --git a/src/abouthandler.hpp b/src/abouthandler.hpp
new file mode 100644
index 0000000..b535d85
--- /dev/null
+++ b/src/abouthandler.hpp
@@ -0,0 +1,23 @@
+#ifndef ABOUTHANDLER_HPP
+#define ABOUTHANDLER_HPP
+
+#include <QObject>
+
+#include "protocolhandler.hpp"
+
+class AboutHandler : public ProtocolHandler
+{
+ Q_OBJECT
+public:
+ AboutHandler();
+
+ bool supportsScheme(QString const & scheme) const override;
+
+ bool startRequest(QUrl const & url) override;
+
+ bool isInProgress() const override;
+
+ bool cancelRequest() override;
+};
+
+#endif // ABOUTHANDLER_HPP
diff --git a/src/browsertab.cpp b/src/browsertab.cpp
index d3cdea4..9e9b7b7 100644
--- a/src/browsertab.cpp
+++ b/src/browsertab.cpp
@@ -9,6 +9,13 @@
#include "certificateselectiondialog.hpp"
+#include "geminiclient.hpp"
+#include "webclient.hpp"
+#include "gopherclient.hpp"
+#include "fingerclient.hpp"
+#include "abouthandler.hpp"
+#include "filehandler.hpp"
+
#include "ioutil.hpp"
#include "kristall.hpp"
@@ -24,43 +31,26 @@
#include <QMimeDatabase>
#include <QMimeType>
#include <QImageReader>
+#include <QClipboard>
#include <QGraphicsPixmapItem>
#include <QGraphicsTextItem>
-
-BrowserTab::BrowserTab(MainWindow * mainWindow) :
- QWidget(nullptr),
- ui(new Ui::BrowserTab),
- mainWindow(mainWindow),
- outline(),
- graphics_scene()
+BrowserTab::BrowserTab(MainWindow *mainWindow) : QWidget(nullptr),
+ ui(new Ui::BrowserTab),
+ mainWindow(mainWindow),
+ current_handler(nullptr),
+ outline(),
+ graphics_scene()
{
ui->setupUi(this);
- connect(&web_client, &WebClient::requestComplete, this, &BrowserTab::on_requestComplete);
- connect(&web_client, &WebClient::requestFailed, this, &BrowserTab::on_requestFailed);
- connect(&web_client, &WebClient::requestProgress, this, &BrowserTab::on_requestProgress);
-
- connect(&gemini_client, &GeminiClient::requestComplete, this, &BrowserTab::on_requestComplete);
- connect(&gemini_client, &GeminiClient::requestProgress, this, &BrowserTab::on_requestProgress);
- connect(&gemini_client, &GeminiClient::protocolViolation, this, &BrowserTab::on_protocolViolation);
- connect(&gemini_client, &GeminiClient::inputRequired, this, &BrowserTab::on_inputRequired);
- connect(&gemini_client, &GeminiClient::redirected, this, &BrowserTab::on_redirected);
- connect(&gemini_client, &GeminiClient::temporaryFailure, this, &BrowserTab::on_temporaryFailure);
- connect(&gemini_client, &GeminiClient::permanentFailure, this, &BrowserTab::on_permanentFailure);
- connect(&gemini_client, &GeminiClient::transientCertificateRequested, this, &BrowserTab::on_transientCertificateRequested);
- connect(&gemini_client, &GeminiClient::authorisedCertificateRequested, this, &BrowserTab::on_authorisedCertificateRequested);
- connect(&gemini_client, &GeminiClient::certificateRejected, this, &BrowserTab::on_certificateRejected);
- connect(&gemini_client, &GeminiClient::networkError, this, &BrowserTab::on_networkError);
-
- connect(&gopher_client, &GopherClient::requestComplete, this, &BrowserTab::on_requestComplete);
- connect(&gopher_client, &GopherClient::requestFailed, this, &BrowserTab::on_requestFailed);
- connect(&gopher_client, &GopherClient::requestProgress, this, &BrowserTab::on_requestProgress);
-
- connect(&finger_client, &FingerClient::requestComplete, this, &BrowserTab::on_requestComplete);
- connect(&finger_client, &FingerClient::requestFailed, this, &BrowserTab::on_requestFailed);
- connect(&finger_client, &FingerClient::requestProgress, this, &BrowserTab::on_requestProgress);
+ addProtocolHandler<GeminiClient>();
+ addProtocolHandler<FingerClient>();
+ addProtocolHandler<GopherClient>();
+ addProtocolHandler<WebClient>();
+ addProtocolHandler<AboutHandler>();
+ addProtocolHandler<FileHandler>();
this->updateUI();
@@ -78,117 +68,59 @@ BrowserTab::~BrowserTab()
void BrowserTab::navigateTo(const QUrl &url, PushToHistory mode)
{
- if(mainWindow->protocols.isSchemeSupported(url.scheme()) != ProtocolSetup::Enabled)
+ if (mainWindow->protocols.isSchemeSupported(url.scheme()) != ProtocolSetup::Enabled)
{
QMessageBox::warning(this, "Kristall", "URI scheme not supported or disabled: " + url.scheme());
return;
}
- this->timer.start();
-
- this->current_location = url;
- this->ui->url_bar->setText(url.toString(QUrl::FormattingOptions(QUrl::FullyEncoded)));
-
- if(not gemini_client.cancelRequest()) {
+ if ((this->current_handler != nullptr) and not this->current_handler->cancelRequest())
+ {
QMessageBox::warning(this, "Kristall", "Failed to cancel running gemini request!");
return;
}
- if(not web_client.cancelRequest()) {
- QMessageBox::warning(this, "Kristall", "Failed to cancel running web request!");
- return;
- }
-
- if(not gopher_client.cancelRequest()) {
- QMessageBox::warning(this, "Kristall", "Failed to cancel running gopher request!");
- return;
- }
-
- if(not finger_client.cancelRequest()) {
- QMessageBox::warning(this, "Kristall", "Failed to cancel running finger request!");
- return;
- }
-
- this->redirection_count = 0;
- this->successfully_loaded = false;
-
- if(url.scheme() == "gemini")
- {
- gemini_client.startRequest(url);
- }
- else if(url.scheme() == "http" or url.scheme() == "https")
- {
- web_client.startRequest(url);
- }
- else if(url.scheme() == "gopher")
- {
- gopher_client.startRequest(url);
- }
- else if(url.scheme() == "finger")
+ this->current_handler = nullptr;
+ for(auto & ptr : this->protocol_handlers)
{
- finger_client.startRequest(url);
+ if(ptr->supportsScheme(url.scheme())) {
+ this->current_handler = ptr.get();
+ break;
+ }
}
- else if(url.scheme() == "file")
- {
- QFile file { url.path() };
- if(file.open(QFile::ReadOnly))
- {
- QMimeDatabase db;
- auto mime = db.mimeTypeForUrl(url).name();
- auto data = file.readAll();
- qDebug() << "database:" << url << mime;
- this->on_requestComplete(data, mime);
- }
- else
- {
+ assert((this->current_handler != nullptr) and "If this error happens, someone forgot to add a new protocol handler class in the constructor. Shame on the programmer!");
+ if(this->current_identitiy.isValid()) {
+ if(not this->current_handler->enableClientCertificate(this->current_identitiy)) {
+ auto answer = QMessageBox::question(
+ this,
+ "Kristall",
+ QString("You requested a %1-URL with a client certificate, but these are not supported for this scheme. Continue?").arg(url.scheme())
+ );
+ if(answer != QMessageBox::Yes)
+ return;
+ this->current_handler->disableClientCertificate();
}
+ } else {
+ this->current_handler->disableClientCertificate();
}
- else if(url.scheme() == "about")
- {
- this->redirection_count = 0;
- if(url.path() == "blank")
- {
- this->on_requestComplete("", "text/gemini");
- }
- else if(url.path() == "favourites")
- {
- QByteArray document;
- document.append("# Favourites\n");
- document.append("\n");
-
- for(auto const & fav : this->mainWindow->favourites.getAll())
- {
- document.append("=> " + fav.toString().toUtf8() + "\n");
- }
+ this->timer.start();
- this->on_requestComplete(document, "text/gemini");
- }
- else
- {
- QFile file(QString(":/about/%1.gemini").arg(url.path()));
- if(file.open(QFile::ReadOnly))
- {
- this->on_requestComplete(file.readAll(), "text/gemini");
- }
- else
- {
- QMessageBox::warning(this, "Kristall", "Unknown location: " + url.path());
- }
- }
- }
+ this->current_location = url;
+ this->ui->url_bar->setText(url.toString(QUrl::FormattingOptions(QUrl::FullyEncoded)));
+ this->redirection_count = 0;
+ this->successfully_loaded = false;
- switch(mode)
- {
- case DontPush:
- break;
+ if(not this->current_handler->startRequest(url)) {
+ QMessageBox::critical(this, "Kristall", QString("Failed to execute request to %1").arg(url.toString()));
+ return;
+ }
- case PushImmediate:
+ if(mode == PushImmediate) {
pushToHistory(url);
- break;
}
this->updateUI();
@@ -198,7 +130,8 @@ void BrowserTab::navigateBack(QModelIndex history_index)
{
auto url = history.get(history_index);
- if(url.isValid()) {
+ if (url.isValid())
+ {
current_history_index = history_index;
navigateTo(url, DontPush);
}
@@ -214,7 +147,7 @@ void BrowserTab::navOneForward()
navigateBack(history.oneForward(current_history_index));
}
-void BrowserTab::scrollToAnchor(QString const & anchor)
+void BrowserTab::scrollToAnchor(QString const &anchor)
{
qDebug() << "scroll to anchor" << anchor;
this->ui->text_browser->scrollToAnchor(anchor);
@@ -222,7 +155,7 @@ void BrowserTab::scrollToAnchor(QString const & anchor)
void BrowserTab::reloadPage()
{
- if(current_location.isValid())
+ if (current_location.isValid())
this->navigateTo(this->current_location, DontPush);
}
@@ -233,10 +166,13 @@ void BrowserTab::toggleIsFavourite()
void BrowserTab::toggleIsFavourite(bool isFavourite)
{
- if(isFavourite) {
- this->mainWindow->favourites.add(this->current_location);
- } else {
- this->mainWindow->favourites.remove(this->current_location);
+ if (isFavourite)
+ {
+ global_favourites.add(this->current_location);
+ }
+ else
+ {
+ global_favourites.remove(this->current_location);
}
this->updateUI();
@@ -250,10 +186,11 @@ void BrowserTab::focusUrlBar()
void BrowserTab::on_url_bar_returnPressed()
{
- QUrl url { this->ui->url_bar->text() };
+ QUrl url{this->ui->url_bar->text()};
- if(url.scheme().isEmpty()) {
- url = QUrl { "gemini://" + this->ui->url_bar->text() };
+ if (url.scheme().isEmpty())
+ {
+ url = QUrl{"gemini://" + this->ui->url_bar->text()};
}
this->navigateTo(url, PushImmediate);
@@ -264,14 +201,22 @@ void BrowserTab::on_refresh_button_clicked()
reloadPage();
}
-void BrowserTab::on_requestFailed(const QString &reason)
+void BrowserTab::on_networkError(ProtocolHandler::NetworkError error_code, const QString &reason)
{
- this->setErrorMessage(QString("Request failed:\n%1").arg(reason));
+ this->setErrorMessage(QString("%1:\n%2").arg(error_code).arg(reason));
}
-void BrowserTab::on_networkError(const QString &reason)
+void BrowserTab::on_certificateRequired(const QString &reason)
{
- this->setErrorMessage(QString("Network error:\n%1").arg(reason));
+ if (not trySetClientCertificate(reason))
+ {
+ setErrorMessage(QString("The page requested a authorized client certificate, but none was provided.\r\nOriginal query was: %1").arg(reason));
+ }
+ else
+ {
+ this->navigateTo(this->current_location, DontPush);
+ }
+ this->updateUI();
}
void BrowserTab::on_requestComplete(const QByteArray &data, const QString &mime)
@@ -286,7 +231,12 @@ void BrowserTab::on_requestComplete(const QByteArray &data, const QString &mime)
ui->text_browser->setStyleSheet("");
- enum DocumentType { Text, Image, Media };
+ enum DocumentType
+ {
+ Text,
+ Image,
+ Media
+ };
DocumentType doc_type = Text;
std::unique_ptr<QTextDocument> document;
@@ -299,23 +249,27 @@ void BrowserTab::on_requestComplete(const QByteArray &data, const QString &mime)
bool plaintext_only = (global_settings.value("text_display").toString() == "plain");
- if(not plaintext_only and mime.startsWith("text/gemini")) {
+ if (not plaintext_only and mime.startsWith("text/gemini"))
+ {
document = GeminiRenderer::render(
data,
this->current_location,
doc_style,
this->outline);
}
- else if(not plaintext_only and mime.startsWith("text/gophermap")) {
+ else if (not plaintext_only and mime.startsWith("text/gophermap"))
+ {
document = GophermapRenderer::render(
data,
this->current_location,
doc_style);
}
- else if(not plaintext_only and mime.startsWith("text/finger")) {
+ else if (not plaintext_only and mime.startsWith("text/finger"))
+ {
document = PlainTextRenderer::render(data, doc_style);
}
- else if(not plaintext_only and mime.startsWith("text/html")) {
+ else if (not plaintext_only and mime.startsWith("text/html"))
+ {
document = std::make_unique<QTextDocument>();
document->setDefaultFont(doc_style.standard_font);
@@ -324,7 +278,8 @@ void BrowserTab::on_requestComplete(const QByteArray &data, const QString &mime)
document->setHtml(QString::fromUtf8(data));
}
#if defined(QT_FEATURE_textmarkdownreader)
- else if(not plaintext_only and mime.startsWith("text/markdown")) {
+ else if (not plaintext_only and mime.startsWith("text/markdown"))
+ {
document = std::make_unique<QTextDocument>();
document->setDefaultFont(doc_style.standard_font);
document->setDefaultStyleSheet(doc_style.toStyleSheet());
@@ -332,22 +287,23 @@ void BrowserTab::on_requestComplete(const QByteArray &data, const QString &mime)
document->setMarkdown(QString::fromUtf8(data));
}
#endif
- else if(mime.startsWith("text/")) {
+ else if (mime.startsWith("text/"))
+ {
document = PlainTextRenderer::render(data, doc_style);
}
- else if(mime.startsWith("image/")) {
+ else if (mime.startsWith("image/"))
+ {
doc_type = Image;
QBuffer buffer;
buffer.setData(data);
- QImageReader reader { &buffer };
+ QImageReader reader{&buffer};
reader.setAutoTransform(true);
reader.setAutoDetectImageFormat(true);
-
QImage img;
- if(reader.read(&img))
+ if (reader.read(&img))
{
auto pixmap = QPixmap::fromImage(img);
this->graphics_scene.addPixmap(pixmap);
@@ -360,7 +316,7 @@ void BrowserTab::on_requestComplete(const QByteArray &data, const QString &mime)
this->ui->graphics_browser->setScene(&graphics_scene);
- auto * invoker = new QObject();
+ auto *invoker = new QObject();
connect(invoker, &QObject::destroyed, [this]() {
this->ui->graphics_browser->fitInView(graphics_scene.sceneRect(), Qt::KeepAspectRatio);
});
@@ -368,11 +324,13 @@ void BrowserTab::on_requestComplete(const QByteArray &data, const QString &mime)
this->ui->graphics_browser->fitInView(graphics_scene.sceneRect(), Qt::KeepAspectRatio);
}
- else if(mime.startsWith("video/") or mime.startsWith("audio/")) {
+ else if (mime.startsWith("video/") or mime.startsWith("audio/"))
+ {
doc_type = Media;
this->ui->media_browser->setMedia(data, this->current_location, mime);
}
- else {
+ else
+ {
document = std::make_unique<QTextDocument>();
document->setDefaultFont(doc_style.standard_font);
document->setDefaultStyleSheet(doc_style.toStyleSheet());
@@ -384,7 +342,9 @@ Use the *File* menu to save the file to your local disk or navigate somewhere el
Info:
MIME Type: %1
File Size: %2
-)md").arg(mime).arg(IoUtil::size_human(data.size())));
+)md")
+ .arg(mime)
+ .arg(IoUtil::size_human(data.size())));
}
assert((document != nullptr) == (doc_type == Text));
@@ -408,19 +368,15 @@ File Size: %2
this->updateUI();
}
-void BrowserTab::on_protocolViolation(const QString &reason)
-{
- this->setErrorMessage(QString("Protocol violation:\n%1").arg(reason));
-}
-
void BrowserTab::on_inputRequired(const QString &query)
{
- QInputDialog dialog { this };
+ QInputDialog dialog{this};
dialog.setInputMode(QInputDialog::TextInput);
dialog.setLabelText(query);
- if(dialog.exec() != QDialog::Accepted) {
+ if (dialog.exec() != QDialog::Accepted)
+ {
setErrorMessage(QString("Site requires input:\n%1").arg(query));
return;
}
@@ -432,12 +388,20 @@ void BrowserTab::on_inputRequired(const QString &query)
void BrowserTab::on_redirected(const QUrl &uri, bool is_permanent)
{
- if(redirection_count >= 5) {
+ Q_UNUSED(is_permanent);
+
+ // TODO: Make this a setting
+ if (redirection_count >= 5)
+ {
setErrorMessage("Too many redirections!");
return;
}
- else {
- if(gemini_client.startRequest(uri)) {
+ else
+ {
+ // TODO: Implement cross-protocol redirections
+ // TODO: Implement cross-host redirection
+ if (this->current_handler->startRequest(uri))
+ {
redirection_count += 1;
this->current_location = uri;
this->ui->url_bar->setText(uri.toString());
@@ -445,88 +409,6 @@ void BrowserTab::on_redirected(const QUrl &uri, bool is_permanent)
}
}
-void BrowserTab::on_temporaryFailure(TemporaryFailure reason, const QString &info)
-{
- switch(reason)
- {
- case TemporaryFailure::cgi_error:
- setErrorMessage(QString("CGI Error\n%1").arg(info));
- break;
- case TemporaryFailure::slow_down:
- setErrorMessage(QString("Slow Down\n%1").arg(info));
- break;
- case TemporaryFailure::proxy_error:
- setErrorMessage(QString("Proxy Error\n%1").arg(info));
- break;
- case TemporaryFailure::unspecified:
- setErrorMessage(QString("Temporary Failure\n%1").arg(info));
- break;
- case TemporaryFailure::server_unavailable:
- setErrorMessage(QString("Server Unavailable\n%1").arg(info));
- break;
- }
-}
-
-void BrowserTab::on_permanentFailure(PermanentFailure reason, const QString &info)
-{
- switch(reason)
- {
- case PermanentFailure::gone:
- setErrorMessage(QString("Gone\n%1").arg(info));
- break;
- case PermanentFailure::not_found:
- setErrorMessage(QString("Not Found\n%1").arg(info));
- break;
- case PermanentFailure::bad_request:
- setErrorMessage(QString("Bad Request\n%1").arg(info));
- break;
- case PermanentFailure::unspecified:
- setErrorMessage(QString("Permanent Failure\n%1").arg(info));
- break;
- case PermanentFailure::proxy_request_required:
- setErrorMessage(QString("Proxy Request Required\n%1").arg(info));
- break;
- }
-}
-
-void BrowserTab::on_transientCertificateRequested(const QString &reason)
-{
- if(not trySetClientCertificate(reason)) {
- setErrorMessage(QString("The page requested a transient client certificate, but none was provided.\r\nOriginal query was: %1").arg(reason));
- } else {
- this->navigateTo(this->current_location, DontPush);
- }
- this->updateUI();
-}
-
-void BrowserTab::on_authorisedCertificateRequested(const QString &reason)
-{
- if(not trySetClientCertificate(reason)) {
- setErrorMessage(QString("The page requested a authorized client certificate, but none was provided.\r\nOriginal query was: %1").arg(reason));
- } else {
- this->navigateTo(this->current_location, DontPush);
- }
- this->updateUI();
-}
-
-void BrowserTab::on_certificateRejected(CertificateRejection reason, const QString &info)
-{
- switch(reason)
- {
- case CertificateRejection::unspecified:
- setErrorMessage(QString("Certificate Rejected\n%1").arg(info));
- break;
- case CertificateRejection::not_accepted:
- setErrorMessage(QString("Certificate not accepted\n%1").arg(info));
- break;
- case CertificateRejection::future_certificate_rejected:
- setErrorMessage(QString("Certificate is not yet valid\n%1").arg(info));
- break;
- case CertificateRejection::expired_certificate_rejected:
- setErrorMessage(QString("Certificate expired\n%1").arg(info));
- break;
- }
-}
void BrowserTab::on_linkHovered(const QString &url)
{
@@ -537,8 +419,7 @@ void BrowserTab::setErrorMessage(const QString &msg)
{
this->on_requestComplete(
QString("An error happened:\r\n%0").arg(msg).toUtf8(),
- "text/plain charset=utf-8"
- );
+ "text/plain charset=utf-8");
this->updateUI();
}
@@ -556,29 +437,37 @@ void BrowserTab::on_fav_button_clicked()
#include <QDesktopServices>
-
void BrowserTab::on_text_browser_anchorClicked(const QUrl &url)
{
qDebug() << url;
QUrl real_url = url;
- if(real_url.isRelative())
+ if (real_url.isRelative())
real_url = this->current_location.resolved(url);
auto support = mainWindow->protocols.isSchemeSupported(real_url.scheme());
- if(support == ProtocolSetup::Enabled) {
+ if (support == ProtocolSetup::Enabled)
+ {
this->navigateTo(real_url, PushImmediate);
- } else {
+ }
+ else
+ {
bool use_os_proxy = global_settings.value("use_os_scheme_handler").toBool();
- if(use_os_proxy) {
- if(not QDesktopServices::openUrl(url)) {
+ if (use_os_proxy)
+ {
+ if (not QDesktopServices::openUrl(url))
+ {
QMessageBox::warning(this, "Kristall", QString("Failed to start system URL handler for\r\n%1").arg(real_url.toString()));
}
- } else if(support == ProtocolSetup::Disabled) {
+ }
+ else if (support == ProtocolSetup::Disabled)
+ {
QMessageBox::warning(this, "Kristall", QString("The requested url uses a scheme that has been disabled in the settings:\r\n%1").arg(real_url.toString()));
- } else {
+ }
+ else
+ {
QMessageBox::warning(this, "Kristall", QString("The requested url cannot be processed by Kristall:\r\n%1").arg(real_url.toString()));
}
}
@@ -586,23 +475,25 @@ void BrowserTab::on_text_browser_anchorClicked(const QUrl &url)
void BrowserTab::on_text_browser_highlighted(const QUrl &url)
{
- if(url.isValid()) {
+ if (url.isValid())
+ {
QUrl real_url = url;
- if(real_url.isRelative())
+ if (real_url.isRelative())
real_url = this->current_location.resolved(url);
this->mainWindow->setUrlPreview(real_url);
}
- else {
- this->mainWindow->setUrlPreview(QUrl { });
+ else
+ {
+ this->mainWindow->setUrlPreview(QUrl{});
}
}
void BrowserTab::on_stop_button_clicked()
{
- gemini_client.cancelRequest();
- web_client.cancelRequest();
- gopher_client.cancelRequest();
- finger_client.cancelRequest();
+ if(this->current_handler != nullptr) {
+ this->current_handler->cancelRequest();
+ }
+ this->updateUI();
}
void BrowserTab::on_requestProgress(qint64 transferred)
@@ -629,31 +520,33 @@ void BrowserTab::updateUI()
this->ui->stop_button->setVisible(not this->successfully_loaded);
this->ui->fav_button->setEnabled(this->successfully_loaded);
- this->ui->fav_button->setChecked(this->mainWindow->favourites.contains(this->current_location));
+ this->ui->fav_button->setChecked(global_favourites.contains(this->current_location));
}
bool BrowserTab::trySetClientCertificate(const QString &query)
{
- CertificateSelectionDialog dialog { this };
+ CertificateSelectionDialog dialog{this};
dialog.setServerQuery(query);
- if(dialog.exec() != QDialog::Accepted) {
- this->gemini_client.disableClientCertificate();
+ if (dialog.exec() != QDialog::Accepted)
+ {
+ for(auto & handler : this->protocol_handlers) {
+ handler->disableClientCertificate();
+ }
this->ui->enable_client_cert_button->setChecked(false);
return false;
}
this->current_identitiy = dialog.identity();
- if(not current_identitiy.isValid()) {
+ if (not current_identitiy.isValid())
+ {
QMessageBox::warning(this, "Kristall", "Failed to generate temporary crypto-identitiy");
- this->gemini_client.disableClientCertificate();
this->ui->enable_client_cert_button->setChecked(false);
return false;
}
- this->gemini_client.enableClientCertificate(this->current_identitiy);
this->ui->enable_client_cert_button->setChecked(true);
return true;
@@ -661,40 +554,56 @@ bool BrowserTab::trySetClientCertificate(const QString &query)
void BrowserTab::resetClientCertificate()
{
- if(this->current_identitiy.isValid() and not this->current_identitiy.is_persistent)
+ if (this->current_identitiy.isValid() and not this->current_identitiy.is_persistent)
{
auto respo = QMessageBox::question(this, "Kristall", "You currently have a transient session active!\r\nIf you disable the session, you will not be able to restore it. Continue?");
- if(respo != QMessageBox::Yes) {
+ if (respo != QMessageBox::Yes)
+ {
this->ui->enable_client_cert_button->setChecked(true);
return;
}
}
- this->gemini_client.disableClientCertificate();
+ this->current_identitiy = CryptoIdentity();
+
+ for(auto & handler : this->protocol_handlers) {
+ handler->disableClientCertificate();
+ }
this->ui->enable_client_cert_button->setChecked(false);
}
-#include <QClipboard>
+void BrowserTab::addProtocolHandler(std::unique_ptr<ProtocolHandler> &&handler)
+{
+ connect(handler.get(), &ProtocolHandler::requestProgress, this, &BrowserTab::on_requestProgress);
+ connect(handler.get(), &ProtocolHandler::requestComplete, this, &BrowserTab::on_requestComplete);
+ connect(handler.get(), &ProtocolHandler::redirected, this, &BrowserTab::on_redirected);
+ connect(handler.get(), &ProtocolHandler::inputRequired, this, &BrowserTab::on_inputRequired);
+ connect(handler.get(), &ProtocolHandler::networkError, this, &BrowserTab::on_networkError);
+ connect(handler.get(), &ProtocolHandler::certificateRequired, this, &BrowserTab::on_certificateRequired);
+
+ this->protocol_handlers.emplace_back(std::move(handler));
+}
void BrowserTab::on_text_browser_customContextMenuRequested(const QPoint &pos)
{
QMenu menu;
QString anchor = ui->text_browser->anchorAt(pos);
- if(not anchor.isEmpty()) {
- QUrl real_url { anchor };
- if(real_url.isRelative())
+ if (not anchor.isEmpty())
+ {
+ QUrl real_url{anchor};
+ if (real_url.isRelative())
real_url = this->current_location.resolved(real_url);
- connect(menu.addAction("Follow link…"), &QAction::triggered, [this,real_url]() {
+ connect(menu.addAction("Follow link…"), &QAction::triggered, [this, real_url]() {
this->navigateTo(real_url, PushImmediate);
});
- connect(menu.addAction("Open in new tab…"), &QAction::triggered, [this,real_url]() {
+ connect(menu.addAction("Open in new tab…"), &QAction::triggered, [this, real_url]() {
mainWindow->addNewTab(false, real_url);
});
- connect(menu.addAction("Copy link"), &QAction::triggered, [this,real_url]() {
+ connect(menu.addAction("Copy link"), &QAction::triggered, [this, real_url]() {
global_clipboard->setText(real_url.toString(QUrl::FullyEncoded));
});
@@ -710,9 +619,12 @@ void BrowserTab::on_text_browser_customContextMenuRequested(const QPoint &pos)
void BrowserTab::on_enable_client_cert_button_clicked(bool checked)
{
- if(checked) {
- trySetClientCertificate(QString{ });
- } else {
+ if (checked)
+ {
+ trySetClientCertificate(QString{});
+ }
+ else
+ {
resetClientCertificate();
}
}
diff --git a/src/browsertab.hpp b/src/browsertab.hpp
index 22a07bb..402b21a 100644
--- a/src/browsertab.hpp
+++ b/src/browsertab.hpp
@@ -13,13 +13,10 @@
#include "tabbrowsinghistory.hpp"
#include "geminirenderer.hpp"
-#include "geminiclient.hpp"
-#include "webclient.hpp"
-#include "gopherclient.hpp"
-#include "fingerclient.hpp"
-
#include "cryptoidentity.hpp"
+#include "protocolhandler.hpp"
+
namespace Ui {
class BrowserTab;
}
@@ -67,28 +64,6 @@ private slots:
void on_refresh_button_clicked();
- void on_requestComplete(QByteArray const & data, QString const & mime);
-
- void on_requestFailed(QString const & reason);
-
- void on_networkError(QString const & reason);
-
- void on_protocolViolation(QString const & reason);
-
- void on_inputRequired(QString const & query);
-
- void on_redirected(QUrl const & uri, bool is_permanent);
-
- void on_temporaryFailure(TemporaryFailure reason, QString const & info);
-
- void on_permanentFailure(PermanentFailure reason, QString const & info);
-
- void on_transientCertificateRequested(QString const & reason);
-
- void on_authorisedCertificateRequested(QString const & reason);
-
- void on_certificateRejected(CertificateRejection reason, QString const & info);
-
void on_linkHovered(const QString &url);
void on_fav_button_clicked();
@@ -103,12 +78,19 @@ private slots:
void on_stop_button_clicked();
- void on_requestProgress(qint64 transferred);
-
void on_text_browser_customContextMenuRequested(const QPoint &pos);
void on_enable_client_cert_button_clicked(bool checked);
+private: // network slots
+
+ void on_requestProgress(qint64 transferred);
+ void on_requestComplete(QByteArray const & data, QString const & mime);
+ void on_redirected(QUrl const & uri, bool is_permanent);
+ void on_inputRequired(QString const & user_query);
+ void on_networkError(ProtocolHandler::NetworkError error, QString const & reason);
+ void on_certificateRequired(QString const & info);
+
private:
void setErrorMessage(QString const & msg);
@@ -119,16 +101,23 @@ private:
bool trySetClientCertificate(QString const & query);
void resetClientCertificate();
+
+ void addProtocolHandler(std::unique_ptr<ProtocolHandler> && handler);
+
+ template<typename T>
+ void addProtocolHandler() {
+ this->addProtocolHandler(std::make_unique<T>());
+ }
public:
Ui::BrowserTab *ui;
MainWindow * mainWindow;
QUrl current_location;
- GeminiClient gemini_client;
- WebClient web_client;
- GopherClient gopher_client;
- FingerClient finger_client;
+ std::vector<std::unique_ptr<ProtocolHandler>> protocol_handlers;
+
+ ProtocolHandler * current_handler;
+
int redirection_count = 0;
bool successfully_loaded = false;
diff --git a/src/filehandler.cpp b/src/filehandler.cpp
new file mode 100644
index 0000000..a3012ba
--- /dev/null
+++ b/src/filehandler.cpp
@@ -0,0 +1,43 @@
+#include "filehandler.hpp"
+
+#include <QMimeDatabase>
+#include <QUrl>
+#include <QFile>
+
+FileHandler::FileHandler()
+{
+
+}
+
+bool FileHandler::supportsScheme(const QString &scheme) const
+{
+ return (scheme == "file");
+}
+
+bool FileHandler::startRequest(const QUrl &url)
+{
+ QFile file { url.path() };
+
+ if (file.open(QFile::ReadOnly))
+ {
+ QMimeDatabase db;
+ auto mime = db.mimeTypeForUrl(url).name();
+ auto data = file.readAll();
+ emit this->requestComplete(data, mime);
+ }
+ else
+ {
+ emit this->networkError(ResourceNotFound, "The requested file does not exist!");
+ }
+ return true;
+}
+
+bool FileHandler::isInProgress() const
+{
+
+}
+
+bool FileHandler::cancelRequest()
+{
+
+}
diff --git a/src/filehandler.hpp b/src/filehandler.hpp
new file mode 100644
index 0000000..f7bbfff
--- /dev/null
+++ b/src/filehandler.hpp
@@ -0,0 +1,23 @@
+#ifndef FILEHANDLER_HPP
+#define FILEHANDLER_HPP
+
+#include <QObject>
+
+#include "protocolhandler.hpp"
+
+class FileHandler : public ProtocolHandler
+{
+ Q_OBJECT
+public:
+ FileHandler();
+
+ bool supportsScheme(QString const & scheme) const override;
+
+ bool startRequest(QUrl const & url) override;
+
+ bool isInProgress() const override;
+
+ bool cancelRequest() override;
+};
+
+#endif // FILEHANDLER_HPP
diff --git a/src/fingerclient.cpp b/src/fingerclient.cpp
index 8d0ea60..433e63b 100644
--- a/src/fingerclient.cpp
+++ b/src/fingerclient.cpp
@@ -1,7 +1,7 @@
#include "fingerclient.hpp"
#include "ioutil.hpp"
-FingerClient::FingerClient(QObject *parent) : QObject(parent)
+FingerClient::FingerClient() : ProtocolHandler(nullptr)
{
connect(&socket, &QTcpSocket::connected, this, &FingerClient::on_connected);
connect(&socket, &QTcpSocket::readyRead, this, &FingerClient::on_readRead);
@@ -13,6 +13,11 @@ FingerClient::~FingerClient()
}
+bool FingerClient::supportsScheme(const QString &scheme) const
+{
+ return (scheme == "finger");
+}
+
bool FingerClient::startRequest(const QUrl &url)
{
if(isInProgress())
diff --git a/src/fingerclient.hpp b/src/fingerclient.hpp
index 7fd2ae5..87ddc7e 100644
--- a/src/fingerclient.hpp
+++ b/src/fingerclient.hpp
@@ -5,26 +5,23 @@
#include <QTcpSocket>
#include <QUrl>
-class FingerClient : public QObject
+#include "protocolhandler.hpp"
+
+class FingerClient : public ProtocolHandler
{
Q_OBJECT
public:
- explicit FingerClient(QObject *parent = nullptr);
+ explicit FingerClient();
~FingerClient() override;
- bool startRequest(QUrl const & url);
-
- bool isInProgress() const;
-
- bool cancelRequest();
+ bool supportsScheme(QString const & scheme) const override;
-signals:
- void requestProgress(qint64 transferred);
+ bool startRequest(QUrl const & url) override;
- void requestComplete(QByteArray const & data, QString const & mime);
+ bool isInProgress() const override;
- void requestFailed(QString const & message);
+ bool cancelRequest() override;
private slots:
void on_connected();
diff --git a/src/geminiclient.cpp b/src/geminiclient.cpp
index bf29ed5..8dff65b 100644
--- a/src/geminiclient.cpp
+++ b/src/geminiclient.cpp
@@ -4,7 +4,7 @@
#include <QSslConfiguration>
#include "kristall.hpp"
-GeminiClient::GeminiClient(QObject *parent) : QObject(parent)
+GeminiClient::GeminiClient() : ProtocolHandler(nullptr)
{
connect(&socket, &QSslSocket::encrypted, this, &GeminiClient::socketEncrypted);
connect(&socket, &QSslSocket::readyRead, this, &GeminiClient::socketReadyRead);
@@ -23,6 +23,11 @@ GeminiClient::~GeminiClient()
is_receiving_body = false;
}
+bool GeminiClient::supportsScheme(const QString &scheme) const
+{
+ return (scheme == "gemini");
+}
+
bool GeminiClient::startRequest(const QUrl &url)
{
if(url.scheme() != "gemini")
@@ -60,10 +65,11 @@ bool GeminiClient::cancelRequest()
return true;
}
-void GeminiClient::enableClientCertificate(const CryptoIdentity &ident)
+bool GeminiClient::enableClientCertificate(const CryptoIdentity &ident)
{
this->socket.setLocalCertificate(ident.certificate);
this->socket.setPrivateKey(ident.private_key);
+ return true;
}
void GeminiClient::disableClientCertificate()
@@ -113,25 +119,25 @@ void GeminiClient::socketReadyRead()
if(buffer.size() <= 5) {
socket.close();
qDebug() << buffer;
- emit protocolViolation("Line is too short for valid protocol");
+ emit networkError(ProtocolViolation, "Line is too short for valid protocol");
return;
}
if(buffer[buffer.size() - 1] != '\r') {
socket.close();
qDebug() << buffer;
- emit protocolViolation("Line does not end with <CR> <LF>");
+ emit networkError(ProtocolViolation, "Line does not end with <CR> <LF>");
return;
}
if(not isdigit(buffer[0])) {
socket.close();
qDebug() << buffer;
- emit protocolViolation("First character is not a digit.");
+ emit networkError(ProtocolViolation, "First character is not a digit.");
return;
}
if(not isdigit(buffer[1])) {
socket.close();
qDebug() << buffer;
- emit protocolViolation("Second character is not a digit.");
+ emit networkError(ProtocolViolation, "Second character is not a digit.");
return;
}
// TODO: Implement stricter version
@@ -139,7 +145,7 @@ void GeminiClient::socketReadyRead()
if(not isspace(buffer[2])) {
socket.close();
qDebug() << buffer;
- emit protocolViolation("Third character is not a space.");
+ emit networkError(ProtocolViolation, "Third character is not a space.");
return;
}
@@ -175,68 +181,57 @@ void GeminiClient::socketReadyRead()
emit redirected(new_url, (secondary_code == 1));
}
else {
- emit protocolViolation("Invalid URL for redirection!");
+ emit networkError(ProtocolViolation, "Invalid URL for redirection!");
}
return;
}
case 4: { // temporary failure
- TemporaryFailure type = TemporaryFailure::unspecified;
+ NetworkError type = UnknownError;
switch(secondary_code)
{
- case 1: type = TemporaryFailure::server_unavailable; break;
- case 2: type = TemporaryFailure::cgi_error; break;
- case 3: type = TemporaryFailure::proxy_error; break;
- case 4: type = TemporaryFailure::slow_down; break;
+ case 1: type = InternalServerError; break;
+ case 2: type = InternalServerError; break;
+ case 3: type = InternalServerError; break;
+ case 4: type = UnknownError; break;
}
- emit temporaryFailure(type, meta);
+ emit networkError(type, meta);
return;
}
case 5: { // permanent failure
- PermanentFailure type = PermanentFailure::unspecified;
+ NetworkError type = UnknownError;
switch(secondary_code)
{
- case 1: type = PermanentFailure::not_found; break;
- case 2: type = PermanentFailure::gone; break;
- case 3: type = PermanentFailure::proxy_request_required; break;
- case 9: type = PermanentFailure::bad_request; break;
+ case 1: type = ResourceNotFound; break;
+ case 2: type = ResourceNotFound; break;
+ case 3: type = BadRequest; break;
+ case 9: type = BadRequest; break;
}
- emit permanentFailure(type, meta);
+ emit networkError(type, meta);
return;
}
case 6: // client certificate required
switch(secondary_code)
{
- case 1:
- emit transientCertificateRequested(meta);
- return;
-
- case 2:
- emit authorisedCertificateRequested(meta);
+ case 0:
+ emit certificateRequired(meta);
return;
- case 3:
- emit certificateRejected(CertificateRejection::not_accepted, meta);
- return;
-
- case 4:
- emit certificateRejected(CertificateRejection::future_certificate_rejected, meta);
- return;
-
- case 5:
- emit certificateRejected(CertificateRejection::expired_certificate_rejected, meta);
+ case 1:
+ emit networkError(Unauthorized, meta);
return;
default:
- emit certificateRejected(CertificateRejection::unspecified, meta);
+ case 2:
+ emit networkError(InvalidClientCertificate, meta);
return;
}
return;
default:
- emit protocolViolation("Unspecified status code used!");
+ emit networkError(ProtocolViolation, "Unspecified status code used!");
return;
}
@@ -283,6 +278,11 @@ void GeminiClient::sslErrors(QList<QSslError> const & errors)
{
ignore = true;
}
+ else
+ {
+ emit this->networkError(UntrustedHost, "The requested host is not trusted.");
+ return;
+ }
}
else if(err.error() == QSslError::UnableToVerifyFirstCertificate)
{
@@ -306,7 +306,7 @@ void GeminiClient::sslErrors(QList<QSslError> const & errors)
}
if(remaining_errors.size() > 0) {
- emit this->networkError(remaining_errors.first().errorString());
+ emit this->networkError(TlsFailure, remaining_errors.first().errorString());
}
}
@@ -319,6 +319,7 @@ void GeminiClient::socketError(QAbstractSocket::SocketError socketError)
socket.close();
} else {
// qWarning() << socketError << socket.errorString();
- emit this->networkError(socket.errorString());
+ // TODO: Make the correct error here!
+ emit this->networkError(HostNotFound, socket.errorString());
}
}
diff --git a/src/geminiclient.hpp b/src/geminiclient.hpp
index deac50b..5558200 100644
--- a/src/geminiclient.hpp
+++ b/src/geminiclient.hpp
@@ -6,74 +6,29 @@
#include <QSslSocket>
#include <QUrl>
-#include "cryptoidentity.hpp"
-
-enum class TemporaryFailure {
- unspecified,
- server_unavailable,
- cgi_error,
- proxy_error,
- slow_down,
-};
-
-enum class PermanentFailure {
- unspecified,
- not_found,
- gone,
- proxy_request_required,
- bad_request,
-};
-
-enum class CertificateRejection {
- unspecified,
- not_accepted,
- future_certificate_rejected,
- expired_certificate_rejected,
-};
+#include "protocolhandler.hpp"
-class GeminiClient : public QObject
+class GeminiClient : public ProtocolHandler
{
private:
Q_OBJECT
public:
- explicit GeminiClient(QObject *parent = nullptr);
+ explicit GeminiClient();
~GeminiClient() override;
- bool startRequest(QUrl const & url);
-
- bool isInProgress() const;
-
- bool cancelRequest();
-
- void enableClientCertificate(CryptoIdentity const & ident);
- void disableClientCertificate();
-
-signals:
- void requestProgress(qint64 transferred);
+ bool supportsScheme(QString const & scheme) const override;
- void requestComplete(QByteArray const & data, QString const & mime);
+ bool startRequest(QUrl const & url) override;
- void protocolViolation(QString const & reason);
+ bool isInProgress() const override;
- void inputRequired(QString const & query);
+ bool cancelRequest() override;
- void redirected(QUrl const & uri, bool is_permanent);
-
- void temporaryFailure(TemporaryFailure reason, QString const & info);
-
- void permanentFailure(PermanentFailure reason, QString const & info);
-
- void transientCertificateRequested(QString const & reason);
-
- void authorisedCertificateRequested(QString const & reason);
-
- void certificateRejected(CertificateRejection reason, QString const & info);
-
- void networkError(QString const & reason);
+ bool enableClientCertificate(CryptoIdentity const & ident) override;
+ void disableClientCertificate() override;
private slots:
-
void socketEncrypted();
void socketReadyRead();
@@ -84,7 +39,6 @@ private slots:
void socketError(QAbstractSocket::SocketError socketError);
-
private:
bool is_receiving_body;
diff --git a/src/gopherclient.cpp b/src/gopherclient.cpp
index 74359c6..6b19b02 100644
--- a/src/gopherclient.cpp
+++ b/src/gopherclient.cpp
@@ -1,7 +1,7 @@
#include "gopherclient.hpp"
#include "ioutil.hpp"
-GopherClient::GopherClient(QObject *parent) : QObject(parent)
+GopherClient::GopherClient(QObject *parent) : ProtocolHandler(parent)
{
connect(&socket, &QTcpSocket::connected, this, &GopherClient::on_connected);
connect(&socket, &QTcpSocket::readyRead, this, &GopherClient::on_readRead);
@@ -13,6 +13,11 @@ GopherClient::~GopherClient()
}
+bool GopherClient::supportsScheme(const QString &scheme) const
+{
+ return (scheme == "gopher");
+}
+
bool GopherClient::startRequest(const QUrl &url)
{
if(isInProgress())
diff --git a/src/gopherclient.hpp b/src/gopherclient.hpp
index f9913c5..2ead2c4 100644
--- a/src/gopherclient.hpp
+++ b/src/gopherclient.hpp
@@ -5,7 +5,9 @@
#include <QTcpSocket>
#include <QUrl>
-class GopherClient : public QObject
+#include "protocolhandler.hpp"
+
+class GopherClient : public ProtocolHandler
{
Q_OBJECT
public:
@@ -13,18 +15,13 @@ public:
~GopherClient() override;
- bool startRequest(QUrl const & url);
-
- bool isInProgress() const;
-
- bool cancelRequest();
+ bool supportsScheme(QString const & scheme) const override;
-signals:
- void requestProgress(qint64 transferred);
+ bool startRequest(QUrl const & url) override;
- void requestComplete(QByteArray const & data, QString const & mime);
+ bool isInProgress() const override;
- void requestFailed(QString const & message);
+ bool cancelRequest() override;
private slots:
void on_connected();
diff --git a/src/kristall.hpp b/src/kristall.hpp
index 00faff5..face253 100644
--- a/src/kristall.hpp
+++ b/src/kristall.hpp
@@ -6,10 +6,12 @@
#include "identitycollection.hpp"
#include "ssltrust.hpp"
+#include "favouritecollection.hpp"
extern QSettings global_settings;
extern IdentityCollection global_identities;
extern QClipboard * global_clipboard;
extern SslTrust global_trust;
+extern FavouriteCollection global_favourites;
#endif // KRISTALL_HPP
diff --git a/src/kristall.pro b/src/kristall.pro
index e46b735..4084fce 100644
--- a/src/kristall.pro
+++ b/src/kristall.pro
@@ -2,7 +2,6 @@ QT += core gui svg
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets network multimedia multimediawidgets
-CONFIG += c++17
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
@@ -19,6 +18,10 @@ LIBS += -lcrypto
QMAKE_CFLAGS += -Wno-unused-parameter
QMAKE_CXXFLAGS += -Wno-unused-parameter
+# Enable C++17
+QMAKE_CXXFLAGS += -std=c++17
+CONFIG += c++17
+
win32-msvc {
QMAKE_CFLAGS -= -Wno-unused-parameter
QMAKE_CXXFLAGS -= -Wno-unused-parameter
@@ -42,6 +45,7 @@ DEPENDPATH += $$PWD/../lib/luis-l-gist/
SOURCES += \
../lib/luis-l-gist/interactiveview.cpp \
+ abouthandler.cpp \
browsertab.cpp \
certificatehelper.cpp \
certificatemanagementdialog.cpp \
@@ -51,6 +55,7 @@ SOURCES += \
documentstyle.cpp \
elidelabel.cpp \
favouritecollection.cpp \
+ filehandler.cpp \
fingerclient.cpp \
geminiclient.cpp \
geminirenderer.cpp \
@@ -63,6 +68,7 @@ SOURCES += \
mediaplayer.cpp \
newidentitiydialog.cpp \
plaintextrenderer.cpp \
+ protocolhandler.cpp \
protocolsetup.cpp \
settingsdialog.cpp \
ssltrust.cpp \
@@ -73,6 +79,7 @@ SOURCES += \
HEADERS += \
../lib/luis-l-gist/interactiveview.hpp \
+ abouthandler.hpp \
browsertab.hpp \
certificatehelper.hpp \
certificatemanagementdialog.hpp \
@@ -82,6 +89,7 @@ HEADERS += \
documentstyle.hpp \
elidelabel.hpp \
favouritecollection.hpp \
+ filehandler.hpp \
fingerclient.hpp \
geminiclient.hpp \
geminirenderer.hpp \
@@ -94,6 +102,7 @@ HEADERS += \
mediaplayer.hpp \
newidentitiydialog.hpp \
plaintextrenderer.hpp \
+ protocolhandler.hpp \
protocolsetup.hpp \
settingsdialog.hpp \
ssltrust.hpp \
diff --git a/src/main.cpp b/src/main.cpp
index 81ace41..d2f56da 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -11,6 +11,7 @@ IdentityCollection global_identities;
QSettings global_settings { "xqTechnologies", "Kristall" };
QClipboard * global_clipboard;
SslTrust global_trust;
+FavouriteCollection global_favourites;
int main(int argc, char *argv[])
{
@@ -33,6 +34,8 @@ int main(int argc, char *argv[])
global_trust.load(global_settings);
global_settings.endGroup();
+ global_favourites.load(global_settings);
+
MainWindow w(&app);
auto urls = cli_parser.positionalArguments();
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index e7dcfeb..eb4806f 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -33,14 +33,13 @@ MainWindow::MainWindow(QApplication * app, QWidget *parent) :
this->statusBar()->addPermanentWidget(this->file_size);
this->statusBar()->addPermanentWidget(this->load_time);
- this->favourites.load(global_settings);
this->protocols.load(global_settings);
global_settings.beginGroup("Theme");
this->current_style.load(global_settings);
global_settings.endGroup();
- ui->favourites_view->setModel(&favourites);
+ ui->favourites_view->setModel(&global_favourites);
this->ui->outline_window->setVisible(false);
this->ui->history_window->setVisible(false);
@@ -58,7 +57,7 @@ MainWindow::MainWindow(QApplication * app, QWidget *parent) :
connect(this->ui->menuNavigation, &QMenu::aboutToShow, [this]() {
BrowserTab * tab = qobject_cast<BrowserTab*>(this->ui->browser_tabs->currentWidget());
if(tab != nullptr) {
- ui->actionAdd_to_favourites->setChecked(this->favourites.contains(tab->current_location));
+ ui->actionAdd_to_favourites->setChecked(global_favourites.contains(tab->current_location));
}
});
@@ -94,7 +93,6 @@ MainWindow::MainWindow(QApplication * app, QWidget *parent) :
MainWindow::~MainWindow()
{
-
this->saveSettings();
delete ui;
}
@@ -142,7 +140,7 @@ void MainWindow::setUrlPreview(const QUrl &url)
void MainWindow::saveSettings()
{
- this->favourites.save(global_settings);
+ global_favourites.save(global_settings);
this->protocols.save(global_settings);
global_settings.beginGroup("Client Identities");
@@ -191,7 +189,7 @@ void MainWindow::on_browser_tabs_currentChanged(int index)
void MainWindow::on_favourites_view_doubleClicked(const QModelIndex &index)
{
- if(auto url = this->favourites.get(index); url.isValid()) {
+ if(auto url = global_favourites.get(index); url.isValid()) {
this->addNewTab(true, url);
}
}
@@ -444,7 +442,7 @@ void MainWindow::on_history_view_customContextMenuRequested(const QPoint &pos)
void MainWindow::on_favourites_view_customContextMenuRequested(const QPoint &pos)
{
if(auto idx = this->ui->favourites_view->indexAt(pos); idx.isValid()) {
- if(QUrl url = favourites.get(idx); url.isValid()) {
+ if(QUrl url = global_favourites.get(idx); url.isValid()) {
QMenu menu;
BrowserTab * tab = qobject_cast<BrowserTab*>(this->ui->browser_tabs->currentWidget());
diff --git a/src/mainwindow.hpp b/src/mainwindow.hpp
index b929484..f0c64ae 100644
--- a/src/mainwindow.hpp
+++ b/src/mainwindow.hpp
@@ -33,9 +33,6 @@ public:
void saveSettings();
-public:
- FavouriteCollection favourites;
-
private slots:
void on_browser_tabs_currentChanged(int index);
diff --git a/src/protocolhandler.cpp b/src/protocolhandler.cpp
new file mode 100644
index 0000000..5a87bac
--- /dev/null
+++ b/src/protocolhandler.cpp
@@ -0,0 +1,17 @@
+#include "protocolhandler.hpp"
+
+ProtocolHandler::ProtocolHandler(QObject *parent) : QObject(parent)
+{
+
+}
+
+bool ProtocolHandler::enableClientCertificate(const CryptoIdentity &ident)
+{
+ Q_UNUSED(ident);
+ return true;
+}
+
+void ProtocolHandler::disableClientCertificate()
+{
+
+}
diff --git a/src/protocolhandler.hpp b/src/protocolhandler.hpp
new file mode 100644
index 0000000..f4ae9eb
--- /dev/null
+++ b/src/protocolhandler.hpp
@@ -0,0 +1,59 @@
+#ifndef GENERICPROTOCOLCLIENT_HPP
+#define GENERICPROTOCOLCLIENT_HPP
+
+#include <QObject>
+
+#include "cryptoidentity.hpp"
+
+class ProtocolHandler : public QObject
+{
+ Q_OBJECT
+public:
+ enum NetworkError {
+ UnknownError, //!< There was an unhandled network error
+ ProtocolViolation, //!< The server responded with something unexpected and violated the protocol
+ HostNotFound, //!< The host
+ ResourceNotFound, //!< The requested resource was not found on the server
+ BadRequest, //!< Our client misbehaved and did a request the server cannot understand
+ ProxyRequest, //!< We requested to
+ InternalServerError,
+ InvalidClientCertificate,
+ UntrustedHost, //!< We don't know the host, and we don't trust it
+ MistrustedHost, //!< We know the host and it's not the server identity we've seen before
+ Unauthorized, //!< The requested resource could not be accessed.
+ TlsFailure, //!< Unspecified TLS failure
+ };
+public:
+ explicit ProtocolHandler(QObject *parent = nullptr);
+
+ virtual bool supportsScheme(QString const & scheme) const = 0;
+
+ virtual bool startRequest(QUrl const & url) = 0;
+
+ virtual bool isInProgress() const = 0;
+
+ virtual bool cancelRequest() = 0;
+
+ virtual bool enableClientCertificate(CryptoIdentity const & ident);
+ virtual void disableClientCertificate();
+signals:
+ //! We successfully transferred some bytes from the server
+ void requestProgress(qint64 transferred);
+
+ //! The request completed with the given data and mime type
+ void requestComplete(QByteArray const & data, QString const & mime);
+
+ //! Server redirected us to another URL
+ void redirected(QUrl const & uri, bool is_permanent);
+
+ //! The server needs some information from the user to process this query.
+ void inputRequired(QString const & user_query);
+
+ //! There was an error while processing the request
+ void networkError(NetworkError error, QString const & reason);
+
+ //! The server wants us to use a client certificate
+ void certificateRequired(QString const & info);
+};
+
+#endif // GENERICPROTOCOLCLIENT_HPP
diff --git a/src/webclient.cpp b/src/webclient.cpp
index eb5f85e..b317692 100644
--- a/src/webclient.cpp
+++ b/src/webclient.cpp
@@ -3,8 +3,8 @@
#include <QNetworkRequest>
#include <QNetworkReply>
-WebClient::WebClient(QObject *parent) :
- QObject(parent),
+WebClient::WebClient() :
+ ProtocolHandler(nullptr),
current_reply(nullptr)
{
manager.setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
@@ -15,6 +15,11 @@ WebClient::~WebClient()
}
+bool WebClient::supportsScheme(const QString &scheme) const
+{
+ return (scheme == "https") or (scheme == "http");
+}
+
bool WebClient::startRequest(const QUrl &url)
{
if(url.scheme() != "http" and url.scheme() != "https")
@@ -67,7 +72,7 @@ void WebClient::on_finished()
if(this->current_reply->error() != QNetworkReply::NoError)
{
qDebug() << "web network error" << this->current_reply->errorString();
- emit this->requestFailed(this->current_reply->errorString());
+ emit this->networkError(UnknownError, this->current_reply->errorString());
}
else
{
diff --git a/src/webclient.hpp b/src/webclient.hpp
index f6b3b92..2a5b80d 100644
--- a/src/webclient.hpp
+++ b/src/webclient.hpp
@@ -5,29 +5,24 @@
#include <QNetworkAccessManager>
#include <QNetworkReply>
-class WebClient: public QObject
+#include "protocolhandler.hpp"
+
+class WebClient: public ProtocolHandler
{
private:
Q_OBJECT
public:
- explicit WebClient(QObject *parent = nullptr);
+ explicit WebClient();
~WebClient() override;
- bool startRequest(QUrl const & url);
-
- bool isInProgress() const;
-
- bool cancelRequest();
-
-signals:
- void requestProgress(qint64 transferred);
+ bool supportsScheme(QString const & scheme) const override;
- void requestComplete(QByteArray const & data, QString const & mime);
+ bool startRequest(QUrl const & url) override;
- void requestFailed(QString const & message);
+ bool isInProgress() const override;
- void networkError(QString const & message);
+ bool cancelRequest() override;
private slots:
void on_data();