File indexing completed on 2024-12-22 04:43:20

0001 /*
0002     This file is part of the KDE project.
0003 
0004     SPDX-FileCopyrightText: 2018 Stefano Crocco <stefano.crocco@alice.it>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-or-later
0007 */
0008 
0009 #include "errorschemehandler.h"
0010 
0011 #include <QBuffer>
0012 #include <QByteArray>
0013 #include <QWebEngineUrlRequestJob>
0014 #include <QRegularExpression>
0015 #include <QStandardPaths>
0016 #include <QDataStream>
0017 #include <QApplication>
0018 #include <QLocale>
0019 #include <QMimeType>
0020 #include <QMimeDatabase>
0021 #include <QDateTime>
0022 
0023 #include <KIO/Job>
0024 #include <KIconLoader>
0025 #include <KLocalizedString>
0026 
0027 #include "utils.h"
0028 
0029 using namespace WebEngine;
0030 
0031 ErrorSchemeHandler::ErrorSchemeHandler(QObject* parent):
0032     QWebEngineUrlSchemeHandler(parent), m_warningIconData(readWarningIconData())
0033 {
0034 }
0035 
0036 QString ErrorSchemeHandler::readWarningIconData() const
0037 {
0038     QString data;
0039     QString path = KIconLoader::global()->iconPath("dialog-warning", -KIconLoader::SizeHuge);
0040     if (path.isEmpty()) {
0041         return data;
0042     }
0043     QFile f(path);
0044     if (f.open(QIODevice::ReadOnly)) {
0045         QMimeDatabase db;
0046         QMimeType mime = db.mimeTypeForFile(f.fileName());
0047         data += QL1S("data:");
0048         data += mime.isValid() ? mime.name() : "application/octet-stream";
0049         data += QL1S(";base64,");
0050         data += f.readAll().toBase64();
0051     }
0052     return data;
0053 
0054 }
0055 
0056 void ErrorSchemeHandler::requestStarted(QWebEngineUrlRequestJob* job)
0057 {
0058     QBuffer *buf = new QBuffer;
0059     buf->open(QBuffer::ReadWrite);
0060     connect(buf, &QIODevice::aboutToClose, buf, &QObject::deleteLater);
0061     ErrorInfo ei = parseErrorUrl(job->requestUrl());
0062     writeErrorPage(buf, ei);
0063     buf->seek(0);
0064     job->reply("text/html", buf);
0065 }
0066 
0067 ErrorSchemeHandler::ErrorInfo ErrorSchemeHandler::parseErrorUrl(const QUrl& url)
0068 {
0069     ErrorInfo ei;
0070     ei.requestUrl = QUrl(url.fragment());
0071     if (ei.requestUrl.isValid()) {
0072         QString const query = url.query(QUrl::FullyDecoded);
0073         QRegularExpression const pattern("error=(\\d+)&errText=(.*)");
0074         QRegularExpressionMatch const match = pattern.match(query);
0075         int const error = match.captured(1).toInt();
0076         // error=0 isn't a valid error code, so 0 means it's missing from the URL
0077         if (error != 0) {
0078                 ei.code = error;
0079         }
0080         ei.text = match.captured(2);
0081     }
0082     return ei;
0083 }
0084 
0085 void ErrorSchemeHandler::writeErrorPage(QBuffer* buf, const ErrorSchemeHandler::ErrorInfo& info)
0086 {
0087     QString errorName, techName, description;
0088     QStringList causes, solutions;
0089     
0090     QByteArray raw = KIO::rawErrorDetail(info.code, info.text, &info.requestUrl);
0091     QDataStream stream(raw);
0092 
0093     stream >> errorName >> techName >> description >> causes >> solutions; 
0094 
0095     QFile file(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QL1S("webenginepart/error.html")));
0096     if (!file.open(QIODevice::ReadOnly)) {
0097         buf->write(i18n("<html><body><h3>Unable to display error message</h3>"
0098                     "<p>The error template file <em>error.html</em> could not be "
0099                     "found.</p></body></html>").toUtf8());
0100         return;
0101     }
0102 
0103     QString html(QL1S(file.readAll()));
0104 
0105     QString doc(QL1S("<h1>"));
0106     doc += i18n("The requested operation could not be completed");
0107     doc += QL1S("</h1><h2>");
0108     doc += errorName;
0109     doc += QL1S("</h2>");
0110 
0111     if (!techName.isEmpty()) {
0112         doc += QL1S("<h2>");
0113         doc += i18n("Technical Reason: %1", techName);
0114         doc += QL1S("</h2>");
0115     }
0116 
0117     doc += QL1S("<h3>");
0118     doc += i18n("Details of the Request:");
0119     doc += QL1S("</h3><ul><li>");
0120     // escape URL twice: once for i18n, and once for HTML.
0121     doc += i18n("URL: %1", info.requestUrl.toDisplayString().toHtmlEscaped().toHtmlEscaped());
0122     doc += QL1S("</li><li>");
0123 
0124     const QString protocol(info.requestUrl.scheme());
0125     if (!protocol.isEmpty()) {
0126         // escape protocol twice: once for i18n, and once for HTML.
0127         doc += i18n("Protocol: %1", protocol.toHtmlEscaped().toHtmlEscaped());
0128         doc += QL1S("</li><li>");
0129     }
0130 
0131     doc += i18n("Date and Time: %1",
0132                  QLocale().toString(QDateTime::currentDateTime(), QLocale::LongFormat));
0133     doc += QL1S("</li><li>");
0134     // escape info.text twice: once for i18n, and once for HTML.
0135     doc += i18n("Additional Information: %1", info.text.toHtmlEscaped().toHtmlEscaped());
0136     doc += QL1S("</li></ul><h3>");
0137     doc += i18n("Description:");
0138     doc += QL1S("</h3><p>");
0139     doc += description;
0140     doc += QL1S("</p>");
0141 
0142     if (!causes.isEmpty()) {
0143         doc += QL1S("<h3>");
0144         doc += i18n("Possible Causes:");
0145         doc += QL1S("</h3><ul><li>");
0146         doc += causes.join("</li><li>");
0147         doc += QL1S("</li></ul>");
0148     }
0149 
0150     if (!solutions.isEmpty()) {
0151         doc += QL1S("<h3>");
0152         doc += i18n("Possible Solutions:");
0153         doc += QL1S("</h3><ul><li>");
0154         doc += solutions.join("</li><li>");
0155         doc += QL1S("</li></ul>");
0156     }
0157     
0158     QString title(i18n("Error: %1", errorName));
0159     QString direction(QApplication::isRightToLeft() ? "rtl" : "ltr");
0160     buf->write(html.arg(title, direction, m_warningIconData, doc).toUtf8());
0161 }
0162