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