1/*
2 This file is part of KDE.
3
4 SPDX-FileCopyrightText: 2008 Cornelius Schumacher <schumacher@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7*/
8
9#include "atticabasejob.h"
10
11#include <QAuthenticator>
12#include <QDebug>
13#include <QNetworkReply>
14#include <QTimer>
15
16#include "platformdependent.h"
17#include "platformdependent_v3.h"
18#include <attica_debug.h>
19#include <atticautils.h>
20
21using namespace Attica;
22
23class Q_DECL_HIDDEN BaseJob::Private
24{
25public:
26 Metadata m_metadata;
27 PlatformDependent *m_internals;
28 QPointer<QNetworkReply> m_reply;
29 bool aborted{false};
30 bool started = false;
31
32 Private(PlatformDependent *internals)
33 : m_internals(internals)
34 {
35 }
36
37 bool redirection(QUrl &newUrl) const
38 {
39 if (m_reply == nullptr || m_reply->error() != QNetworkReply::NoError) {
40 return false;
41 }
42
43 const int httpStatusCode = m_reply->attribute(code: QNetworkRequest::HttpStatusCodeAttribute).toInt();
44 if (httpStatusCode == 301 // Moved Permanently
45 || httpStatusCode == 302 // Found
46 || httpStatusCode == 303 // See Other
47 || httpStatusCode == 307) { // Temporary Redirect
48 QNetworkRequest request = m_reply->request();
49 QUrl redirectUrl(m_reply->attribute(code: QNetworkRequest::RedirectionTargetAttribute).toUrl());
50 if (redirectUrl.isRelative()) {
51 QUrl baseUrl(request.url());
52 newUrl = baseUrl.resolved(relative: redirectUrl);
53 qCDebug(ATTICA) << "resolving relative URL redirection to" << newUrl.toString();
54 } else {
55 newUrl = redirectUrl;
56 qCDebug(ATTICA) << "resolving absolute URL redirection to" << newUrl.toString();
57 }
58 return true;
59 }
60
61 return false;
62 }
63};
64
65BaseJob::BaseJob(PlatformDependent *internals)
66 : d(std::make_unique<Private>(args&: internals))
67{
68}
69
70BaseJob::~BaseJob() = default;
71
72void BaseJob::dataFinished()
73{
74 if (!d->m_reply) {
75 return;
76 }
77
78 bool error = d->m_reply->error() != QNetworkReply::NoError && d->m_reply->error() != QNetworkReply::OperationCanceledError;
79
80 // handle redirections automatically
81 QUrl newUrl;
82 if (d->redirection(newUrl)) {
83 // qCDebug(ATTICA) << "BaseJob::dataFinished" << newUrl;
84 QNetworkRequest request = d->m_reply->request();
85 QNetworkAccessManager::Operation operation = d->m_reply->operation();
86 if (newUrl.isValid() && operation == QNetworkAccessManager::GetOperation) {
87 d->m_reply->deleteLater();
88 // reissue same request with different Url
89 request.setUrl(newUrl);
90 d->m_reply = internals()->get(request);
91 connect(sender: d->m_reply, signal: &QNetworkReply::finished, context: this, slot: &BaseJob::dataFinished);
92 return;
93 }
94 error = true;
95 }
96
97 if (error) {
98 d->m_metadata.setError(Metadata::NetworkError);
99 d->m_metadata.setStatusCode(d->m_reply->attribute(code: QNetworkRequest::HttpStatusCodeAttribute).toInt());
100 d->m_metadata.setStatusString(d->m_reply->errorString());
101 d->m_metadata.setHeaders(d->m_reply->rawHeaderPairs());
102 } else if (d->m_reply->error() == QNetworkReply::OperationCanceledError) {
103 d->m_metadata.setError(Metadata::NoError);
104 } else {
105 QByteArray data = d->m_reply->readAll();
106 // qCDebug(ATTICA) << "XML Returned:\n" << data;
107 parse(xml: QString::fromUtf8(utf8: data.constData()));
108 if (d->m_metadata.statusCode() >= 100 && d->m_metadata.statusCode() < 200) {
109 d->m_metadata.setError(Metadata::NoError);
110 } else {
111 d->m_metadata.setError(Metadata::OcsError);
112 }
113 }
114 Q_EMIT finished(job: this);
115
116 d->m_reply->deleteLater();
117 deleteLater();
118}
119
120void BaseJob::start()
121{
122 if (!d->started) {
123 d->started = true;
124 QTimer::singleShot(interval: 0, receiver: this, slot: &BaseJob::doWork);
125 }
126}
127
128void BaseJob::doWork()
129{
130 if (d->aborted) {
131 return;
132 }
133
134 auto platformDependentV3 = dynamic_cast<Attica::PlatformDependentV3 *>(d->m_internals);
135 if (platformDependentV3 && !platformDependentV3->isReady()) {
136 connect(sender: platformDependentV3, signal: &Attica::PlatformDependentV3::readyChanged, context: this, slot: &BaseJob::doWork);
137 return;
138 }
139
140 d->m_reply = executeRequest();
141 qCDebug(ATTICA) << "executing" << Utils::toString(operation: d->m_reply->operation()) << "request for" << d->m_reply->url();
142 connect(sender: d->m_reply, signal: &QNetworkReply::finished, context: this, slot: &BaseJob::dataFinished);
143 connect(sender: d->m_reply->manager(), signal: &QNetworkAccessManager::authenticationRequired, context: this, slot: &BaseJob::authenticationRequired);
144 connect(sender: d->m_reply, signal: &QNetworkReply::errorOccurred, slot: [](QNetworkReply::NetworkError code) {
145 qCDebug(ATTICA) << "error found" << code;
146 });
147}
148
149void BaseJob::authenticationRequired(QNetworkReply *reply, QAuthenticator *auth)
150{
151 auth->setUser(reply->request().attribute(code: (QNetworkRequest::Attribute)BaseJob::UserAttribute).toString());
152 auth->setPassword(reply->request().attribute(code: (QNetworkRequest::Attribute)BaseJob::PasswordAttribute).toString());
153}
154
155void BaseJob::abort()
156{
157 d->aborted = true;
158 if (d->m_reply) {
159 d->m_reply->abort();
160 d->m_reply->deleteLater();
161 }
162 deleteLater();
163}
164
165bool BaseJob::isAborted() const
166{
167 return d->aborted;
168}
169
170PlatformDependent *BaseJob::internals()
171{
172 return d->m_internals;
173}
174
175Metadata BaseJob::metadata() const
176{
177 return d->m_metadata;
178}
179
180void BaseJob::setMetadata(const Attica::Metadata &data) const
181{
182 d->m_metadata = data;
183}
184
185#include "moc_atticabasejob.cpp"
186

source code of attica/src/atticabasejob.cpp