1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Assistant of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "helpbrowsersupport.h"
30#include "helpenginewrapper.h"
31#include "helpviewer.h"
32#include "tracer.h"
33
34#include <QtHelp/QHelpEngineCore>
35
36#include <QtNetwork/QNetworkAccessManager>
37#include <QtNetwork/QNetworkReply>
38#include <QtNetwork/QNetworkRequest>
39
40#include <QtCore/QTimer>
41#include <QtCore/QCoreApplication>
42#include <QtCore/QUrl>
43#include <QtCore/QDebug>
44
45QT_BEGIN_NAMESPACE
46
47// -- messages
48
49static const char g_htmlPage[] = "<html><head><meta http-equiv=\"content-type\" content=\"text/html; "
50 "charset=UTF-8\"><title>%1</title><style>body{padding: 3em 0em;background: #eeeeee;}"
51 "hr{color: lightgray;width: 100%;}img{float: left;opacity: .8;}#box{background: white;border: 1px solid "
52 "lightgray;width: 600px;padding: 60px;margin: auto;}h1{font-size: 130%;font-weight: bold;border-bottom: "
53 "1px solid lightgray;margin-left: 48px;}h2{font-size: 100%;font-weight: normal;border-bottom: 1px solid "
54 "lightgray;margin-left: 48px;}ul{font-size: 80%;padding-left: 48px;margin: 0;}#reloadButton{padding-left:"
55 "48px;}</style></head><body><div id=\"box\"><h1>%2</h1><h2>%3</h2><h2><b>%4</b></h2></div></body></html>";
56
57QString HelpBrowserSupport::msgError404()
58{
59 return QCoreApplication::translate(context: "HelpViewer", key: "Error 404...");
60}
61
62QString HelpBrowserSupport::msgPageNotFound()
63{
64 return QCoreApplication::translate(context: "HelpViewer", key: "The page could not be found");
65}
66
67QString HelpBrowserSupport::msgAllDocumentationSets()
68{
69 return QCoreApplication::translate(context: "HelpViewer",
70 key: "Please make sure that you have all "
71 "documentation sets installed.");
72}
73
74QString HelpBrowserSupport::msgLoadError(const QUrl &url)
75{
76 return HelpViewer::tr(s: "Error loading: %1").arg(a: url.toString());
77}
78
79QString HelpBrowserSupport::msgHtmlErrorPage(const QUrl &url)
80{
81 return QString::fromLatin1(str: g_htmlPage)
82 .arg(args: HelpBrowserSupport::msgError404(), args: HelpBrowserSupport::msgPageNotFound(),
83 args: HelpBrowserSupport::msgLoadError(url), args: HelpBrowserSupport::msgAllDocumentationSets());
84}
85
86// A QNetworkAccessManager implementing unhandled URL schema handling for WebKit-type browsers.
87
88// -- HelpNetworkReply
89
90class HelpNetworkReply : public QNetworkReply
91{
92public:
93 HelpNetworkReply(const QNetworkRequest &request, const QByteArray &fileData,
94 const QString &mimeType);
95
96 void abort() override;
97
98 qint64 bytesAvailable() const override
99 { return data.length() + QNetworkReply::bytesAvailable(); }
100
101protected:
102 qint64 readData(char *data, qint64 maxlen) override;
103
104private:
105 QByteArray data;
106 const qint64 origLen;
107};
108
109HelpNetworkReply::HelpNetworkReply(const QNetworkRequest &request,
110 const QByteArray &fileData, const QString& mimeType)
111 : data(fileData), origLen(fileData.length())
112{
113 TRACE_OBJ
114 setRequest(request);
115 setUrl(request.url());
116 setOpenMode(QIODevice::ReadOnly);
117
118 setHeader(header: QNetworkRequest::ContentTypeHeader, value: mimeType);
119 setHeader(header: QNetworkRequest::ContentLengthHeader, value: QByteArray::number(origLen));
120 QTimer::singleShot(interval: 0, receiver: this, slot: &QNetworkReply::metaDataChanged);
121 QTimer::singleShot(interval: 0, receiver: this, slot: &QNetworkReply::readyRead);
122 QTimer::singleShot(interval: 0, receiver: this, slot: &QNetworkReply::finished);
123}
124
125void HelpNetworkReply::abort()
126{
127 TRACE_OBJ
128}
129
130qint64 HelpNetworkReply::readData(char *buffer, qint64 maxlen)
131{
132 TRACE_OBJ
133 qint64 len = qMin(a: qint64(data.length()), b: maxlen);
134 if (len) {
135 memcpy(dest: buffer, src: data.constData(), n: len);
136 data.remove(index: 0, len);
137 }
138 if (!data.length())
139 QTimer::singleShot(interval: 0, receiver: this, slot: &QNetworkReply::finished);
140 return len;
141}
142
143// -- HelpRedirectNetworkReply
144
145class HelpRedirectNetworkReply : public QNetworkReply
146{
147public:
148 HelpRedirectNetworkReply(const QNetworkRequest &request, const QUrl &newUrl)
149 {
150 setRequest(request);
151 setAttribute(code: QNetworkRequest::HttpStatusCodeAttribute, value: 301);
152 setAttribute(code: QNetworkRequest::RedirectionTargetAttribute, value: newUrl);
153
154 QTimer::singleShot(interval: 0, receiver: this, slot: &QNetworkReply::finished);
155 }
156
157protected:
158 void abort() override { TRACE_OBJ }
159 qint64 readData(char*, qint64) override { TRACE_OBJ return qint64(-1); }
160};
161
162// -- HelpNetworkAccessManager
163
164class HelpNetworkAccessManager : public QNetworkAccessManager
165{
166public:
167 HelpNetworkAccessManager(QObject *parent);
168
169protected:
170 QNetworkReply *createRequest(Operation op,
171 const QNetworkRequest &request, QIODevice *outgoingData = nullptr) override;
172};
173
174HelpNetworkAccessManager::HelpNetworkAccessManager(QObject *parent)
175 : QNetworkAccessManager(parent)
176{
177 TRACE_OBJ
178}
179
180QNetworkReply *HelpNetworkAccessManager::createRequest(Operation, const QNetworkRequest &request, QIODevice*)
181{
182 TRACE_OBJ
183
184 QByteArray data;
185 const QUrl url = request.url();
186 QUrl redirectedUrl;
187 switch (HelpBrowserSupport::resolveUrl(url, targetUrl: &redirectedUrl, data: &data)) {
188 case HelpBrowserSupport::UrlRedirect:
189 return new HelpRedirectNetworkReply(request, redirectedUrl);
190 case HelpBrowserSupport::UrlLocalData: {
191 const QString mimeType = HelpViewer::mimeFromUrl(url);
192 return new HelpNetworkReply(request, data, mimeType);
193 }
194 case HelpBrowserSupport::UrlResolveError:
195 break;
196 }
197 return new HelpNetworkReply(request, HelpBrowserSupport::msgHtmlErrorPage(url: request.url()).toUtf8(),
198 QStringLiteral("text/html"));
199}
200
201QByteArray HelpBrowserSupport::fileDataForLocalUrl(const QUrl &url)
202{
203 return HelpEngineWrapper::instance().fileData(url);
204}
205
206HelpBrowserSupport::ResolveUrlResult HelpBrowserSupport::resolveUrl(const QUrl &url,
207 QUrl *targetUrlP,
208 QByteArray *dataP)
209{
210 const HelpEngineWrapper &engine = HelpEngineWrapper::instance();
211
212 const QUrl targetUrl = engine.findFile(url);
213 if (!targetUrl.isValid())
214 return UrlResolveError;
215
216 if (targetUrl != url) {
217 if (targetUrlP)
218 *targetUrlP = targetUrl;
219 return UrlRedirect;
220 }
221
222 if (dataP)
223 *dataP = HelpBrowserSupport::fileDataForLocalUrl(url: targetUrl);
224 return UrlLocalData;
225}
226
227QNetworkAccessManager *HelpBrowserSupport::createNetworkAccessManager(QObject *parent)
228{
229 return new HelpNetworkAccessManager(parent);
230}
231
232QT_END_NAMESPACE
233

source code of qttools/src/assistant/assistant/helpbrowsersupport.cpp