Alkimia API

alkdownloadengine.cpp
1/*
2 SPDX-FileCopyrightText: 2024 Ralf Habacker ralf.habacker @freenet.de
3
4 This file is part of libalkimia.
5
6 SPDX-License-Identifier: LGPL-2.1-or-later
7*/
8
9#include "alkdownloadengine.h"
10
11#include "alkdebug.h"
12#include "alkimia/alkversion.h"
13#include "alkwebpage.h"
14
15#include <QEventLoop>
16#include <QTimer>
17#ifdef BUILD_WITH_QTNETWORK
18#include <QNetworkAccessManager>
19#include <QNetworkReply>
20#endif
21
22#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
23#include <klocalizedstring.h>
24#else
25#include <KGlobal>
26#include <KLocale>
27#endif
28
29class AlkDownloadEngine::Private : public QObject {
31public:
32 enum Result {
33 NoError = 0,
34 Redirect = 2,
35 TimeoutError,
36 FileNotFoundError,
37 OtherError,
38 };
39
41 QString m_acceptLanguage;
42 QEventLoop *m_eventLoop{nullptr};
43 Type m_type{DefaultEngine};
44 int m_timeout{-1};
45 QUrl m_url;
46#ifdef BUILD_WITH_QTNETWORK
47 bool downloadUrlQt(const QUrl& url);
48#endif
49 bool downloadUrlWithJavaScriptEngine(const QUrl &url);
50 AlkWebPage *m_webPage{nullptr};
51 bool m_webPageCreated{false};
52
53 Private(AlkDownloadEngine *p, QObject *parent)
55 , m_p(p)
56 {}
57
58 ~Private()
59 {
60 if (m_webPageCreated)
61 delete m_webPage;
62 }
63
64public Q_SLOTS:
65#ifdef BUILD_WITH_QTNETWORK
66 void downloadUrlDoneQt(QNetworkReply *reply);
67#endif
68#if QT_VERSION < QT_VERSION_CHECK(5,0,0) || defined(BUILD_WITH_WEBKIT) || defined(BUILD_WITH_WEBENGINE)
69 void slotFinishedJavaScriptEngine(bool ok);
70#endif
71 void slotLoadStarted();
72 void slotLoadTimeout();
73 void slotLoadRedirectedTo(const QUrl &url);
74};
75
76void AlkDownloadEngine::Private::slotLoadStarted()
77{
78 Q_EMIT m_p->started(m_url);
79}
80
81void AlkDownloadEngine::Private::slotLoadTimeout()
82{
83 Q_EMIT m_p->timeout(m_url);
84 if (m_eventLoop)
85 m_eventLoop->exit(Result::TimeoutError);
86}
87
88void AlkDownloadEngine::Private::slotLoadRedirectedTo(const QUrl &url)
89{
90 Q_EMIT m_p->redirected(m_url, url);
91 m_url = url;
92}
93
94#ifdef BUILD_WITH_QTNETWORK
95void AlkDownloadEngine::Private::downloadUrlDoneQt(QNetworkReply *reply)
96{
97 Result result = Result::NoError;
98 for (const auto &header: reply->request().rawHeaderList())
99 alkDebug() << "request header" << header << reply->request().rawHeader(header);
100
101 alkDebug() << "reply header" << reply->rawHeaderPairs();
102
103 if (reply->error() == QNetworkReply::NoError) {
105 if (!newUrl.isEmpty() && newUrl != reply->url()) {
106 slotLoadRedirectedTo(reply->url().resolved(newUrl));
107 result = Result::Redirect;
108 } else {
109 //alkDebug() << "Downloaded data from" << reply->url();
110 Q_EMIT m_p->finished(reply->url(), reply->readAll());
111 }
112 } else {
113 Q_EMIT m_p->error(reply->url(), reply->errorString());
114 result = Result::OtherError;
115 }
116 m_eventLoop->exit(result);
117}
118
119bool AlkDownloadEngine::Private::downloadUrlQt(const QUrl &url)
120{
121 QNetworkAccessManager manager(this);
122 connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadUrlDoneQt(QNetworkReply*)));
123
124 m_url = url;
125 QNetworkRequest request;
126 request.setUrl(url);
127 request.setRawHeader("User-Agent", "alkimia " ALK_VERSION_STRING);
128 if (!m_acceptLanguage.isEmpty())
129 request.setRawHeader("Accept-Language: ", m_acceptLanguage.toLocal8Bit());
130 manager.get(request);
131
132 m_eventLoop = new QEventLoop;
133 Q_EMIT m_p->started(m_url);
134
135 if (m_timeout != -1)
136 QTimer::singleShot(m_timeout, this, SLOT(slotLoadTimeout()));
137
138 Result result = static_cast<Result>(m_eventLoop->exec(QEventLoop::ExcludeUserInputEvents));
139 delete m_eventLoop;
140 m_eventLoop = nullptr;
141 if (result == Result::Redirect) {
142 QNetworkRequest req;
143 req.setUrl(m_url);
144 req.setRawHeader("User-Agent", "alkimia " ALK_VERSION_STRING);
145 manager.get(req);
146
147 m_eventLoop = new QEventLoop;
148 Q_EMIT m_p->started(m_url);
149
150 if (m_timeout != -1)
151 QTimer::singleShot(m_timeout, this, SLOT(slotLoadTimeout()));
152
153 result = static_cast<Result>(m_eventLoop->exec(QEventLoop::ExcludeUserInputEvents));
154 delete m_eventLoop;
155 m_eventLoop = nullptr;
156 }
157 return result == Result::NoError;
158}
159#endif
160
161#if defined(BUILD_WITH_WEBKIT) || defined(BUILD_WITH_WEBENGINE)
162void AlkDownloadEngine::Private::slotFinishedJavaScriptEngine(bool ok)
163{
164 Result result = Result::NoError;
165 if (!ok) {
166 Q_EMIT m_p->error(m_url, QString());
167 result = Result::OtherError;
168 } else {
169 if (m_type == JavaScriptEngineCSS)
170 Q_EMIT m_p->finishedPage(m_url, m_webPage);
171 else
172 Q_EMIT m_p->finished(m_url, m_webPage->toHtml());
173 }
174 if (m_eventLoop)
175 m_eventLoop->exit(result);
176}
177
178bool AlkDownloadEngine::Private::downloadUrlWithJavaScriptEngine(const QUrl &url)
179{
180 if (!m_webPage) {
181 m_webPage = new AlkWebPage;
182 m_webPageCreated = true;
183 }
184 connect(m_webPage, SIGNAL(loadStarted()), this, SLOT(slotLoadStarted()));
185 connect(m_webPage, SIGNAL(loadFinished(bool)), this, SLOT(slotFinishedJavaScriptEngine(bool)));
186 connect(m_webPage, SIGNAL(loadRedirectedTo(QUrl)), this, SLOT(slotLoadRedirectedTo(QUrl)));
187 m_eventLoop = new QEventLoop;
188
189 int saveTimeout = m_webPage->timeout();
190 if (m_timeout != -1) {
191 m_webPage->setTimeout(m_timeout);
192 QTimer::singleShot(m_timeout, this, SLOT(slotLoadTimeout()));
193 }
194
195 m_url = url;
196 m_webPage->load(url, m_acceptLanguage);
197 Result result = static_cast<Result>(m_eventLoop->exec());
198 delete m_eventLoop;
199 m_eventLoop = nullptr;
200
201 if (m_timeout != -1)
202 m_webPage->setTimeout(saveTimeout);
203
204 if (!m_webPageCreated) {
205 disconnect(m_webPage, SIGNAL(loadStarted()), this, SIGNAL(slotLoadStarted()));
206 disconnect(m_webPage, SIGNAL(loadFinished(bool)), this, SLOT(slotFinishedJavaScriptEngine(bool)));
207 disconnect(m_webPage, SIGNAL(loadRedirectedTo(QUrl)), this, SLOT(slotLoadRedirectedTo(QUrl)));
208 }
209 return result == NoError;
210}
211#endif
212
214 : QObject{parent}
215 , d(new Private(this, parent))
216{
217}
218
223
225{
226 d->m_webPage = webPage;
227}
228
230{
231 d->m_timeout = timeout;
232}
233
235{
236 return d->m_timeout;
237}
238
240{
241 d->m_type = type;
242 switch(type) {
243 case DefaultEngine:
244#ifdef BUILD_WITH_QTNETWORK
245 case QtEngine:
246 return d->downloadUrlQt(url);
247#endif
248 case JavaScriptEngine:
249#if defined(BUILD_WITH_WEBKIT)
250 case WebKitEngine:
252 return d->downloadUrlWithJavaScriptEngine(url);
253#elif defined(BUILD_WITH_WEBENGINE)
254 case WebEngine:
255 return d->downloadUrlWithJavaScriptEngine(url);
256#endif
257 default:
258 return false;
259 }
260 return false;
261}
262
264{
265 d->m_acceptLanguage = language;
266}
267
268#include "alkdownloadengine.moc"
Wrapper for debug output.
The DownloadEngine class provides a general interface for the supported implementation for downloadin...
@ JavaScriptEngine
Use an HTML engine with Javascript support.
@ JavaScriptEngineCSS
Use an HTML engine with Javascript support,.
@ DefaultEngine
Use Qt network support.
void setTimeout(int timeout=-1)
Set timeout for download operation A timeout value of -1 disables timeout support.
void started(const QUrl &url)
Is emitted if the download has been started.
bool downloadUrl(const QUrl &url, Type type)
Download content from a URL using the engine specified by the type.
void error(const QUrl &url, const QString &message)
Emitted in case of of errors.
void finishedPage(const QUrl &url, AlkWebPage *page)
Is emitted if the type parameter of AlkDownloadEngine::downloadUrl() is JavaScriptEngineCSS.
void redirected(const QUrl &url, const QUrl &newUrl)
Is emitted if the url has been redirected.
void finished(const QUrl &url, const QString &data)
Is emitted if the type parameter of AlkDownloadEngine::downloadUrl() is AlkDownloadEngine::DefaultEng...
AlkDownloadEngine(QObject *parent=nullptr)
Constructor.
void setWebPage(AlkWebPage *webPage)
Specifies a custom instance of the HTML engine that is used to download content.
void setAcceptedLanguage(const QString &language)
Set specific language to be accepted for the download.
~AlkDownloadEngine()
Destructor.
int timeout()
Return timeout for download operation.
The AlkWebPage class provides an object to load and view web documents to provide functionality like ...
Definition alkwebpage.h:100
QString errorString() const const
QByteArray readAll()
QVariant attribute(QNetworkRequest::Attribute code) const const
NetworkError error() const const
const QList< RawHeaderPair > & rawHeaderPairs() const const
QNetworkRequest request() const const
QUrl url() const const
QByteArray rawHeader(const QByteArray &headerName) const const
QList< QByteArray > rawHeaderList() const const
void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
void setUrl(const QUrl &url)
Q_EMITQ_EMIT
Q_OBJECTQ_OBJECT
Q_SLOTSQ_SLOTS
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QObject * parent() const const
bool isEmpty() const const
QByteArray toLocal8Bit() const const
bool isEmpty() const const
QUrl resolved(const QUrl &relative) const const
QUrl toUrl() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:01:13 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.