diff options
| author | Felix (xq) Queißner <git@mq32.de> | 2020-06-16 22:01:59 +0200 |
|---|---|---|
| committer | Felix (xq) Queißner <git@mq32.de> | 2020-06-16 22:01:59 +0200 |
| commit | a3f3e3933c4a2522e233917a6795c6e9d677e65c (patch) | |
| tree | 6e1bd483bbd5e8ca6fee4566e544de48bfa754a6 /src | |
| parent | bc18d9356828f1ae40d3b4ce5432b30ca13cfc15 (diff) | |
| download | kristall-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.gemini | 2 | ||||
| -rw-r--r-- | src/abouthandler.cpp | 60 | ||||
| -rw-r--r-- | src/abouthandler.hpp | 23 | ||||
| -rw-r--r-- | src/browsertab.cpp | 470 | ||||
| -rw-r--r-- | src/browsertab.hpp | 55 | ||||
| -rw-r--r-- | src/filehandler.cpp | 43 | ||||
| -rw-r--r-- | src/filehandler.hpp | 23 | ||||
| -rw-r--r-- | src/fingerclient.cpp | 7 | ||||
| -rw-r--r-- | src/fingerclient.hpp | 19 | ||||
| -rw-r--r-- | src/geminiclient.cpp | 81 | ||||
| -rw-r--r-- | src/geminiclient.hpp | 64 | ||||
| -rw-r--r-- | src/gopherclient.cpp | 7 | ||||
| -rw-r--r-- | src/gopherclient.hpp | 17 | ||||
| -rw-r--r-- | src/kristall.hpp | 2 | ||||
| -rw-r--r-- | src/kristall.pro | 11 | ||||
| -rw-r--r-- | src/main.cpp | 3 | ||||
| -rw-r--r-- | src/mainwindow.cpp | 12 | ||||
| -rw-r--r-- | src/mainwindow.hpp | 3 | ||||
| -rw-r--r-- | src/protocolhandler.cpp | 17 | ||||
| -rw-r--r-- | src/protocolhandler.hpp | 59 | ||||
| -rw-r--r-- | src/webclient.cpp | 11 | ||||
| -rw-r--r-- | src/webclient.hpp | 21 |
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(); |
