Gravatar

gravatarresolvurljob.cpp
1/*
2 SPDX-FileCopyrightText: 2015-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "gravatarresolvurljob.h"
8using namespace Qt::Literals::StringLiterals;
9
10#include "gravatar_debug.h"
11#include "misc/gravatarcache.h"
12#include "misc/hash.h"
13#include <PimCommon/NetworkManager>
14
15#include <QCryptographicHash>
16#include <QNetworkReply>
17#include <QUrlQuery>
18
19using namespace Gravatar;
20
21class Gravatar::GravatarResolvUrlJobPrivate
22{
23public:
24 GravatarResolvUrlJobPrivate() = default;
25 QPixmap mPixmap;
26 QString mEmail;
27 Hash mCalculatedHash;
28 QNetworkAccessManager *mNetworkAccessManager = nullptr;
29 int mSize = 80;
30
31 enum Backend {
32 None = 0x0,
33 Libravatar = 0x1,
34 Gravatar = 0x2
35 };
36 int mBackends = Gravatar;
37
38 bool mHasGravatar = false;
39 bool mUseDefaultPixmap = false;
40};
41
42GravatarResolvUrlJob::GravatarResolvUrlJob(QObject *parent)
43 : QObject(parent)
44 , d(new Gravatar::GravatarResolvUrlJobPrivate)
45{
46}
47
48GravatarResolvUrlJob::~GravatarResolvUrlJob() = default;
49
50bool GravatarResolvUrlJob::canStart() const
51{
52 if (PimCommon::NetworkManager::self()->isOnline()) {
53 // qCDebug(GRAVATAR_LOG) << "email " << d->mEmail;
54 return !d->mEmail.trimmed().isEmpty() && (d->mEmail.contains(QLatin1Char('@')));
55 } else {
56 return false;
57 }
58}
59
60QUrl GravatarResolvUrlJob::generateGravatarUrl(bool useLibravatar)
61{
62 return createUrl(useLibravatar);
63}
64
65bool GravatarResolvUrlJob::hasGravatar() const
66{
67 return d->mHasGravatar;
68}
69
70void GravatarResolvUrlJob::startNetworkManager(const QUrl &url)
71{
72 if (!d->mNetworkAccessManager) {
73 d->mNetworkAccessManager = new QNetworkAccessManager(this);
74 d->mNetworkAccessManager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
75 d->mNetworkAccessManager->setStrictTransportSecurityEnabled(true);
76 d->mNetworkAccessManager->enableStrictTransportSecurityStore(true);
77 connect(d->mNetworkAccessManager, &QNetworkAccessManager::finished, this, &GravatarResolvUrlJob::slotFinishLoadPixmap);
78 }
79
80 QNetworkRequest req(url);
82 req.setAttribute(QNetworkRequest::Http2AllowedAttribute, true);
83 d->mNetworkAccessManager->get(req);
84}
85
86void GravatarResolvUrlJob::start()
87{
88 if (d->mBackends == GravatarResolvUrlJobPrivate::None) {
89 d->mBackends = GravatarResolvUrlJobPrivate::Gravatar; // default is Gravatar if nothing else is selected
90 }
91
92 d->mHasGravatar = false;
93 if (canStart()) {
94 processNextBackend();
95 } else {
96 qCDebug(GRAVATAR_LOG) << "Gravatar can not start";
98 }
99}
100
101void GravatarResolvUrlJob::processNextBackend()
102{
103 if (d->mHasGravatar || d->mBackends == GravatarResolvUrlJobPrivate::None) {
104 Q_EMIT finished(this);
105 deleteLater();
106 return;
107 }
108
109 QUrl url;
110 if (d->mBackends & GravatarResolvUrlJobPrivate::Libravatar) {
111 d->mBackends &= ~GravatarResolvUrlJobPrivate::Libravatar;
112 url = createUrl(true);
113 } else if (d->mBackends & GravatarResolvUrlJobPrivate::Gravatar) {
114 d->mBackends &= ~GravatarResolvUrlJobPrivate::Gravatar;
115 url = createUrl(false);
116 }
117
118 // qDebug() << " url " << url;
119 Q_EMIT resolvUrl(url);
120 if (!cacheLookup(d->mCalculatedHash)) {
121 startNetworkManager(url);
122 } else {
123 processNextBackend();
124 }
125}
126
127void GravatarResolvUrlJob::slotFinishLoadPixmap(QNetworkReply *reply)
128{
129 if (reply->error() == QNetworkReply::NoError) {
130 const QByteArray data = reply->readAll();
131 d->mPixmap.loadFromData(data);
132 d->mHasGravatar = true;
133 // For the moment don't use cache other we will store a lot of pixmap
134 if (!d->mUseDefaultPixmap) {
135 GravatarCache::self()->saveGravatarPixmap(d->mCalculatedHash, d->mPixmap);
136 }
137 } else {
139 GravatarCache::self()->saveMissingGravatar(d->mCalculatedHash);
140 } else {
141 qCDebug(GRAVATAR_LOG) << "Network error:" << reply->request().url() << reply->errorString();
142 }
143 }
144 reply->deleteLater();
145
146 processNextBackend();
147}
148
149QString GravatarResolvUrlJob::email() const
150{
151 return d->mEmail;
152}
153
154void GravatarResolvUrlJob::setEmail(const QString &email)
155{
156 d->mEmail = email;
157}
158
159Hash GravatarResolvUrlJob::calculateHash()
160{
161 const auto email = d->mEmail.toLower().toUtf8();
162 return Hash(QCryptographicHash::hash(email, QCryptographicHash::Md5), Hash::Md5);
163}
164
165bool GravatarResolvUrlJob::fallbackGravatar() const
166{
167 return d->mBackends & GravatarResolvUrlJobPrivate::Gravatar;
168}
169
170void GravatarResolvUrlJob::setFallbackGravatar(bool fallbackGravatar)
171{
172 if (fallbackGravatar) {
173 d->mBackends |= GravatarResolvUrlJobPrivate::Gravatar;
174 } else {
175 d->mBackends &= ~GravatarResolvUrlJobPrivate::Gravatar;
176 }
177}
178
179bool GravatarResolvUrlJob::useLibravatar() const
180{
181 return d->mBackends & GravatarResolvUrlJobPrivate::Libravatar;
182}
183
184void GravatarResolvUrlJob::setUseLibravatar(bool useLibravatar)
185{
186 if (useLibravatar) {
187 d->mBackends |= GravatarResolvUrlJobPrivate::Libravatar;
188 } else {
189 d->mBackends &= ~GravatarResolvUrlJobPrivate::Libravatar;
190 }
191}
192
193bool GravatarResolvUrlJob::useDefaultPixmap() const
194{
195 return d->mUseDefaultPixmap;
196}
197
198void GravatarResolvUrlJob::setUseDefaultPixmap(bool useDefaultPixmap)
199{
200 d->mUseDefaultPixmap = useDefaultPixmap;
201}
202
203int GravatarResolvUrlJob::size() const
204{
205 return d->mSize;
206}
207
208QPixmap GravatarResolvUrlJob::pixmap() const
209{
210 return d->mPixmap;
211}
212
213void GravatarResolvUrlJob::setSize(int size)
214{
215 if (size <= 0) {
216 size = 80;
217 } else if (size > 2048) {
218 size = 2048;
219 }
220 d->mSize = size;
221}
222
223Hash GravatarResolvUrlJob::calculatedHash() const
224{
225 return d->mCalculatedHash;
226}
227
228QUrl GravatarResolvUrlJob::createUrl(bool useLibravatar)
229{
230 QUrl url;
231 d->mCalculatedHash = Hash();
232 if (!canStart()) {
233 return url;
234 }
236 if (!d->mUseDefaultPixmap) {
237 // Add ?d=404
238 query.addQueryItem(QStringLiteral("d"), QStringLiteral("404"));
239 }
240 if (d->mSize != 80) {
241 query.addQueryItem(QStringLiteral("s"), QString::number(d->mSize));
242 }
243 url.setScheme(QStringLiteral("https"));
244 if (useLibravatar) {
245 url.setHost(QStringLiteral("seccdn.libravatar.org"));
246 } else {
247 url.setHost(QStringLiteral("secure.gravatar.com"));
248 }
249 d->mCalculatedHash = calculateHash();
250 url.setPath("/avatar/"_L1 + d->mCalculatedHash.hexString());
251 url.setQuery(query);
252 return url;
253}
254
255bool GravatarResolvUrlJob::cacheLookup(const Hash &hash)
256{
257 bool haveStoredPixmap = false;
258 const QPixmap pix = GravatarCache::self()->loadGravatarPixmap(hash, haveStoredPixmap);
259 if (haveStoredPixmap && !pix.isNull()) { // we know a Gravatar for this hash
260 d->mPixmap = pix;
261 d->mHasGravatar = true;
262 Q_EMIT finished(this);
263 deleteLater();
264 return true;
265 }
266 return haveStoredPixmap;
267}
268
269#include "moc_gravatarresolvurljob.cpp"
std::optional< QSqlQuery > query(const QString &queryStatement)
QByteArray hash(QByteArrayView data, Algorithm method)
QString errorString() const const
QByteArray readAll()
void finished(QNetworkReply *reply)
NetworkError error() const const
QNetworkRequest request() const const
QUrl url() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
bool isNull() const const
QString number(double n, char format, int precision)
void setHost(const QString &host, ParsingMode mode)
void setPath(const QString &path, ParsingMode mode)
void setQuery(const QString &query, ParsingMode mode)
void setScheme(const QString &scheme)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:59:32 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.