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#include <QNetworkAccessManager>
18#include <QNetworkReply>
19
20#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
21#include <klocalizedstring.h>
22#else
23#include <KGlobal>
24#include <KLocale>
25#endif
26
27class AlkDownloadEngine::Private : public QObject {
29public:
30 enum Result {
31 NoError = 0,
32 Redirect = 2,
33 TimeoutError,
34 FileNotFoundError,
35 OtherError,
36 };
37
39 QString m_acceptLanguage;
40 QEventLoop *m_eventLoop{nullptr};
41 Type m_type{DefaultEngine};
42 int m_timeout{-1};
43 QUrl m_url;
44 bool downloadUrlQt(const QUrl& url);
45 bool downloadUrlWithJavaScriptEngine(const QUrl &url);
46 AlkWebPage *m_webPage{nullptr};
47 bool m_webPageCreated{false};
48
49 Private(AlkDownloadEngine *p, QObject *parent)
51 , m_p(p)
52 {}
53
54 ~Private()
55 {
56 if (m_webPageCreated)
57 delete m_webPage;
58 }
59
60public Q_SLOTS:
61 void downloadUrlDoneQt(QNetworkReply *reply);
62#if QT_VERSION < QT_VERSION_CHECK(5,0,0) || defined(BUILD_WITH_WEBKIT) || defined(BUILD_WITH_WEBENGINE)
63 void slotFinishedJavaScriptEngine(bool ok);
64#endif
65 void slotLoadStarted();
66 void slotLoadTimeout();
67 void slotLoadRedirectedTo(const QUrl &url);
68};
69
70void AlkDownloadEngine::Private::slotLoadStarted()
71{
72 Q_EMIT m_p->started(m_url);
73}
74
75void AlkDownloadEngine::Private::slotLoadTimeout()
76{
77 Q_EMIT m_p->timeout(m_url);
78 if (m_eventLoop)
79 m_eventLoop->exit(Result::TimeoutError);
80}
81
82void AlkDownloadEngine::Private::slotLoadRedirectedTo(const QUrl &url)
83{
84 Q_EMIT m_p->redirected(m_url, url);
85 m_url = url;
86}
87
88void AlkDownloadEngine::Private::downloadUrlDoneQt(QNetworkReply *reply)
89{
90 Result result = Result::NoError;
91 for (const auto &header: reply->request().rawHeaderList())
92 alkDebug() << "request header" << header << reply->request().rawHeader(header);
93
94 alkDebug() << "reply header" << reply->rawHeaderPairs();
95
96 if (reply->error() == QNetworkReply::NoError) {
98 if (!newUrl.isEmpty() && newUrl != reply->url()) {
99 slotLoadRedirectedTo(reply->url().resolved(newUrl));
100 result = Result::Redirect;
101 } else {
102 //alkDebug() << "Downloaded data from" << reply->url();
103 Q_EMIT m_p->finished(reply->url(), reply->readAll());
104 }
105 } else {
106 Q_EMIT m_p->error(reply->url(), reply->errorString());
107 result = Result::OtherError;
108 }
109 m_eventLoop->exit(result);
110}
111
112bool AlkDownloadEngine::Private::downloadUrlQt(const QUrl &url)
113{
114 QNetworkAccessManager manager(this);
115 connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadUrlDoneQt(QNetworkReply*)));
116
117 m_url = url;
118 QNetworkRequest request;
119 request.setUrl(url);
120 request.setRawHeader("User-Agent", "alkimia " ALK_VERSION_STRING);
121 if (!m_acceptLanguage.isEmpty())
122 request.setRawHeader("Accept-Language: ", m_acceptLanguage.toLocal8Bit());
123 manager.get(request);
124
125 m_eventLoop = new QEventLoop;
126 Q_EMIT m_p->started(m_url);
127
128 if (m_timeout != -1)
129 QTimer::singleShot(m_timeout, this, SLOT(slotLoadTimeout()));
130
131 Result result = static_cast<Result>(m_eventLoop->exec(QEventLoop::ExcludeUserInputEvents));
132 delete m_eventLoop;
133 m_eventLoop = nullptr;
134 if (result == Result::Redirect) {
135 QNetworkRequest req;
136 req.setUrl(m_url);
137 req.setRawHeader("User-Agent", "alkimia " ALK_VERSION_STRING);
138 manager.get(req);
139
140 m_eventLoop = new QEventLoop;
141 Q_EMIT m_p->started(m_url);
142
143 if (m_timeout != -1)
144 QTimer::singleShot(m_timeout, this, SLOT(slotLoadTimeout()));
145
146 result = static_cast<Result>(m_eventLoop->exec(QEventLoop::ExcludeUserInputEvents));
147 delete m_eventLoop;
148 m_eventLoop = nullptr;
149 }
150 return result == Result::NoError;
151}
152
153#if defined(BUILD_WITH_WEBKIT) || defined(BUILD_WITH_WEBENGINE)
154void AlkDownloadEngine::Private::slotFinishedJavaScriptEngine(bool ok)
155{
156 Result result = Result::NoError;
157 if (!ok) {
158 Q_EMIT m_p->error(m_url, QString());
159 result = Result::OtherError;
160 } else {
161 if (m_type == JavaScriptEngineCSS)
162 Q_EMIT m_p->finishedPage(m_url, m_webPage);
163 else
164 Q_EMIT m_p->finished(m_url, m_webPage->toHtml());
165 }
166 if (m_eventLoop)
167 m_eventLoop->exit(result);
168}
169
170bool AlkDownloadEngine::Private::downloadUrlWithJavaScriptEngine(const QUrl &url)
171{
172 if (!m_webPage) {
173 m_webPage = new AlkWebPage;
174 m_webPageCreated = true;
175 }
176 connect(m_webPage, SIGNAL(loadStarted()), this, SLOT(slotLoadStarted()));
177 connect(m_webPage, SIGNAL(loadFinished(bool)), this, SLOT(slotFinishedJavaScriptEngine(bool)));
178 connect(m_webPage, SIGNAL(loadRedirectedTo(QUrl)), this, SLOT(slotLoadRedirectedTo(QUrl)));
179 m_eventLoop = new QEventLoop;
180
181 int saveTimeout = m_webPage->timeout();
182 if (m_timeout != -1) {
183 m_webPage->setTimeout(m_timeout);
184 QTimer::singleShot(m_timeout, this, SLOT(slotLoadTimeout()));
185 }
186
187 m_url = url;
188 m_webPage->load(url, m_acceptLanguage);
189 Result result = static_cast<Result>(m_eventLoop->exec());
190 delete m_eventLoop;
191 m_eventLoop = nullptr;
192
193 if (m_timeout != -1)
194 m_webPage->setTimeout(saveTimeout);
195
196 if (!m_webPageCreated) {
197 disconnect(m_webPage, SIGNAL(loadStarted()), this, SIGNAL(slotLoadStarted()));
198 disconnect(m_webPage, SIGNAL(loadFinished(bool)), this, SLOT(slotFinishedJavaScriptEngine(bool)));
199 disconnect(m_webPage, SIGNAL(loadRedirectedTo(QUrl)), this, SLOT(slotLoadRedirectedTo(QUrl)));
200 }
201 return result == NoError;
202}
203#endif
204
206 : QObject{parent}
207 , d(new Private(this, parent))
208{
209}
210
215
217{
218 d->m_webPage = webPage;
219}
220
222{
223 d->m_timeout = timeout;
224}
225
227{
228 return d->m_timeout;
229}
230
232{
233 d->m_type = type;
234 switch(type) {
235 case DefaultEngine:
236 case QtEngine:
237 return d->downloadUrlQt(url);
238 case JavaScriptEngine:
239#if defined(BUILD_WITH_WEBKIT)
240 case WebKitEngine:
242 return d->downloadUrlWithJavaScriptEngine(url);
243#elif defined(BUILD_WITH_WEBENGINE)
244 case WebEngine:
245 return d->downloadUrlWithJavaScriptEngine(url);
246#endif
247 default:
248 return false;
249 }
250 return false;
251}
252
254{
255 d->m_acceptLanguage = language;
256}
257
258#include "alkdownloadengine.moc"
Wrapper for debug output.
@ 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)
QObject(QObject *parent)
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-2025 The KDE developers.
Generated on Thu Jan 23 2025 18:59:03 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.