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 | |
21 | using namespace Attica; |
22 | |
23 | class Q_DECL_HIDDEN BaseJob::Private |
24 | { |
25 | public: |
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 | |
65 | BaseJob::BaseJob(PlatformDependent *internals) |
66 | : d(std::make_unique<Private>(args&: internals)) |
67 | { |
68 | } |
69 | |
70 | BaseJob::~BaseJob() = default; |
71 | |
72 | void 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 | |
120 | void BaseJob::start() |
121 | { |
122 | if (!d->started) { |
123 | d->started = true; |
124 | QTimer::singleShot(interval: 0, receiver: this, slot: &BaseJob::doWork); |
125 | } |
126 | } |
127 | |
128 | void 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 | |
149 | void 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 | |
155 | void 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 | |
165 | bool BaseJob::isAborted() const |
166 | { |
167 | return d->aborted; |
168 | } |
169 | |
170 | PlatformDependent *BaseJob::internals() |
171 | { |
172 | return d->m_internals; |
173 | } |
174 | |
175 | Metadata BaseJob::metadata() const |
176 | { |
177 | return d->m_metadata; |
178 | } |
179 | |
180 | void BaseJob::setMetadata(const Attica::Metadata &data) const |
181 | { |
182 | d->m_metadata = data; |
183 | } |
184 | |
185 | #include "moc_atticabasejob.cpp" |
186 | |